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