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