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