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