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