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