Annotation of src/usr.bin/mg/echo.c, Revision 1.65
1.65 ! jsg 1: /* $OpenBSD: echo.c,v 1.64 2015/11/11 08:52:12 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)
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;
326: while ((y = kremove(i++)) >= 0 && y != '\n') {
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.47 kjell 339: ewprintf("Line too long");
340: return (emptyval);
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.47 kjell 495: ewprintf("Line too long");
496: return (emptyval);
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: }
510: }
1.7 art 511: done:
1.38 kjell 512: if (cwin == TRUE) {
513: /* blow away cpltion window */
514: bp = bfind("*Completions*", TRUE);
1.49 kjell 515: if ((wp = popbuf(bp, WEPHEM)) != NULL) {
516: if (wp->w_flag & WEPHEM) {
517: curwp = wp;
518: delwind(FFRAND, 1);
519: } else {
520: killbuffer(bp);
521: }
1.38 kjell 522: }
523: }
524: return (ret);
1.47 kjell 525: memfail:
526: if (dynbuf && buf)
527: free(buf);
1.56 lum 528: dobeep();
1.38 kjell 529: ewprintf("Out of memory");
1.47 kjell 530: return (emptyval);
1.1 deraadt 531: }
532:
533: /*
1.31 db 534: * Do completion on a list of objects.
1.38 kjell 535: * c is SPACE, TAB, or CR
536: * return TRUE if matched (or partially matched)
537: * FALSE is result is ambiguous,
538: * ABORT on error.
1.1 deraadt 539: */
1.2 millert 540: static int
1.38 kjell 541: complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
1.3 millert 542: {
1.40 deraadt 543: struct list *lh, *lh2;
544: struct list *wholelist = NULL;
1.3 millert 545: int i, nxtra, nhits, bxtra, msglen, nshown;
546: int wflag = FALSE;
547: char *msg;
548:
549: lh = lh2 = NULL;
1.2 millert 550:
551: if ((flags & EFFUNC) != 0) {
552: buf[cpos] = '\0';
1.32 otto 553: wholelist = lh = complete_function_list(buf);
1.9 art 554: } else if ((flags & EFBUF) != 0) {
1.6 art 555: lh = &(bheadp->b_list);
1.9 art 556: } else if ((flags & EFFILE) != 0) {
1.6 art 557: buf[cpos] = '\0';
558: wholelist = lh = make_file_list(buf);
559: } else
560: panic("broken complt call: flags");
561:
562: if (c == ' ')
563: wflag = TRUE;
564: else if (c != '\t' && c != CCHR('M'))
565: panic("broken complt call: c");
566:
567: nhits = 0;
568: nxtra = HUGE;
569:
570: for (; lh != NULL; lh = lh->l_next) {
571: if (memcmp(buf, lh->l_name, cpos) != 0)
572: continue;
1.2 millert 573: if (nhits == 0)
1.6 art 574: lh2 = lh;
575: ++nhits;
576: if (lh->l_name[cpos] == '\0')
1.38 kjell 577: nxtra = -1; /* exact match */
1.3 millert 578: else {
1.6 art 579: bxtra = getxtra(lh, lh2, cpos, wflag);
580: if (bxtra < nxtra)
581: nxtra = bxtra;
582: lh2 = lh;
583: }
584: }
585: if (nhits == 0)
586: msg = " [No match]";
587: else if (nhits > 1 && nxtra == 0)
1.37 kjell 588: msg = " [Ambiguous. Ctrl-G to cancel]";
1.6 art 589: else {
590: /*
591: * Being lazy - ought to check length, but all things
592: * autocompleted have known types/lengths.
593: */
594: if (nxtra < 0 && nhits > 1 && c == ' ')
1.38 kjell 595: nxtra = 1; /* ??? */
1.29 vincent 596: for (i = 0; i < nxtra && cpos < nbuf; ++i) {
1.6 art 597: buf[cpos] = lh2->l_name[cpos];
598: eputc(buf[cpos++]);
1.1 deraadt 599: }
1.38 kjell 600: /* XXX should grow nbuf */
1.6 art 601: ttflush();
602: free_file_list(wholelist);
1.38 kjell 603: *nx = nxtra;
604: if (nxtra < 0 && c != CCHR('M')) /* exact */
605: *nx = 0;
606: return (TRUE);
1.1 deraadt 607: }
1.3 millert 608:
1.2 millert 609: /*
1.31 db 610: * wholelist is NULL if we are doing buffers. Want to free lists
1.2 millert 611: * that were created for us, but not the buffer list!
612: */
1.1 deraadt 613: free_file_list(wholelist);
1.3 millert 614:
1.31 db 615: /* Set up backspaces, etc., being mindful of echo line limit. */
1.1 deraadt 616: msglen = strlen(msg);
617: nshown = (ttcol + msglen + 2 > ncol) ?
1.2 millert 618: ncol - ttcol - 2 : msglen;
1.1 deraadt 619: eputs(msg);
1.2 millert 620: ttcol -= (i = nshown); /* update ttcol! */
621: while (i--) /* move back before msg */
1.1 deraadt 622: ttputc('\b');
1.2 millert 623: ttflush(); /* display to user */
1.1 deraadt 624: i = nshown;
1.3 millert 625: while (i--) /* blank out on next flush */
1.1 deraadt 626: eputc(' ');
1.2 millert 627: ttcol -= (i = nshown); /* update ttcol on BS's */
1.1 deraadt 628: while (i--)
1.2 millert 629: ttputc('\b'); /* update ttcol again! */
1.38 kjell 630: *nx = nxtra;
631: return ((nhits > 0) ? TRUE : FALSE);
1.1 deraadt 632: }
633:
634: /*
1.31 db 635: * Do completion on a list of objects, listing instead of completing.
1.1 deraadt 636: */
1.2 millert 637: static int
1.41 kjell 638: complt_list(int flags, char *buf, int cpos)
1.3 millert 639: {
1.40 deraadt 640: struct list *lh, *lh2, *lh3;
641: struct list *wholelist = NULL;
642: struct buffer *bp;
1.3 millert 643: int i, maxwidth, width;
644: int preflen = 0;
645: int oldrow = ttrow;
646: int oldcol = ttcol;
647: int oldhue = tthue;
1.17 art 648: char *linebuf;
1.26 millert 649: size_t linesize, len;
1.43 kjell 650: char *cp;
1.3 millert 651:
652: lh = NULL;
1.1 deraadt 653:
654: ttflush();
655:
1.38 kjell 656: /* The results are put into a completion buffer. */
657: bp = bfind("*Completions*", TRUE);
1.2 millert 658: if (bclear(bp) == FALSE)
1.31 db 659: return (FALSE);
1.61 jasper 660: bp->b_flag |= BFREADONLY;
1.2 millert 661:
1.24 vincent 662: /*
1.31 db 663: * First get the list of objects. This list may contain only
1.24 vincent 664: * the ones that complete what has been typed, or may be the
665: * whole list of all objects of this type. They are filtered
666: * later in any case. Set wholelist if the list has been
667: * cons'ed up just for us, so we can free it later. We have
668: * to copy the buffer list for this function even though we
669: * didn't for complt. The sorting code does destructive
670: * changes to the list, which we don't want to happen to the
671: * main buffer list!
672: */
673: if ((flags & EFBUF) != 0)
674: wholelist = lh = copy_list(&(bheadp->b_list));
675: else if ((flags & EFFUNC) != 0) {
676: buf[cpos] = '\0';
1.32 otto 677: wholelist = lh = complete_function_list(buf);
1.24 vincent 678: } else if ((flags & EFFILE) != 0) {
679: buf[cpos] = '\0';
680: wholelist = lh = make_file_list(buf);
1.2 millert 681: /*
1.24 vincent 682: * We don't want to display stuff up to the / for file
683: * names preflen is the list of a prefix of what the
684: * user typed that should not be displayed.
1.2 millert 685: */
1.24 vincent 686: cp = strrchr(buf, '/');
687: if (cp)
688: preflen = cp - buf + 1;
689: } else
690: panic("broken complt call: flags");
1.1 deraadt 691:
1.24 vincent 692: /*
693: * Sort the list, since users expect to see it in alphabetic
694: * order.
695: */
696: lh2 = lh;
697: while (lh2 != NULL) {
698: lh3 = lh2->l_next;
699: while (lh3 != NULL) {
700: if (strcmp(lh2->l_name, lh3->l_name) > 0) {
701: cp = lh2->l_name;
702: lh2->l_name = lh3->l_name;
703: lh3->l_name = cp;
1.2 millert 704: }
1.24 vincent 705: lh3 = lh3->l_next;
1.2 millert 706: }
1.24 vincent 707: lh2 = lh2->l_next;
708: }
1.1 deraadt 709:
1.24 vincent 710: /*
711: * First find max width of object to be displayed, so we can
712: * put several on a line.
713: */
714: maxwidth = 0;
715: lh2 = lh;
716: while (lh2 != NULL) {
717: for (i = 0; i < cpos; ++i) {
718: if (buf[i] != lh2->l_name[i])
719: break;
720: }
721: if (i == cpos) {
722: width = strlen(lh2->l_name);
723: if (width > maxwidth)
724: maxwidth = width;
1.1 deraadt 725: }
1.24 vincent 726: lh2 = lh2->l_next;
727: }
728: maxwidth += 1 - preflen;
1.2 millert 729:
1.24 vincent 730: /*
1.31 db 731: * Now do the display. Objects are written into linebuf until
1.24 vincent 732: * it fills, and then put into the help buffer.
733: */
1.57 guenther 734: linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
1.54 florian 735: if ((linebuf = malloc(linesize)) == NULL) {
736: free_file_list(wholelist);
1.31 db 737: return (FALSE);
1.54 florian 738: }
1.24 vincent 739: width = 0;
1.19 vincent 740:
1.25 deraadt 741: /*
1.24 vincent 742: * We're going to strlcat() into the buffer, so it has to be
1.31 db 743: * NUL terminated.
1.24 vincent 744: */
745: linebuf[0] = '\0';
746: for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
747: for (i = 0; i < cpos; ++i) {
748: if (buf[i] != lh2->l_name[i])
749: break;
1.2 millert 750: }
1.24 vincent 751: /* if we have a match */
752: if (i == cpos) {
753: /* if it wraps */
754: if ((width + maxwidth) > ncol) {
755: addline(bp, linebuf);
756: linebuf[0] = '\0';
757: width = 0;
758: }
1.43 kjell 759: len = strlcat(linebuf, lh2->l_name + preflen,
760: linesize);
1.24 vincent 761: width += maxwidth;
1.26 millert 762: if (len < width && width < linesize) {
763: /* pad so the objects nicely line up */
764: memset(linebuf + len, ' ',
765: maxwidth - strlen(lh2->l_name + preflen));
766: linebuf[width] = '\0';
767: }
1.1 deraadt 768: }
1.2 millert 769: }
1.24 vincent 770: if (width > 0)
771: addline(bp, linebuf);
772: free(linebuf);
773:
1.2 millert 774: /*
775: * Note that we free lists only if they are put in wholelist lists
776: * that were built just for us should be freed. However when we use
777: * the buffer list, obviously we don't want it freed.
1.1 deraadt 778: */
779: free_file_list(wholelist);
1.49 kjell 780: popbuftop(bp, WEPHEM); /* split the screen and put up the help
1.2 millert 781: * buffer */
1.55 lum 782: update(CMODE); /* needed to make the new stuff actually
1.2 millert 783: * appear */
784: ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
785: ttcolor(oldhue); /* with arbitrary color */
1.1 deraadt 786: ttflush();
1.31 db 787: return (0);
1.1 deraadt 788: }
789:
790: /*
1.3 millert 791: * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
1.14 mickey 792: * position in the name. Return the longest block of characters that can be
793: * autocompleted at this point. Sometimes the two symbols are the same, but
1.3 millert 794: * this is normal.
1.2 millert 795: */
796: int
1.40 deraadt 797: getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
1.2 millert 798: {
1.3 millert 799: int i;
1.1 deraadt 800:
801: i = cpos;
802: for (;;) {
1.2 millert 803: if (lp1->l_name[i] != lp2->l_name[i])
804: break;
805: if (lp1->l_name[i] == '\0')
806: break;
1.1 deraadt 807: ++i;
1.2 millert 808: if (wflag && !ISWORD(lp1->l_name[i - 1]))
809: break;
1.1 deraadt 810: }
811: return (i - cpos);
812: }
813:
814: /*
1.14 mickey 815: * Special "printf" for the echo line. Each call to "ewprintf" starts a
816: * new line in the echo area, and ends with an erase to end of the echo
817: * line. The formatting is done by a call to the standard formatting
1.3 millert 818: * routine.
1.1 deraadt 819: */
1.10 art 820: void
1.2 millert 821: ewprintf(const char *fmt, ...)
1.1 deraadt 822: {
1.3 millert 823: va_list ap;
1.1 deraadt 824:
1.2 millert 825: if (inmacro)
826: return;
1.50 lum 827:
1.2 millert 828: va_start(ap, fmt);
1.1 deraadt 829: ttcolor(CTEXT);
1.2 millert 830: ttmove(nrow - 1, 0);
831: eformat(fmt, ap);
832: va_end(ap);
1.1 deraadt 833: tteeol();
834: ttflush();
835: epresf = TRUE;
836: }
837:
838: /*
1.59 bcallah 839: * Printf style formatting. This is called by "ewprintf" to provide
840: * formatting services to its clients. The move to the start of the
841: * echo line, and the erase to the end of the echo line, is done by
1.48 kjell 842: * the caller.
843: * %c prints the "name" of the supplied character.
844: * %k prints the name of the current key (and takes no arguments).
845: * %d prints a decimal integer
846: * %o prints an octal integer
847: * %p prints a pointer
848: * %s prints a string
849: * %ld prints a long word
850: * Anything else is echoed verbatim
1.1 deraadt 851: */
1.10 art 852: static void
1.15 mickey 853: eformat(const char *fp, va_list ap)
1.1 deraadt 854: {
1.27 vincent 855: char kname[NKNAME], tmp[100], *cp;
1.12 mickey 856: int c;
1.1 deraadt 857:
858: while ((c = *fp++) != '\0') {
859: if (c != '%')
860: eputc(c);
861: else {
862: c = *fp++;
863: switch (c) {
864: case 'c':
1.48 kjell 865: getkeyname(kname, sizeof(kname),
866: va_arg(ap, int));
1.1 deraadt 867: eputs(kname);
868: break;
869:
870: case 'k':
1.12 mickey 871: for (cp = kname, c = 0; c < key.k_count; c++) {
872: if (c)
873: *cp++ = ' ';
1.42 kjell 874: cp = getkeyname(cp, sizeof(kname) -
1.12 mickey 875: (cp - kname) - 1, key.k_chars[c]);
1.1 deraadt 876: }
877: eputs(kname);
878: break;
879:
880: case 'd':
1.2 millert 881: eputi(va_arg(ap, int), 10);
1.1 deraadt 882: break;
883:
884: case 'o':
1.2 millert 885: eputi(va_arg(ap, int), 8);
1.27 vincent 886: break;
887:
888: case 'p':
1.31 db 889: snprintf(tmp, sizeof(tmp), "%p",
1.27 vincent 890: va_arg(ap, void *));
891: eputs(tmp);
1.1 deraadt 892: break;
893:
894: case 's':
1.2 millert 895: eputs(va_arg(ap, char *));
1.1 deraadt 896: break;
897:
1.3 millert 898: case 'l':
899: /* explicit longword */
1.1 deraadt 900: c = *fp++;
1.2 millert 901: switch (c) {
1.1 deraadt 902: case 'd':
1.13 mickey 903: eputl(va_arg(ap, long), 10);
1.1 deraadt 904: break;
905: default:
906: eputc(c);
907: break;
908: }
909: break;
910:
911: default:
912: eputc(c);
913: }
914: }
915: }
916: }
917:
918: /*
919: * Put integer, in radix "r".
920: */
1.10 art 921: static void
1.21 vincent 922: eputi(int i, int r)
1.1 deraadt 923: {
1.3 millert 924: int q;
1.1 deraadt 925:
1.2 millert 926: if (i < 0) {
927: eputc('-');
928: i = -i;
1.1 deraadt 929: }
1.2 millert 930: if ((q = i / r) != 0)
1.1 deraadt 931: eputi(q, r);
1.2 millert 932: eputc(i % r + '0');
1.1 deraadt 933: }
934:
935: /*
936: * Put long, in radix "r".
937: */
1.10 art 938: static void
1.21 vincent 939: eputl(long l, int r)
1.1 deraadt 940: {
1.3 millert 941: long q;
1.1 deraadt 942:
1.2 millert 943: if (l < 0) {
944: eputc('-');
945: l = -l;
1.1 deraadt 946: }
1.2 millert 947: if ((q = l / r) != 0)
1.1 deraadt 948: eputl(q, r);
1.3 millert 949: eputc((int)(l % r) + '0');
1.1 deraadt 950: }
951:
952: /*
953: * Put string.
954: */
1.10 art 955: static void
1.21 vincent 956: eputs(const char *s)
1.1 deraadt 957: {
1.3 millert 958: int c;
1.1 deraadt 959:
960: while ((c = *s++) != '\0')
961: eputc(c);
962: }
963:
964: /*
1.14 mickey 965: * Put character. Watch for control characters, and for the line getting
1.3 millert 966: * too long.
1.1 deraadt 967: */
1.10 art 968: static void
1.21 vincent 969: eputc(char c)
1.1 deraadt 970: {
1.2 millert 971: if (ttcol + 2 < ncol) {
1.1 deraadt 972: if (ISCTRL(c)) {
973: eputc('^');
974: c = CCHR(c);
975: }
976: ttputc(c);
977: ++ttcol;
978: }
979: }
980:
1.10 art 981: void
1.40 deraadt 982: free_file_list(struct list *lp)
1.1 deraadt 983: {
1.40 deraadt 984: struct list *next;
1.2 millert 985:
986: while (lp) {
987: next = lp->l_next;
1.43 kjell 988: free(lp->l_name);
1.2 millert 989: free(lp);
990: lp = next;
991: }
1.1 deraadt 992: }
993:
1.40 deraadt 994: static struct list *
995: copy_list(struct list *lp)
1.2 millert 996: {
1.40 deraadt 997: struct list *current, *last, *nxt;
1.2 millert 998:
999: last = NULL;
1000: while (lp) {
1.45 kjell 1001: current = malloc(sizeof(struct list));
1.23 vincent 1002: if (current == NULL) {
1.43 kjell 1003: /* Free what we have allocated so far */
1.23 vincent 1004: for (current = last; current; current = nxt) {
1005: nxt = current->l_next;
1.43 kjell 1006: free(current->l_name);
1.23 vincent 1007: free(current);
1008: }
1.28 vincent 1009: return (NULL);
1.23 vincent 1010: }
1.2 millert 1011: current->l_next = last;
1.43 kjell 1012: current->l_name = strdup(lp->l_name);
1.28 vincent 1013: last = current;
1.2 millert 1014: lp = lp->l_next;
1015: }
1016: return (last);
1.1 deraadt 1017: }