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