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