Annotation of src/usr.bin/mg/echo.c, Revision 1.54
1.54 ! florian 1: /* $OpenBSD: echo.c,v 1.53 2012/11/03 16:26:32 florian 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) {
319: ewprintf("Line too long");
320: return (emptyval);
321: }
1.38 kjell 322: for (t = epos; t > cpos; t--)
323: buf[t] = buf[t - 1];
324: buf[cpos++] = (char)y;
325: epos++;
326: eputc((char)y);
327: cc = ttcol;
328: rr = ttrow;
329: for (t = cpos; t < epos; t++)
330: eputc(buf[t]);
331: ttmove(rr, cc);
332: }
333: ttflush();
334: break;
335: case CCHR('K'): /* copy here-EOL to kill buffer */
336: kdelete();
337: for (i = cpos; i < epos; i++)
338: kinsert(buf[i], KFORW);
339: tteeol();
340: epos = cpos;
341: ttflush();
342: break;
343: case CCHR('['):
344: ml = mr = esc = 1;
345: break;
1.2 millert 346: case CCHR('J'):
1.3 millert 347: c = CCHR('M');
1.22 vincent 348: /* FALLTHROUGH */
1.3 millert 349: case CCHR('M'): /* return, done */
1.36 kjell 350: /* if there's nothing in the minibuffer, abort */
1.38 kjell 351: if (epos == 0 && !(flag & EFNUL)) {
1.33 cloder 352: (void)ctrlg(FFRAND, 0);
353: ttflush();
354: return (NULL);
355: }
1.2 millert 356: if ((flag & EFFUNC) != 0) {
1.38 kjell 357: if (complt(flag, c, buf, nbuf, epos, &i)
358: == FALSE)
1.1 deraadt 359: continue;
1.2 millert 360: if (i > 0)
1.38 kjell 361: epos += i;
1.1 deraadt 362: }
1.38 kjell 363: buf[epos] = '\0';
1.2 millert 364: if ((flag & EFCR) != 0) {
1.1 deraadt 365: ttputc(CCHR('M'));
366: ttflush();
367: }
1.2 millert 368: if (macrodef) {
1.40 deraadt 369: struct line *lp;
1.1 deraadt 370:
1.47 kjell 371: if ((lp = lalloc(cpos)) == NULL)
372: goto memfail;
1.2 millert 373: lp->l_fp = maclcur->l_fp;
374: maclcur->l_fp = lp;
375: lp->l_bp = maclcur;
376: maclcur = lp;
377: bcopy(buf, lp->l_text, cpos);
1.1 deraadt 378: }
1.38 kjell 379: ret = buf;
1.1 deraadt 380: goto done;
1.3 millert 381: case CCHR('G'): /* bell, abort */
1.1 deraadt 382: eputc(CCHR('G'));
1.10 art 383: (void)ctrlg(FFRAND, 0);
1.1 deraadt 384: ttflush();
1.38 kjell 385: ret = NULL;
386: goto done;
1.3 millert 387: case CCHR('H'): /* rubout, erase */
1.14 mickey 388: case CCHR('?'):
1.1 deraadt 389: if (cpos != 0) {
1.38 kjell 390: y = buf[--cpos];
391: epos--;
1.1 deraadt 392: ttputc('\b');
1.38 kjell 393: ttcol--;
394: if (ISCTRL(y) != FALSE) {
395: ttputc('\b');
396: ttcol--;
397: }
398: rr = ttrow;
399: cc = ttcol;
400: for (i = cpos; i < epos; i++) {
401: buf[i] = buf[i + 1];
402: eputc(buf[i]);
403: }
1.1 deraadt 404: ttputc(' ');
1.38 kjell 405: if (ISCTRL(y) != FALSE) {
1.1 deraadt 406: ttputc(' ');
407: ttputc('\b');
408: }
1.38 kjell 409: ttputc('\b');
410: ttmove(rr, cc);
1.1 deraadt 411: ttflush();
412: }
413: break;
1.3 millert 414: case CCHR('X'): /* kill line */
415: case CCHR('U'):
1.1 deraadt 416: while (cpos != 0) {
417: ttputc('\b');
418: ttputc(' ');
419: ttputc('\b');
420: --ttcol;
421: if (ISCTRL(buf[--cpos]) != FALSE) {
422: ttputc('\b');
423: ttputc(' ');
424: ttputc('\b');
425: --ttcol;
426: }
1.38 kjell 427: epos--;
1.1 deraadt 428: }
429: ttflush();
430: break;
1.3 millert 431: case CCHR('W'): /* kill to beginning of word */
1.1 deraadt 432: while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
433: ttputc('\b');
434: ttputc(' ');
435: ttputc('\b');
436: --ttcol;
437: if (ISCTRL(buf[--cpos]) != FALSE) {
438: ttputc('\b');
439: ttputc(' ');
440: ttputc('\b');
441: --ttcol;
442: }
1.46 kjell 443: epos--;
1.1 deraadt 444: }
445: while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
446: ttputc('\b');
447: ttputc(' ');
448: ttputc('\b');
449: --ttcol;
450: if (ISCTRL(buf[--cpos]) != FALSE) {
451: ttputc('\b');
452: ttputc(' ');
453: ttputc('\b');
454: --ttcol;
455: }
1.46 kjell 456: epos--;
1.1 deraadt 457: }
458: ttflush();
459: break;
1.2 millert 460: case CCHR('\\'):
1.3 millert 461: case CCHR('Q'): /* quote next */
462: c = getkey(FALSE);
1.22 vincent 463: /* FALLTHROUGH */
1.30 deraadt 464: default:
1.38 kjell 465: if (dynbuf && epos + 1 >= nbuf) {
1.29 vincent 466: void *newp;
1.38 kjell 467: size_t newsize = epos + epos + 16;
468: if ((newp = realloc(buf, newsize)) == NULL)
1.47 kjell 469: goto memfail;
1.29 vincent 470: buf = newp;
471: nbuf = newsize;
472: }
1.47 kjell 473: if (!dynbuf && epos + 1 >= nbuf) {
474: ewprintf("Line too long");
475: return (emptyval);
476: }
1.38 kjell 477: for (i = epos; i > cpos; i--)
478: buf[i] = buf[i - 1];
479: buf[cpos++] = (char)c;
480: epos++;
481: eputc((char)c);
482: cc = ttcol;
483: rr = ttrow;
484: for (i = cpos; i < epos; i++)
485: eputc(buf[i]);
486: ttmove(rr, cc);
487: ttflush();
1.1 deraadt 488: }
489: }
1.7 art 490: done:
1.38 kjell 491: if (cwin == TRUE) {
492: /* blow away cpltion window */
493: bp = bfind("*Completions*", TRUE);
1.49 kjell 494: if ((wp = popbuf(bp, WEPHEM)) != NULL) {
495: if (wp->w_flag & WEPHEM) {
496: curwp = wp;
497: delwind(FFRAND, 1);
498: } else {
499: killbuffer(bp);
500: }
1.38 kjell 501: }
502: }
503: return (ret);
1.47 kjell 504: memfail:
505: if (dynbuf && buf)
506: free(buf);
1.38 kjell 507: ewprintf("Out of memory");
1.47 kjell 508: return (emptyval);
1.1 deraadt 509: }
510:
511: /*
1.31 db 512: * Do completion on a list of objects.
1.38 kjell 513: * c is SPACE, TAB, or CR
514: * return TRUE if matched (or partially matched)
515: * FALSE is result is ambiguous,
516: * ABORT on error.
1.1 deraadt 517: */
1.2 millert 518: static int
1.38 kjell 519: complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
1.3 millert 520: {
1.40 deraadt 521: struct list *lh, *lh2;
522: struct list *wholelist = NULL;
1.3 millert 523: int i, nxtra, nhits, bxtra, msglen, nshown;
524: int wflag = FALSE;
525: char *msg;
526:
527: lh = lh2 = NULL;
1.2 millert 528:
529: if ((flags & EFFUNC) != 0) {
530: buf[cpos] = '\0';
1.32 otto 531: wholelist = lh = complete_function_list(buf);
1.9 art 532: } else if ((flags & EFBUF) != 0) {
1.6 art 533: lh = &(bheadp->b_list);
1.9 art 534: } else if ((flags & EFFILE) != 0) {
1.6 art 535: buf[cpos] = '\0';
536: wholelist = lh = make_file_list(buf);
537: } else
538: panic("broken complt call: flags");
539:
540: if (c == ' ')
541: wflag = TRUE;
542: else if (c != '\t' && c != CCHR('M'))
543: panic("broken complt call: c");
544:
545: nhits = 0;
546: nxtra = HUGE;
547:
548: for (; lh != NULL; lh = lh->l_next) {
549: if (memcmp(buf, lh->l_name, cpos) != 0)
550: continue;
1.2 millert 551: if (nhits == 0)
1.6 art 552: lh2 = lh;
553: ++nhits;
554: if (lh->l_name[cpos] == '\0')
1.38 kjell 555: nxtra = -1; /* exact match */
1.3 millert 556: else {
1.6 art 557: bxtra = getxtra(lh, lh2, cpos, wflag);
558: if (bxtra < nxtra)
559: nxtra = bxtra;
560: lh2 = lh;
561: }
562: }
563: if (nhits == 0)
564: msg = " [No match]";
565: else if (nhits > 1 && nxtra == 0)
1.37 kjell 566: msg = " [Ambiguous. Ctrl-G to cancel]";
1.6 art 567: else {
568: /*
569: * Being lazy - ought to check length, but all things
570: * autocompleted have known types/lengths.
571: */
572: if (nxtra < 0 && nhits > 1 && c == ' ')
1.38 kjell 573: nxtra = 1; /* ??? */
1.29 vincent 574: for (i = 0; i < nxtra && cpos < nbuf; ++i) {
1.6 art 575: buf[cpos] = lh2->l_name[cpos];
576: eputc(buf[cpos++]);
1.1 deraadt 577: }
1.38 kjell 578: /* XXX should grow nbuf */
1.6 art 579: ttflush();
580: free_file_list(wholelist);
1.38 kjell 581: *nx = nxtra;
582: if (nxtra < 0 && c != CCHR('M')) /* exact */
583: *nx = 0;
584: return (TRUE);
1.1 deraadt 585: }
1.3 millert 586:
1.2 millert 587: /*
1.31 db 588: * wholelist is NULL if we are doing buffers. Want to free lists
1.2 millert 589: * that were created for us, but not the buffer list!
590: */
1.1 deraadt 591: free_file_list(wholelist);
1.3 millert 592:
1.31 db 593: /* Set up backspaces, etc., being mindful of echo line limit. */
1.1 deraadt 594: msglen = strlen(msg);
595: nshown = (ttcol + msglen + 2 > ncol) ?
1.2 millert 596: ncol - ttcol - 2 : msglen;
1.1 deraadt 597: eputs(msg);
1.2 millert 598: ttcol -= (i = nshown); /* update ttcol! */
599: while (i--) /* move back before msg */
1.1 deraadt 600: ttputc('\b');
1.2 millert 601: ttflush(); /* display to user */
1.1 deraadt 602: i = nshown;
1.3 millert 603: while (i--) /* blank out on next flush */
1.1 deraadt 604: eputc(' ');
1.2 millert 605: ttcol -= (i = nshown); /* update ttcol on BS's */
1.1 deraadt 606: while (i--)
1.2 millert 607: ttputc('\b'); /* update ttcol again! */
1.38 kjell 608: *nx = nxtra;
609: return ((nhits > 0) ? TRUE : FALSE);
1.1 deraadt 610: }
611:
612: /*
1.31 db 613: * Do completion on a list of objects, listing instead of completing.
1.1 deraadt 614: */
1.2 millert 615: static int
1.41 kjell 616: complt_list(int flags, char *buf, int cpos)
1.3 millert 617: {
1.40 deraadt 618: struct list *lh, *lh2, *lh3;
619: struct list *wholelist = NULL;
620: struct buffer *bp;
1.3 millert 621: int i, maxwidth, width;
622: int preflen = 0;
623: int oldrow = ttrow;
624: int oldcol = ttcol;
625: int oldhue = tthue;
1.17 art 626: char *linebuf;
1.26 millert 627: size_t linesize, len;
1.43 kjell 628: char *cp;
1.3 millert 629:
630: lh = NULL;
1.1 deraadt 631:
632: ttflush();
633:
1.38 kjell 634: /* The results are put into a completion buffer. */
635: bp = bfind("*Completions*", TRUE);
1.2 millert 636: if (bclear(bp) == FALSE)
1.31 db 637: return (FALSE);
1.2 millert 638:
1.24 vincent 639: /*
1.31 db 640: * First get the list of objects. This list may contain only
1.24 vincent 641: * the ones that complete what has been typed, or may be the
642: * whole list of all objects of this type. They are filtered
643: * later in any case. Set wholelist if the list has been
644: * cons'ed up just for us, so we can free it later. We have
645: * to copy the buffer list for this function even though we
646: * didn't for complt. The sorting code does destructive
647: * changes to the list, which we don't want to happen to the
648: * main buffer list!
649: */
650: if ((flags & EFBUF) != 0)
651: wholelist = lh = copy_list(&(bheadp->b_list));
652: else if ((flags & EFFUNC) != 0) {
653: buf[cpos] = '\0';
1.32 otto 654: wholelist = lh = complete_function_list(buf);
1.24 vincent 655: } else if ((flags & EFFILE) != 0) {
656: buf[cpos] = '\0';
657: wholelist = lh = make_file_list(buf);
1.2 millert 658: /*
1.24 vincent 659: * We don't want to display stuff up to the / for file
660: * names preflen is the list of a prefix of what the
661: * user typed that should not be displayed.
1.2 millert 662: */
1.24 vincent 663: cp = strrchr(buf, '/');
664: if (cp)
665: preflen = cp - buf + 1;
666: } else
667: panic("broken complt call: flags");
1.1 deraadt 668:
1.24 vincent 669: /*
670: * Sort the list, since users expect to see it in alphabetic
671: * order.
672: */
673: lh2 = lh;
674: while (lh2 != NULL) {
675: lh3 = lh2->l_next;
676: while (lh3 != NULL) {
677: if (strcmp(lh2->l_name, lh3->l_name) > 0) {
678: cp = lh2->l_name;
679: lh2->l_name = lh3->l_name;
680: lh3->l_name = cp;
1.2 millert 681: }
1.24 vincent 682: lh3 = lh3->l_next;
1.2 millert 683: }
1.24 vincent 684: lh2 = lh2->l_next;
685: }
1.1 deraadt 686:
1.24 vincent 687: /*
688: * First find max width of object to be displayed, so we can
689: * put several on a line.
690: */
691: maxwidth = 0;
692: lh2 = lh;
693: while (lh2 != NULL) {
694: for (i = 0; i < cpos; ++i) {
695: if (buf[i] != lh2->l_name[i])
696: break;
697: }
698: if (i == cpos) {
699: width = strlen(lh2->l_name);
700: if (width > maxwidth)
701: maxwidth = width;
1.1 deraadt 702: }
1.24 vincent 703: lh2 = lh2->l_next;
704: }
705: maxwidth += 1 - preflen;
1.2 millert 706:
1.24 vincent 707: /*
1.31 db 708: * Now do the display. Objects are written into linebuf until
1.24 vincent 709: * it fills, and then put into the help buffer.
710: */
1.26 millert 711: linesize = MAX(ncol, maxwidth) + 1;
1.54 ! florian 712: if ((linebuf = malloc(linesize)) == NULL) {
! 713: free_file_list(wholelist);
1.31 db 714: return (FALSE);
1.54 ! florian 715: }
1.24 vincent 716: width = 0;
1.19 vincent 717:
1.25 deraadt 718: /*
1.24 vincent 719: * We're going to strlcat() into the buffer, so it has to be
1.31 db 720: * NUL terminated.
1.24 vincent 721: */
722: linebuf[0] = '\0';
723: for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
724: for (i = 0; i < cpos; ++i) {
725: if (buf[i] != lh2->l_name[i])
726: break;
1.2 millert 727: }
1.24 vincent 728: /* if we have a match */
729: if (i == cpos) {
730: /* if it wraps */
731: if ((width + maxwidth) > ncol) {
732: addline(bp, linebuf);
733: linebuf[0] = '\0';
734: width = 0;
735: }
1.43 kjell 736: len = strlcat(linebuf, lh2->l_name + preflen,
737: linesize);
1.24 vincent 738: width += maxwidth;
1.26 millert 739: if (len < width && width < linesize) {
740: /* pad so the objects nicely line up */
741: memset(linebuf + len, ' ',
742: maxwidth - strlen(lh2->l_name + preflen));
743: linebuf[width] = '\0';
744: }
1.1 deraadt 745: }
1.2 millert 746: }
1.24 vincent 747: if (width > 0)
748: addline(bp, linebuf);
749: free(linebuf);
750:
1.2 millert 751: /*
752: * Note that we free lists only if they are put in wholelist lists
753: * that were built just for us should be freed. However when we use
754: * the buffer list, obviously we don't want it freed.
1.1 deraadt 755: */
756: free_file_list(wholelist);
1.49 kjell 757: popbuftop(bp, WEPHEM); /* split the screen and put up the help
1.2 millert 758: * buffer */
759: update(); /* needed to make the new stuff actually
760: * appear */
761: ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
762: ttcolor(oldhue); /* with arbitrary color */
1.1 deraadt 763: ttflush();
1.31 db 764: return (0);
1.1 deraadt 765: }
766:
767: /*
1.3 millert 768: * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
1.14 mickey 769: * position in the name. Return the longest block of characters that can be
770: * autocompleted at this point. Sometimes the two symbols are the same, but
1.3 millert 771: * this is normal.
1.2 millert 772: */
773: int
1.40 deraadt 774: getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
1.2 millert 775: {
1.3 millert 776: int i;
1.1 deraadt 777:
778: i = cpos;
779: for (;;) {
1.2 millert 780: if (lp1->l_name[i] != lp2->l_name[i])
781: break;
782: if (lp1->l_name[i] == '\0')
783: break;
1.1 deraadt 784: ++i;
1.2 millert 785: if (wflag && !ISWORD(lp1->l_name[i - 1]))
786: break;
1.1 deraadt 787: }
788: return (i - cpos);
789: }
790:
791: /*
1.14 mickey 792: * Special "printf" for the echo line. Each call to "ewprintf" starts a
793: * new line in the echo area, and ends with an erase to end of the echo
794: * line. The formatting is done by a call to the standard formatting
1.3 millert 795: * routine.
1.1 deraadt 796: */
1.2 millert 797: /* VARARGS */
1.10 art 798: void
1.2 millert 799: ewprintf(const char *fmt, ...)
1.1 deraadt 800: {
1.3 millert 801: va_list ap;
1.1 deraadt 802:
1.2 millert 803: if (inmacro)
804: return;
1.50 lum 805:
1.2 millert 806: va_start(ap, fmt);
1.1 deraadt 807: ttcolor(CTEXT);
1.2 millert 808: ttmove(nrow - 1, 0);
809: eformat(fmt, ap);
810: va_end(ap);
1.1 deraadt 811: tteeol();
812: ttflush();
813: epresf = TRUE;
814: }
815:
816: /*
1.14 mickey 817: * Printf style formatting. This is called by both "ewprintf" and "ereply"
818: * to provide formatting services to their clients. The move to the start
819: * of the echo line, and the erase to the end of the echo line, is done by
1.48 kjell 820: * the caller.
821: * %c prints the "name" of the supplied character.
822: * %k prints the name of the current key (and takes no arguments).
823: * %d prints a decimal integer
824: * %o prints an octal integer
825: * %p prints a pointer
826: * %s prints a string
827: * %ld prints a long word
828: * Anything else is echoed verbatim
1.1 deraadt 829: */
1.10 art 830: static void
1.15 mickey 831: eformat(const char *fp, va_list ap)
1.1 deraadt 832: {
1.27 vincent 833: char kname[NKNAME], tmp[100], *cp;
1.12 mickey 834: int c;
1.1 deraadt 835:
836: while ((c = *fp++) != '\0') {
837: if (c != '%')
838: eputc(c);
839: else {
840: c = *fp++;
841: switch (c) {
842: case 'c':
1.48 kjell 843: getkeyname(kname, sizeof(kname),
844: va_arg(ap, int));
1.1 deraadt 845: eputs(kname);
846: break;
847:
848: case 'k':
1.12 mickey 849: for (cp = kname, c = 0; c < key.k_count; c++) {
850: if (c)
851: *cp++ = ' ';
1.42 kjell 852: cp = getkeyname(cp, sizeof(kname) -
1.12 mickey 853: (cp - kname) - 1, key.k_chars[c]);
1.1 deraadt 854: }
855: eputs(kname);
856: break;
857:
858: case 'd':
1.2 millert 859: eputi(va_arg(ap, int), 10);
1.1 deraadt 860: break;
861:
862: case 'o':
1.2 millert 863: eputi(va_arg(ap, int), 8);
1.27 vincent 864: break;
865:
866: case 'p':
1.31 db 867: snprintf(tmp, sizeof(tmp), "%p",
1.27 vincent 868: va_arg(ap, void *));
869: eputs(tmp);
1.1 deraadt 870: break;
871:
872: case 's':
1.2 millert 873: eputs(va_arg(ap, char *));
1.1 deraadt 874: break;
875:
1.3 millert 876: case 'l':
877: /* explicit longword */
1.1 deraadt 878: c = *fp++;
1.2 millert 879: switch (c) {
1.1 deraadt 880: case 'd':
1.13 mickey 881: eputl(va_arg(ap, long), 10);
1.1 deraadt 882: break;
883: default:
884: eputc(c);
885: break;
886: }
887: break;
888:
889: default:
890: eputc(c);
891: }
892: }
893: }
894: }
895:
896: /*
897: * Put integer, in radix "r".
898: */
1.10 art 899: static void
1.21 vincent 900: eputi(int i, int r)
1.1 deraadt 901: {
1.3 millert 902: int q;
1.1 deraadt 903:
1.2 millert 904: if (i < 0) {
905: eputc('-');
906: i = -i;
1.1 deraadt 907: }
1.2 millert 908: if ((q = i / r) != 0)
1.1 deraadt 909: eputi(q, r);
1.2 millert 910: eputc(i % r + '0');
1.1 deraadt 911: }
912:
913: /*
914: * Put long, in radix "r".
915: */
1.10 art 916: static void
1.21 vincent 917: eputl(long l, int r)
1.1 deraadt 918: {
1.3 millert 919: long q;
1.1 deraadt 920:
1.2 millert 921: if (l < 0) {
922: eputc('-');
923: l = -l;
1.1 deraadt 924: }
1.2 millert 925: if ((q = l / r) != 0)
1.1 deraadt 926: eputl(q, r);
1.3 millert 927: eputc((int)(l % r) + '0');
1.1 deraadt 928: }
929:
930: /*
931: * Put string.
932: */
1.10 art 933: static void
1.21 vincent 934: eputs(const char *s)
1.1 deraadt 935: {
1.3 millert 936: int c;
1.1 deraadt 937:
938: while ((c = *s++) != '\0')
939: eputc(c);
940: }
941:
942: /*
1.14 mickey 943: * Put character. Watch for control characters, and for the line getting
1.3 millert 944: * too long.
1.1 deraadt 945: */
1.10 art 946: static void
1.21 vincent 947: eputc(char c)
1.1 deraadt 948: {
1.2 millert 949: if (ttcol + 2 < ncol) {
1.1 deraadt 950: if (ISCTRL(c)) {
951: eputc('^');
952: c = CCHR(c);
953: }
954: ttputc(c);
955: ++ttcol;
956: }
957: }
958:
1.10 art 959: void
1.40 deraadt 960: free_file_list(struct list *lp)
1.1 deraadt 961: {
1.40 deraadt 962: struct list *next;
1.2 millert 963:
964: while (lp) {
965: next = lp->l_next;
1.43 kjell 966: free(lp->l_name);
1.2 millert 967: free(lp);
968: lp = next;
969: }
1.1 deraadt 970: }
971:
1.40 deraadt 972: static struct list *
973: copy_list(struct list *lp)
1.2 millert 974: {
1.40 deraadt 975: struct list *current, *last, *nxt;
1.2 millert 976:
977: last = NULL;
978: while (lp) {
1.45 kjell 979: current = malloc(sizeof(struct list));
1.23 vincent 980: if (current == NULL) {
1.43 kjell 981: /* Free what we have allocated so far */
1.23 vincent 982: for (current = last; current; current = nxt) {
983: nxt = current->l_next;
1.43 kjell 984: free(current->l_name);
1.23 vincent 985: free(current);
986: }
1.28 vincent 987: return (NULL);
1.23 vincent 988: }
1.2 millert 989: current->l_next = last;
1.43 kjell 990: current->l_name = strdup(lp->l_name);
1.28 vincent 991: last = current;
1.2 millert 992: lp = lp->l_next;
993: }
994: return (last);
1.1 deraadt 995: }