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