Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / edit / emacs.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: emacs.c /main/3 1995/11/01 16:35:06 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        <ast.h>
96 #include        <ctype.h>
97 #ifdef KSHELL
98 #   include     "defs.h"
99 #endif  /* KSHELL */
100 #include        "io.h"
101
102 #include        "history.h"
103 #include        "edit.h"
104 #include        "terminal.h"
105
106 #define ESH_NFIRST
107 #define ESH_KAPPEND
108 #define ESH_BETTER
109
110 #undef putchar
111 #define putchar(c)      ed_putchar(c)
112 #define beep()          ed_ringbell()
113
114
115 #ifdef SHOPT_MULTIBYTE
116 #   define gencpy(a,b)  ed_gencpy(a,b)
117 #   define genncpy(a,b,n)       ed_genncpy(a,b,n)
118 #   define genlen(str)  ed_genlen(str)
119 static int      print __PROTO__((int));
120 static int      isword __PROTO__((int));
121
122 #else
123 #   define gencpy(a,b)  strcpy((char*)(a),(char*)(b))
124 #   define genncpy(a,b,n)       strncpy((char*)(a),(char*)(b),n)
125 #   define genlen(str)  strlen(str)
126 #   define print(c)     isprint(c)
127 #   define isword(c)    isalnum(out[c])
128 #endif /*SHOPT_MULTIBYTE */
129
130 #define eol             editb.e_eol
131 #define cur             editb.e_cur
132 #define mark            editb.e_fchar
133 #define hline           editb.e_hline
134 #define hloff           editb.e_hloff
135 #define hismin          editb.e_hismin
136 #define usrkill         editb.e_kill
137 #define usreof          editb.e_eof
138 #define usrerase        editb.e_erase
139 #define crallowed       editb.e_crlf
140 #define Prompt          editb.e_prompt
141 #define plen            editb.e_plen
142 #define kstack          editb.e_killbuf
143 #define lstring         editb.e_search
144 #define lookahead       editb.e_index
145 #define env             editb.e_env
146 #define raw             editb.e_raw
147 #define histlines       editb.e_hismax
148 #define w_size          editb.e_wsize
149 #define drawbuff        editb.e_inbuf
150 #define killing         editb.e_mode
151 #define in_mult         editb.e_saved
152 #define LBUF    100
153 #define KILLCHAR        UKILL
154 #define ERASECHAR       UERASE
155 #define EOFCHAR         UEOF
156 #define DELETE          '\177'
157
158 /**********************
159 A large lookahead helps when the user is inserting
160 characters in the middle of the line.
161 ************************/
162
163
164 static genchar *screen;         /* pointer to window buffer */
165 static genchar *cursor;         /* Cursor in real screen */
166 static enum
167 {
168         CRT=0,  /* Crt terminal */
169         PAPER   /* Paper terminal */
170 } terminal ;
171
172 typedef enum
173 {
174         FIRST,          /* First time thru for logical line, prompt on screen */
175         REFRESH,        /* Redraw entire screen */
176         APPEND,         /* Append char before cursor to screen */
177         UPDATE,         /* Update the screen as need be */
178         FINAL           /* Update screen even if pending look ahead */
179 } Draw_t;
180
181 static void draw __PROTO__((Draw_t));
182 static int escape __PROTO__((genchar*, int));
183 static void putstring __PROTO__((char*));
184 static void search __PROTO__((genchar*,int));
185 static void setcursor __PROTO__((int, int));
186 static void show_info __PROTO__((const char*));
187 static void xcommands __PROTO__((int));
188
189 static int cr_ok;
190 static  Histloc_t location = { -5, 0 };
191
192 ed_emacsread __PARAM__((int fd,char *buff,int scend), (fd, buff, scend)) __OTORP__(int fd;char *buff;int scend;){
193         register int c;
194         register int i;
195         register genchar *out;
196         register int count;
197         int adjust,oadjust;
198         char backslash;
199         genchar *kptr;
200 static int CntrlO;
201         char prompt[PRSIZE];
202         genchar Screen[MAXWINDOW];
203 #if KSHELL && (2*CHARSIZE*MAXLINE)<IOBSIZE
204         kstack = buff + MAXLINE*sizeof(genchar);
205 #else
206         if(kstack==0)
207         {
208                 kstack = (genchar*)malloc(sizeof(genchar)*(MAXLINE));
209                 kstack[0] = '\0';
210         }
211 #endif
212         Prompt = prompt;
213         screen = Screen;
214         drawbuff = out = (genchar*)buff;
215         if(tty_raw(ERRIO,0) < 0)
216         {
217                  return(ed_read(fd,buff,scend));
218         }
219         raw = 1;
220         /* This mess in case the read system call fails */
221         
222         ed_setup(fd);
223 #ifdef ESH_NFIRST
224         if (location.hist_command == -5)                /* to be initialized */
225         {
226                 kstack[0] = '\0';               /* also clear kstack... */
227                 location.hist_command = hline;
228                 location.hist_line = hloff;
229         }
230         if (location.hist_command <= hismin)    /* don't start below minimum */
231         {
232                 location.hist_command = hismin + 1;
233                 location.hist_line = 0;
234         }
235         in_mult = hloff;                        /* save pos in last command */
236 #endif /* ESH_NFIRST */
237         i = sigsetjmp(env,0);
238         if (i !=0)
239         {
240                 tty_cooked(ERRIO);
241                 if (i == UEOF)
242                 {
243                         return(0); /* EOF */
244                 }
245                 return(-1); /* some other error */
246         }
247         *out = 0;
248         if(scend+plen > (MAXLINE-2))
249                 scend = (MAXLINE-2)-plen;
250         mark = eol = cur = 0;
251         draw(FIRST);
252         adjust = -1;
253         backslash = 0;
254         if (CntrlO)
255         {
256 #ifdef ESH_NFIRST
257                 ed_ungetchar(cntl('N'));
258 #else
259                 location = hist_locate(sh.hist_ptr,location.hist_command,location.hist_line,1);
260                 if (location.hist_command < histlines)
261                 {
262                         hline = location.hist_command;
263                         hloff = location.hist_line;
264                         hist_copy((char*)kstack,MAXLINE, hline,hloff);
265 #   ifdef SHOPT_MULTIBYTE
266                         ed_internal((char*)kstack,kstack);
267 #   endif /* SHOPT_MULTIBYTE */
268                         ed_ungetchar(cntl('Y'));
269                 }
270 #endif /* ESH_NFIRST */
271         }
272         CntrlO = 0;
273         while ((c = ed_getchar(0)) != (-1))
274         {
275                 if (backslash)
276                 {
277                         backslash = 0;
278                         if (c==usrerase||c==usrkill||(!print(c) &&
279                                 (c!='\r'&&c!='\n')))
280                         {
281                                 /* accept a backslashed character */
282                                 cur--;
283                                 out[cur++] = c;
284                                 out[eol] = '\0';
285                                 draw(APPEND);
286                                 continue;
287                         }
288                 }
289                 if (c == usrkill)
290                 {
291                         c = KILLCHAR ;
292                 }
293                 else if (c == usrerase)
294                 {
295                         c = ERASECHAR ;
296                 } 
297                 else if ((c == usreof)&&(eol == 0))
298                 {
299                         c = EOFCHAR;
300                 }
301 #ifdef ESH_KAPPEND
302                 if (--killing <= 0)     /* reset killing flag */
303                         killing = 0;
304 #endif
305                 oadjust = count = adjust;
306                 if(count<0)
307                         count = 1;
308                 adjust = -1;
309                 i = cur;
310                 switch(c)
311                 {
312                 case cntl('V'):
313                         show_info(&e_version[5]);
314                         continue;
315                 case '\0':
316                         mark = i;
317                         continue;
318                 case cntl('X'):
319                         xcommands(count);
320                         continue;
321                 case EOFCHAR:
322                         ed_flush();
323                         tty_cooked(ERRIO);
324                         return(0);
325 #ifdef u370
326                 case cntl('S') :
327                 case cntl('Q') :
328                         continue;
329 #endif  /* u370 */
330                 default:
331                         if ((eol+1) >= (scend)) /*  will not fit on line */
332                         {
333                                 ed_ungetchar(c); /* save character for next line */
334                                 goto process;
335                         }
336                         for(i= ++eol; i>cur; i--)
337                                 out[i] = out[i-1];
338                         backslash =  (c == '\\');
339                         out[cur++] = c;
340                         draw(APPEND);
341                         continue;
342                 case cntl('Y') :
343                         {
344                                 c = genlen(kstack);
345                                 if ((c + eol) > scend)
346                                 {
347                                         beep();
348                                         continue;
349                                 }
350                                 mark = i;
351                                 for(i=eol;i>=cur;i--)
352                                         out[c+i] = out[i];
353                                 kptr=kstack;
354                                 while (i = *kptr++)
355                                         out[cur++] = i;
356                                 draw(UPDATE);
357                                 eol = genlen(out);
358                                 continue;
359                         }
360                 case '\n':
361                 case '\r':
362                         c = '\n';
363                         goto process;
364
365                 case DELETE:    /* delete char 0x7f */
366                 case '\b':      /* backspace, ^h */
367                 case ERASECHAR :
368                         if (count > i)
369                                 count = i;
370 #ifdef ESH_KAPPEND
371                         kptr = &kstack[count];  /* move old contents here */
372                         if (killing)            /* prepend to killbuf */
373                         {
374                                 c = genlen(kstack) + CHARSIZE; /* include '\0' */
375                                 while(c--)      /* copy stuff */
376                                         kptr[c] = kstack[c];
377                         }
378                         else
379                                 *kptr = 0;      /* this is end of data */
380                         killing = 2;            /* we are killing */
381                         i -= count;
382                         eol -= count;
383                         genncpy(kstack,out+i,cur-i);
384 #else
385                         while ((count--)&&(i>0))
386                         {
387                                 i--;
388                                 eol--;
389                         }
390                         genncpy(kstack,out+i,cur-i);
391                         kstack[cur-i] = 0;
392 #endif /* ESH_KAPPEND */
393                         gencpy(out+i,out+cur);
394                         mark = i;
395                         goto update;
396                 case cntl('W') :
397 #ifdef ESH_KAPPEND
398                         ++killing;              /* keep killing flag */
399 #endif
400                         if (mark > eol )
401                                 mark = eol;
402                         if (mark == i)
403                                 continue;
404                         if (mark > i)
405                         {
406                                 adjust = mark - i;
407                                 ed_ungetchar(cntl('D'));
408                                 continue;
409                         }
410                         adjust = i - mark;
411                         ed_ungetchar(usrerase);
412                         continue;
413                 case cntl('D') :
414                         mark = i;
415 #ifdef ESH_KAPPEND
416                         if (killing)
417                                 kptr = &kstack[genlen(kstack)]; /* append here */
418                         else
419                                 kptr = kstack;
420                         killing = 2;                    /* we are now killing */
421 #else
422                         kptr = kstack;
423 #endif /* ESH_KAPPEND */
424                         while ((count--)&&(eol>0)&&(i<eol))
425                         {
426                                 *kptr++ = out[i];
427                                 eol--;
428                                 while(1)
429                                 {
430                                         if ((out[i] = out[(i+1)])==0)
431                                                 break;
432                                         i++;
433                                 }
434                                 i = cur;
435                         }
436                         *kptr = '\0';
437                         goto update;
438                 case cntl('C') :
439                 case cntl('F') :
440                 {
441                         int cntlC = (c==cntl('C'));
442                         while (count-- && eol>i)
443                         {
444                                 if (cntlC)
445                                 {
446                                         c = out[i];
447 #ifdef SHOPT_MULTIBYTE
448                                         if((c&~STRIP)==0 && islower(c))
449 #else
450                                         if(islower(c))
451 #endif /* SHOPT_MULTIBYTE */
452                                         {
453                                                 c += 'A' - 'a';
454                                                 out[i] = c;
455                                         }
456                                 }
457                                 i++;
458                         }
459                         goto update;
460                 }
461                 case cntl(']') :
462                         c = ed_getchar(1);
463                         if ((count == 0) || (count > eol))
464                         {
465                                 beep();
466                                 continue;
467                         }
468                         if (out[i])
469                                 i++;
470                         while (i < eol)
471                         {
472                                 if (out[i] == c && --count==0)
473                                         goto update;
474                                 i++;
475                         }
476                         i = 0;
477                         while (i < cur)
478                         {
479                                 if (out[i] == c && --count==0)
480                                         break;
481                                 i++;
482                         };
483
484 update:
485                         cur = i;
486                         draw(UPDATE);
487                         continue;
488
489                 case cntl('B') :
490                         if (count > i)
491                                 count = i;
492                         i -= count;
493                         goto update;
494                 case cntl('T') :
495                         if ((sh_isoption(SH_EMACS))&& (eol!=i))
496                                 i++;
497                         if (i >= 2)
498                         {
499                                 c = out[i - 1];
500                                 out[i-1] = out[i-2];
501                                 out[i-2] = c;
502                         }
503                         else
504                         {
505                                 if(sh_isoption(SH_EMACS))
506                                         i--;
507                                 beep();
508                                 continue;
509                         }
510                         goto update;
511                 case cntl('A') :
512                         i = 0;
513                         goto update;
514                 case cntl('E') :
515                         i = eol;
516                         goto update;
517                 case cntl('U') :
518                         adjust = 4*count;
519                         continue;
520                 case KILLCHAR :
521                         cur = 0;
522                         oadjust = -1;
523                 case cntl('K') :
524                         if(oadjust >= 0)
525                         {
526 #ifdef ESH_KAPPEND
527                                 killing = 2;            /* set killing signal */
528 #endif
529                                 mark = count;
530                                 ed_ungetchar(cntl('W'));
531                                 continue;
532                         }
533                         i = cur;
534                         eol = i;
535                         mark = i;
536 #ifdef ESH_KAPPEND
537                         if (killing)                    /* append to kill buffer */
538                                 gencpy(&kstack[genlen(kstack)], &out[i]);
539                         else
540                                 gencpy(kstack,&out[i]);
541                         killing = 2;                    /* set killing signal */
542 #else
543                         gencpy(kstack,&out[i]);
544 #endif /* ESH_KAPPEND */
545                         out[i] = 0;
546                         draw(UPDATE);
547                         if (c == KILLCHAR)
548                         {
549                                 if (terminal == PAPER)
550                                 {
551                                         putchar('\n');
552                                         putstring(Prompt);
553                                 }
554                                 c = ed_getchar(0);
555                                 if (c != usrkill)
556                                 {
557                                         ed_ungetchar(c);
558                                         continue;
559                                 }
560                                 if (terminal == PAPER)
561                                         terminal = CRT;
562                                 else
563                                 {
564                                         terminal = PAPER;
565                                         putchar('\n');
566                                         putstring(Prompt);
567                                 }
568                         }
569                         continue;
570                 case cntl('L'):
571                         ed_crlf();
572                         draw(REFRESH);
573                         continue;
574                 case cntl('[') :
575                         adjust = escape(out,oadjust);
576                         continue;
577                 case cntl('R') :
578                         search(out,count);
579                         goto drawline;
580                 case cntl('P') :
581                         if (count <= hloff)
582                                 hloff -= count;
583                         else
584                         {
585                                 hline -= count - hloff;
586                                 hloff = 0;
587                         }
588 #ifdef ESH_NFIRST
589                         if (hline <= hismin)
590 #else
591                         if (hline < hismin)
592 #endif /* ESH_NFIRST */
593                         {
594                                 hline = hismin+1;
595                                 beep();
596 #ifndef ESH_NFIRST
597                                 continue;
598 #endif
599                         }
600                         goto common;
601
602                 case cntl('O') :
603                         location.hist_command = hline;
604                         location.hist_line = hloff;
605                         CntrlO = 1;
606                         c = '\n';
607                         goto process;
608                 case cntl('N') :
609 #ifdef ESH_NFIRST
610                         hline = location.hist_command;  /* start at saved position */
611                         hloff = location.hist_line;
612 #endif /* ESH_NFIRST */
613                         location = hist_locate(sh.hist_ptr,hline,hloff,count);
614                         if (location.hist_command > histlines)
615                         {
616                                 beep();
617 #ifdef ESH_NFIRST
618                                 location.hist_command = histlines;
619                                 location.hist_line = in_mult;
620 #else
621                                 continue;
622 #endif /* ESH_NFIRST */
623                         }
624                         hline = location.hist_command;
625                         hloff = location.hist_line;
626                 common:
627 #ifdef ESH_NFIRST
628                         location.hist_command = hline;  /* save current position */
629                         location.hist_line = hloff;
630 #endif
631                         hist_copy((char*)out,MAXLINE, hline,hloff);
632 #ifdef SHOPT_MULTIBYTE
633                         ed_internal((char*)(out),out);
634 #endif /* SHOPT_MULTIBYTE */
635                 drawline:
636                         eol = genlen(out);
637                         cur = eol;
638                         draw(UPDATE);
639                         continue;
640                 }
641                 
642         }
643         
644 process:
645
646         if (c == (-1))
647         {
648                 lookahead = 0;
649                 beep();
650                 *out = '\0';
651         }
652         draw(FINAL);
653         tty_cooked(ERRIO);
654         if(c == '\n')
655         {
656                 out[eol++] = '\n';
657                 out[eol] = '\0';
658                 ed_crlf();
659         }
660 #ifdef SHOPT_MULTIBYTE
661         ed_external(out,buff);
662 #endif /* SHOPT_MULTIBYTE */
663         i = strlen(buff);
664         if (i)
665                 return(i);
666         return(-1);
667 }
668
669 static void show_info __PARAM__((const char *str), (str)) __OTORP__(const char *str;){
670         register genchar *out = drawbuff;
671         register int c;
672         genchar string[LBUF];
673         int sav_cur = cur;
674         /* save current line */
675         genncpy(string,out,sizeof(string)/CHARSIZE-1);
676         *out = 0;
677         cur = 0;
678 #ifdef SHOPT_MULTIBYTE
679         ed_internal(str,out);
680 #else
681         gencpy(out,str);
682 #endif  /* SHOPT_MULTIBYTE */
683         draw(UPDATE);
684         c = ed_getchar(0);
685         if(c!=' ')
686                 ed_ungetchar(c);
687         /* restore line */
688         cur = sav_cur;
689         genncpy(out,string,sizeof(string)/CHARSIZE-1);
690         draw(UPDATE);
691 }
692
693 static void putstring __PARAM__((register char *sp), (sp)) __OTORP__(register char *sp;){
694         register int c;
695         while (c= *sp++)
696                  putchar(c);
697 }
698
699
700 static int escape __PARAM__((register genchar *out,int count), (out, count)) __OTORP__(register genchar *out;int count;){
701         register int i,value;
702         int digit,ch;
703         digit = 0;
704         value = 0;
705         while ((i=ed_getchar(0)),isdigit(i))
706         {
707                 value *= 10;
708                 value += (i - '0');
709                 digit = 1;
710         }
711         if (digit)
712         {
713                 ed_ungetchar(i) ;
714 #ifdef ESH_KAPPEND
715                 ++killing;              /* don't modify killing signal */
716 #endif
717                 return(value);
718         }
719         value = count;
720         if(value<0)
721                 value = 1;
722         switch(ch=i)
723         {
724                 case ' ':
725                         mark = cur;
726                         return(-1);
727
728 #ifdef ESH_KAPPEND
729                 case '+':               /* M-+ = append next kill */
730                         killing = 2;
731                         return -1;      /* no argument for next command */
732 #endif
733
734                 case 'p':       /* M-p == ^W^Y (copy stack == kill & yank) */
735                         ed_ungetchar(cntl('Y'));
736                         ed_ungetchar(cntl('W'));
737 #ifdef ESH_KAPPEND
738                         killing = 0;    /* start fresh */
739 #endif
740                         return(-1);
741
742                 case 'l':       /* M-l == lower-case */
743                 case 'd':
744                 case 'c':
745                 case 'f':
746                 {
747                         i = cur;
748                         while(value-- && i<eol)
749                         {
750                                 while ((out[i])&&(!isword(i)))
751                                         i++;
752                                 while ((out[i])&&(isword(i)))
753                                         i++;
754                         }
755                         if(ch=='l')
756                         {
757                                 value = i-cur;
758                                 while (value-- > 0)
759                                 {
760                                         i = out[cur];
761 #ifdef SHOPT_MULTIBYTE
762                                         if((i&~STRIP)==0 && isupper(i))
763 #else
764                                         if(isupper(i))
765 #endif /* SHOPT_MULTIBYTE */
766                                         {
767                                                 i += 'a' - 'A';
768                                                 out[cur] = i;
769                                         }
770                                         cur++;
771                                 }
772                                 draw(UPDATE);
773                                 return(-1);
774                         }
775
776                         else if(ch=='f')
777                                 goto update;
778                         else if(ch=='c')
779                         {
780                                 ed_ungetchar(cntl('C'));
781                                 return(i-cur);
782                         }
783                         else
784                         {
785                                 if (i-cur)
786                                 {
787                                         ed_ungetchar(cntl('D'));
788 #ifdef ESH_KAPPEND
789                                         ++killing;      /* keep killing signal */
790 #endif
791                                         return(i-cur);
792                                 }
793                                 beep();
794                                 return(-1);
795                         }
796                 }
797                 
798                 
799                 case 'b':
800                 case DELETE :
801                 case '\b':
802                 case 'h':
803                 {
804                         i = cur;
805                         while(value-- && i>0)
806                         {
807                                 i--;
808                                 while ((i>0)&&(!isword(i)))
809                                         i--;
810                                 while ((i>0)&&(isword(i-1)))
811                                         i--;
812                         }
813                         if(ch=='b')
814                                 goto update;
815                         else
816                         {
817                                 ed_ungetchar(usrerase);
818 #ifdef ESH_KAPPEND
819                                 ++killing;
820 #endif
821                                 return(cur-i);
822                         }
823                 }
824                 
825                 case '>':
826                         ed_ungetchar(cntl('N'));
827 #ifdef ESH_NFIRST
828                         if (in_mult)
829                         {
830                                 location.hist_command = histlines;
831                                 location.hist_line = in_mult - 1;
832                         }
833                         else
834                         {
835                                 location.hist_command = histlines - 1;
836                                 location.hist_line = 0;
837                         }
838 #else
839                         hline = histlines-1;
840                         hloff = 0;
841 #endif /* ESH_NFIRST */
842                         return(0);
843                 
844                 case '<':
845                         ed_ungetchar(cntl('P'));
846                         hloff = 0;
847 #ifdef ESH_NFIRST
848                         hline = hismin + 1;
849                         return 0;
850 #else
851                         return(hline-hismin);
852 #endif /* ESH_NFIRST */
853
854
855                 case '#':
856                         ed_ungetchar('\n');
857                         ed_ungetchar((out[0]=='#')?cntl('D'):'#');
858                         ed_ungetchar(cntl('A'));
859                         return(-1);
860                 case '_' :
861                 case '.' :
862                 {
863                         genchar name[MAXLINE];
864                         char buf[MAXLINE];
865                         char *ptr;
866                         ptr = hist_word(buf,MAXLINE,(count?count:-1));
867 #ifndef KSHELL
868                         if(ptr==0)
869                         {
870                                 beep();
871                                 break;
872                         }
873 #endif  /* KSHELL */
874                         if ((eol - cur) >= sizeof(name))
875                         {
876                                 beep();
877                                 return(-1);
878                         }
879                         mark = cur;
880                         gencpy(name,&out[cur]);
881                         while(*ptr)
882                         {
883                                 out[cur++] = *ptr++;
884                                 eol++;
885                         }
886                         gencpy(&out[cur],name);
887                         draw(UPDATE);
888                         return(-1);
889                 }
890 #ifdef KSHELL
891
892                 /* file name expansion */
893                 case cntl('[') :        /* filename completion */
894                         i = '\\';
895                 case '*':               /* filename expansion */
896                 case '=':       /* escape = - list all matching file names */
897                         mark = cur;
898                         if(ed_expand((char*)out,&cur,&eol,i) < 0)
899                                 beep();
900                         else if(i=='=')
901                                 draw(REFRESH);
902                         else
903                                 draw(UPDATE);
904                         return(-1);
905
906                 /* search back for character */
907                 case cntl(']'): /* feature not in book */
908                 {
909                         int c = ed_getchar(1);
910                         if ((value == 0) || (value > eol))
911                         {
912                                 beep();
913                                 return(-1);
914                         }
915                         i = cur;
916                         if (i > 0)
917                                 i--;
918                         while (i >= 0)
919                         {
920                                 if (out[i] == c && --value==0)
921                                         goto update;
922                                 i--;
923                         }
924                         i = eol;
925                         while (i > cur)
926                         {
927                                 if (out[i] == c && --value==0)
928                                         break;
929                                 i--;
930                         };
931
932                 }
933                 update:
934                         cur = i;
935                         draw(UPDATE);
936                         return(-1);
937
938                 case '[':       /* feature not in book */
939                         i = '_';
940
941                 default:
942                         /* look for user defined macro definitions */
943                         if(ed_macro(i))
944 #   ifdef ESH_BETTER
945                                 return(count);  /* pass argument to macro */
946 #   else
947                                 return(-1);
948 #   endif /* ESH_BETTER */
949 #else
950                 update:
951                         cur = i;
952                         draw(UPDATE);
953                         return(-1);
954
955                 default:
956 #endif  /* KSHELL */
957                 beep();
958                 return(-1);
959         }
960 }
961
962
963 /*
964  * This routine process all commands starting with ^X
965  */
966
967 static void xcommands __PARAM__((int count), (count)) __OTORP__(int count;){
968         register int i = ed_getchar(0);
969         NOT_USED(count);
970         switch(i)
971         {
972                 case cntl('X'): /* exchange dot and mark */
973                         if (mark > eol)
974                                 mark = eol;
975                         i = mark;
976                         mark = cur;
977                         cur = i;
978                         draw(UPDATE);
979                         return;
980
981 #ifdef KSHELL
982 #   ifdef ESH_BETTER
983                 case cntl('E'): /* invoke emacs on current command */
984                         if(ed_fulledit()==-1)
985                                 beep();
986                         else
987                                 ed_ungetchar('\n');
988                         return;
989
990 #       define itos(i)  fmtbase((long)(i),0,0)/* want signed conversion */
991
992                 case cntl('H'):         /* ^X^H show history info */
993                         {
994                                 char hbuf[MAXLINE];
995
996                                 strcpy(hbuf, "Current command ");
997                                 strcat(hbuf, itos(hline));
998                                 if (hloff)
999                                 {
1000                                         strcat(hbuf, " (line ");
1001                                         strcat(hbuf, itos(hloff+1));
1002                                         strcat(hbuf, ")");
1003                                 }
1004                                 if ((hline != location.hist_command) ||
1005                                     (hloff != location.hist_line))
1006                                 {
1007                                         strcat(hbuf, "; Previous command ");
1008                                         strcat(hbuf, itos(location.hist_command));
1009                                         if (location.hist_line)
1010                                         {
1011                                                 strcat(hbuf, " (line ");
1012                                                 strcat(hbuf, itos(location.hist_line+1));
1013                                                 strcat(hbuf, ")");
1014                                         }
1015                                 }
1016                                 show_info(hbuf);
1017                                 return;
1018                         }
1019 #       if 0    /* debugging, modify as required */
1020                 case cntl('D'):         /* ^X^D show debugging info */
1021                         {
1022                                 char debugbuf[MAXLINE];
1023
1024                                 strcpy(debugbuf, "count=");
1025                                 strcat(debugbuf, itos(count));
1026                                 strcat(debugbuf, " eol=");
1027                                 strcat(debugbuf, itos(eol));
1028                                 strcat(debugbuf, " cur=");
1029                                 strcat(debugbuf, itos(cur));
1030                                 strcat(debugbuf, " crallowed=");
1031                                 strcat(debugbuf, itos(crallowed));
1032                                 strcat(debugbuf, " plen=");
1033                                 strcat(debugbuf, itos(plen));
1034                                 strcat(debugbuf, " w_size=");
1035                                 strcat(debugbuf, itos(w_size));
1036
1037                                 show_info(debugbuf);
1038                                 return;
1039                         }
1040 #       endif /* debugging code */
1041 #   endif /* ESH_BETTER */
1042 #endif /* KSHELL */
1043
1044                 default:
1045                         beep();
1046                         return;
1047         }
1048 }
1049
1050 static void search __PARAM__((genchar *out,int direction), (out, direction)) __OTORP__(genchar *out;int direction;){
1051         static int prevdirection =  1 ;
1052 #ifndef ESH_NFIRST
1053         Histloc_t location;
1054 #endif
1055         register int i,sl;
1056         genchar str_buff[LBUF];
1057         register genchar *string = drawbuff;
1058         /* save current line */
1059         char sav_cur = cur;
1060         genncpy(str_buff,string,sizeof(str_buff)/CHARSIZE-1);
1061         string[0] = '^';
1062         string[1] = 'R';
1063         string[2] = '\0';
1064         sl = 2;
1065         cur = sl;
1066         draw(UPDATE);
1067         while ((i = ed_getchar(1))&&(i != '\r')&&(i != '\n'))
1068         {
1069                 if (i==usrerase)
1070                 {
1071                         if (sl > 2)
1072                         {
1073                                 string[--sl] = '\0';
1074                                 cur = sl;
1075                                 draw(UPDATE);
1076                         }
1077                         else
1078                                 beep();
1079                         continue;
1080                 }
1081                 if (i==usrkill)
1082                 {
1083                         beep();
1084                         goto restore;
1085                 }
1086                 if (i == '\\')
1087                 {
1088                         string[sl++] = '\\';
1089                         string[sl] = '\0';
1090                         cur = sl;
1091                         draw(APPEND);
1092                         i = ed_getchar(1);
1093                         string[--sl] = '\0';
1094                 }
1095                 string[sl++] = i;
1096                 string[sl] = '\0';
1097                 cur = sl;
1098                 draw(APPEND);
1099         }
1100         i = genlen(string);
1101         
1102         if (direction < 1)
1103         {
1104                 prevdirection = -prevdirection;
1105                 direction = 1;
1106         }
1107         else
1108                 direction = -1;
1109         if (i != 2)
1110         {
1111 #ifdef SHOPT_MULTIBYTE
1112                 ed_external(string,(char*)string);
1113 #endif /* SHOPT_MULTIBYTE */
1114                 strncpy(lstring,((char*)string)+2,SEARCHSIZE);
1115                 prevdirection = direction;
1116         }
1117         else
1118                 direction = prevdirection ;
1119         location = hist_find(sh.hist_ptr,(char*)lstring,hline,1,direction);
1120         i = location.hist_command;
1121         if(i>0)
1122         {
1123                 hline = i;
1124 #ifdef ESH_NFIRST
1125                 hloff = location.hist_line = 0; /* display first line of multi line command */
1126 #else
1127                 hloff = location.hist_line;
1128 #endif /* ESH_NFIRST */
1129                 hist_copy((char*)out,MAXLINE, hline,hloff);
1130 #ifdef SHOPT_MULTIBYTE
1131                 ed_internal((char*)out,out);
1132 #endif /* SHOPT_MULTIBYTE */
1133                 return;
1134         }
1135         if (i < 0)
1136         {
1137                 beep();
1138 #ifdef ESH_NFIRST
1139                 location.hist_command = hline;
1140                 location.hist_line = hloff;
1141 #else
1142                 hloff = 0;
1143                 hline = histlines;
1144 #endif /* ESH_NFIRST */
1145         }
1146 restore:
1147         genncpy(string,str_buff,sizeof(str_buff)/CHARSIZE-1);
1148         cur = sav_cur;
1149         return;
1150 }
1151
1152
1153 /* Adjust screen to agree with inputs: logical line and cursor */
1154 /* If 'first' assume screen is blank */
1155 /* Prompt is always kept on the screen */
1156
1157 static void draw __PARAM__((Draw_t option), (option)) __OTORP__(Draw_t option;){
1158 #define NORMAL ' '
1159 #define LOWER  '<'
1160 #define BOTH   '*'
1161 #define UPPER  '>'
1162
1163         static char overflow;           /* Screen overflow flag set */
1164         register genchar *sptr;         /* Pointer within screen */
1165         
1166         static int offset;              /* Screen offset */
1167         static char scvalid;            /* Screen is up to date */
1168         
1169         genchar nscreen[2*MAXLINE];     /* New entire screen */
1170         genchar *ncursor;               /* New cursor */
1171         register genchar *nptr;         /* Pointer to New screen */
1172         char  longline;                 /* Line overflow */
1173         genchar *logcursor;
1174         genchar *nscend;                /* end of logical screen */
1175         register int i;
1176         
1177         nptr = nscreen;
1178         sptr = drawbuff;
1179         logcursor = sptr + cur;
1180         longline = NORMAL;
1181         
1182         if (option == FIRST || option == REFRESH)
1183         {
1184                 overflow = NORMAL;
1185                 cursor = screen;
1186                 offset = 0;
1187                 cr_ok = crallowed;
1188                 if (option == FIRST)
1189                 {
1190                         scvalid = 1;
1191                         return;
1192                 }
1193                 *cursor = '\0';
1194                 putstring(Prompt);      /* start with prompt */
1195         }
1196         
1197         /*********************
1198          Do not update screen if pending characters
1199         **********************/
1200         
1201         if ((lookahead)&&(option != FINAL))
1202         {
1203                 
1204                 scvalid = 0; /* Screen is out of date, APPEND will not work */
1205                 
1206                 return;
1207         }
1208         
1209         /***************************************
1210         If in append mode, cursor at end of line, screen up to date,
1211         the previous character was a 'normal' character,
1212         and the window has room for another character.
1213         Then output the character and adjust the screen only.
1214         *****************************************/
1215         
1216
1217         i = *(logcursor-1);     /* last character inserted */
1218         
1219         if ((option == APPEND)&&(scvalid)&&(*logcursor == '\0')&&
1220             print(i)&&((cursor-screen)<(w_size-1)))
1221         {
1222                 putchar(i);
1223                 *cursor++ = i;
1224                 *cursor = '\0';
1225                 return;
1226         }
1227
1228         /* copy the line */
1229         ncursor = nptr + ed_virt_to_phys(sptr,nptr,cur,0,0);
1230         nptr += genlen(nptr);
1231         sptr += genlen(sptr);
1232         nscend = nptr - 1;
1233         if(sptr == logcursor)
1234                 ncursor = nptr;
1235         
1236         /*********************
1237          Does ncursor appear on the screen?
1238          If not, adjust the screen offset so it does.
1239         **********************/
1240         
1241         i = ncursor - nscreen;
1242         
1243         if ((offset && i<=offset)||(i >= (offset+w_size)))
1244         {
1245                 /* Center the cursor on the screen */
1246                 offset = i - (w_size>>1);
1247                 if (--offset < 0)
1248                         offset = 0;
1249         }
1250                         
1251         /*********************
1252          Is the range of screen[0] thru screen[w_size] up-to-date
1253          with nscreen[offset] thru nscreen[offset+w_size] ?
1254          If not, update as need be.
1255         ***********************/
1256         
1257         nptr = &nscreen[offset];
1258         sptr = screen;
1259         
1260         i = w_size;
1261         
1262         while (i-- > 0)
1263         {
1264                 
1265                 if (*nptr == '\0')
1266                 {
1267                         *(nptr + 1) = '\0';
1268                         *nptr = ' ';
1269                 }
1270                 if (*sptr == '\0')
1271                 {
1272                         *(sptr + 1) = '\0';
1273                         *sptr = ' ';
1274                 }
1275                 if (*nptr == *sptr)
1276                 {
1277                         nptr++;
1278                         sptr++;
1279                         continue;
1280                 }
1281                 setcursor(sptr-screen,*nptr);
1282                 *sptr++ = *nptr++;
1283 #ifdef SHOPT_MULTIBYTE
1284                 while(*nptr==MARKER)
1285                 {
1286                         if(*sptr=='\0')
1287                                 *(sptr + 1) = '\0';
1288                         *sptr++ = *nptr++;
1289                         i--;
1290                         cursor++;
1291                 }
1292 #endif /* SHOPT_MULTIBYTE */
1293         }
1294         
1295         /******************
1296         
1297         Screen overflow checks 
1298         
1299         ********************/
1300         
1301         if (nscend >= &nscreen[offset+w_size])
1302         {
1303                 if (offset > 0)
1304                         longline = BOTH;
1305                 else
1306                         longline = UPPER;
1307         }
1308         else
1309         {
1310                 if (offset > 0)
1311                         longline = LOWER;
1312         }
1313         
1314         /* Update screen overflow indicator if need be */
1315         
1316         if (longline != overflow)
1317         {
1318                 setcursor(w_size,longline);
1319                 overflow = longline;
1320         }
1321         i = (ncursor-nscreen) - offset;
1322         setcursor(i,0);
1323         scvalid = 1;
1324         return;
1325 }
1326
1327 /*
1328  * put the cursor to the <newp> position within screen buffer
1329  * if <c> is non-zero then output this character
1330  * cursor is set to reflect the change
1331  */
1332
1333 static void setcursor __PARAM__((register int newp,int c), (newp, c)) __OTORP__(register int newp;int c;){
1334         register int oldp = cursor - screen;
1335         if (oldp > newp)
1336         {
1337                 if (!cr_ok || (2*(newp+plen)>(oldp+plen)))
1338                 {
1339                         while (oldp > newp)
1340                         {
1341                                 putchar('\b');
1342                                 oldp--;
1343                         }
1344                         goto skip;
1345                 }
1346                 putstring(Prompt);
1347                 oldp = 0;
1348         }
1349         while (newp > oldp)
1350                 putchar(screen[oldp++]);
1351 skip:
1352         if(c)
1353         {
1354                 putchar(c);
1355                 newp++;
1356         }
1357         cursor = screen+newp;
1358         return;
1359 }
1360
1361 #ifdef SHOPT_MULTIBYTE
1362 static int print __PARAM__((register int c), (c)) __OTORP__(register int c;){
1363         return((c&~STRIP)==0 && isprint(c));
1364 }
1365
1366 static int isword __PARAM__((register int i), (i)) __OTORP__(register int i;){
1367         register int c = drawbuff[i];
1368         return((c&~STRIP) || isalnum(c));
1369 }
1370 #endif /* SHOPT_MULTIBYTE */