|
|
1.1 root 1: /*
2: * EDIT.C -- Emacs-like command line editing and history
3: *
4: * created by Ron Natalie at BRL
5: * modified by Doug Kingston, Doug Gwyn, and Lou Salkind
6: * adapted to PD ksh by Eric Gisin
7: */
8:
9: #if EDIT
10:
11: static char *RCSid = "$Header: /newbits/usr/bin/korn/RCS/edit.c,v 1.2 91/08/01 12:39:10 bin Exp Locker: bin $";
12:
13: #include <stddef.h>
14: #include <stdlib.h>
15: #include <string.h>
16: #include <stdio.h>
17: #include <unistd.h>
18: #include <signal.h>
19: #if !COHERENT
20: #include <sys/types.h>
21: #endif
22: #include <sys/stat.h>
23: #include "dirent.h"
24: #include <sys/fcntl.h>
25: #include <ctype.h>
26: #include <errno.h>
27: #include <setjmp.h>
28: #include "sh.h"
29: #include "lex.h"
30: #include "tree.h" /* DOTILDE */
31: #include "tty.h"
32: #include "table.h"
33: #include "expand.h"
34:
35: #if COHERENT
36: struct dirent *readdir(); /* missing from headers */
37: DIR *opendir();
38: #endif
39:
40: Area aedit;
41: #define AEDIT &aedit /* area for kill ring and macro defns */
42:
43: #undef CTRL /* _BSD brain damage */
44: #define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
45: #define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */
46:
47: #if ! defined S_ISDIR
48: #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
49: #endif
50:
51: #if ! defined S_ISREG
52: #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
53: #endif
54:
55: #if defined _CRAY2
56: extern unsigned sleep();
57: #endif
58:
59: /* values returned by keyboard functions */
60: #define KSTD 0
61: #define KPREF 1 /* ^[, ^X */
62: #define KEOL 2 /* ^M, ^J */
63: #define KINTR 3 /* ^G, ^C */
64:
65: struct x_ftab {
66: int (*xf_func)();
67: char *xf_name;
68: char xf_db_tab;
69: char xf_db_char;
70: short xf_flags;
71: };
72:
73: #define XF_NINPUT 1
74: #define XF_ALLOC 2
75: #define XF_NOBIND 4
76:
77: #define isfs(c) (c == ' ' || c == '\t')
78: #define BEL 0x07
79: #define CMASK 0x7F /* 7-bit ASCII character mask */
80:
81: static bool_t x_mode = FALSE;
82: static int x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');
83: static char **x_histp; /* history position */
84: static char *xbuf; /* beg input buffer */
85: static char *xend; /* end input buffer */
86: static char *xcp;
87: static char *xep;
88: static int (*x_last_command)();
89: /*static struct x_ftab *x_tab[3][128];*/
90: static struct x_ftab Const *(*x_tab)[128] = NULL; /* key definition */
91: static char *(*x_atab)[128] = NULL; /* macro definitions */
92: #define KILLSIZE 20
93: static char *killstack[KILLSIZE];
94: static int killsp, killtp;
95: static int x_curprefix;
96: static char *macroptr;
97: static int first_time = 1;
98: static int x_maxlen; /* to determine column width */
99: static int x_cols = 80; /* todo: $COLUMNS */
100:
101: static void x_flush(), x_putc(), x_puts();
102: static void x_goto(), x_bs(), x_delete(), x_ins(), x_mapin();
103: static int x_fword(), x_bword(), x_size(), x_size_str();
104: static void x_zotc(), x_zots(), x_push(), x_redraw(), x_load_hist();
105: static void compl_command(), compl_dec(), compl_file();
106: static int x_insert(), x_ins_string(), x_del_back();
107: static int x_del_char(), x_del_bword(), x_mv_bword(), x_mv_fword();
108: static int x_del_fword(), x_mv_back(), x_mv_forw(), x_search_char();
109: static int x_newline(), x_end_of_text(), x_abort(), x_error();
110: static int x_beg_hist(), x_end_hist(), x_prev_com(), x_next_com();
111: static int x_search_hist(), x_del_line(), x_mv_end(), x_mv_begin();
112: static int x_draw_line(), x_transpose(), x_meta1(), x_meta2();
113: static int x_kill(), x_yank(), x_meta_yank(), x_literal();
114: static int x_stuffreset(), x_stuff(), x_complete(), x_enumerate();
115: #if SILLY
116: static int x_game_of_life();
117: #endif
118: static int x_comp_file(), x_comp_comm();
119: static int x_list_file(), x_list_comm();
120: static int strmatch();
121:
122: static struct x_ftab Const x_ftab[] = {
123: {x_insert, "auto-insert", 0, 0, 0 },
124: {x_error, "error", 0, 0, 0 },
125: {x_ins_string, "macro-string", 0, 0, XF_NOBIND|XF_ALLOC},
126: /* Do not move the above! */
127: {x_del_back, "delete-char-backward", 0, CTRL('H'), 0 },
128: {x_del_char, "delete-char-forward", 0, CTRL('D'), 0 },
129: {x_del_bword, "delete-word-backward", 0, CTRL('W'), 0 },
130: {x_mv_bword, "backward-word", 1, 'b', 0 },
131: {x_mv_fword, "forward-word", 1, 'f', 0 },
132: {x_del_fword, "delete-word-forward", 1, 'd', 0 },
133: {x_mv_back, "backward-char", 0, CTRL('B'), 0 },
134: {x_mv_forw, "forward-char", 0, CTRL('F'), 0 },
135: {x_search_char, "search-character", 0, CTRL(']'), 0 },
136: {x_newline, "newline", 0, CTRL('M'), 0 },
137: {x_newline, "newline", 0, CTRL('J'), 0 },
138: {x_end_of_text, "eot", 0, CTRL('_'), 0 },
139: {x_abort, "abort", 0, CTRL('G'), 0 },
140: {x_prev_com, "up-history", 0, CTRL('P'), XF_NINPUT},
141: {x_next_com, "down-history", 0, CTRL('N'), XF_NINPUT},
142: {x_search_hist, "search-history", 0, CTRL('R'), XF_NINPUT},
143: {x_beg_hist, "beginning-of-history", 1, '<', XF_NINPUT},
144: {x_end_hist, "end-of-history", 1, '>', XF_NINPUT},
145: {x_del_line, "kill-line", 0, CTRL('U'), 0 },
146: {x_mv_end, "end-of-line", 0, CTRL('E'), 0 },
147: {x_mv_begin, "beginning-of-line", 0, CTRL('A'), 0 },
148: {x_draw_line, "redraw", 0, CTRL('L'), 0 },
149: {x_meta1, "prefix-1", 0, CTRL('['), 0 },
150: {x_meta2, "prefix-2", 0, CTRL('X'), 0 },
151: {x_kill, "kill-to-eol", 0, CTRL('K'), 0 },
152: {x_yank, "yank", 0, CTRL('Y'), 0 },
153: {x_meta_yank, "yank-pop", 1, 'y', 0 },
154: {x_literal, "quote", 0, CTRL('^'), 0 },
155: {x_stuffreset, "stuff-reset", 0, 0, 0 },
156: #if BRL && defined(TIOCSTI)
157: {x_stuff, "stuff", 0, CTRL('T'), 0 },
158: {x_transpose, "transpose-chars", 0, 0, 0 },
159: #else
160: {x_stuff, "stuff", 0, 0, 0 },
161: {x_transpose, "transpose-chars", 0, CTRL('T'), 0 },
162: #endif
163: {x_complete, "complete", 1, CTRL('['), 0 },
164: {x_enumerate, "list", 1, '?', 0 },
165: {x_comp_file, "complete-file", 2, CTRL('X'), 0 },
166: {x_comp_comm, "complete-command", 2, CTRL('['), 0 },
167: {x_list_file, "list-file", 0, 0, 0 },
168: {x_list_comm, "list-command", 2, '?', 0 },
169: #if SILLY
170: {x_game_of_life, "play-game-of-life", 0, 0, 0 },
171: #endif
172: { 0 }
173: };
174:
175: #define xft_insert &x_ftab[0]
176: #define xft_error &x_ftab[1]
177: #define xft_ins_string &x_ftab[2]
178:
179: int
180: x_read(fd, buf, len)
181: int fd; /* not used */
182: char *buf;
183: size_t len;
184: {
185: char c;
186: int i;
187: int (*func)();
188: extern x_insert();
189:
190: (void)set_xmode(TRUE);
191: xbuf = buf; xend = buf + len;
192: xcp = xep = buf;
193: *xcp = 0;
194: x_curprefix = 0;
195: macroptr = null;
196: x_histp = histptr + 1;
197:
198: while (1) {
199: x_flush();
200: if (*macroptr) {
201: c = *macroptr++;
202: if (*macroptr == 0)
203: macroptr = null;
204: } else {
205: i = read(ttyfd, &c, 1);
206: if (i != 1)
207: goto Exit;
208: }
209:
210: if (x_curprefix == -1)
211: func = x_insert;
212: else
213: func = x_tab[x_curprefix][c&CMASK]->xf_func;
214: if (func == NULL)
215: func = x_error;
216: i = c | (x_curprefix << 8);
217: x_curprefix = 0;
218: switch (i = (*func)(i)) {
219: case KSTD:
220: x_last_command = func;
221: case KPREF:
222: break;
223: case KEOL:
224: i = xep - xbuf;
225: x_last_command = 0;
226: /* XXX -- doesn't get them all */
227: if (strncmp(xbuf, "stty", 4) == 0)
228: first_time = 1;
229: goto Exit;
230: case KINTR: /* special case for interrupt */
231: i = -1;
232: errno = EINTR;
233: *xbuf = '\0';
234: goto Exit;
235: }
236: }
237: Exit:
238: (void)set_xmode(FALSE);
239: if (i < 0 && errno == EINTR)
240: trapsig(SIGINT);
241: return i;
242: }
243:
244: static int
245: x_insert(c) {
246: char str[2];
247:
248: /*
249: * Should allow tab and control chars.
250: */
251: if (c == 0) {
252: x_putc(BEL);
253: return KSTD;
254: }
255: str[0] = c;
256: str[1] = 0;
257: x_ins(str);
258: return KSTD;
259: }
260:
261: static int
262: x_ins_string(c)
263: {
264: if (*macroptr) {
265: x_putc(BEL);
266: return KSTD;
267: }
268: macroptr = x_atab[c>>8][c & CMASK];
269: return KSTD;
270: }
271:
272: static void
273: x_ins(cp)
274: char *cp;
275: {
276: int count, i;
277:
278: count = strlen(cp);
279: if (xep+count >= xend) {
280: x_putc(BEL);
281: return;
282: }
283:
284: if (xcp != xep)
285: memmove(xcp+count, xcp, xep - xcp + 1);
286: else
287: xcp[count] = 0;
288: memmove(xcp, cp, count);
289: x_zots(xcp);
290: xcp += count;
291: xep += count;
292: i = xep - xcp;
293: cp = xep;
294: while (i--)
295: x_bs(*--cp);
296: return;
297: }
298:
299: static int
300: x_del_back(c) {
301: if (xcp == xbuf) {
302: x_putc(BEL);
303: return KSTD;
304: }
305: x_goto(xcp - 1);
306: x_delete(1);
307: return KSTD;
308: }
309:
310: static int
311: x_del_char(c) {
312: if (xcp == xep) {
313: x_putc(BEL);
314: return KSTD;
315: }
316: x_delete(1);
317: return KSTD;
318: }
319:
320: static void
321: x_delete(nc) {
322: int i,j;
323: char *cp;
324:
325: if (nc == 0)
326: return;
327: xep -= nc;
328: cp = xcp;
329: j = 0;
330: i = nc;
331: while (i--) {
332: j += x_size(*cp++);
333: }
334: memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */
335: x_zots(xcp);
336: i = j;
337: while (i--)
338: x_putc(' ');
339: i = j;
340: while (i--)
341: x_putc('\b');
342: /*x_goto(xcp);*/
343: i = xep - xcp;
344: cp = xep;
345: while (i--)
346: x_bs(*--cp);
347: return;
348: }
349:
350: static int
351: x_del_bword(c) {
352: x_delete(x_bword());
353: return KSTD;
354: }
355:
356: static int
357: x_mv_bword(c) {
358: (void)x_bword();
359: return KSTD;
360: }
361:
362: static int
363: x_mv_fword(c) {
364: x_goto(xcp + x_fword());
365: return KSTD;
366: }
367:
368: static int
369: x_del_fword(c) {
370: x_delete(x_fword());
371: return KSTD;
372: }
373:
374: static int
375: x_bword() {
376: int nc = 0;
377: register char *cp = xcp;
378:
379: if (cp == xbuf) {
380: x_putc(BEL);
381: return 0;
382: }
383: while (cp != xbuf && isfs(cp[-1])) {
384: cp--;
385: nc++;
386: }
387: while (cp != xbuf && !isfs(cp[-1])) {
388: cp--;
389: nc++;
390: }
391: x_goto(cp);
392: return nc;
393: }
394:
395: static int
396: x_fword() {
397: int nc = 0;
398: char *cp = xcp;
399:
400: if (cp == xep) {
401: x_putc(BEL);
402: return 0;
403: }
404: while (cp != xep && !isfs(*cp)) {
405: cp++;
406: nc++;
407: }
408: while (cp != xep && isfs(*cp)) {
409: cp++;
410: nc++;
411: }
412: return nc;
413: }
414:
415: static void
416: x_goto(cp)
417: register char *cp;
418: {
419: if (cp < xcp) { /* move back */
420: while (cp < xcp)
421: x_bs(*--xcp);
422: } else
423: if (cp > xcp) { /* move forward */
424: while (cp > xcp)
425: x_zotc(*xcp++);
426: }
427: }
428:
429: static void
430: x_bs(c) {
431: register i;
432: i = x_size(c);
433: while (i--)
434: x_putc('\b');
435: }
436:
437: static int
438: x_size_str(cp)
439: register char *cp;
440: {
441: register size = 0;
442: while (*cp)
443: size += x_size(*cp++);
444: return size;
445: }
446:
447: static int
448: x_size(c) {
449: if (c=='\t')
450: return 4; /* Kludge, tabs are always four spaces. */
451: if (c < ' ' || c == 0x7F) /* ASCII control char */
452: return 2;
453: return 1;
454: }
455:
456: static void
457: x_zots(str)
458: register char *str;
459: {
460: while (*str)
461: x_zotc(*str++);
462: }
463:
464: static void
465: x_zotc(c)
466: int c;
467: {
468: if (c == '\t') {
469: /* Kludge, tabs are always four spaces. */
470: x_puts(" ");
471: } else if (c < ' ' || c == 0x7F) { /* ASCII */
472: x_putc('^');
473: x_putc(UNCTRL(c));
474: } else
475: x_putc(c);
476: }
477:
478: /* tty output */
479:
480: void
481: x_flush()
482: {
483: fflush(stdout);
484: }
485:
486: void
487: x_putc(c)
488: int c;
489: {
490: putc(c, stdout);
491: }
492:
493: void
494: x_puts(s)
495: register char *s;
496: {
497: while (*s != 0)
498: putc(*s++, stdout);
499: }
500:
501: static int
502: x_mv_back(c) {
503: if (xcp == xbuf) {
504: x_putc(BEL);
505: return KSTD;
506: }
507: x_goto(xcp-1);
508: return KSTD;
509: }
510:
511: static int
512: x_mv_forw(c) {
513: if (xcp == xep) {
514: x_putc(BEL);
515: return KSTD;
516: }
517: x_goto(xcp+1);
518: return KSTD;
519: }
520:
521: static int
522: x_search_char(c) {
523: char ci, *cp;
524:
525: *xep = '\0';
526: if (read(ttyfd, &ci, 1) != 1 ||
527: /* we search forward, I don't know what Korn does */
528: (cp = (xcp == xep) ? NULL : strchr(xcp+1, ci)) == NULL &&
529: (cp = strchr(xbuf, ci)) == NULL) {
530: x_putc(BEL);
531: return KSTD;
532: }
533: x_goto(cp);
534: return KSTD;
535: }
536:
537: static int
538: x_newline(c) {
539: x_putc('\n');
540: x_flush();
541: *xep++ = '\n';
542: return KEOL;
543: }
544:
545: static int
546: x_end_of_text(c) {
547: #if 0
548: x_store_hist();
549: #endif
550: return KEOL;
551: }
552:
553: static int x_beg_hist(c) {x_load_hist(history); return KSTD;}
554:
555: static int x_end_hist(c) {x_load_hist(histptr); return KSTD;}
556:
557: static int x_prev_com(c) {x_load_hist(x_histp-1); return KSTD;}
558:
559: static int x_next_com(c) {x_load_hist(x_histp+1); return KSTD;}
560:
561: static void
562: x_load_hist(hp)
563: register char **hp;
564: {
565: int oldsize;
566:
567: if (hp < history || hp > histptr) {
568: x_putc(BEL);
569: return;
570: }
571: x_histp = hp;
572: oldsize = x_size_str(xbuf);
573: (void)strcpy(xbuf, *hp);
574: xep = xcp = xbuf + strlen(*hp);
575: x_redraw(oldsize);
576: }
577:
578: static int x_search(), x_match();
579:
580: /* reverse incremental history search */
581: static int
582: x_search_hist(ci)
583: {
584: int offset = -1; /* offset of match in xbuf, else -1 */
585: static char c[2]; /* input buffer */
586: char pat [256+1]; /* pattern buffer */
587: register char *p = pat;
588: int (*func)();
589:
590: *p = 0;
591: while (1) {
592: if (offset < 0) {
593: x_puts("\nI-search: ");
594: x_zots(pat);
595: }
596: x_flush();
597: if (read(ttyfd, c, 1) < 1)
598: return KSTD;
599: func = x_tab[0][*c&CMASK]->xf_func;
600: if (*c == CTRL('['))
601: break;
602: else if (func == x_search_hist)
603: offset = x_search(pat, offset);
604: else if (func == x_del_back)
605: continue; /* todo */
606: else if (func == x_insert) {
607: /* add char to pattern */
608: *p++ = *c, *p = 0;
609: if (offset >= 0) {
610: /* already have partial match */
611: offset = x_match(xbuf, pat);
612: if (offset >= 0) {
613: x_goto(xbuf + offset + (p - pat) - (*pat == '^'));
614: continue;
615: }
616: }
617: offset = x_search(pat, offset);
618: } else { /* other command */
619: macroptr = c; /* push command */
620: break;
621: }
622: }
623: if (offset < 0)
624: x_redraw(-1);
625: return KSTD;
626: }
627:
628: /* search backward from current line */
629: static int
630: x_search(pat, offset)
631: char *pat;
632: int offset;
633: {
634: register char **hp;
635: int i;
636:
637: for (hp = x_histp; --hp >= history; ) {
638: i = x_match(*hp, pat);
639: if (i >= 0) {
640: if (offset < 0)
641: x_putc('\n');
642: x_load_hist(hp);
643: x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
644: return i;
645: }
646: }
647: x_putc(BEL);
648: x_histp = histptr;
649: return -1;
650: }
651:
652: /* return position of first match of pattern in string, else -1 */
653: static int
654: x_match(str, pat)
655: char *str, *pat;
656: {
657: if (*pat == '^') {
658: return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
659: } else {
660: char *q = strstr(str, pat);
661: return (q == NULL) ? -1 : q - str;
662: }
663: }
664:
665: static int
666: x_del_line(c) {
667: int i, j;
668:
669: *xep = 0;
670: i = xep- xbuf;
671: j = x_size_str(xbuf);
672: xcp = xbuf;
673: x_push(i);
674: xep = xbuf;
675: *xcp = 0;
676: x_redraw(j);
677: return KSTD;
678: }
679:
680: static int
681: x_mv_end(c) {
682: x_goto(xep);
683: return KSTD;
684: }
685:
686: static int
687: x_mv_begin(c) {
688: x_goto(xbuf);
689: return KSTD;
690: }
691:
692: static int
693: x_draw_line(c)
694: {
695: x_redraw(-1);
696: return KSTD;
697:
698: }
699:
700: static void
701: x_redraw(limit) {
702: int i, j;
703: char *cp;
704:
705: if (limit == -1)
706: x_putc('\n');
707: else
708: x_putc('\r');
709: x_flush();
710: pprompt(prompt);
711: x_zots(xbuf);
712: if (limit != -1) {
713: i = limit - x_size_str(xbuf);
714: j = 0;
715: while (j < i) {
716: x_putc(' ');
717: j++;
718: }
719: while (j--)
720: x_putc('\b');
721: }
722: i = xep - xcp;
723: cp = xep;
724: while (i--)
725: x_bs(*--cp);
726: return;
727: }
728:
729: static int
730: x_transpose(c) {
731: char tmp;
732: if (xcp == xbuf || xcp == xep) {
733: x_putc(BEL);
734: return KSTD;
735: }
736: x_bs(xcp[-1]);
737: x_zotc(xcp[0]);
738: x_zotc(xcp[-1]);
739: tmp = xcp[-1];
740: xcp[-1] = xcp[0];
741: xcp[0] = tmp;
742: x_bs(xcp[0]);
743: return KSTD;
744: }
745:
746: static int
747: x_literal(c) {
748: x_curprefix = -1;
749: return KSTD;
750: }
751:
752: static int
753: x_meta1(c) {
754: x_curprefix = 1;
755: return KPREF;
756: }
757:
758: static int
759: x_meta2(c) {
760: x_curprefix = 2;
761: return KPREF;
762: }
763:
764: static int
765: x_kill(c) {
766: int i;
767:
768: i = xep - xcp;
769: x_push(i);
770: x_delete(i);
771: return KSTD;
772: }
773:
774: static void
775: x_push(nchars) {
776: char *cp;
777: cp = alloc((size_t)(nchars+1), AEDIT);
778: memmove(cp, xcp, nchars);
779: cp[nchars] = 0;
780: if (killstack[killsp])
781: afree((Void *)killstack[killsp], AEDIT);
782: killstack[killsp] = cp;
783: killsp = (killsp + 1) % KILLSIZE;
784: }
785:
786: static int
787: x_yank(c) {
788: if (killsp == 0)
789: killtp = KILLSIZE;
790: else
791: killtp = killsp;
792: killtp --;
793: if (killstack[killtp] == 0) {
794: x_puts("\nnothing to yank");
795: x_redraw(-1);
796: return KSTD;
797: }
798: x_ins(killstack[killtp]);
799: return KSTD;
800: }
801:
802: static int
803: x_meta_yank(c) {
804: int len;
805: if (x_last_command != x_yank && x_last_command != x_meta_yank) {
806: x_puts("\nyank something first");
807: x_redraw(-1);
808: return KSTD;
809: }
810: len = strlen(killstack[killtp]);
811: x_goto(xcp - len);
812: x_delete(len);
813: do {
814: if (killtp == 0)
815: killtp = KILLSIZE - 1;
816: else
817: killtp--;
818: } while (killstack[killtp] == 0);
819: x_ins(killstack[killtp]);
820: return KSTD;
821: }
822:
823: static int
824: x_abort(c) {
825: /* x_zotc(c); */
826: return KINTR;
827: }
828:
829: static int
830: x_error(c) {
831: x_putc(BEL);
832: return KSTD;
833: }
834:
835: #if _BSD
836: #if defined TIOCGATC
837: static struct ttychars lchars, lcharsorig;
838: #else
839: static struct tchars tchars, tcharsorig;
840: static struct ltchars ltchars, ltcharsorig;
841: #endif
842: #endif
843:
844: #if _BSD || (COHERENT && !MWC_HP)
845: bool_t
846: set_xmode(onoff)
847: bool_t onoff;
848: {
849: bool_t prev;
850: typedef struct sgttyb Sgttyb;
851: static Sgttyb cb;
852: static Sgttyb orig;
853:
854: if (x_mode == onoff) return x_mode;
855: prev = x_mode;
856: x_mode = onoff;
857: if (onoff) {
858: if (first_time) {
859: (void)ioctl(0, TIOCGETP, &cb);
860: orig = cb;
861: cb.sg_flags &= ~ECHO;
862: cb.sg_flags |= CBREAK;
863: #if defined TIOCGATC
864: (void)ioctl(0, TIOCGATC, &lcharsorig);
865: lchars = lcharsorig;
866: lchars.tc_suspc = -1;
867: lchars.tc_dsuspc = -1;
868: lchars.tc_lnextc = -1;
869: lchars.tc_statc = -1;
870: lchars.tc_intrc = -1;
871: lchars.tc_quitc = -1;
872: lchars.tc_rprntc = -1;
873: #else
874: #if !COHERENT
875: (void)ioctl(0, TIOCGETC, &tcharsorig);
876: (void)ioctl(0, TIOCGLTC, <charsorig);
877: tchars = tcharsorig;
878: ltchars = ltcharsorig;
879: ltchars.t_suspc = -1;
880: ltchars.t_dsuspc = -1;
881: ltchars.t_lnextc = -1;
882: tchars.t_intrc = -1;
883: tchars.t_quitc = -1;
884: ltchars.t_rprntc = -1;
885: #endif
886: #endif
887: first_time = 0;
888: }
889: (void)ioctl(0, TIOCSETN, &cb);
890: #if defined TIOCGATC
891: (void)ioctl(0, TIOCSATC, &lchars);
892: #else
893: #if !COHERENT
894: (void)ioctl(0, TIOCSETC, &tchars);
895: (void)ioctl(0, TIOCSLTC, <chars);
896: #endif
897: #endif
898: }
899: else {
900: (void)ioctl(0, TIOCSETN, &orig);
901: #if defined TIOCGATC
902: (void)ioctl(0, TIOCSATC, &lcharsorig);
903: #else
904: #if !COHERENT
905: (void)ioctl(0, TIOCSETC, &tcharsorig);
906: (void)ioctl(0, TIOCSLTC, <charsorig);
907: #endif
908: #endif
909: }
910: return prev;
911: }
912:
913: #else /* !_BSD */
914:
915: bool_t
916: set_xmode(onoff)
917: bool_t onoff;
918: {
919: bool_t prev;
920: static struct termio cb, orig;
921:
922: if (x_mode == onoff) return x_mode;
923: prev = x_mode;
924: x_mode = onoff;
925:
926: if (onoff) {
927: if (first_time) {
928: (void)ioctl(0, TCGETA, &cb);
929: orig = cb;
930: #if defined _CRAY2 /* brain-damaged terminal handler */
931: cb.c_lflag &= ~(ICANON|ECHO);
932: /* rely on print routine to map '\n' to CR,LF */
933: #else
934: cb.c_iflag &= ~(INLCR|ICRNL);
935: #if _BSD_SYSV /* need to force CBREAK instead of RAW (need CRMOD on output) */
936: cb.c_lflag &= ~(ICANON|ECHO);
937: #else
938: cb.c_lflag &= ~(ISIG|ICANON|ECHO);
939: #endif
940: cb.c_cc[VTIME] = 0;
941: cb.c_cc[VMIN] = 1;
942: #endif /* _CRAY2 */
943: first_time = 0;
944: }
945: #if ! defined TCSETAW /* e.g. Cray-2 */
946: /* first wait for output to drain */
947: #if defined TCSBRK
948: (void)ioctl(0, TCSBRK, 1);
949: #else /* the following kludge is minimally intrusive, but sometimes fails */
950: (void)sleep((unsigned)1); /* fake it */
951: #endif
952: #endif
953: #if _BSD_SYSV || !defined(TCSETAW)
954: /* _BSD_SYSV needs to force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
955: (void)ioctl(0, TCSETA, &cb);
956: #else
957: (void)ioctl(0, TCSETAW, &cb);
958: #endif
959: }
960: else {
961: #if ! defined TCSETAW /* e.g. Cray-2 */
962: /* first wait for output to drain */
963: #if defined TCSBRK
964: (void)ioctl(0, TCSBRK, 1);
965: #else
966: /* doesn't seem to be necessary when leaving xmode */
967: /* (void)sleep((unsigned)1); */
968: #endif
969: #endif
970: #if _BSD_SYSV || !defined(TCSETAW)
971: /* _BSD_SYSV needs to force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
972: (void)ioctl(0, TCSETA, &orig);
973: #else
974: (void)ioctl(0, TCSETAW, &orig);
975: #endif
976: }
977: return prev;
978: }
979: #endif /* _BSD */
980:
981: static int
982: x_stuffreset(c)
983: {
984: #if defined TIOCSTI
985: (void)x_stuff(c);
986: return KINTR;
987: #else
988: x_zotc(c);
989: xcp = xep = xbuf;
990: *xcp = 0;
991: x_redraw(-1);
992: return KSTD;
993: #endif
994: }
995:
996: static int
997: x_stuff(c)
998: {
999: #if defined TIOCSTI
1000: char ch = c;
1001: bool_t savmode = set_xmode(FALSE);
1002:
1003: (void)ioctl(0, TIOCSTI, &ch);
1004: (void)set_xmode(savmode);
1005: x_redraw(-1);
1006: #endif
1007: return KSTD;
1008: }
1009:
1010: static void
1011: x_mapin(cp)
1012: char *cp;
1013: {
1014: char *op;
1015:
1016: op = cp;
1017: while (*cp) {
1018: /* XXX -- should handle \^ escape? */
1019: if (*cp == '^') {
1020: cp++;
1021: if (*cp >= '?') /* includes '?'; ASCII */
1022: *op++ = CTRL(*cp);
1023: else {
1024: *op++ = '^';
1025: cp--;
1026: }
1027: } else
1028: *op++ = *cp;
1029: cp++;
1030: }
1031: *op = 0;
1032: }
1033:
1034: static char *
1035: x_mapout(c)
1036: int c;
1037: {
1038: static char buf[8];
1039: register char *p = buf;
1040:
1041: if (c < ' ' || c == 0x7F) { /* ASCII */
1042: *p++ = '^';
1043: *p++ = (c == 0x7F) ? '?' : (c | 0x40);
1044: } else
1045: *p++ = c;
1046: *p = 0;
1047: return buf;
1048: }
1049:
1050: static void
1051: x_print(prefix, key)
1052: int prefix, key;
1053: {
1054: if (prefix == 1)
1055: shellf("%s", x_mapout(x_prefix1));
1056: if (prefix == 2)
1057: shellf("%s", x_mapout(x_prefix2));
1058: shellf("%s = ", x_mapout(key));
1059: if (x_tab[prefix][key]->xf_func != x_ins_string)
1060: shellf("%s\n", x_tab[prefix][key]->xf_name);
1061: else
1062: shellf("'%s'\n", x_atab[prefix][key]);
1063: }
1064:
1065: void
1066: x_bind(a1, a2, macro)
1067: char *a1, *a2;
1068: int macro; /* bind -m */
1069: {
1070: struct x_ftab Const *fp;
1071: int prefix, key;
1072: char *sp = NULL;
1073:
1074: if (x_tab == NULL)
1075: errorf("cannot bind, not a tty\n");
1076:
1077: if (a1 == NULL) {
1078: for (prefix = 0; prefix < 3; prefix++)
1079: for (key = 0; key < 0x80; key++) {
1080: fp = x_tab[prefix][key];
1081: if (fp == NULL ||
1082: fp->xf_func == x_insert || fp->xf_func == x_error)
1083: continue;
1084: x_print(prefix, key);
1085: }
1086: return;
1087: }
1088:
1089: x_mapin(a1);
1090: prefix = key = 0;
1091: for (;; a1++) {
1092: key = *a1;
1093: if (x_tab[prefix][key]->xf_func == x_meta1)
1094: prefix = 1;
1095: else
1096: if (x_tab[prefix][key]->xf_func == x_meta2)
1097: prefix = 2;
1098: else
1099: break;
1100: }
1101:
1102: if (a2 == NULL) {
1103: x_print(prefix, key);
1104: return;
1105: }
1106:
1107: if (*a2 == 0)
1108: fp = xft_insert;
1109: else if (!macro) {
1110: for (fp = x_ftab; fp->xf_func; fp++)
1111: if (strcmp(fp->xf_name, a2) == 0)
1112: break;
1113: if (fp->xf_func == NULL || (fp->xf_flags & XF_NOBIND))
1114: errorf("%s: no such function\n", a2);
1115: if (fp->xf_func == x_meta1)
1116: x_prefix1 = key;
1117: if (fp->xf_func == x_meta2)
1118: x_prefix2 = key;
1119: } else {
1120: fp = xft_ins_string;
1121: x_mapin(a2);
1122: sp = strsave(a2, AEDIT);
1123: }
1124:
1125: if ((x_tab[prefix][key]->xf_flags & XF_ALLOC) && x_atab[prefix][key])
1126: afree((Void *)x_atab[prefix][key], AEDIT);
1127: x_tab[prefix][key] = fp;
1128: x_atab[prefix][key] = sp;
1129: }
1130:
1131: void
1132: x_init()
1133: {
1134: register int i, j;
1135: struct x_ftab Const *fp;
1136:
1137: ainit(AEDIT);
1138:
1139: x_tab = alloc(sizeofN(*x_tab, 3), AEDIT);
1140: for (j = 0; j < 128; j++)
1141: x_tab[0][j] = xft_insert;
1142: for (i = 1; i < 3; i++)
1143: for (j = 0; j < 128; j++)
1144: x_tab[i][j] = xft_error;
1145: for (fp = x_ftab; fp->xf_func; fp++)
1146: if (fp->xf_db_char || fp->xf_db_tab)
1147: x_tab[fp->xf_db_tab][fp->xf_db_char] = fp;
1148:
1149: x_atab = alloc(sizeofN(*x_atab, 3), AEDIT);
1150: for (i = 1; i < 3; i++)
1151: for (j = 0; j < 128; j++)
1152: x_atab[i][j] = NULL;
1153: }
1154:
1155: #if SILLY
1156: static int
1157: x_game_of_life(c) {
1158: char newbuf [256+1];
1159: register char *ip, *op;
1160: int i, len;
1161:
1162: i = xep - xbuf;
1163: *xep = 0;
1164: len = x_size_str(xbuf);
1165: xcp = xbuf;
1166: memmove(newbuf+1, xbuf, i);
1167: newbuf[0] = 'A';
1168: newbuf[i] = 'A';
1169: for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++) {
1170: /* Empty space */
1171: if (*ip < '@' || *ip == '_' || *ip == 0x7F) {
1172: /* Two adults, make whoopee */
1173: if (ip[-1] < '_' && ip[1] < '_') {
1174: /* Make kid look like parents. */
1175: *op = '`' + ((ip[-1] + ip[1])/2)%32;
1176: if (*op == 0x7F) /* Birth defect */
1177: *op = '`';
1178: }
1179: else
1180: *op = ' '; /* nothing happens */
1181: continue;
1182: }
1183: /* Child */
1184: if (*ip > '`') {
1185: /* All alone, dies */
1186: if (ip[-1] == ' ' && ip[1] == ' ')
1187: *op = ' ';
1188: else /* Gets older */
1189: *op = *ip-'`'+'@';
1190: continue;
1191: }
1192: /* Adult */
1193: /* Overcrowded, dies */
1194: if (ip[-1] >= '@' && ip[1] >= '@') {
1195: *op = ' ';
1196: continue;
1197: }
1198: *op = *ip;
1199: }
1200: *op = 0;
1201: x_redraw(len);
1202: return KSTD;
1203: }
1204: #endif
1205:
1206: /*
1207: * File/command name completion routines
1208: */
1209:
1210: /* type: 0 for list, 1 for completion */
1211:
1212: static XPtrV words;
1213:
1214: static void
1215: add_stash(dirnam, name)
1216: char *dirnam; /* directory name, if file */
1217: char *name;
1218: {
1219: char *cp;
1220: register int type = 0; /* '*' if executable, '/' if directory, else 0 */
1221: register int len = strlen(name);
1222:
1223: /* determine file type */
1224: if (dirnam) {
1225: struct stat statb;
1226: char *buf = alloc((size_t)(strlen(dirnam)+len+2), ATEMP);
1227:
1228: if (strcmp(dirnam, ".") == 0)
1229: *buf = '\0';
1230: else if (strcmp(dirnam, "/") == 0)
1231: (void)strcpy(buf, "/");
1232: else
1233: (void)strcat(strcpy(buf, dirnam), "/");
1234: (void)strcat(buf, name);
1235: if (stat(buf, &statb)==0)
1236: if (S_ISDIR(statb.st_mode))
1237: type = '/';
1238: else if (S_ISREG(statb.st_mode) && eaccess(buf, 01)==0)
1239: type = '*';
1240: if (type)
1241: ++len;
1242: afree((Void *)buf, ATEMP);
1243: }
1244:
1245: if (len > x_maxlen)
1246: x_maxlen = len;
1247:
1248: /* stash name for later sorting */
1249: cp = alloc((size_t)(len+1), ATEMP);
1250: (void)strcpy(cp = alloc((size_t)(len+1), ATEMP), name);
1251: if (dirnam && type) { /* append file type indicator */
1252: cp[len-1] = type;
1253: cp[len] = '\0';
1254: }
1255: XPput(words, cp);
1256: }
1257:
1258: static void
1259: list_stash()
1260: {
1261: register char **array, **record;
1262: int items = 0, tabstop, loc, nrows, jump, offset;
1263:
1264: items = XPsize(words);
1265: array = (char**) XPptrv(words);
1266: if (items == 0)
1267: return;
1268: qsortp(XPptrv(words), (size_t)XPsize(words), xstrcmp);
1269:
1270: /* print names */
1271: x_maxlen = (x_maxlen/8 + 1) * 8; /* column width */
1272: nrows = (items-1) / (x_cols/x_maxlen) + 1;
1273: for (offset = 0; offset < nrows; ++offset) {
1274: tabstop = loc = 0;
1275: x_putc('\n');
1276: for (jump = 0; offset+jump < items; jump += nrows) {
1277: if (jump)
1278: while (loc < tabstop) {
1279: x_putc('\t');
1280: loc = (loc/8 + 1) * 8;
1281: }
1282: record = array + (offset + jump);
1283: x_puts(*record);
1284: afree((Void *)*record, ATEMP);
1285: loc += strlen(*record);
1286: tabstop += x_maxlen; /* next tab stop */
1287: }
1288: }
1289:
1290: afree((Void*)array, ATEMP);
1291: x_redraw(-1);
1292: }
1293:
1294: static int
1295: x_comp_comm(c) {
1296: compl_command(1);
1297: return KSTD;
1298: }
1299: static int
1300: x_list_comm(c) {
1301: compl_command(0);
1302: return KSTD;
1303: }
1304: static int
1305: x_complete(c) {
1306: compl_dec(1);
1307: return KSTD;
1308: }
1309: static int
1310: x_enumerate(c) {
1311: compl_dec(0);
1312: return KSTD;
1313: }
1314: static int
1315: x_comp_file(c) {
1316: compl_file(1);
1317: return KSTD;
1318: }
1319: static int
1320: x_list_file(c) {
1321: compl_file(0);
1322: return KSTD;
1323: }
1324:
1325: static void
1326: compl_dec(type)
1327: {
1328: char *cp;
1329: cp = xcp;
1330:
1331: while (cp != xbuf && !isfs(*cp))
1332: cp--;
1333: if (cp == xbuf && strchr(cp, '/') == NULL)
1334: compl_command(type);
1335: else
1336: compl_file(type);
1337: }
1338:
1339: static void
1340: compl_file(type)
1341: {
1342: char *str;
1343: register char *cp, *xp;
1344: char *lastp;
1345: char *dirnam;
1346: char buf [256+1];
1347: char bug [256+1];
1348: DIR *dirp;
1349: struct dirent *dp;
1350: long loc = -1;
1351: int len;
1352: int multi = 0;
1353:
1354: str = xcp;
1355: cp = buf;
1356: xp = str;
1357: while (xp != xbuf) {
1358: --xp;
1359: if (isfs(*xp)) {
1360: xp++;
1361: break;
1362: }
1363: }
1364: if (digit(*xp) && (xp[1] == '<' || xp[1] == '>'))
1365: xp++;
1366: while (*xp == '<' || *xp == '>')
1367: xp++;
1368: if (type)
1369: while (*xcp && !isfs(*xcp))
1370: x_zotc(*xcp++);
1371: else {
1372: x_maxlen = 0;
1373: XPinit(words, 16);
1374: }
1375: while (*xp && !isfs(*xp))
1376: *cp++ = *xp++;
1377:
1378: *cp = 0;
1379: strcpy(buf, cp = substitute(buf, DOTILDE));
1380: afree((Void*)cp, ATEMP);
1381: lastp = strrchr(buf, '/');
1382: if (lastp)
1383: *lastp = 0;
1384:
1385: dirnam = (lastp == NULL) ? "." : (lastp == buf) ? "/" : buf;
1386: dirp = opendir(dirnam);
1387: if (dirp == NULL) {
1388: x_putc(BEL);
1389: return;
1390: }
1391:
1392: if (lastp == NULL)
1393: lastp = buf;
1394: else
1395: lastp++;
1396: len = strlen(lastp);
1397:
1398: while ((dp = readdir(dirp)) != NULL) {
1399: cp = dp->d_name;
1400: if (cp[0] == '.' &&
1401: (cp[1] == '\0' || cp[1] == '.' && cp[2] == '\0'))
1402: continue; /* always ignore . and .. */
1403: if (strncmp(lastp, cp, len) == 0)
1404: if (type) {
1405: if (loc == -1) {
1406: (void)strcpy(bug, cp);
1407: loc = strlen(cp);
1408: } else {
1409: multi = 1;
1410: loc = strmatch(bug, cp);
1411: bug[loc] = 0;
1412: }
1413: } else
1414: add_stash(dirnam, cp);
1415: }
1416: (void)closedir(dirp);
1417:
1418: if (type) {
1419: if (loc <= 0) {
1420: x_putc(BEL);
1421: return;
1422: }
1423: cp = bug + len;
1424: x_ins(cp);
1425: if (!multi) {
1426: struct stat statb;
1427: if (lastp == buf)
1428: buf[0] = 0;
1429: else if (lastp == buf + 1) {
1430: buf[1] = 0;
1431: buf[0] = '/';
1432: } else
1433: (void)strcat(buf, "/");
1434: (void)strcat(buf, bug);
1435: if (stat(buf, &statb) == 0 && S_ISDIR(statb.st_mode))
1436: x_ins("/");
1437: else
1438: x_ins(" ");
1439: }
1440: } else
1441: list_stash();
1442: }
1443:
1444: static void
1445: compl_command(type)
1446: {
1447: register struct tbl *tp;
1448: char *str;
1449: char buf [256+1];
1450: char bug [256+1];
1451: char *xp;
1452: char *cp;
1453: int len;
1454: int multi;
1455: int loc;
1456:
1457: str = xcp;
1458: cp = buf;
1459: xp = str;
1460: while (xp != xbuf) {
1461: --xp;
1462: if (isfs(*xp)) {
1463: xp++;
1464: break;
1465: }
1466: }
1467: if (type)
1468: while (*xcp && !isfs(*xcp))
1469: x_zotc(*xcp++);
1470: else {
1471: x_maxlen = 0;
1472: XPinit(words, 16);
1473: }
1474: while (*xp && !isfs(*xp))
1475: *cp++ = *xp++;
1476: *cp = 0;
1477:
1478: len = strlen(buf);
1479: loc = -1;
1480: multi = 0;
1481:
1482: for (twalk(&commands); (tp = tnext()) != NULL; ) {
1483: int klen;
1484:
1485: if (!(tp->flag&ISSET))
1486: continue;
1487: klen = strlen(tp->name);
1488: if (klen < len)
1489: return;
1490: if (strncmp(buf, tp->name, len) ==0)
1491: if (type) {
1492: if (loc == -1) {
1493: (void)strcpy(bug, tp->name);
1494: loc = klen;
1495: } else {
1496: multi = 1;
1497: loc = strmatch(bug, tp->name);
1498: bug[loc] = 0;
1499: }
1500: } else
1501: add_stash((char *)0, tp->name);
1502: }
1503:
1504: if (type) {
1505: if (loc <= 0) {
1506: x_putc(BEL);
1507: return;
1508: }
1509: cp = bug + len;
1510: x_ins(cp);
1511: if (!multi)
1512: x_ins(" ");
1513: } else
1514: list_stash();
1515: }
1516:
1517: static int
1518: strmatch(s1, s2)
1519: register char *s1, *s2;
1520: {
1521: register char *p;
1522:
1523: for (p = s1; *p == *s2++ && *p != 0; p++)
1524: ;
1525: return p - s1;
1526: }
1527:
1528: #endif /* EDIT */
1529:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.