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