Annotation of src/usr.bin/mg/echo.c, Revision 1.68
1.68 ! lum 1: /* $OpenBSD: echo.c,v 1.67 2021/03/01 10:51:14 lum Exp $ */
1.35 kjell 2:
3: /* This file is in the public domain. */
4:
1.1 deraadt 5: /*
1.3 millert 6: * Echo line reading and writing.
1.1 deraadt 7: *
1.3 millert 8: * Common routines for reading and writing characters in the echo line area
9: * of the display screen. Used by the entire known universe.
1.1 deraadt 10: */
1.3 millert 11:
1.58 bcallah 12: #include <sys/queue.h>
13: #include <signal.h>
14: #include <stdarg.h>
15: #include <stdio.h>
16: #include <stdlib.h>
17: #include <string.h>
18: #include <term.h>
19:
1.3 millert 20: #include "def.h"
1.58 bcallah 21: #include "funmap.h"
1.3 millert 22: #include "key.h"
23: #include "macro.h"
1.1 deraadt 24:
1.60 guenther 25: static char *veread(const char *, char *, size_t, int, va_list)
26: __attribute__((__format__ (printf, 1, 0)));
1.38 kjell 27: static int complt(int, int, char *, size_t, int, int *);
1.41 kjell 28: static int complt_list(int, char *, int);
1.60 guenther 29: static void eformat(const char *, va_list)
1.66 jasper 30: __attribute__((__format__ (printf, 1, 0)));
1.31 db 31: static void eputi(int, int);
32: static void eputl(long, int);
33: static void eputs(const char *);
34: static void eputc(char);
1.40 deraadt 35: static struct list *copy_list(struct list *);
1.1 deraadt 36:
1.14 mickey 37: int epresf = FALSE; /* stuff in echo line flag */
1.1 deraadt 38:
39: /*
40: * Erase the echo line.
41: */
1.10 art 42: void
1.21 vincent 43: eerase(void)
1.2 millert 44: {
1.1 deraadt 45: ttcolor(CTEXT);
1.2 millert 46: ttmove(nrow - 1, 0);
1.1 deraadt 47: tteeol();
48: ttflush();
49: epresf = FALSE;
50: }
51:
52: /*
1.14 mickey 53: * Ask a "yes" or "no" question. Return ABORT if the user answers the
54: * question with the abort ("^G") character. Return FALSE for "no" and
55: * TRUE for "yes". No formatting services are available. No newline
1.3 millert 56: * required.
1.1 deraadt 57: */
1.2 millert 58: int
1.21 vincent 59: eyorn(const char *sp)
1.2 millert 60: {
1.3 millert 61: int s;
1.1 deraadt 62:
1.2 millert 63: if (inmacro)
1.31 db 64: return (TRUE);
1.50 lum 65:
1.1 deraadt 66: ewprintf("%s? (y or n) ", sp);
67: for (;;) {
68: s = getkey(FALSE);
1.64 lum 69: if (s == 'y' || s == 'Y' || s == ' ') {
70: ewprintf("");
1.31 db 71: return (TRUE);
1.64 lum 72: }
73: if (s == 'n' || s == 'N' || s == CCHR('M')) {
74: ewprintf("");
1.31 db 75: return (FALSE);
1.64 lum 76: }
77: if (s == CCHR('G')) {
78: ewprintf("");
1.31 db 79: return (ctrlg(FFRAND, 1));
1.64 lum 80: }
1.1 deraadt 81: ewprintf("Please answer y or n. %s? (y or n) ", sp);
1.52 florian 82: }
83: /* NOTREACHED */
84: }
85:
86: /*
87: * Ask a "yes", "no" or "revert" question. Return ABORT if the user answers
88: * the question with the abort ("^G") character. Return FALSE for "no",
89: * TRUE for "yes" and REVERT for "revert". No formatting services are
90: * available. No newline required.
91: */
92: int
93: eynorr(const char *sp)
94: {
95: int s;
96:
97: if (inmacro)
98: return (TRUE);
99:
100: ewprintf("%s? (y, n or r) ", sp);
101: for (;;) {
102: s = getkey(FALSE);
1.64 lum 103: if (s == 'y' || s == 'Y' || s == ' ') {
104: ewprintf("");
1.52 florian 105: return (TRUE);
1.64 lum 106: }
107: if (s == 'n' || s == 'N' || s == CCHR('M')) {
108: ewprintf("");
1.52 florian 109: return (FALSE);
1.64 lum 110: }
111: if (s == 'r' || s == 'R') {
112: ewprintf("");
1.52 florian 113: return (REVERT);
1.64 lum 114: }
115: if (s == CCHR('G')) {
116: ewprintf("");
1.52 florian 117: return (ctrlg(FFRAND, 1));
1.64 lum 118: }
1.52 florian 119: ewprintf("Please answer y, n or r.");
1.1 deraadt 120: }
1.2 millert 121: /* NOTREACHED */
1.1 deraadt 122: }
123:
124: /*
1.3 millert 125: * Like eyorn, but for more important questions. User must type all of
1.31 db 126: * "yes" or "no" and the trailing newline.
1.1 deraadt 127: */
1.2 millert 128: int
1.21 vincent 129: eyesno(const char *sp)
1.2 millert 130: {
1.29 vincent 131: char buf[64], *rep;
1.1 deraadt 132:
1.2 millert 133: if (inmacro)
1.31 db 134: return (TRUE);
1.50 lum 135:
1.36 kjell 136: rep = eread("%s? (yes or no) ", buf, sizeof(buf),
137: EFNUL | EFNEW | EFCR, sp);
1.1 deraadt 138: for (;;) {
1.64 lum 139: if (rep == NULL) {
140: ewprintf("");
1.31 db 141: return (ABORT);
1.64 lum 142: }
1.29 vincent 143: if (rep[0] != '\0') {
1.1 deraadt 144: if (macrodef) {
1.40 deraadt 145: struct line *lp = maclcur;
1.1 deraadt 146:
1.2 millert 147: maclcur = lp->l_bp;
148: maclcur->l_fp = lp->l_fp;
1.43 kjell 149: free(lp);
1.1 deraadt 150: }
1.65 jsg 151: if (strcasecmp(rep, "yes") == 0) {
1.64 lum 152: ewprintf("");
1.31 db 153: return (TRUE);
1.64 lum 154: }
1.65 jsg 155: if (strcasecmp(rep, "no") == 0) {
1.64 lum 156: ewprintf("");
1.31 db 157: return (FALSE);
1.64 lum 158: }
1.1 deraadt 159: }
1.36 kjell 160: rep = eread("Please answer yes or no. %s? (yes or no) ",
161: buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp);
1.1 deraadt 162: }
1.2 millert 163: /* NOTREACHED */
1.1 deraadt 164: }
1.2 millert 165:
1.1 deraadt 166: /*
1.14 mickey 167: * This is the general "read input from the echo line" routine. The basic
1.3 millert 168: * idea is that the prompt string "prompt" is written to the echo line, and
1.14 mickey 169: * a one line reply is read back into the supplied "buf" (with maximum
1.36 kjell 170: * length "len").
171: * XXX: When checking for an empty return value, always check rep, *not* buf
172: * as buf may be freed in pathological cases.
1.1 deraadt 173: */
1.29 vincent 174: char *
175: eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
1.1 deraadt 176: {
1.3 millert 177: va_list ap;
1.31 db 178: char *rep;
1.29 vincent 179:
1.2 millert 180: va_start(ap, flag);
1.29 vincent 181: rep = veread(fmt, buf, nbuf, flag, ap);
1.2 millert 182: va_end(ap);
1.31 db 183: return (rep);
1.1 deraadt 184: }
185:
1.29 vincent 186: static char *
187: veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
1.2 millert 188: {
1.38 kjell 189: int dynbuf = (buf == NULL);
190: int cpos, epos; /* cursor, end position in buf */
191: int c, i, y;
1.62 jasper 192: int cplflag; /* display completion list */
1.38 kjell 193: int cwin = FALSE; /* completion list created */
1.62 jasper 194: int mr, ml; /* match left/right arrows */
195: int esc; /* position in esc pattern */
1.40 deraadt 196: struct buffer *bp; /* completion list buffer */
197: struct mgwin *wp; /* window for compl list */
1.38 kjell 198: int match; /* esc match found */
199: int cc, rr; /* saved ttcol, ttrow */
200: char *ret; /* return value */
1.1 deraadt 201:
1.47 kjell 202: static char emptyval[] = ""; /* XXX hackish way to return err msg*/
203:
1.2 millert 204: if (inmacro) {
1.29 vincent 205: if (dynbuf) {
206: if ((buf = malloc(maclcur->l_used + 1)) == NULL)
1.31 db 207: return (NULL);
1.29 vincent 208: } else if (maclcur->l_used >= nbuf)
1.31 db 209: return (NULL);
1.2 millert 210: bcopy(maclcur->l_text, buf, maclcur->l_used);
211: buf[maclcur->l_used] = '\0';
212: maclcur = maclcur->l_fp;
1.31 db 213: return (buf);
1.1 deraadt 214: }
1.38 kjell 215: epos = cpos = 0;
216: ml = mr = esc = 0;
217: cplflag = FALSE;
218:
1.2 millert 219: if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
1.1 deraadt 220: ttcolor(CTEXT);
1.2 millert 221: ttmove(nrow - 1, 0);
1.1 deraadt 222: epresf = TRUE;
223: } else
224: eputc(' ');
225: eformat(fp, ap);
1.16 art 226: if ((flag & EFDEF) != 0) {
1.29 vincent 227: if (buf == NULL)
1.31 db 228: return (NULL);
1.16 art 229: eputs(buf);
1.38 kjell 230: epos = cpos += strlen(buf);
1.16 art 231: }
1.1 deraadt 232: tteeol();
233: ttflush();
234: for (;;) {
235: c = getkey(FALSE);
1.51 lum 236: if ((flag & EFAUTO) != 0 && c == CCHR('I')) {
1.38 kjell 237: if (cplflag == TRUE) {
1.41 kjell 238: complt_list(flag, buf, cpos);
1.38 kjell 239: cwin = TRUE;
240: } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
241: cplflag = TRUE;
242: epos += i;
243: cpos = epos;
244: }
1.1 deraadt 245: continue;
246: }
1.38 kjell 247: cplflag = FALSE;
248:
249: if (esc > 0) { /* ESC sequence started */
250: match = 0;
251: if (ml == esc && key_left[ml] && c == key_left[ml]) {
252: match++;
253: if (key_left[++ml] == '\0') {
254: c = CCHR('B');
255: esc = 0;
256: }
257: }
258: if (mr == esc && key_right[mr] && c == key_right[mr]) {
259: match++;
260: if (key_right[++mr] == '\0') {
261: c = CCHR('F');
262: esc = 0;
263: }
264: }
265: if (match == 0) {
266: esc = 0;
267: continue;
268: /* hack. how do we know esc pattern is done? */
269: }
270: if (esc > 0) {
271: esc++;
272: continue;
273: }
1.1 deraadt 274: }
1.39 deraadt 275: switch (c) {
1.38 kjell 276: case CCHR('A'): /* start of line */
277: while (cpos > 0) {
278: if (ISCTRL(buf[--cpos]) != FALSE) {
279: ttputc('\b');
280: --ttcol;
281: }
282: ttputc('\b');
283: --ttcol;
284: }
285: ttflush();
286: break;
287: case CCHR('D'):
288: if (cpos != epos) {
289: tteeol();
290: epos--;
291: rr = ttrow;
292: cc = ttcol;
293: for (i = cpos; i < epos; i++) {
294: buf[i] = buf[i + 1];
295: eputc(buf[i]);
296: }
297: ttmove(rr, cc);
298: ttflush();
299: }
300: break;
301: case CCHR('E'): /* end of line */
302: while (cpos < epos) {
303: eputc(buf[cpos++]);
304: }
305: ttflush();
306: break;
307: case CCHR('B'): /* back */
308: if (cpos > 0) {
309: if (ISCTRL(buf[--cpos]) != FALSE) {
310: ttputc('\b');
311: --ttcol;
312: }
313: ttputc('\b');
314: --ttcol;
315: ttflush();
316: }
317: break;
318: case CCHR('F'): /* forw */
319: if (cpos < epos) {
320: eputc(buf[cpos++]);
321: ttflush();
322: }
323: break;
324: case CCHR('Y'): /* yank from kill buffer */
325: i = 0;
1.67 lum 326: while ((y = kremove(i++)) >= 0 && y != *curbp->b_nlchr) {
1.38 kjell 327: int t;
328: if (dynbuf && epos + 1 >= nbuf) {
329: void *newp;
330: size_t newsize = epos + epos + 16;
331: if ((newp = realloc(buf, newsize))
332: == NULL)
1.47 kjell 333: goto memfail;
1.38 kjell 334: buf = newp;
335: nbuf = newsize;
336: }
1.47 kjell 337: if (!dynbuf && epos + 1 >= nbuf) {
1.56 lum 338: dobeep();
1.68 ! lum 339: ewprintf("Line too long. Press Control-g to escape.");
! 340: goto skipkey;
1.47 kjell 341: }
1.38 kjell 342: for (t = epos; t > cpos; t--)
343: buf[t] = buf[t - 1];
344: buf[cpos++] = (char)y;
345: epos++;
346: eputc((char)y);
347: cc = ttcol;
348: rr = ttrow;
349: for (t = cpos; t < epos; t++)
350: eputc(buf[t]);
351: ttmove(rr, cc);
352: }
353: ttflush();
354: break;
355: case CCHR('K'): /* copy here-EOL to kill buffer */
356: kdelete();
357: for (i = cpos; i < epos; i++)
358: kinsert(buf[i], KFORW);
359: tteeol();
360: epos = cpos;
361: ttflush();
362: break;
363: case CCHR('['):
364: ml = mr = esc = 1;
365: break;
1.2 millert 366: case CCHR('J'):
1.3 millert 367: c = CCHR('M');
1.22 vincent 368: /* FALLTHROUGH */
1.3 millert 369: case CCHR('M'): /* return, done */
1.36 kjell 370: /* if there's nothing in the minibuffer, abort */
1.38 kjell 371: if (epos == 0 && !(flag & EFNUL)) {
1.33 cloder 372: (void)ctrlg(FFRAND, 0);
373: ttflush();
374: return (NULL);
375: }
1.2 millert 376: if ((flag & EFFUNC) != 0) {
1.38 kjell 377: if (complt(flag, c, buf, nbuf, epos, &i)
378: == FALSE)
1.1 deraadt 379: continue;
1.2 millert 380: if (i > 0)
1.38 kjell 381: epos += i;
1.1 deraadt 382: }
1.38 kjell 383: buf[epos] = '\0';
1.2 millert 384: if ((flag & EFCR) != 0) {
1.1 deraadt 385: ttputc(CCHR('M'));
386: ttflush();
387: }
1.2 millert 388: if (macrodef) {
1.40 deraadt 389: struct line *lp;
1.1 deraadt 390:
1.47 kjell 391: if ((lp = lalloc(cpos)) == NULL)
392: goto memfail;
1.2 millert 393: lp->l_fp = maclcur->l_fp;
394: maclcur->l_fp = lp;
395: lp->l_bp = maclcur;
396: maclcur = lp;
397: bcopy(buf, lp->l_text, cpos);
1.1 deraadt 398: }
1.38 kjell 399: ret = buf;
1.1 deraadt 400: goto done;
1.3 millert 401: case CCHR('G'): /* bell, abort */
1.1 deraadt 402: eputc(CCHR('G'));
1.10 art 403: (void)ctrlg(FFRAND, 0);
1.1 deraadt 404: ttflush();
1.38 kjell 405: ret = NULL;
406: goto done;
1.3 millert 407: case CCHR('H'): /* rubout, erase */
1.14 mickey 408: case CCHR('?'):
1.1 deraadt 409: if (cpos != 0) {
1.38 kjell 410: y = buf[--cpos];
411: epos--;
1.1 deraadt 412: ttputc('\b');
1.38 kjell 413: ttcol--;
414: if (ISCTRL(y) != FALSE) {
415: ttputc('\b');
416: ttcol--;
417: }
418: rr = ttrow;
419: cc = ttcol;
420: for (i = cpos; i < epos; i++) {
421: buf[i] = buf[i + 1];
422: eputc(buf[i]);
423: }
1.1 deraadt 424: ttputc(' ');
1.38 kjell 425: if (ISCTRL(y) != FALSE) {
1.1 deraadt 426: ttputc(' ');
427: ttputc('\b');
428: }
1.38 kjell 429: ttputc('\b');
430: ttmove(rr, cc);
1.1 deraadt 431: ttflush();
432: }
433: break;
1.3 millert 434: case CCHR('X'): /* kill line */
435: case CCHR('U'):
1.1 deraadt 436: while (cpos != 0) {
437: ttputc('\b');
438: ttputc(' ');
439: ttputc('\b');
440: --ttcol;
441: if (ISCTRL(buf[--cpos]) != FALSE) {
442: ttputc('\b');
443: ttputc(' ');
444: ttputc('\b');
445: --ttcol;
446: }
1.38 kjell 447: epos--;
1.1 deraadt 448: }
449: ttflush();
450: break;
1.3 millert 451: case CCHR('W'): /* kill to beginning of word */
1.1 deraadt 452: while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
453: ttputc('\b');
454: ttputc(' ');
455: ttputc('\b');
456: --ttcol;
457: if (ISCTRL(buf[--cpos]) != FALSE) {
458: ttputc('\b');
459: ttputc(' ');
460: ttputc('\b');
461: --ttcol;
462: }
1.46 kjell 463: epos--;
1.1 deraadt 464: }
465: while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
466: ttputc('\b');
467: ttputc(' ');
468: ttputc('\b');
469: --ttcol;
470: if (ISCTRL(buf[--cpos]) != FALSE) {
471: ttputc('\b');
472: ttputc(' ');
473: ttputc('\b');
474: --ttcol;
475: }
1.46 kjell 476: epos--;
1.1 deraadt 477: }
478: ttflush();
479: break;
1.2 millert 480: case CCHR('\\'):
1.3 millert 481: case CCHR('Q'): /* quote next */
482: c = getkey(FALSE);
1.22 vincent 483: /* FALLTHROUGH */
1.30 deraadt 484: default:
1.38 kjell 485: if (dynbuf && epos + 1 >= nbuf) {
1.29 vincent 486: void *newp;
1.38 kjell 487: size_t newsize = epos + epos + 16;
488: if ((newp = realloc(buf, newsize)) == NULL)
1.47 kjell 489: goto memfail;
1.29 vincent 490: buf = newp;
491: nbuf = newsize;
492: }
1.47 kjell 493: if (!dynbuf && epos + 1 >= nbuf) {
1.56 lum 494: dobeep();
1.68 ! lum 495: ewprintf("Line too long. Press Control-g to escape.");
! 496: goto skipkey;
1.47 kjell 497: }
1.38 kjell 498: for (i = epos; i > cpos; i--)
499: buf[i] = buf[i - 1];
500: buf[cpos++] = (char)c;
501: epos++;
502: eputc((char)c);
503: cc = ttcol;
504: rr = ttrow;
505: for (i = cpos; i < epos; i++)
506: eputc(buf[i]);
507: ttmove(rr, cc);
508: ttflush();
1.1 deraadt 509: }
1.68 ! lum 510:
! 511: skipkey: /* ignore key press */
! 512: ;
1.1 deraadt 513: }
1.7 art 514: done:
1.38 kjell 515: if (cwin == TRUE) {
516: /* blow away cpltion window */
517: bp = bfind("*Completions*", TRUE);
1.49 kjell 518: if ((wp = popbuf(bp, WEPHEM)) != NULL) {
519: if (wp->w_flag & WEPHEM) {
520: curwp = wp;
521: delwind(FFRAND, 1);
522: } else {
523: killbuffer(bp);
524: }
1.38 kjell 525: }
526: }
527: return (ret);
1.47 kjell 528: memfail:
529: if (dynbuf && buf)
530: free(buf);
1.56 lum 531: dobeep();
1.38 kjell 532: ewprintf("Out of memory");
1.47 kjell 533: return (emptyval);
1.1 deraadt 534: }
535:
536: /*
1.31 db 537: * Do completion on a list of objects.
1.38 kjell 538: * c is SPACE, TAB, or CR
539: * return TRUE if matched (or partially matched)
540: * FALSE is result is ambiguous,
541: * ABORT on error.
1.1 deraadt 542: */
1.2 millert 543: static int
1.38 kjell 544: complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
1.3 millert 545: {
1.40 deraadt 546: struct list *lh, *lh2;
547: struct list *wholelist = NULL;
1.3 millert 548: int i, nxtra, nhits, bxtra, msglen, nshown;
549: int wflag = FALSE;
550: char *msg;
551:
552: lh = lh2 = NULL;
1.2 millert 553:
554: if ((flags & EFFUNC) != 0) {
555: buf[cpos] = '\0';
1.32 otto 556: wholelist = lh = complete_function_list(buf);
1.9 art 557: } else if ((flags & EFBUF) != 0) {
1.6 art 558: lh = &(bheadp->b_list);
1.9 art 559: } else if ((flags & EFFILE) != 0) {
1.6 art 560: buf[cpos] = '\0';
561: wholelist = lh = make_file_list(buf);
562: } else
563: panic("broken complt call: flags");
564:
565: if (c == ' ')
566: wflag = TRUE;
567: else if (c != '\t' && c != CCHR('M'))
568: panic("broken complt call: c");
569:
570: nhits = 0;
571: nxtra = HUGE;
572:
573: for (; lh != NULL; lh = lh->l_next) {
574: if (memcmp(buf, lh->l_name, cpos) != 0)
575: continue;
1.2 millert 576: if (nhits == 0)
1.6 art 577: lh2 = lh;
578: ++nhits;
579: if (lh->l_name[cpos] == '\0')
1.38 kjell 580: nxtra = -1; /* exact match */
1.3 millert 581: else {
1.6 art 582: bxtra = getxtra(lh, lh2, cpos, wflag);
583: if (bxtra < nxtra)
584: nxtra = bxtra;
585: lh2 = lh;
586: }
587: }
588: if (nhits == 0)
589: msg = " [No match]";
590: else if (nhits > 1 && nxtra == 0)
1.37 kjell 591: msg = " [Ambiguous. Ctrl-G to cancel]";
1.6 art 592: else {
593: /*
594: * Being lazy - ought to check length, but all things
595: * autocompleted have known types/lengths.
596: */
597: if (nxtra < 0 && nhits > 1 && c == ' ')
1.38 kjell 598: nxtra = 1; /* ??? */
1.29 vincent 599: for (i = 0; i < nxtra && cpos < nbuf; ++i) {
1.6 art 600: buf[cpos] = lh2->l_name[cpos];
601: eputc(buf[cpos++]);
1.1 deraadt 602: }
1.38 kjell 603: /* XXX should grow nbuf */
1.6 art 604: ttflush();
605: free_file_list(wholelist);
1.38 kjell 606: *nx = nxtra;
607: if (nxtra < 0 && c != CCHR('M')) /* exact */
608: *nx = 0;
609: return (TRUE);
1.1 deraadt 610: }
1.3 millert 611:
1.2 millert 612: /*
1.31 db 613: * wholelist is NULL if we are doing buffers. Want to free lists
1.2 millert 614: * that were created for us, but not the buffer list!
615: */
1.1 deraadt 616: free_file_list(wholelist);
1.3 millert 617:
1.31 db 618: /* Set up backspaces, etc., being mindful of echo line limit. */
1.1 deraadt 619: msglen = strlen(msg);
620: nshown = (ttcol + msglen + 2 > ncol) ?
1.2 millert 621: ncol - ttcol - 2 : msglen;
1.1 deraadt 622: eputs(msg);
1.2 millert 623: ttcol -= (i = nshown); /* update ttcol! */
624: while (i--) /* move back before msg */
1.1 deraadt 625: ttputc('\b');
1.2 millert 626: ttflush(); /* display to user */
1.1 deraadt 627: i = nshown;
1.3 millert 628: while (i--) /* blank out on next flush */
1.1 deraadt 629: eputc(' ');
1.2 millert 630: ttcol -= (i = nshown); /* update ttcol on BS's */
1.1 deraadt 631: while (i--)
1.2 millert 632: ttputc('\b'); /* update ttcol again! */
1.38 kjell 633: *nx = nxtra;
634: return ((nhits > 0) ? TRUE : FALSE);
1.1 deraadt 635: }
636:
637: /*
1.31 db 638: * Do completion on a list of objects, listing instead of completing.
1.1 deraadt 639: */
1.2 millert 640: static int
1.41 kjell 641: complt_list(int flags, char *buf, int cpos)
1.3 millert 642: {
1.40 deraadt 643: struct list *lh, *lh2, *lh3;
644: struct list *wholelist = NULL;
645: struct buffer *bp;
1.3 millert 646: int i, maxwidth, width;
647: int preflen = 0;
648: int oldrow = ttrow;
649: int oldcol = ttcol;
650: int oldhue = tthue;
1.17 art 651: char *linebuf;
1.26 millert 652: size_t linesize, len;
1.43 kjell 653: char *cp;
1.3 millert 654:
655: lh = NULL;
1.1 deraadt 656:
657: ttflush();
658:
1.38 kjell 659: /* The results are put into a completion buffer. */
660: bp = bfind("*Completions*", TRUE);
1.2 millert 661: if (bclear(bp) == FALSE)
1.31 db 662: return (FALSE);
1.61 jasper 663: bp->b_flag |= BFREADONLY;
1.2 millert 664:
1.24 vincent 665: /*
1.31 db 666: * First get the list of objects. This list may contain only
1.24 vincent 667: * the ones that complete what has been typed, or may be the
668: * whole list of all objects of this type. They are filtered
669: * later in any case. Set wholelist if the list has been
670: * cons'ed up just for us, so we can free it later. We have
671: * to copy the buffer list for this function even though we
672: * didn't for complt. The sorting code does destructive
673: * changes to the list, which we don't want to happen to the
674: * main buffer list!
675: */
676: if ((flags & EFBUF) != 0)
677: wholelist = lh = copy_list(&(bheadp->b_list));
678: else if ((flags & EFFUNC) != 0) {
679: buf[cpos] = '\0';
1.32 otto 680: wholelist = lh = complete_function_list(buf);
1.24 vincent 681: } else if ((flags & EFFILE) != 0) {
682: buf[cpos] = '\0';
683: wholelist = lh = make_file_list(buf);
1.2 millert 684: /*
1.24 vincent 685: * We don't want to display stuff up to the / for file
686: * names preflen is the list of a prefix of what the
687: * user typed that should not be displayed.
1.2 millert 688: */
1.24 vincent 689: cp = strrchr(buf, '/');
690: if (cp)
691: preflen = cp - buf + 1;
692: } else
693: panic("broken complt call: flags");
1.1 deraadt 694:
1.24 vincent 695: /*
696: * Sort the list, since users expect to see it in alphabetic
697: * order.
698: */
699: lh2 = lh;
700: while (lh2 != NULL) {
701: lh3 = lh2->l_next;
702: while (lh3 != NULL) {
703: if (strcmp(lh2->l_name, lh3->l_name) > 0) {
704: cp = lh2->l_name;
705: lh2->l_name = lh3->l_name;
706: lh3->l_name = cp;
1.2 millert 707: }
1.24 vincent 708: lh3 = lh3->l_next;
1.2 millert 709: }
1.24 vincent 710: lh2 = lh2->l_next;
711: }
1.1 deraadt 712:
1.24 vincent 713: /*
714: * First find max width of object to be displayed, so we can
715: * put several on a line.
716: */
717: maxwidth = 0;
718: lh2 = lh;
719: while (lh2 != NULL) {
720: for (i = 0; i < cpos; ++i) {
721: if (buf[i] != lh2->l_name[i])
722: break;
723: }
724: if (i == cpos) {
725: width = strlen(lh2->l_name);
726: if (width > maxwidth)
727: maxwidth = width;
1.1 deraadt 728: }
1.24 vincent 729: lh2 = lh2->l_next;
730: }
731: maxwidth += 1 - preflen;
1.2 millert 732:
1.24 vincent 733: /*
1.31 db 734: * Now do the display. Objects are written into linebuf until
1.24 vincent 735: * it fills, and then put into the help buffer.
736: */
1.57 guenther 737: linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
1.54 florian 738: if ((linebuf = malloc(linesize)) == NULL) {
739: free_file_list(wholelist);
1.31 db 740: return (FALSE);
1.54 florian 741: }
1.24 vincent 742: width = 0;
1.19 vincent 743:
1.25 deraadt 744: /*
1.24 vincent 745: * We're going to strlcat() into the buffer, so it has to be
1.31 db 746: * NUL terminated.
1.24 vincent 747: */
748: linebuf[0] = '\0';
749: for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
750: for (i = 0; i < cpos; ++i) {
751: if (buf[i] != lh2->l_name[i])
752: break;
1.2 millert 753: }
1.24 vincent 754: /* if we have a match */
755: if (i == cpos) {
756: /* if it wraps */
757: if ((width + maxwidth) > ncol) {
758: addline(bp, linebuf);
759: linebuf[0] = '\0';
760: width = 0;
761: }
1.43 kjell 762: len = strlcat(linebuf, lh2->l_name + preflen,
763: linesize);
1.24 vincent 764: width += maxwidth;
1.26 millert 765: if (len < width && width < linesize) {
766: /* pad so the objects nicely line up */
767: memset(linebuf + len, ' ',
768: maxwidth - strlen(lh2->l_name + preflen));
769: linebuf[width] = '\0';
770: }
1.1 deraadt 771: }
1.2 millert 772: }
1.24 vincent 773: if (width > 0)
774: addline(bp, linebuf);
775: free(linebuf);
776:
1.2 millert 777: /*
778: * Note that we free lists only if they are put in wholelist lists
779: * that were built just for us should be freed. However when we use
780: * the buffer list, obviously we don't want it freed.
1.1 deraadt 781: */
782: free_file_list(wholelist);
1.49 kjell 783: popbuftop(bp, WEPHEM); /* split the screen and put up the help
1.2 millert 784: * buffer */
1.55 lum 785: update(CMODE); /* needed to make the new stuff actually
1.2 millert 786: * appear */
787: ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
788: ttcolor(oldhue); /* with arbitrary color */
1.1 deraadt 789: ttflush();
1.31 db 790: return (0);
1.1 deraadt 791: }
792:
793: /*
1.3 millert 794: * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
1.14 mickey 795: * position in the name. Return the longest block of characters that can be
796: * autocompleted at this point. Sometimes the two symbols are the same, but
1.3 millert 797: * this is normal.
1.2 millert 798: */
799: int
1.40 deraadt 800: getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
1.2 millert 801: {
1.3 millert 802: int i;
1.1 deraadt 803:
804: i = cpos;
805: for (;;) {
1.2 millert 806: if (lp1->l_name[i] != lp2->l_name[i])
807: break;
808: if (lp1->l_name[i] == '\0')
809: break;
1.1 deraadt 810: ++i;
1.2 millert 811: if (wflag && !ISWORD(lp1->l_name[i - 1]))
812: break;
1.1 deraadt 813: }
814: return (i - cpos);
815: }
816:
817: /*
1.14 mickey 818: * Special "printf" for the echo line. Each call to "ewprintf" starts a
819: * new line in the echo area, and ends with an erase to end of the echo
820: * line. The formatting is done by a call to the standard formatting
1.3 millert 821: * routine.
1.1 deraadt 822: */
1.10 art 823: void
1.2 millert 824: ewprintf(const char *fmt, ...)
1.1 deraadt 825: {
1.3 millert 826: va_list ap;
1.1 deraadt 827:
1.2 millert 828: if (inmacro)
829: return;
1.50 lum 830:
1.2 millert 831: va_start(ap, fmt);
1.1 deraadt 832: ttcolor(CTEXT);
1.2 millert 833: ttmove(nrow - 1, 0);
834: eformat(fmt, ap);
835: va_end(ap);
1.1 deraadt 836: tteeol();
837: ttflush();
838: epresf = TRUE;
839: }
840:
841: /*
1.59 bcallah 842: * Printf style formatting. This is called by "ewprintf" to provide
843: * formatting services to its clients. The move to the start of the
844: * echo line, and the erase to the end of the echo line, is done by
1.48 kjell 845: * the caller.
846: * %c prints the "name" of the supplied character.
847: * %k prints the name of the current key (and takes no arguments).
848: * %d prints a decimal integer
849: * %o prints an octal integer
850: * %p prints a pointer
851: * %s prints a string
852: * %ld prints a long word
853: * Anything else is echoed verbatim
1.1 deraadt 854: */
1.10 art 855: static void
1.15 mickey 856: eformat(const char *fp, va_list ap)
1.1 deraadt 857: {
1.27 vincent 858: char kname[NKNAME], tmp[100], *cp;
1.12 mickey 859: int c;
1.1 deraadt 860:
861: while ((c = *fp++) != '\0') {
862: if (c != '%')
863: eputc(c);
864: else {
865: c = *fp++;
866: switch (c) {
867: case 'c':
1.48 kjell 868: getkeyname(kname, sizeof(kname),
869: va_arg(ap, int));
1.1 deraadt 870: eputs(kname);
871: break;
872:
873: case 'k':
1.12 mickey 874: for (cp = kname, c = 0; c < key.k_count; c++) {
875: if (c)
876: *cp++ = ' ';
1.42 kjell 877: cp = getkeyname(cp, sizeof(kname) -
1.12 mickey 878: (cp - kname) - 1, key.k_chars[c]);
1.1 deraadt 879: }
880: eputs(kname);
881: break;
882:
883: case 'd':
1.2 millert 884: eputi(va_arg(ap, int), 10);
1.1 deraadt 885: break;
886:
887: case 'o':
1.2 millert 888: eputi(va_arg(ap, int), 8);
1.27 vincent 889: break;
890:
891: case 'p':
1.31 db 892: snprintf(tmp, sizeof(tmp), "%p",
1.27 vincent 893: va_arg(ap, void *));
894: eputs(tmp);
1.1 deraadt 895: break;
896:
897: case 's':
1.2 millert 898: eputs(va_arg(ap, char *));
1.1 deraadt 899: break;
900:
1.3 millert 901: case 'l':
902: /* explicit longword */
1.1 deraadt 903: c = *fp++;
1.2 millert 904: switch (c) {
1.1 deraadt 905: case 'd':
1.13 mickey 906: eputl(va_arg(ap, long), 10);
1.1 deraadt 907: break;
908: default:
909: eputc(c);
910: break;
911: }
912: break;
913:
914: default:
915: eputc(c);
916: }
917: }
918: }
919: }
920:
921: /*
922: * Put integer, in radix "r".
923: */
1.10 art 924: static void
1.21 vincent 925: eputi(int i, int r)
1.1 deraadt 926: {
1.3 millert 927: int q;
1.1 deraadt 928:
1.2 millert 929: if (i < 0) {
930: eputc('-');
931: i = -i;
1.1 deraadt 932: }
1.2 millert 933: if ((q = i / r) != 0)
1.1 deraadt 934: eputi(q, r);
1.2 millert 935: eputc(i % r + '0');
1.1 deraadt 936: }
937:
938: /*
939: * Put long, in radix "r".
940: */
1.10 art 941: static void
1.21 vincent 942: eputl(long l, int r)
1.1 deraadt 943: {
1.3 millert 944: long q;
1.1 deraadt 945:
1.2 millert 946: if (l < 0) {
947: eputc('-');
948: l = -l;
1.1 deraadt 949: }
1.2 millert 950: if ((q = l / r) != 0)
1.1 deraadt 951: eputl(q, r);
1.3 millert 952: eputc((int)(l % r) + '0');
1.1 deraadt 953: }
954:
955: /*
956: * Put string.
957: */
1.10 art 958: static void
1.21 vincent 959: eputs(const char *s)
1.1 deraadt 960: {
1.3 millert 961: int c;
1.1 deraadt 962:
963: while ((c = *s++) != '\0')
964: eputc(c);
965: }
966:
967: /*
1.14 mickey 968: * Put character. Watch for control characters, and for the line getting
1.3 millert 969: * too long.
1.1 deraadt 970: */
1.10 art 971: static void
1.21 vincent 972: eputc(char c)
1.1 deraadt 973: {
1.2 millert 974: if (ttcol + 2 < ncol) {
1.1 deraadt 975: if (ISCTRL(c)) {
976: eputc('^');
977: c = CCHR(c);
978: }
979: ttputc(c);
980: ++ttcol;
981: }
982: }
983:
1.10 art 984: void
1.40 deraadt 985: free_file_list(struct list *lp)
1.1 deraadt 986: {
1.40 deraadt 987: struct list *next;
1.2 millert 988:
989: while (lp) {
990: next = lp->l_next;
1.43 kjell 991: free(lp->l_name);
1.2 millert 992: free(lp);
993: lp = next;
994: }
1.1 deraadt 995: }
996:
1.40 deraadt 997: static struct list *
998: copy_list(struct list *lp)
1.2 millert 999: {
1.40 deraadt 1000: struct list *current, *last, *nxt;
1.2 millert 1001:
1002: last = NULL;
1003: while (lp) {
1.45 kjell 1004: current = malloc(sizeof(struct list));
1.23 vincent 1005: if (current == NULL) {
1.43 kjell 1006: /* Free what we have allocated so far */
1.23 vincent 1007: for (current = last; current; current = nxt) {
1008: nxt = current->l_next;
1.43 kjell 1009: free(current->l_name);
1.23 vincent 1010: free(current);
1011: }
1.28 vincent 1012: return (NULL);
1.23 vincent 1013: }
1.2 millert 1014: current->l_next = last;
1.43 kjell 1015: current->l_name = strdup(lp->l_name);
1.28 vincent 1016: last = current;
1.2 millert 1017: lp = lp->l_next;
1018: }
1019: return (last);
1.1 deraadt 1020: }