Annotation of src/usr.bin/mg/echo.c, Revision 1.45
1.45 ! kjell 1: /* $OpenBSD: echo.c,v 1.44 2006/03/28 20:16:24 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:
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) {
1.41 kjell 195: complt_list(flag, buf, cpos);
1.38 kjell 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.39 deraadt 232: switch (c) {
1.38 kjell 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.40 deraadt 343: struct 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.7 art 464: done:
1.38 kjell 465: if (cwin == TRUE) {
466: /* blow away cpltion window */
467: bp = bfind("*Completions*", TRUE);
468: if ((wp = popbuf(bp)) != NULL) {
469: curwp = wp;
470: delwind(FFRAND, 1);
471: }
472: }
473: return (ret);
474: fail:
475: ewprintf("Out of memory");
476: free(buf);
477: return (NULL);
1.1 deraadt 478: }
479:
480: /*
1.31 db 481: * Do completion on a list of objects.
1.38 kjell 482: * c is SPACE, TAB, or CR
483: * return TRUE if matched (or partially matched)
484: * FALSE is result is ambiguous,
485: * ABORT on error.
1.1 deraadt 486: */
1.2 millert 487: static int
1.38 kjell 488: complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
1.3 millert 489: {
1.40 deraadt 490: struct list *lh, *lh2;
491: struct list *wholelist = NULL;
1.3 millert 492: int i, nxtra, nhits, bxtra, msglen, nshown;
493: int wflag = FALSE;
494: char *msg;
495:
496: lh = lh2 = NULL;
1.2 millert 497:
498: if ((flags & EFFUNC) != 0) {
499: buf[cpos] = '\0';
1.32 otto 500: wholelist = lh = complete_function_list(buf);
1.9 art 501: } else if ((flags & EFBUF) != 0) {
1.6 art 502: lh = &(bheadp->b_list);
1.9 art 503: } else if ((flags & EFFILE) != 0) {
1.6 art 504: buf[cpos] = '\0';
505: wholelist = lh = make_file_list(buf);
506: } else
507: panic("broken complt call: flags");
508:
509: if (c == ' ')
510: wflag = TRUE;
511: else if (c != '\t' && c != CCHR('M'))
512: panic("broken complt call: c");
513:
514: nhits = 0;
515: nxtra = HUGE;
516:
517: for (; lh != NULL; lh = lh->l_next) {
518: if (memcmp(buf, lh->l_name, cpos) != 0)
519: continue;
1.2 millert 520: if (nhits == 0)
1.6 art 521: lh2 = lh;
522: ++nhits;
523: if (lh->l_name[cpos] == '\0')
1.38 kjell 524: nxtra = -1; /* exact match */
1.3 millert 525: else {
1.6 art 526: bxtra = getxtra(lh, lh2, cpos, wflag);
527: if (bxtra < nxtra)
528: nxtra = bxtra;
529: lh2 = lh;
530: }
531: }
532: if (nhits == 0)
533: msg = " [No match]";
534: else if (nhits > 1 && nxtra == 0)
1.37 kjell 535: msg = " [Ambiguous. Ctrl-G to cancel]";
1.6 art 536: else {
537: /*
538: * Being lazy - ought to check length, but all things
539: * autocompleted have known types/lengths.
540: */
541: if (nxtra < 0 && nhits > 1 && c == ' ')
1.38 kjell 542: nxtra = 1; /* ??? */
1.29 vincent 543: for (i = 0; i < nxtra && cpos < nbuf; ++i) {
1.6 art 544: buf[cpos] = lh2->l_name[cpos];
545: eputc(buf[cpos++]);
1.1 deraadt 546: }
1.38 kjell 547: /* XXX should grow nbuf */
1.6 art 548: ttflush();
549: free_file_list(wholelist);
1.38 kjell 550: *nx = nxtra;
551: if (nxtra < 0 && c != CCHR('M')) /* exact */
552: *nx = 0;
553: return (TRUE);
1.1 deraadt 554: }
1.3 millert 555:
1.2 millert 556: /*
1.31 db 557: * wholelist is NULL if we are doing buffers. Want to free lists
1.2 millert 558: * that were created for us, but not the buffer list!
559: */
1.1 deraadt 560: free_file_list(wholelist);
1.3 millert 561:
1.31 db 562: /* Set up backspaces, etc., being mindful of echo line limit. */
1.1 deraadt 563: msglen = strlen(msg);
564: nshown = (ttcol + msglen + 2 > ncol) ?
1.2 millert 565: ncol - ttcol - 2 : msglen;
1.1 deraadt 566: eputs(msg);
1.2 millert 567: ttcol -= (i = nshown); /* update ttcol! */
568: while (i--) /* move back before msg */
1.1 deraadt 569: ttputc('\b');
1.2 millert 570: ttflush(); /* display to user */
1.1 deraadt 571: i = nshown;
1.3 millert 572: while (i--) /* blank out on next flush */
1.1 deraadt 573: eputc(' ');
1.2 millert 574: ttcol -= (i = nshown); /* update ttcol on BS's */
1.1 deraadt 575: while (i--)
1.2 millert 576: ttputc('\b'); /* update ttcol again! */
1.38 kjell 577: *nx = nxtra;
578: return ((nhits > 0) ? TRUE : FALSE);
1.1 deraadt 579: }
580:
581: /*
1.31 db 582: * Do completion on a list of objects, listing instead of completing.
1.1 deraadt 583: */
1.2 millert 584: static int
1.41 kjell 585: complt_list(int flags, char *buf, int cpos)
1.3 millert 586: {
1.40 deraadt 587: struct list *lh, *lh2, *lh3;
588: struct list *wholelist = NULL;
589: struct buffer *bp;
1.3 millert 590: int i, maxwidth, width;
591: int preflen = 0;
592: int oldrow = ttrow;
593: int oldcol = ttcol;
594: int oldhue = tthue;
1.17 art 595: char *linebuf;
1.26 millert 596: size_t linesize, len;
1.43 kjell 597: char *cp;
1.3 millert 598:
599: lh = NULL;
1.1 deraadt 600:
601: ttflush();
602:
1.38 kjell 603: /* The results are put into a completion buffer. */
604: bp = bfind("*Completions*", TRUE);
1.2 millert 605: if (bclear(bp) == FALSE)
1.31 db 606: return (FALSE);
1.2 millert 607:
1.24 vincent 608: /*
1.31 db 609: * First get the list of objects. This list may contain only
1.24 vincent 610: * the ones that complete what has been typed, or may be the
611: * whole list of all objects of this type. They are filtered
612: * later in any case. Set wholelist if the list has been
613: * cons'ed up just for us, so we can free it later. We have
614: * to copy the buffer list for this function even though we
615: * didn't for complt. The sorting code does destructive
616: * changes to the list, which we don't want to happen to the
617: * main buffer list!
618: */
619: if ((flags & EFBUF) != 0)
620: wholelist = lh = copy_list(&(bheadp->b_list));
621: else if ((flags & EFFUNC) != 0) {
622: buf[cpos] = '\0';
1.32 otto 623: wholelist = lh = complete_function_list(buf);
1.24 vincent 624: } else if ((flags & EFFILE) != 0) {
625: buf[cpos] = '\0';
626: wholelist = lh = make_file_list(buf);
1.2 millert 627: /*
1.24 vincent 628: * We don't want to display stuff up to the / for file
629: * names preflen is the list of a prefix of what the
630: * user typed that should not be displayed.
1.2 millert 631: */
1.24 vincent 632: cp = strrchr(buf, '/');
633: if (cp)
634: preflen = cp - buf + 1;
635: } else
636: panic("broken complt call: flags");
1.1 deraadt 637:
1.24 vincent 638: /*
639: * Sort the list, since users expect to see it in alphabetic
640: * order.
641: */
642: lh2 = lh;
643: while (lh2 != NULL) {
644: lh3 = lh2->l_next;
645: while (lh3 != NULL) {
646: if (strcmp(lh2->l_name, lh3->l_name) > 0) {
647: cp = lh2->l_name;
648: lh2->l_name = lh3->l_name;
649: lh3->l_name = cp;
1.2 millert 650: }
1.24 vincent 651: lh3 = lh3->l_next;
1.2 millert 652: }
1.24 vincent 653: lh2 = lh2->l_next;
654: }
1.1 deraadt 655:
1.24 vincent 656: /*
657: * First find max width of object to be displayed, so we can
658: * put several on a line.
659: */
660: maxwidth = 0;
661: lh2 = lh;
662: while (lh2 != NULL) {
663: for (i = 0; i < cpos; ++i) {
664: if (buf[i] != lh2->l_name[i])
665: break;
666: }
667: if (i == cpos) {
668: width = strlen(lh2->l_name);
669: if (width > maxwidth)
670: maxwidth = width;
1.1 deraadt 671: }
1.24 vincent 672: lh2 = lh2->l_next;
673: }
674: maxwidth += 1 - preflen;
1.2 millert 675:
1.24 vincent 676: /*
1.31 db 677: * Now do the display. Objects are written into linebuf until
1.24 vincent 678: * it fills, and then put into the help buffer.
679: */
1.26 millert 680: linesize = MAX(ncol, maxwidth) + 1;
681: if ((linebuf = malloc(linesize)) == NULL)
1.31 db 682: return (FALSE);
1.24 vincent 683: width = 0;
1.19 vincent 684:
1.25 deraadt 685: /*
1.24 vincent 686: * We're going to strlcat() into the buffer, so it has to be
1.31 db 687: * NUL terminated.
1.24 vincent 688: */
689: linebuf[0] = '\0';
690: for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
691: for (i = 0; i < cpos; ++i) {
692: if (buf[i] != lh2->l_name[i])
693: break;
1.2 millert 694: }
1.24 vincent 695: /* if we have a match */
696: if (i == cpos) {
697: /* if it wraps */
698: if ((width + maxwidth) > ncol) {
699: addline(bp, linebuf);
700: linebuf[0] = '\0';
701: width = 0;
702: }
1.43 kjell 703: len = strlcat(linebuf, lh2->l_name + preflen,
704: 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.40 deraadt 741: getxtra(struct list *lp1, struct 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.42 kjell 805: getkeyname(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++ = ' ';
1.42 kjell 813: cp = getkeyname(cp, sizeof(kname) -
1.12 mickey 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.40 deraadt 921: free_file_list(struct list *lp)
1.1 deraadt 922: {
1.40 deraadt 923: struct list *next;
1.2 millert 924:
925: while (lp) {
926: next = lp->l_next;
1.43 kjell 927: free(lp->l_name);
1.2 millert 928: free(lp);
929: lp = next;
930: }
1.1 deraadt 931: }
932:
1.40 deraadt 933: static struct list *
934: copy_list(struct list *lp)
1.2 millert 935: {
1.40 deraadt 936: struct list *current, *last, *nxt;
1.2 millert 937:
938: last = NULL;
939: while (lp) {
1.45 ! kjell 940: current = malloc(sizeof(struct list));
1.23 vincent 941: if (current == NULL) {
1.43 kjell 942: /* Free what we have allocated so far */
1.23 vincent 943: for (current = last; current; current = nxt) {
944: nxt = current->l_next;
1.43 kjell 945: free(current->l_name);
1.23 vincent 946: free(current);
947: }
1.28 vincent 948: return (NULL);
1.23 vincent 949: }
1.2 millert 950: current->l_next = last;
1.43 kjell 951: current->l_name = strdup(lp->l_name);
1.28 vincent 952: last = current;
1.2 millert 953: lp = lp->l_next;
954: }
955: return (last);
1.1 deraadt 956: }