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