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