1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /* Original version by Michael T. Veach
22 * Adapted for ksh by David Korn */
23 /* EMACS_MODES: c tabstop=4
24
25 One line screen editor for any program
26
27 */
28
29
30 /* The following is provided by:
31 *
32 * Matthijs N. Melchior
33 * AT&T Network Systems International
34 * APT Nederland
35 * HV BZ335 x2962
36 * hvlpb!mmelchio
37 *
38 * These are now on by default
39 *
40 * ESH_NFIRST
41 * - A ^N as first history related command after the prompt will move
42 * to the next command relative to the last known history position.
43 * It will not start at the position where the last command was entered
44 * as is done by the ^P command. Every history related command will
45 * set both the current and last position. Executing a command will
46 * only set the current position.
47 *
48 * ESH_KAPPEND
49 * - Successive kill and delete commands will accumulate their data
50 * in the kill buffer, by appending or prepending as appropriate.
51 * This mode will be reset by any command not adding something to the
52 * kill buffer.
53 *
54 * ESH_BETTER
55 * - Some enhancements:
56 * - argument for a macro is passed to its replacement
57 * - ^X^H command to find out about history position (debugging)
58 * - ^X^D command to show any debugging info
59 *
60 * I do not pretend these for changes are completely independent,
61 * but you can use them to seperate features.
62 */
63
64 #include <ast.h>
65 #include "FEATURE/cmds"
66 #if KSHELL
67 # include "defs.h"
68 #else
69 # include <ctype.h>
70 #endif /* KSHELL */
71 #include "io.h"
72
73 #include "history.h"
74 #include "edit.h"
75 #include "terminal.h"
76
77 #define ESH_NFIRST
78 #define ESH_KAPPEND
79 #define ESH_BETTER
80
81 #undef putchar
82 #define putchar(ed,c) ed_putchar(ed,c)
83 #define beep() ed_ringbell()
84
85
86 #if SHOPT_MULTIBYTE
87 # define gencpy(a,b) ed_gencpy(a,b)
88 # define genncpy(a,b,n) ed_genncpy(a,b,n)
89 # define genlen(str) ed_genlen(str)
90 static int print(int);
91 static int _isword(int);
92 # define isword(c) _isword(out[c])
93 # define digit(c) ((c&~STRIP)==0 && isdigit(c))
94
95 #else
96 # define gencpy(a,b) strcpy((char*)(a),(char*)(b))
97 # define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
98 # define genlen(str) strlen(str)
99 # define print(c) isprint(c)
100 # define isword(c) (isalnum(out[c]) || (out[c]=='_'))
101 # define digit(c) isdigit(c)
102 #endif /*SHOPT_MULTIBYTE */
103
104 typedef struct _emacs_
105 {
106 genchar *screen; /* pointer to window buffer */
107 genchar *cursor; /* Cursor in real screen */
108 int mark;
109 int in_mult;
110 char cr_ok;
111 char CntrlO;
112 char overflow; /* Screen overflow flag set */
113 char scvalid; /* Screen is up to date */
114 char lastdraw; /* last update type */
115 int offset; /* Screen offset */
116 enum
117 {
118 CRT=0, /* Crt terminal */
119 PAPER /* Paper terminal */
120 } terminal;
121 Histloc_t _location;
122 int prevdirection;
123 Edit_t *ed; /* pointer to edit data */
124 } Emacs_t;
125
126 #define editb (*ep->ed)
127 #define eol editb.e_eol
128 #define cur editb.e_cur
129 #define hline editb.e_hline
130 #define hloff editb.e_hloff
131 #define hismin editb.e_hismin
132 #define usrkill editb.e_kill
133 #define usrlnext editb.e_lnext
134 #define usreof editb.e_eof
135 #define usrerase editb.e_erase
136 #define crallowed editb.e_crlf
137 #define Prompt editb.e_prompt
138 #define plen editb.e_plen
139 #define kstack editb.e_killbuf
140 #define lstring editb.e_search
141 #define lookahead editb.e_lookahead
142 #define env editb.e_env
143 #define raw editb.e_raw
144 #define histlines editb.e_hismax
145 #define w_size editb.e_wsize
146 #define drawbuff editb.e_inbuf
147 #define killing editb.e_mode
148 #define location ep->_location
149
150 #define LBUF 100
151 #define KILLCHAR UKILL
152 #define ERASECHAR UERASE
153 #define EOFCHAR UEOF
154 #define LNEXTCHAR ULNEXT
155 #define DELETE ('a'==97?0177:7)
156
157 /**********************
158 A large lookahead helps when the user is inserting
159 characters in the middle of the line.
160 ************************/
161
162
163 typedef enum
164 {
165 FIRST, /* First time thru for logical line, prompt on screen */
166 REFRESH, /* Redraw entire screen */
167 APPEND, /* Append char before cursor to screen */
168 UPDATE, /* Update the screen as need be */
169 FINAL /* Update screen even if pending look ahead */
170 } Draw_t;
171
172 static void draw(Emacs_t*,Draw_t);
173 static int escape(Emacs_t*,genchar*, int);
174 static void putstring(Emacs_t*,char*);
175 static void search(Emacs_t*,genchar*,int);
176 static void setcursor(Emacs_t*,int, int);
177 static void show_info(Emacs_t*,const char*);
178 static void xcommands(Emacs_t*,int);
179
ed_emacsread(void * context,int fd,char * buff,int scend,int reedit)180 int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
181 {
182 Edit_t *ed = (Edit_t*)context;
183 register int c;
184 register int i;
185 register genchar *out;
186 register int count;
187 register Emacs_t *ep = ed->e_emacs;
188 int adjust,oadjust;
189 char backslash;
190 genchar *kptr;
191 char prompt[PRSIZE];
192 genchar Screen[MAXLINE];
193 memset(Screen,0,sizeof(Screen));
194 if(!ep)
195 {
196 ep = ed->e_emacs = newof(0,Emacs_t,1,0);
197 ep->ed = ed;
198 ep->prevdirection = 1;
199 location.hist_command = -5;
200 }
201 Prompt = prompt;
202 ep->screen = Screen;
203 ep->lastdraw = FINAL;
204 if(tty_raw(ERRIO,0) < 0)
205 {
206 return(reedit?reedit:ed_read(context, fd,buff,scend,0));
207 }
208 raw = 1;
209 /* This mess in case the read system call fails */
210
211 ed_setup(ep->ed,fd,reedit);
212 out = (genchar*)buff;
213 #if SHOPT_MULTIBYTE
214 out = (genchar*)roundof(buff-(char*)0,sizeof(genchar));
215 if(reedit)
216 ed_internal(buff,out);
217 #endif /* SHOPT_MULTIBYTE */
218 if(!kstack)
219 {
220 kstack = (genchar*)malloc(CHARSIZE*MAXLINE);
221 kstack[0] = '\0';
222 }
223 drawbuff = out;
224 #ifdef ESH_NFIRST
225 if (location.hist_command == -5) /* to be initialized */
226 {
227 kstack[0] = '\0'; /* also clear kstack... */
228 location.hist_command = hline;
229 location.hist_line = hloff;
230 }
231 if (location.hist_command <= hismin) /* don't start below minimum */
232 {
233 location.hist_command = hismin + 1;
234 location.hist_line = 0;
235 }
236 ep->in_mult = hloff; /* save pos in last command */
237 #endif /* ESH_NFIRST */
238 i = sigsetjmp(env,0);
239 if (i !=0)
240 {
241 if(ep->ed->e_multiline)
242 {
243 cur = eol;
244 draw(ep,FINAL);
245 ed_flush(ep->ed);
246 }
247 tty_cooked(ERRIO);
248 if (i == UEOF)
249 {
250 return(0); /* EOF */
251 }
252 return(-1); /* some other error */
253 }
254 out[reedit] = 0;
255 if(scend+plen > (MAXLINE-2))
256 scend = (MAXLINE-2)-plen;
257 ep->mark = 0;
258 cur = eol;
259 draw(ep,reedit?REFRESH:FIRST);
260 adjust = -1;
261 backslash = 0;
262 if (ep->CntrlO)
263 {
264 #ifdef ESH_NFIRST
265 ed_ungetchar(ep->ed,cntl('N'));
266 #else
267 location = hist_locate(shgd->hist_ptr,location.hist_command,location.hist_line,1);
268 if (location.hist_command < histlines)
269 {
270 hline = location.hist_command;
271 hloff = location.hist_line;
272 hist_copy((char*)kstack,MAXLINE, hline,hloff);
273 # if SHOPT_MULTIBYTE
274 ed_internal((char*)kstack,kstack);
275 # endif /* SHOPT_MULTIBYTE */
276 ed_ungetchar(ep->ed,cntl('Y'));
277 }
278 #endif /* ESH_NFIRST */
279 }
280 ep->CntrlO = 0;
281 while ((c = ed_getchar(ep->ed,0)) != (-1))
282 {
283 if (backslash)
284 {
285 backslash = 0;
286 if (c==usrerase||c==usrkill||(!print(c) &&
287 (c!='\r'&&c!='\n')))
288 {
289 /* accept a backslashed character */
290 cur--;
291 out[cur++] = c;
292 out[eol] = '\0';
293 draw(ep,APPEND);
294 continue;
295 }
296 }
297 if (c == usrkill)
298 {
299 c = KILLCHAR ;
300 }
301 else if (c == usrerase)
302 {
303 c = ERASECHAR ;
304 }
305 else if (c == usrlnext)
306 {
307 c = LNEXTCHAR ;
308 }
309 else if ((c == usreof)&&(eol == 0))
310 {
311 c = EOFCHAR;
312 }
313 #ifdef ESH_KAPPEND
314 if (--killing <= 0) /* reset killing flag */
315 killing = 0;
316 #endif
317 oadjust = count = adjust;
318 if(count<0)
319 count = 1;
320 adjust = -1;
321 i = cur;
322 if(c!='\t' && c!=ESC && !digit(c))
323 ep->ed->e_tabcount = 0;
324 switch(c)
325 {
326 case LNEXTCHAR:
327 c = ed_getchar(ep->ed,2);
328 goto do_default_processing;
329 case cntl('V'):
330 show_info(ep,fmtident(e_version));
331 continue;
332 case '\0':
333 ep->mark = i;
334 continue;
335 case cntl('X'):
336 xcommands(ep,count);
337 continue;
338 case EOFCHAR:
339 ed_flush(ep->ed);
340 tty_cooked(ERRIO);
341 return(0);
342 #ifdef u370
343 case cntl('S') :
344 case cntl('Q') :
345 continue;
346 #endif /* u370 */
347 case '\t':
348 if(cur>0 && ep->ed->sh->nextprompt)
349 {
350 if(ep->ed->e_tabcount==0)
351 {
352 ep->ed->e_tabcount=1;
353 ed_ungetchar(ep->ed,ESC);
354 goto do_escape;
355 }
356 else if(ep->ed->e_tabcount==1)
357 {
358 ed_ungetchar(ep->ed,'=');
359 goto do_escape;
360 }
361 ep->ed->e_tabcount = 0;
362 }
363 /* FALLTHROUGH */
364 do_default_processing:
365 default:
366
367 if ((eol+1) >= (scend)) /* will not fit on line */
368 {
369 ed_ungetchar(ep->ed,c); /* save character for next line */
370 goto process;
371 }
372 for(i= ++eol; i>cur; i--)
373 out[i] = out[i-1];
374 backslash = (c == '\\');
375 out[cur++] = c;
376 draw(ep,APPEND);
377 continue;
378 case cntl('Y') :
379 {
380 c = genlen(kstack);
381 if ((c + eol) > scend)
382 {
383 beep();
384 continue;
385 }
386 ep->mark = i;
387 for(i=eol;i>=cur;i--)
388 out[c+i] = out[i];
389 kptr=kstack;
390 while (i = *kptr++)
391 out[cur++] = i;
392 draw(ep,UPDATE);
393 eol = genlen(out);
394 continue;
395 }
396 case '\n':
397 case '\r':
398 c = '\n';
399 goto process;
400
401 case DELETE: /* delete char 0x7f */
402 case '\b': /* backspace, ^h */
403 case ERASECHAR :
404 if (count > i)
405 count = i;
406 #ifdef ESH_KAPPEND
407 kptr = &kstack[count]; /* move old contents here */
408 if (killing) /* prepend to killbuf */
409 {
410 c = genlen(kstack) + CHARSIZE; /* include '\0' */
411 while(c--) /* copy stuff */
412 kptr[c] = kstack[c];
413 }
414 else
415 *kptr = 0; /* this is end of data */
416 killing = 2; /* we are killing */
417 i -= count;
418 eol -= count;
419 genncpy(kstack,out+i,cur-i);
420 #else
421 while ((count--)&&(i>0))
422 {
423 i--;
424 eol--;
425 }
426 genncpy(kstack,out+i,cur-i);
427 kstack[cur-i] = 0;
428 #endif /* ESH_KAPPEND */
429 gencpy(out+i,out+cur);
430 ep->mark = i;
431 goto update;
432 case cntl('W') :
433 #ifdef ESH_KAPPEND
434 ++killing; /* keep killing flag */
435 #endif
436 if (ep->mark > eol )
437 ep->mark = eol;
438 if (ep->mark == i)
439 continue;
440 if (ep->mark > i)
441 {
442 adjust = ep->mark - i;
443 ed_ungetchar(ep->ed,cntl('D'));
444 continue;
445 }
446 adjust = i - ep->mark;
447 ed_ungetchar(ep->ed,usrerase);
448 continue;
449 case cntl('D') :
450 ep->mark = i;
451 #ifdef ESH_KAPPEND
452 if (killing)
453 kptr = &kstack[genlen(kstack)]; /* append here */
454 else
455 kptr = kstack;
456 killing = 2; /* we are now killing */
457 #else
458 kptr = kstack;
459 #endif /* ESH_KAPPEND */
460 while ((count--)&&(eol>0)&&(i<eol))
461 {
462 *kptr++ = out[i];
463 eol--;
464 while(1)
465 {
466 if ((out[i] = out[(i+1)])==0)
467 break;
468 i++;
469 }
470 i = cur;
471 }
472 *kptr = '\0';
473 goto update;
474 case cntl('C') :
475 case cntl('F') :
476 {
477 int cntlC = (c==cntl('C'));
478 while (count-- && eol>i)
479 {
480 if (cntlC)
481 {
482 c = out[i];
483 #if SHOPT_MULTIBYTE
484 if((c&~STRIP)==0 && islower(c))
485 #else
486 if(islower(c))
487 #endif /* SHOPT_MULTIBYTE */
488 {
489 c += 'A' - 'a';
490 out[i] = c;
491 }
492 }
493 i++;
494 }
495 goto update;
496 }
497 case cntl(']') :
498 c = ed_getchar(ep->ed,1);
499 if ((count == 0) || (count > eol))
500 {
501 beep();
502 continue;
503 }
504 if (out[i])
505 i++;
506 while (i < eol)
507 {
508 if (out[i] == c && --count==0)
509 goto update;
510 i++;
511 }
512 i = 0;
513 while (i < cur)
514 {
515 if (out[i] == c && --count==0)
516 break;
517 i++;
518 };
519
520 update:
521 cur = i;
522 draw(ep,UPDATE);
523 continue;
524
525 case cntl('B') :
526 if (count > i)
527 count = i;
528 i -= count;
529 goto update;
530 case cntl('T') :
531 if ((sh_isoption(SH_EMACS))&& (eol!=i))
532 i++;
533 if (i >= 2)
534 {
535 c = out[i - 1];
536 out[i-1] = out[i-2];
537 out[i-2] = c;
538 }
539 else
540 {
541 if(sh_isoption(SH_EMACS))
542 i--;
543 beep();
544 continue;
545 }
546 goto update;
547 case cntl('A') :
548 i = 0;
549 goto update;
550 case cntl('E') :
551 i = eol;
552 goto update;
553 case cntl('U') :
554 adjust = 4*count;
555 continue;
556 case KILLCHAR :
557 cur = 0;
558 oadjust = -1;
559 /* FALLTHROUGH */
560 case cntl('K') :
561 if(oadjust >= 0)
562 {
563 #ifdef ESH_KAPPEND
564 killing = 2; /* set killing signal */
565 #endif
566 ep->mark = count;
567 ed_ungetchar(ep->ed,cntl('W'));
568 continue;
569 }
570 i = cur;
571 eol = i;
572 ep->mark = i;
573 #ifdef ESH_KAPPEND
574 if (killing) /* append to kill buffer */
575 gencpy(&kstack[genlen(kstack)], &out[i]);
576 else
577 gencpy(kstack,&out[i]);
578 killing = 2; /* set killing signal */
579 #else
580 gencpy(kstack,&out[i]);
581 #endif /* ESH_KAPPEND */
582 out[i] = 0;
583 draw(ep,UPDATE);
584 if (c == KILLCHAR)
585 {
586 if (ep->terminal == PAPER)
587 {
588 putchar(ep->ed,'\n');
589 putstring(ep,Prompt);
590 }
591 c = ed_getchar(ep->ed,0);
592 if (c != usrkill)
593 {
594 ed_ungetchar(ep->ed,c);
595 continue;
596 }
597 if (ep->terminal == PAPER)
598 ep->terminal = CRT;
599 else
600 {
601 ep->terminal = PAPER;
602 putchar(ep->ed,'\n');
603 putstring(ep,Prompt);
604 }
605 }
606 continue;
607 case cntl('L'):
608 if(!ep->ed->e_nocrnl)
609 ed_crlf(ep->ed);
610 draw(ep,REFRESH);
611 ep->ed->e_nocrnl = 0;
612 continue;
613 case cntl('[') :
614 do_escape:
615 adjust = escape(ep,out,oadjust);
616 continue;
617 case cntl('R') :
618 search(ep,out,count);
619 goto drawline;
620 case cntl('P') :
621 #if SHOPT_EDPREDICT
622 if(ep->ed->hlist)
623 {
624 if(ep->ed->hoff == 0)
625 {
626 beep();
627 continue;
628 }
629 ep->ed->hoff--;
630 goto hupdate;
631 }
632 #endif /* SHOPT_EDPREDICT */
633 if (count <= hloff)
634 hloff -= count;
635 else
636 {
637 hline -= count - hloff;
638 hloff = 0;
639 }
640 #ifdef ESH_NFIRST
641 if (hline <= hismin)
642 #else
643 if (hline < hismin)
644 #endif /* ESH_NFIRST */
645 {
646 hline = hismin+1;
647 beep();
648 #ifndef ESH_NFIRST
649 continue;
650 #endif
651 }
652 goto common;
653
654 case cntl('O') :
655 location.hist_command = hline;
656 location.hist_line = hloff;
657 ep->CntrlO = 1;
658 c = '\n';
659 goto process;
660 case cntl('N') :
661 #if SHOPT_EDPREDICT
662 if(ep->ed->hlist)
663 {
664 if(ep->ed->hoff >= ep->ed->hmax)
665 {
666 beep();
667 continue;
668 }
669 ep->ed->hoff++;
670 hupdate:
671 ed_histlist(ep->ed,*ep->ed->hlist!=0);
672 draw(ep,REFRESH);
673 continue;
674 }
675 #endif /* SHOPT_EDPREDICT */
676 #ifdef ESH_NFIRST
677 hline = location.hist_command; /* start at saved position */
678 hloff = location.hist_line;
679 #endif /* ESH_NFIRST */
680 location = hist_locate(shgd->hist_ptr,hline,hloff,count);
681 if (location.hist_command > histlines)
682 {
683 beep();
684 #ifdef ESH_NFIRST
685 location.hist_command = histlines;
686 location.hist_line = ep->in_mult;
687 #else
688 continue;
689 #endif /* ESH_NFIRST */
690 }
691 hline = location.hist_command;
692 hloff = location.hist_line;
693 common:
694 #ifdef ESH_NFIRST
695 location.hist_command = hline; /* save current position */
696 location.hist_line = hloff;
697 #endif
698 cur = 0;
699 draw(ep,UPDATE);
700 hist_copy((char*)out,MAXLINE, hline,hloff);
701 #if SHOPT_MULTIBYTE
702 ed_internal((char*)(out),out);
703 #endif /* SHOPT_MULTIBYTE */
704 drawline:
705 eol = genlen(out);
706 cur = eol;
707 draw(ep,UPDATE);
708 continue;
709 }
710
711 }
712
713 process:
714
715 if (c == (-1))
716 {
717 lookahead = 0;
718 beep();
719 *out = '\0';
720 }
721 draw(ep,FINAL);
722 tty_cooked(ERRIO);
723 if(ed->e_nlist)
724 {
725 ed->e_nlist = 0;
726 stakset(ed->e_stkptr,ed->e_stkoff);
727 }
728 if(c == '\n')
729 {
730 out[eol++] = '\n';
731 out[eol] = '\0';
732 ed_crlf(ep->ed);
733 }
734 #if SHOPT_MULTIBYTE
735 ed_external(out,buff);
736 #endif /* SHOPT_MULTIBYTE */
737 i = (int)strlen(buff);
738 if (i)
739 return(i);
740 return(-1);
741 }
742
show_info(Emacs_t * ep,const char * str)743 static void show_info(Emacs_t *ep,const char *str)
744 {
745 register genchar *out = drawbuff;
746 register int c;
747 genchar string[LBUF];
748 int sav_cur = cur;
749 /* save current line */
750 genncpy(string,out,sizeof(string)/sizeof(*string));
751 *out = 0;
752 cur = 0;
753 #if SHOPT_MULTIBYTE
754 ed_internal(str,out);
755 #else
756 gencpy(out,str);
757 #endif /* SHOPT_MULTIBYTE */
758 draw(ep,UPDATE);
759 c = ed_getchar(ep->ed,0);
760 if(c!=' ')
761 ed_ungetchar(ep->ed,c);
762 /* restore line */
763 cur = sav_cur;
764 genncpy(out,string,sizeof(string)/sizeof(*string));
765 draw(ep,UPDATE);
766 }
767
putstring(Emacs_t * ep,register char * sp)768 static void putstring(Emacs_t* ep,register char *sp)
769 {
770 register int c;
771 while (c= *sp++)
772 putchar(ep->ed,c);
773 }
774
775
escape(register Emacs_t * ep,register genchar * out,int count)776 static int escape(register Emacs_t* ep,register genchar *out,int count)
777 {
778 register int i,value;
779 int digit,ch;
780 digit = 0;
781 value = 0;
782 while ((i=ed_getchar(ep->ed,0)),digit(i))
783 {
784 value *= 10;
785 value += (i - '0');
786 digit = 1;
787 }
788 if (digit)
789 {
790 ed_ungetchar(ep->ed,i) ;
791 #ifdef ESH_KAPPEND
792 ++killing; /* don't modify killing signal */
793 #endif
794 return(value);
795 }
796 value = count;
797 if(value<0)
798 value = 1;
799 switch(ch=i)
800 {
801 case cntl('V'):
802 show_info(ep,fmtident(e_version));
803 return(-1);
804 case ' ':
805 ep->mark = cur;
806 return(-1);
807
808 #ifdef ESH_KAPPEND
809 case '+': /* M-+ = append next kill */
810 killing = 2;
811 return -1; /* no argument for next command */
812 #endif
813
814 case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */
815 ed_ungetchar(ep->ed,cntl('Y'));
816 ed_ungetchar(ep->ed,cntl('W'));
817 #ifdef ESH_KAPPEND
818 killing = 0; /* start fresh */
819 #endif
820 return(-1);
821
822 case 'l': /* M-l == lower-case */
823 case 'd':
824 case 'c':
825 case 'f':
826 {
827 i = cur;
828 while(value-- && i<eol)
829 {
830 while ((out[i])&&(!isword(i)))
831 i++;
832 while ((out[i])&&(isword(i)))
833 i++;
834 }
835 if(ch=='l')
836 {
837 value = i-cur;
838 while (value-- > 0)
839 {
840 i = out[cur];
841 #if SHOPT_MULTIBYTE
842 if((i&~STRIP)==0 && isupper(i))
843 #else
844 if(isupper(i))
845 #endif /* SHOPT_MULTIBYTE */
846 {
847 i += 'a' - 'A';
848 out[cur] = i;
849 }
850 cur++;
851 }
852 draw(ep,UPDATE);
853 return(-1);
854 }
855
856 else if(ch=='f')
857 goto update;
858 else if(ch=='c')
859 {
860 ed_ungetchar(ep->ed,cntl('C'));
861 return(i-cur);
862 }
863 else
864 {
865 if (i-cur)
866 {
867 ed_ungetchar(ep->ed,cntl('D'));
868 #ifdef ESH_KAPPEND
869 ++killing; /* keep killing signal */
870 #endif
871 return(i-cur);
872 }
873 beep();
874 return(-1);
875 }
876 }
877
878
879 case 'b':
880 case DELETE :
881 case '\b':
882 case 'h':
883 {
884 i = cur;
885 while(value-- && i>0)
886 {
887 i--;
888 while ((i>0)&&(!isword(i)))
889 i--;
890 while ((i>0)&&(isword(i-1)))
891 i--;
892 }
893 if(ch=='b')
894 goto update;
895 else
896 {
897 ed_ungetchar(ep->ed,usrerase);
898 #ifdef ESH_KAPPEND
899 ++killing;
900 #endif
901 return(cur-i);
902 }
903 }
904
905 case '>':
906 ed_ungetchar(ep->ed,cntl('N'));
907 #ifdef ESH_NFIRST
908 if (ep->in_mult)
909 {
910 location.hist_command = histlines;
911 location.hist_line = ep->in_mult - 1;
912 }
913 else
914 {
915 location.hist_command = histlines - 1;
916 location.hist_line = 0;
917 }
918 #else
919 hline = histlines-1;
920 hloff = 0;
921 #endif /* ESH_NFIRST */
922 return(0);
923
924 case '<':
925 ed_ungetchar(ep->ed,cntl('P'));
926 hloff = 0;
927 #ifdef ESH_NFIRST
928 hline = hismin + 1;
929 return 0;
930 #else
931 return(hline-hismin);
932 #endif /* ESH_NFIRST */
933
934
935 case '#':
936 ed_ungetchar(ep->ed,'\n');
937 ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#');
938 ed_ungetchar(ep->ed,cntl('A'));
939 return(-1);
940 case '_' :
941 case '.' :
942 {
943 genchar name[MAXLINE];
944 char buf[MAXLINE];
945 char *ptr;
946 ptr = hist_word(buf,MAXLINE,(count?count:-1));
947 if(ptr==0)
948 {
949 beep();
950 break;
951 }
952 if ((eol - cur) >= sizeof(name))
953 {
954 beep();
955 return(-1);
956 }
957 ep->mark = cur;
958 gencpy(name,&out[cur]);
959 while(*ptr)
960 {
961 out[cur++] = *ptr++;
962 eol++;
963 }
964 gencpy(&out[cur],name);
965 draw(ep,UPDATE);
966 return(-1);
967 }
968 #if KSHELL
969
970 #if SHOPT_EDPREDICT
971 case '\n': case '\t':
972 if(!ep->ed->hlist)
973 {
974 beep();
975 break;
976 }
977 if(ch=='\n')
978 ed_ungetchar(ep->ed,'\n');
979 #endif /* SHOPT_EDPREDICT */
980 /* file name expansion */
981 case cntl('[') : /* filename completion */
982 #if SHOPT_EDPREDICT
983 if(ep->ed->hlist)
984 {
985 value += ep->ed->hoff;
986 if(value > ep->ed->nhlist)
987 beep();
988 else
989 {
990 value = histlines - ep->ed->hlist[value-1]->index;
991 ed_histlist(ep->ed,0);
992 ed_ungetchar(ep->ed,cntl('P'));
993 return(value);
994 }
995 }
996 #endif /* SHOPT_EDPREDICT */
997 i = '\\';
998 /* FALLTHROUGH */
999 case '*': /* filename expansion */
1000 case '=': /* escape = - list all matching file names */
1001 ep->mark = cur;
1002 if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0)
1003 {
1004 if(ep->ed->e_tabcount==1)
1005 {
1006 ep->ed->e_tabcount=2;
1007 ed_ungetchar(ep->ed,cntl('\t'));
1008 return(-1);
1009 }
1010 beep();
1011 }
1012 else if(i=='=' || (i=='\\' && out[cur-1]=='/'))
1013 {
1014 draw(ep,REFRESH);
1015 if(count>0)
1016 ep->ed->e_tabcount=0;
1017 else
1018 {
1019 i=ed_getchar(ep->ed,0);
1020 ed_ungetchar(ep->ed,i);
1021 if(digit(i))
1022 ed_ungetchar(ep->ed,ESC);
1023 }
1024 }
1025 else
1026 {
1027 if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' '))
1028 ep->ed->e_tabcount=0;
1029 draw(ep,UPDATE);
1030 }
1031 return(-1);
1032
1033 /* search back for character */
1034 case cntl(']'): /* feature not in book */
1035 {
1036 int c = ed_getchar(ep->ed,1);
1037 if ((value == 0) || (value > eol))
1038 {
1039 beep();
1040 return(-1);
1041 }
1042 i = cur;
1043 if (i > 0)
1044 i--;
1045 while (i >= 0)
1046 {
1047 if (out[i] == c && --value==0)
1048 goto update;
1049 i--;
1050 }
1051 i = eol;
1052 while (i > cur)
1053 {
1054 if (out[i] == c && --value==0)
1055 break;
1056 i--;
1057 };
1058
1059 }
1060 update:
1061 cur = i;
1062 draw(ep,UPDATE);
1063 return(-1);
1064
1065 #ifdef _cmd_tput
1066 case cntl('L'): /* clear screen */
1067 sh_trap("tput clear", 0);
1068 draw(ep,REFRESH);
1069 return(-1);
1070 #endif
1071 case '[': /* feature not in book */
1072 switch(i=ed_getchar(ep->ed,1))
1073 {
1074 case 'A':
1075 #if SHOPT_EDPREDICT
1076 if(!ep->ed->hlist && cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
1077 #else
1078 if(cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
1079 #endif /* SHOPT_EDPREDICT */
1080 {
1081 if(ep->lastdraw==APPEND && ep->prevdirection != -2)
1082 {
1083 out[cur] = 0;
1084 gencpy((genchar*)lstring+1,out);
1085 #if SHOPT_MULTIBYTE
1086 ed_external((genchar*)lstring+1,lstring+1);
1087 #endif /* SHOPT_MULTIBYTE */
1088 *lstring = '^';
1089 ep->prevdirection = -2;
1090 }
1091 if(*lstring)
1092 {
1093 ed_ungetchar(ep->ed,'\r');
1094 ed_ungetchar(ep->ed,cntl('R'));
1095 return(-1);
1096 }
1097 }
1098 *lstring = 0;
1099 ed_ungetchar(ep->ed,cntl('P'));
1100 return(-1);
1101 case 'B':
1102 ed_ungetchar(ep->ed,cntl('N'));
1103 return(-1);
1104 case 'C':
1105 ed_ungetchar(ep->ed,cntl('F'));
1106 return(-1);
1107 case 'D':
1108 ed_ungetchar(ep->ed,cntl('B'));
1109 return(-1);
1110 case 'H':
1111 ed_ungetchar(ep->ed,cntl('A'));
1112 return(-1);
1113 case 'Y':
1114 ed_ungetchar(ep->ed,cntl('E'));
1115 return(-1);
1116 default:
1117 ed_ungetchar(ep->ed,i);
1118 }
1119 i = '_';
1120 /* FALLTHROUGH */
1121
1122 default:
1123 /* look for user defined macro definitions */
1124 if(ed_macro(ep->ed,i))
1125 # ifdef ESH_BETTER
1126 return(count); /* pass argument to macro */
1127 # else
1128 return(-1);
1129 # endif /* ESH_BETTER */
1130 #else
1131 update:
1132 cur = i;
1133 draw(ep,UPDATE);
1134 return(-1);
1135
1136 default:
1137 #endif /* KSHELL */
1138 beep();
1139 return(-1);
1140 }
1141 return(-1);
1142 }
1143
1144
1145 /*
1146 * This routine process all commands starting with ^X
1147 */
1148
xcommands(register Emacs_t * ep,int count)1149 static void xcommands(register Emacs_t *ep,int count)
1150 {
1151 register int i = ed_getchar(ep->ed,0);
1152 NOT_USED(count);
1153 switch(i)
1154 {
1155 case cntl('X'): /* exchange dot and mark */
1156 if (ep->mark > eol)
1157 ep->mark = eol;
1158 i = ep->mark;
1159 ep->mark = cur;
1160 cur = i;
1161 draw(ep,UPDATE);
1162 return;
1163
1164 #if KSHELL
1165 # ifdef ESH_BETTER
1166 case cntl('E'): /* invoke emacs on current command */
1167 if(ed_fulledit(ep->ed)==-1)
1168 beep();
1169 else
1170 {
1171 #if SHOPT_MULTIBYTE
1172 ed_internal((char*)drawbuff,drawbuff);
1173 #endif /* SHOPT_MULTIBYTE */
1174 ed_ungetchar(ep->ed,'\n');
1175 }
1176 return;
1177
1178 # define itos(i) fmtbase((long)(i),0,0)/* want signed conversion */
1179
1180 case cntl('H'): /* ^X^H show history info */
1181 {
1182 char hbuf[MAXLINE];
1183
1184 strcpy(hbuf, "Current command ");
1185 strcat(hbuf, itos(hline));
1186 if (hloff)
1187 {
1188 strcat(hbuf, " (line ");
1189 strcat(hbuf, itos(hloff+1));
1190 strcat(hbuf, ")");
1191 }
1192 if ((hline != location.hist_command) ||
1193 (hloff != location.hist_line))
1194 {
1195 strcat(hbuf, "; Previous command ");
1196 strcat(hbuf, itos(location.hist_command));
1197 if (location.hist_line)
1198 {
1199 strcat(hbuf, " (line ");
1200 strcat(hbuf, itos(location.hist_line+1));
1201 strcat(hbuf, ")");
1202 }
1203 }
1204 show_info(ep,hbuf);
1205 return;
1206 }
1207 # if 0 /* debugging, modify as required */
1208 case cntl('D'): /* ^X^D show debugging info */
1209 {
1210 char debugbuf[MAXLINE];
1211
1212 strcpy(debugbuf, "count=");
1213 strcat(debugbuf, itos(count));
1214 strcat(debugbuf, " eol=");
1215 strcat(debugbuf, itos(eol));
1216 strcat(debugbuf, " cur=");
1217 strcat(debugbuf, itos(cur));
1218 strcat(debugbuf, " crallowed=");
1219 strcat(debugbuf, itos(crallowed));
1220 strcat(debugbuf, " plen=");
1221 strcat(debugbuf, itos(plen));
1222 strcat(debugbuf, " w_size=");
1223 strcat(debugbuf, itos(w_size));
1224
1225 show_info(ep,debugbuf);
1226 return;
1227 }
1228 # endif /* debugging code */
1229 # endif /* ESH_BETTER */
1230 #endif /* KSHELL */
1231
1232 default:
1233 beep();
1234 return;
1235 }
1236 }
1237
search(Emacs_t * ep,genchar * out,int direction)1238 static void search(Emacs_t* ep,genchar *out,int direction)
1239 {
1240 #ifndef ESH_NFIRST
1241 Histloc_t location;
1242 #endif
1243 register int i,sl;
1244 genchar str_buff[LBUF];
1245 register genchar *string = drawbuff;
1246 /* save current line */
1247 int sav_cur = cur;
1248 genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff));
1249 string[0] = '^';
1250 string[1] = 'R';
1251 string[2] = '\0';
1252 sl = 2;
1253 cur = sl;
1254 draw(ep,UPDATE);
1255 while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n'))
1256 {
1257 if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR)
1258 {
1259 if (sl > 2)
1260 {
1261 string[--sl] = '\0';
1262 cur = sl;
1263 draw(ep,UPDATE);
1264 }
1265 else
1266 goto restore;
1267 continue;
1268 }
1269 if(i == ep->ed->e_intr)
1270 goto restore;
1271 if (i==usrkill)
1272 {
1273 beep();
1274 goto restore;
1275 }
1276 if (i == '\\')
1277 {
1278 string[sl++] = '\\';
1279 string[sl] = '\0';
1280 cur = sl;
1281 draw(ep,APPEND);
1282 i = ed_getchar(ep->ed,1);
1283 string[--sl] = '\0';
1284 }
1285 string[sl++] = i;
1286 string[sl] = '\0';
1287 cur = sl;
1288 draw(ep,APPEND);
1289 }
1290 i = genlen(string);
1291
1292 if(ep->prevdirection == -2 && i!=2 || direction!=1)
1293 ep->prevdirection = -1;
1294 if (direction < 1)
1295 {
1296 ep->prevdirection = -ep->prevdirection;
1297 direction = 1;
1298 }
1299 else
1300 direction = -1;
1301 if (i != 2)
1302 {
1303 #if SHOPT_MULTIBYTE
1304 ed_external(string,(char*)string);
1305 #endif /* SHOPT_MULTIBYTE */
1306 strncpy(lstring,((char*)string)+2,SEARCHSIZE);
1307 lstring[SEARCHSIZE-1] = 0;
1308 ep->prevdirection = direction;
1309 }
1310 else
1311 direction = ep->prevdirection ;
1312 location = hist_find(shgd->hist_ptr,(char*)lstring,hline,1,direction);
1313 i = location.hist_command;
1314 if(i>0)
1315 {
1316 hline = i;
1317 #ifdef ESH_NFIRST
1318 hloff = location.hist_line = 0; /* display first line of multi line command */
1319 #else
1320 hloff = location.hist_line;
1321 #endif /* ESH_NFIRST */
1322 hist_copy((char*)out,MAXLINE, hline,hloff);
1323 #if SHOPT_MULTIBYTE
1324 ed_internal((char*)out,out);
1325 #endif /* SHOPT_MULTIBYTE */
1326 return;
1327 }
1328 if (i < 0)
1329 {
1330 beep();
1331 #ifdef ESH_NFIRST
1332 location.hist_command = hline;
1333 location.hist_line = hloff;
1334 #else
1335 hloff = 0;
1336 hline = histlines;
1337 #endif /* ESH_NFIRST */
1338 }
1339 restore:
1340 genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff));
1341 cur = sav_cur;
1342 return;
1343 }
1344
1345
1346 /* Adjust screen to agree with inputs: logical line and cursor */
1347 /* If 'first' assume screen is blank */
1348 /* Prompt is always kept on the screen */
1349
draw(register Emacs_t * ep,Draw_t option)1350 static void draw(register Emacs_t *ep,Draw_t option)
1351 {
1352 #define NORMAL ' '
1353 #define LOWER '<'
1354 #define BOTH '*'
1355 #define UPPER '>'
1356
1357 register genchar *sptr; /* Pointer within screen */
1358 genchar nscreen[2*MAXLINE]; /* New entire screen */
1359 genchar *ncursor; /* New cursor */
1360 register genchar *nptr; /* Pointer to New screen */
1361 char longline; /* Line overflow */
1362 genchar *logcursor;
1363 genchar *nscend; /* end of logical screen */
1364 register int i;
1365
1366 nptr = nscreen;
1367 sptr = drawbuff;
1368 logcursor = sptr + cur;
1369 longline = NORMAL;
1370 ep->lastdraw = option;
1371
1372 if (option == FIRST || option == REFRESH)
1373 {
1374 ep->overflow = NORMAL;
1375 ep->cursor = ep->screen;
1376 ep->offset = 0;
1377 ep->cr_ok = crallowed;
1378 if (option == FIRST)
1379 {
1380 ep->scvalid = 1;
1381 return;
1382 }
1383 *ep->cursor = '\0';
1384 putstring(ep,Prompt); /* start with prompt */
1385 }
1386
1387 /*********************
1388 Do not update screen if pending characters
1389 **********************/
1390
1391 if ((lookahead)&&(option != FINAL))
1392 {
1393
1394 ep->scvalid = 0; /* Screen is out of date, APPEND will not work */
1395
1396 return;
1397 }
1398
1399 /***************************************
1400 If in append mode, cursor at end of line, screen up to date,
1401 the previous character was a 'normal' character,
1402 and the window has room for another character.
1403 Then output the character and adjust the screen only.
1404 *****************************************/
1405
1406
1407 i = *(logcursor-1); /* last character inserted */
1408 #if SHOPT_EDPREDICT
1409 if(option==FINAL)
1410 {
1411 if(ep->ed->hlist)
1412 ed_histlist(ep->ed,0);
1413 }
1414 else if((option==UPDATE||option==APPEND) && drawbuff[0]=='#' && cur>1 && cur==eol && drawbuff[cur-1]!='*')
1415 {
1416 int n;
1417 drawbuff[cur+1]=0;
1418 # if SHOPT_MULTIBYTE
1419 ed_external(drawbuff,(char*)drawbuff);
1420 # endif /*SHOPT_MULTIBYTE */
1421 n = ed_histgen(ep->ed,(char*)drawbuff);
1422 # if SHOPT_MULTIBYTE
1423 ed_internal((char*)drawbuff,drawbuff);
1424 # endif /*SHOPT_MULTIBYTE */
1425 if(ep->ed->hlist)
1426 {
1427 ed_histlist(ep->ed,n);
1428 putstring(ep,Prompt);
1429 ed_setcursor(ep->ed,ep->screen,0,ep->cursor-ep->screen, 0);
1430 }
1431 else
1432 ed_ringbell();
1433
1434 }
1435 #endif /* SHOPT_EDPREDICT */
1436
1437 if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&&
1438 print(i)&&((ep->cursor-ep->screen)<(w_size-1)))
1439 {
1440 putchar(ep->ed,i);
1441 *ep->cursor++ = i;
1442 *ep->cursor = '\0';
1443 return;
1444 }
1445
1446 /* copy the line */
1447 ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0);
1448 nptr += genlen(nptr);
1449 sptr += genlen(sptr);
1450 nscend = nptr - 1;
1451 if(sptr == logcursor)
1452 ncursor = nptr;
1453
1454 /*********************
1455 Does ncursor appear on the screen?
1456 If not, adjust the screen offset so it does.
1457 **********************/
1458
1459 i = ncursor - nscreen;
1460
1461 if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size)))
1462 {
1463 /* Center the cursor on the screen */
1464 ep->offset = i - (w_size>>1);
1465 if (--ep->offset < 0)
1466 ep->offset = 0;
1467 }
1468
1469 /*********************
1470 Is the range of screen[0] thru screen[w_size] up-to-date
1471 with nscreen[offset] thru nscreen[offset+w_size] ?
1472 If not, update as need be.
1473 ***********************/
1474
1475 nptr = &nscreen[ep->offset];
1476 sptr = ep->screen;
1477
1478 i = w_size;
1479
1480 while (i-- > 0)
1481 {
1482
1483 if (*nptr == '\0')
1484 {
1485 *(nptr + 1) = '\0';
1486 *nptr = ' ';
1487 }
1488 if (*sptr == '\0')
1489 {
1490 *(sptr + 1) = '\0';
1491 *sptr = ' ';
1492 }
1493 if (*nptr == *sptr)
1494 {
1495 nptr++;
1496 sptr++;
1497 continue;
1498 }
1499 setcursor(ep,sptr-ep->screen,*nptr);
1500 *sptr++ = *nptr++;
1501 #if SHOPT_MULTIBYTE
1502 while(*nptr==MARKER)
1503 {
1504 if(*sptr=='\0')
1505 *(sptr + 1) = '\0';
1506 *sptr++ = *nptr++;
1507 i--;
1508 ep->cursor++;
1509 }
1510 #endif /* SHOPT_MULTIBYTE */
1511 }
1512 if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0)
1513 ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
1514
1515
1516 /******************
1517
1518 Screen overflow checks
1519
1520 ********************/
1521
1522 if (nscend >= &nscreen[ep->offset+w_size])
1523 {
1524 if (ep->offset > 0)
1525 longline = BOTH;
1526 else
1527 longline = UPPER;
1528 }
1529 else
1530 {
1531 if (ep->offset > 0)
1532 longline = LOWER;
1533 }
1534
1535 /* Update screen overflow indicator if need be */
1536
1537 if (longline != ep->overflow)
1538 {
1539 setcursor(ep,w_size,longline);
1540 ep->overflow = longline;
1541 }
1542 i = (ncursor-nscreen) - ep->offset;
1543 setcursor(ep,i,0);
1544 if(option==FINAL && ep->ed->e_multiline)
1545 setcursor(ep,nscend+1-nscreen,0);
1546 ep->scvalid = 1;
1547 return;
1548 }
1549
1550 /*
1551 * put the cursor to the <newp> position within screen buffer
1552 * if <c> is non-zero then output this character
1553 * cursor is set to reflect the change
1554 */
1555
setcursor(register Emacs_t * ep,register int newp,int c)1556 static void setcursor(register Emacs_t *ep,register int newp,int c)
1557 {
1558 register int oldp = ep->cursor - ep->screen;
1559 newp = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0);
1560 if(c)
1561 {
1562 putchar(ep->ed,c);
1563 newp++;
1564 }
1565 ep->cursor = ep->screen+newp;
1566 return;
1567 }
1568
1569 #if SHOPT_MULTIBYTE
print(register int c)1570 static int print(register int c)
1571 {
1572 return((c&~STRIP)==0 && isprint(c));
1573 }
1574
_isword(register int c)1575 static int _isword(register int c)
1576 {
1577 return((c&~STRIP) || isalnum(c) || c=='_');
1578 }
1579 #endif /* SHOPT_MULTIBYTE */
1580