+++ /dev/null
-/* $OpenBSD: echo.c,v 1.50 2012/04/12 04:47:59 lum Exp $ */
-
-/* This file is in the public domain. */
-
-/*
- * Echo line reading and writing.
- *
- * Common routines for reading and writing characters in the echo line area
- * of the display screen. Used by the entire known universe.
- */
-
-#include "def.h"
-#include "key.h"
-#include "macro.h"
-
-#include "funmap.h"
-
-#include <stdarg.h>
-#include <term.h>
-
-static char *veread(const char *, char *, size_t, int, va_list);
-static int complt(int, int, char *, size_t, int, int *);
-static int complt_list(int, char *, int);
-static void eformat(const char *, va_list);
-static void eputi(int, int);
-static void eputl(long, int);
-static void eputs(const char *);
-static void eputc(char);
-static struct list *copy_list(struct list *);
-
-int epresf = FALSE; /* stuff in echo line flag */
-
-/*
- * Erase the echo line.
- */
-void
-eerase(void)
-{
- ttcolor(CTEXT);
- ttmove(nrow - 1, 0);
- tteeol();
- ttflush();
- epresf = FALSE;
-}
-
-/*
- * Ask a "yes" or "no" question. Return ABORT if the user answers the
- * question with the abort ("^G") character. Return FALSE for "no" and
- * TRUE for "yes". No formatting services are available. No newline
- * required.
- */
-int
-eyorn(const char *sp)
-{
- int s;
-
- if (inmacro)
- return (TRUE);
-
- ewprintf("%s? (y or n) ", sp);
- for (;;) {
- s = getkey(FALSE);
- if (s == 'y' || s == 'Y' || s == ' ')
- return (TRUE);
- if (s == 'n' || s == 'N' || s == CCHR('M'))
- return (FALSE);
- if (s == CCHR('G'))
- return (ctrlg(FFRAND, 1));
- ewprintf("Please answer y or n. %s? (y or n) ", sp);
- }
- /* NOTREACHED */
-}
-
-/*
- * Like eyorn, but for more important questions. User must type all of
- * "yes" or "no" and the trailing newline.
- */
-int
-eyesno(const char *sp)
-{
- char buf[64], *rep;
-
- if (inmacro)
- return (TRUE);
-
- rep = eread("%s? (yes or no) ", buf, sizeof(buf),
- EFNUL | EFNEW | EFCR, sp);
- for (;;) {
- if (rep == NULL)
- return (ABORT);
- if (rep[0] != '\0') {
- if (macrodef) {
- struct line *lp = maclcur;
-
- maclcur = lp->l_bp;
- maclcur->l_fp = lp->l_fp;
- free(lp);
- }
- if ((rep[0] == 'y' || rep[0] == 'Y') &&
- (rep[1] == 'e' || rep[1] == 'E') &&
- (rep[2] == 's' || rep[2] == 'S') &&
- (rep[3] == '\0'))
- return (TRUE);
- if ((rep[0] == 'n' || rep[0] == 'N') &&
- (rep[1] == 'o' || rep[0] == 'O') &&
- (rep[2] == '\0'))
- return (FALSE);
- }
- rep = eread("Please answer yes or no. %s? (yes or no) ",
- buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp);
- }
- /* NOTREACHED */
-}
-
-/*
- * This is the general "read input from the echo line" routine. The basic
- * idea is that the prompt string "prompt" is written to the echo line, and
- * a one line reply is read back into the supplied "buf" (with maximum
- * length "len").
- * XXX: When checking for an empty return value, always check rep, *not* buf
- * as buf may be freed in pathological cases.
- */
-/* VARARGS */
-char *
-eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
-{
- va_list ap;
- char *rep;
-
- va_start(ap, flag);
- rep = veread(fmt, buf, nbuf, flag, ap);
- va_end(ap);
- return (rep);
-}
-
-static char *
-veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
-{
- int dynbuf = (buf == NULL);
- int cpos, epos; /* cursor, end position in buf */
- int c, i, y;
- int cplflag = FALSE; /* display completion list */
- int cwin = FALSE; /* completion list created */
- int mr = 0; /* match left arrow */
- int ml = 0; /* match right arrow */
- int esc = 0; /* position in esc pattern */
- struct buffer *bp; /* completion list buffer */
- struct mgwin *wp; /* window for compl list */
- int match; /* esc match found */
- int cc, rr; /* saved ttcol, ttrow */
- char *ret; /* return value */
-
- static char emptyval[] = ""; /* XXX hackish way to return err msg*/
-
- if (inmacro) {
- if (dynbuf) {
- if ((buf = malloc(maclcur->l_used + 1)) == NULL)
- return (NULL);
- } else if (maclcur->l_used >= nbuf)
- return (NULL);
- bcopy(maclcur->l_text, buf, maclcur->l_used);
- buf[maclcur->l_used] = '\0';
- maclcur = maclcur->l_fp;
- return (buf);
- }
- epos = cpos = 0;
- ml = mr = esc = 0;
- cplflag = FALSE;
-
- if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
- ttcolor(CTEXT);
- ttmove(nrow - 1, 0);
- epresf = TRUE;
- } else
- eputc(' ');
- eformat(fp, ap);
- if ((flag & EFDEF) != 0) {
- if (buf == NULL)
- return (NULL);
- eputs(buf);
- epos = cpos += strlen(buf);
- }
- tteeol();
- ttflush();
- for (;;) {
- c = getkey(FALSE);
- if ((flag & EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
- if (cplflag == TRUE) {
- complt_list(flag, buf, cpos);
- cwin = TRUE;
- } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
- cplflag = TRUE;
- epos += i;
- cpos = epos;
- }
- continue;
- }
- cplflag = FALSE;
-
- if (esc > 0) { /* ESC sequence started */
- match = 0;
- if (ml == esc && key_left[ml] && c == key_left[ml]) {
- match++;
- if (key_left[++ml] == '\0') {
- c = CCHR('B');
- esc = 0;
- }
- }
- if (mr == esc && key_right[mr] && c == key_right[mr]) {
- match++;
- if (key_right[++mr] == '\0') {
- c = CCHR('F');
- esc = 0;
- }
- }
- if (match == 0) {
- esc = 0;
- continue;
- /* hack. how do we know esc pattern is done? */
- }
- if (esc > 0) {
- esc++;
- continue;
- }
- }
- switch (c) {
- case CCHR('A'): /* start of line */
- while (cpos > 0) {
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- --ttcol;
- }
- ttputc('\b');
- --ttcol;
- }
- ttflush();
- break;
- case CCHR('D'):
- if (cpos != epos) {
- tteeol();
- y = buf[cpos];
- epos--;
- rr = ttrow;
- cc = ttcol;
- for (i = cpos; i < epos; i++) {
- buf[i] = buf[i + 1];
- eputc(buf[i]);
- }
- ttmove(rr, cc);
- ttflush();
- }
- break;
- case CCHR('E'): /* end of line */
- while (cpos < epos) {
- eputc(buf[cpos++]);
- }
- ttflush();
- break;
- case CCHR('B'): /* back */
- if (cpos > 0) {
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- --ttcol;
- }
- ttputc('\b');
- --ttcol;
- ttflush();
- }
- break;
- case CCHR('F'): /* forw */
- if (cpos < epos) {
- eputc(buf[cpos++]);
- ttflush();
- }
- break;
- case CCHR('Y'): /* yank from kill buffer */
- i = 0;
- while ((y = kremove(i++)) >= 0 && y != '\n') {
- int t;
- if (dynbuf && epos + 1 >= nbuf) {
- void *newp;
- size_t newsize = epos + epos + 16;
- if ((newp = realloc(buf, newsize))
- == NULL)
- goto memfail;
- buf = newp;
- nbuf = newsize;
- }
- if (!dynbuf && epos + 1 >= nbuf) {
- ewprintf("Line too long");
- return (emptyval);
- }
- for (t = epos; t > cpos; t--)
- buf[t] = buf[t - 1];
- buf[cpos++] = (char)y;
- epos++;
- eputc((char)y);
- cc = ttcol;
- rr = ttrow;
- for (t = cpos; t < epos; t++)
- eputc(buf[t]);
- ttmove(rr, cc);
- }
- ttflush();
- break;
- case CCHR('K'): /* copy here-EOL to kill buffer */
- kdelete();
- for (i = cpos; i < epos; i++)
- kinsert(buf[i], KFORW);
- tteeol();
- epos = cpos;
- ttflush();
- break;
- case CCHR('['):
- ml = mr = esc = 1;
- break;
- case CCHR('J'):
- c = CCHR('M');
- /* FALLTHROUGH */
- case CCHR('M'): /* return, done */
- /* if there's nothing in the minibuffer, abort */
- if (epos == 0 && !(flag & EFNUL)) {
- (void)ctrlg(FFRAND, 0);
- ttflush();
- return (NULL);
- }
- if ((flag & EFFUNC) != 0) {
- if (complt(flag, c, buf, nbuf, epos, &i)
- == FALSE)
- continue;
- if (i > 0)
- epos += i;
- }
- buf[epos] = '\0';
- if ((flag & EFCR) != 0) {
- ttputc(CCHR('M'));
- ttflush();
- }
- if (macrodef) {
- struct line *lp;
-
- if ((lp = lalloc(cpos)) == NULL)
- goto memfail;
- lp->l_fp = maclcur->l_fp;
- maclcur->l_fp = lp;
- lp->l_bp = maclcur;
- maclcur = lp;
- bcopy(buf, lp->l_text, cpos);
- }
- ret = buf;
- goto done;
- case CCHR('G'): /* bell, abort */
- eputc(CCHR('G'));
- (void)ctrlg(FFRAND, 0);
- ttflush();
- ret = NULL;
- goto done;
- case CCHR('H'): /* rubout, erase */
- case CCHR('?'):
- if (cpos != 0) {
- y = buf[--cpos];
- epos--;
- ttputc('\b');
- ttcol--;
- if (ISCTRL(y) != FALSE) {
- ttputc('\b');
- ttcol--;
- }
- rr = ttrow;
- cc = ttcol;
- for (i = cpos; i < epos; i++) {
- buf[i] = buf[i + 1];
- eputc(buf[i]);
- }
- ttputc(' ');
- if (ISCTRL(y) != FALSE) {
- ttputc(' ');
- ttputc('\b');
- }
- ttputc('\b');
- ttmove(rr, cc);
- ttflush();
- }
- break;
- case CCHR('X'): /* kill line */
- case CCHR('U'):
- while (cpos != 0) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- epos--;
- }
- ttflush();
- break;
- case CCHR('W'): /* kill to beginning of word */
- while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- epos--;
- }
- while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- epos--;
- }
- ttflush();
- break;
- case CCHR('\\'):
- case CCHR('Q'): /* quote next */
- c = getkey(FALSE);
- /* FALLTHROUGH */
- default:
- if (dynbuf && epos + 1 >= nbuf) {
- void *newp;
- size_t newsize = epos + epos + 16;
- if ((newp = realloc(buf, newsize)) == NULL)
- goto memfail;
- buf = newp;
- nbuf = newsize;
- }
- if (!dynbuf && epos + 1 >= nbuf) {
- ewprintf("Line too long");
- return (emptyval);
- }
- for (i = epos; i > cpos; i--)
- buf[i] = buf[i - 1];
- buf[cpos++] = (char)c;
- epos++;
- eputc((char)c);
- cc = ttcol;
- rr = ttrow;
- for (i = cpos; i < epos; i++)
- eputc(buf[i]);
- ttmove(rr, cc);
- ttflush();
- }
- }
-done:
- if (cwin == TRUE) {
- /* blow away cpltion window */
- bp = bfind("*Completions*", TRUE);
- if ((wp = popbuf(bp, WEPHEM)) != NULL) {
- if (wp->w_flag & WEPHEM) {
- curwp = wp;
- delwind(FFRAND, 1);
- } else {
- killbuffer(bp);
- }
- }
- }
- return (ret);
-memfail:
- if (dynbuf && buf)
- free(buf);
- ewprintf("Out of memory");
- return (emptyval);
-}
-
-/*
- * Do completion on a list of objects.
- * c is SPACE, TAB, or CR
- * return TRUE if matched (or partially matched)
- * FALSE is result is ambiguous,
- * ABORT on error.
- */
-static int
-complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
-{
- struct list *lh, *lh2;
- struct list *wholelist = NULL;
- int i, nxtra, nhits, bxtra, msglen, nshown;
- int wflag = FALSE;
- char *msg;
-
- lh = lh2 = NULL;
-
- if ((flags & EFFUNC) != 0) {
- buf[cpos] = '\0';
- wholelist = lh = complete_function_list(buf);
- } else if ((flags & EFBUF) != 0) {
- lh = &(bheadp->b_list);
- } else if ((flags & EFFILE) != 0) {
- buf[cpos] = '\0';
- wholelist = lh = make_file_list(buf);
- } else
- panic("broken complt call: flags");
-
- if (c == ' ')
- wflag = TRUE;
- else if (c != '\t' && c != CCHR('M'))
- panic("broken complt call: c");
-
- nhits = 0;
- nxtra = HUGE;
-
- for (; lh != NULL; lh = lh->l_next) {
- if (memcmp(buf, lh->l_name, cpos) != 0)
- continue;
- if (nhits == 0)
- lh2 = lh;
- ++nhits;
- if (lh->l_name[cpos] == '\0')
- nxtra = -1; /* exact match */
- else {
- bxtra = getxtra(lh, lh2, cpos, wflag);
- if (bxtra < nxtra)
- nxtra = bxtra;
- lh2 = lh;
- }
- }
- if (nhits == 0)
- msg = " [No match]";
- else if (nhits > 1 && nxtra == 0)
- msg = " [Ambiguous. Ctrl-G to cancel]";
- else {
- /*
- * Being lazy - ought to check length, but all things
- * autocompleted have known types/lengths.
- */
- if (nxtra < 0 && nhits > 1 && c == ' ')
- nxtra = 1; /* ??? */
- for (i = 0; i < nxtra && cpos < nbuf; ++i) {
- buf[cpos] = lh2->l_name[cpos];
- eputc(buf[cpos++]);
- }
- /* XXX should grow nbuf */
- ttflush();
- free_file_list(wholelist);
- *nx = nxtra;
- if (nxtra < 0 && c != CCHR('M')) /* exact */
- *nx = 0;
- return (TRUE);
- }
-
- /*
- * wholelist is NULL if we are doing buffers. Want to free lists
- * that were created for us, but not the buffer list!
- */
- free_file_list(wholelist);
-
- /* Set up backspaces, etc., being mindful of echo line limit. */
- msglen = strlen(msg);
- nshown = (ttcol + msglen + 2 > ncol) ?
- ncol - ttcol - 2 : msglen;
- eputs(msg);
- ttcol -= (i = nshown); /* update ttcol! */
- while (i--) /* move back before msg */
- ttputc('\b');
- ttflush(); /* display to user */
- i = nshown;
- while (i--) /* blank out on next flush */
- eputc(' ');
- ttcol -= (i = nshown); /* update ttcol on BS's */
- while (i--)
- ttputc('\b'); /* update ttcol again! */
- *nx = nxtra;
- return ((nhits > 0) ? TRUE : FALSE);
-}
-
-/*
- * Do completion on a list of objects, listing instead of completing.
- */
-static int
-complt_list(int flags, char *buf, int cpos)
-{
- struct list *lh, *lh2, *lh3;
- struct list *wholelist = NULL;
- struct buffer *bp;
- int i, maxwidth, width;
- int preflen = 0;
- int oldrow = ttrow;
- int oldcol = ttcol;
- int oldhue = tthue;
- char *linebuf;
- size_t linesize, len;
- char *cp;
-
- lh = NULL;
-
- ttflush();
-
- /* The results are put into a completion buffer. */
- bp = bfind("*Completions*", TRUE);
- if (bclear(bp) == FALSE)
- return (FALSE);
-
- /*
- * First get the list of objects. This list may contain only
- * the ones that complete what has been typed, or may be the
- * whole list of all objects of this type. They are filtered
- * later in any case. Set wholelist if the list has been
- * cons'ed up just for us, so we can free it later. We have
- * to copy the buffer list for this function even though we
- * didn't for complt. The sorting code does destructive
- * changes to the list, which we don't want to happen to the
- * main buffer list!
- */
- if ((flags & EFBUF) != 0)
- wholelist = lh = copy_list(&(bheadp->b_list));
- else if ((flags & EFFUNC) != 0) {
- buf[cpos] = '\0';
- wholelist = lh = complete_function_list(buf);
- } else if ((flags & EFFILE) != 0) {
- buf[cpos] = '\0';
- wholelist = lh = make_file_list(buf);
- /*
- * We don't want to display stuff up to the / for file
- * names preflen is the list of a prefix of what the
- * user typed that should not be displayed.
- */
- cp = strrchr(buf, '/');
- if (cp)
- preflen = cp - buf + 1;
- } else
- panic("broken complt call: flags");
-
- /*
- * Sort the list, since users expect to see it in alphabetic
- * order.
- */
- lh2 = lh;
- while (lh2 != NULL) {
- lh3 = lh2->l_next;
- while (lh3 != NULL) {
- if (strcmp(lh2->l_name, lh3->l_name) > 0) {
- cp = lh2->l_name;
- lh2->l_name = lh3->l_name;
- lh3->l_name = cp;
- }
- lh3 = lh3->l_next;
- }
- lh2 = lh2->l_next;
- }
-
- /*
- * First find max width of object to be displayed, so we can
- * put several on a line.
- */
- maxwidth = 0;
- lh2 = lh;
- while (lh2 != NULL) {
- for (i = 0; i < cpos; ++i) {
- if (buf[i] != lh2->l_name[i])
- break;
- }
- if (i == cpos) {
- width = strlen(lh2->l_name);
- if (width > maxwidth)
- maxwidth = width;
- }
- lh2 = lh2->l_next;
- }
- maxwidth += 1 - preflen;
-
- /*
- * Now do the display. Objects are written into linebuf until
- * it fills, and then put into the help buffer.
- */
- linesize = MAX(ncol, maxwidth) + 1;
- if ((linebuf = malloc(linesize)) == NULL)
- return (FALSE);
- width = 0;
-
- /*
- * We're going to strlcat() into the buffer, so it has to be
- * NUL terminated.
- */
- linebuf[0] = '\0';
- for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
- for (i = 0; i < cpos; ++i) {
- if (buf[i] != lh2->l_name[i])
- break;
- }
- /* if we have a match */
- if (i == cpos) {
- /* if it wraps */
- if ((width + maxwidth) > ncol) {
- addline(bp, linebuf);
- linebuf[0] = '\0';
- width = 0;
- }
- len = strlcat(linebuf, lh2->l_name + preflen,
- linesize);
- width += maxwidth;
- if (len < width && width < linesize) {
- /* pad so the objects nicely line up */
- memset(linebuf + len, ' ',
- maxwidth - strlen(lh2->l_name + preflen));
- linebuf[width] = '\0';
- }
- }
- }
- if (width > 0)
- addline(bp, linebuf);
- free(linebuf);
-
- /*
- * Note that we free lists only if they are put in wholelist lists
- * that were built just for us should be freed. However when we use
- * the buffer list, obviously we don't want it freed.
- */
- free_file_list(wholelist);
- popbuftop(bp, WEPHEM); /* split the screen and put up the help
- * buffer */
- update(); /* needed to make the new stuff actually
- * appear */
- ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
- ttcolor(oldhue); /* with arbitrary color */
- ttflush();
- return (0);
-}
-
-/*
- * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
- * position in the name. Return the longest block of characters that can be
- * autocompleted at this point. Sometimes the two symbols are the same, but
- * this is normal.
- */
-int
-getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
-{
- int i;
-
- i = cpos;
- for (;;) {
- if (lp1->l_name[i] != lp2->l_name[i])
- break;
- if (lp1->l_name[i] == '\0')
- break;
- ++i;
- if (wflag && !ISWORD(lp1->l_name[i - 1]))
- break;
- }
- return (i - cpos);
-}
-
-/*
- * Special "printf" for the echo line. Each call to "ewprintf" starts a
- * new line in the echo area, and ends with an erase to end of the echo
- * line. The formatting is done by a call to the standard formatting
- * routine.
- */
-/* VARARGS */
-void
-ewprintf(const char *fmt, ...)
-{
- va_list ap;
-
- if (inmacro)
- return;
-
- va_start(ap, fmt);
- ttcolor(CTEXT);
- ttmove(nrow - 1, 0);
- eformat(fmt, ap);
- va_end(ap);
- tteeol();
- ttflush();
- epresf = TRUE;
-}
-
-/*
- * Printf style formatting. This is called by both "ewprintf" and "ereply"
- * to provide formatting services to their clients. The move to the start
- * of the echo line, and the erase to the end of the echo line, is done by
- * the caller.
- * %c prints the "name" of the supplied character.
- * %k prints the name of the current key (and takes no arguments).
- * %d prints a decimal integer
- * %o prints an octal integer
- * %p prints a pointer
- * %s prints a string
- * %ld prints a long word
- * Anything else is echoed verbatim
- */
-static void
-eformat(const char *fp, va_list ap)
-{
- char kname[NKNAME], tmp[100], *cp;
- int c;
-
- while ((c = *fp++) != '\0') {
- if (c != '%')
- eputc(c);
- else {
- c = *fp++;
- switch (c) {
- case 'c':
- getkeyname(kname, sizeof(kname),
- va_arg(ap, int));
- eputs(kname);
- break;
-
- case 'k':
- for (cp = kname, c = 0; c < key.k_count; c++) {
- if (c)
- *cp++ = ' ';
- cp = getkeyname(cp, sizeof(kname) -
- (cp - kname) - 1, key.k_chars[c]);
- }
- eputs(kname);
- break;
-
- case 'd':
- eputi(va_arg(ap, int), 10);
- break;
-
- case 'o':
- eputi(va_arg(ap, int), 8);
- break;
-
- case 'p':
- snprintf(tmp, sizeof(tmp), "%p",
- va_arg(ap, void *));
- eputs(tmp);
- break;
-
- case 's':
- eputs(va_arg(ap, char *));
- break;
-
- case 'l':
- /* explicit longword */
- c = *fp++;
- switch (c) {
- case 'd':
- eputl(va_arg(ap, long), 10);
- break;
- default:
- eputc(c);
- break;
- }
- break;
-
- default:
- eputc(c);
- }
- }
- }
-}
-
-/*
- * Put integer, in radix "r".
- */
-static void
-eputi(int i, int r)
-{
- int q;
-
- if (i < 0) {
- eputc('-');
- i = -i;
- }
- if ((q = i / r) != 0)
- eputi(q, r);
- eputc(i % r + '0');
-}
-
-/*
- * Put long, in radix "r".
- */
-static void
-eputl(long l, int r)
-{
- long q;
-
- if (l < 0) {
- eputc('-');
- l = -l;
- }
- if ((q = l / r) != 0)
- eputl(q, r);
- eputc((int)(l % r) + '0');
-}
-
-/*
- * Put string.
- */
-static void
-eputs(const char *s)
-{
- int c;
-
- while ((c = *s++) != '\0')
- eputc(c);
-}
-
-/*
- * Put character. Watch for control characters, and for the line getting
- * too long.
- */
-static void
-eputc(char c)
-{
- if (ttcol + 2 < ncol) {
- if (ISCTRL(c)) {
- eputc('^');
- c = CCHR(c);
- }
- ttputc(c);
- ++ttcol;
- }
-}
-
-void
-free_file_list(struct list *lp)
-{
- struct list *next;
-
- while (lp) {
- next = lp->l_next;
- free(lp->l_name);
- free(lp);
- lp = next;
- }
-}
-
-static struct list *
-copy_list(struct list *lp)
-{
- struct list *current, *last, *nxt;
-
- last = NULL;
- while (lp) {
- current = malloc(sizeof(struct list));
- if (current == NULL) {
- /* Free what we have allocated so far */
- for (current = last; current; current = nxt) {
- nxt = current->l_next;
- free(current->l_name);
- free(current);
- }
- return (NULL);
- }
- current->l_next = last;
- current->l_name = strdup(lp->l_name);
- last = current;
- lp = lp->l_next;
- }
- return (last);
-}