xref: /openbsd/usr.bin/mg/echo.c (revision 4cfece93)
1 /*	$OpenBSD: echo.c,v 1.66 2016/10/24 17:18:42 jasper 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 
25 static char	*veread(const char *, char *, size_t, int, va_list)
26 			__attribute__((__format__ (printf, 1, 0)));
27 static int	 complt(int, int, char *, size_t, int, int *);
28 static int	 complt_list(int, char *, int);
29 static void	 eformat(const char *, va_list)
30 			__attribute__((__format__ (printf, 1, 0)));
31 static void	 eputi(int, int);
32 static void	 eputl(long, int);
33 static void	 eputs(const char *);
34 static void	 eputc(char);
35 static struct list	*copy_list(struct list *);
36 
37 int		epresf = FALSE;		/* stuff in echo line flag */
38 
39 /*
40  * Erase the echo line.
41  */
42 void
43 eerase(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  */
58 int
59 eyorn(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 			ewprintf("");
71 			return (TRUE);
72 		}
73 		if (s == 'n' || s == 'N' || s == CCHR('M')) {
74 			ewprintf("");
75 			return (FALSE);
76 		}
77 		if (s == CCHR('G')) {
78 			ewprintf("");
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  */
92 int
93 eynorr(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 			ewprintf("");
105 			return (TRUE);
106 		}
107 		if (s == 'n' || s == 'N' || s == CCHR('M')) {
108 			ewprintf("");
109 			return (FALSE);
110 		}
111 		if (s == 'r' || s == 'R') {
112 			ewprintf("");
113 			return (REVERT);
114 		}
115 		if (s == CCHR('G')) {
116 			ewprintf("");
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  */
128 int
129 eyesno(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 			ewprintf("");
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 				ewprintf("");
153 				return (TRUE);
154 			}
155 			if (strcasecmp(rep, "no") == 0) {
156 				ewprintf("");
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  */
174 char *
175 eread(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 
186 static char *
187 veread(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 != '\n') {
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");
340 					return (emptyval);
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");
496 				return (emptyval);
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 	}
511 done:
512 	if (cwin == TRUE) {
513 		/* blow away cpltion window */
514 		bp = bfind("*Completions*", TRUE);
515 		if ((wp = popbuf(bp, WEPHEM)) != NULL) {
516 			if (wp->w_flag & WEPHEM) {
517 				curwp = wp;
518 				delwind(FFRAND, 1);
519 			} else {
520 				killbuffer(bp);
521 			}
522 		}
523 	}
524 	return (ret);
525 memfail:
526 	if (dynbuf && buf)
527 		free(buf);
528 	dobeep();
529 	ewprintf("Out of memory");
530 	return (emptyval);
531 }
532 
533 /*
534  * Do completion on a list of objects.
535  * c is SPACE, TAB, or CR
536  * return TRUE if matched (or partially matched)
537  * FALSE is result is ambiguous,
538  * ABORT on error.
539  */
540 static int
541 complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
542 {
543 	struct list	*lh, *lh2;
544 	struct list	*wholelist = NULL;
545 	int	 i, nxtra, nhits, bxtra, msglen, nshown;
546 	int	 wflag = FALSE;
547 	char	*msg;
548 
549 	lh = lh2 = NULL;
550 
551 	if ((flags & EFFUNC) != 0) {
552 		buf[cpos] = '\0';
553 		wholelist = lh = complete_function_list(buf);
554 	} else if ((flags & EFBUF) != 0) {
555 		lh = &(bheadp->b_list);
556 	} else if ((flags & EFFILE) != 0) {
557 		buf[cpos] = '\0';
558 		wholelist = lh = make_file_list(buf);
559 	} else
560 		panic("broken complt call: flags");
561 
562 	if (c == ' ')
563 		wflag = TRUE;
564 	else if (c != '\t' && c != CCHR('M'))
565 		panic("broken complt call: c");
566 
567 	nhits = 0;
568 	nxtra = HUGE;
569 
570 	for (; lh != NULL; lh = lh->l_next) {
571 		if (memcmp(buf, lh->l_name, cpos) != 0)
572 			continue;
573 		if (nhits == 0)
574 			lh2 = lh;
575 		++nhits;
576 		if (lh->l_name[cpos] == '\0')
577 			nxtra = -1; /* exact match */
578 		else {
579 			bxtra = getxtra(lh, lh2, cpos, wflag);
580 			if (bxtra < nxtra)
581 				nxtra = bxtra;
582 			lh2 = lh;
583 		}
584 	}
585 	if (nhits == 0)
586 		msg = " [No match]";
587 	else if (nhits > 1 && nxtra == 0)
588 		msg = " [Ambiguous. Ctrl-G to cancel]";
589 	else {
590 		/*
591 		 * Being lazy - ought to check length, but all things
592 		 * autocompleted have known types/lengths.
593 		 */
594 		if (nxtra < 0 && nhits > 1 && c == ' ')
595 			nxtra = 1; /* ??? */
596 		for (i = 0; i < nxtra && cpos < nbuf; ++i) {
597 			buf[cpos] = lh2->l_name[cpos];
598 			eputc(buf[cpos++]);
599 		}
600 		/* XXX should grow nbuf */
601 		ttflush();
602 		free_file_list(wholelist);
603 		*nx = nxtra;
604 		if (nxtra < 0 && c != CCHR('M')) /* exact */
605 			*nx = 0;
606 		return (TRUE);
607 	}
608 
609 	/*
610 	 * wholelist is NULL if we are doing buffers.  Want to free lists
611 	 * that were created for us, but not the buffer list!
612 	 */
613 	free_file_list(wholelist);
614 
615 	/* Set up backspaces, etc., being mindful of echo line limit. */
616 	msglen = strlen(msg);
617 	nshown = (ttcol + msglen + 2 > ncol) ?
618 		ncol - ttcol - 2 : msglen;
619 	eputs(msg);
620 	ttcol -= (i = nshown);	/* update ttcol!		 */
621 	while (i--)		/* move back before msg		 */
622 		ttputc('\b');
623 	ttflush();		/* display to user		 */
624 	i = nshown;
625 	while (i--)		/* blank out on next flush	 */
626 		eputc(' ');
627 	ttcol -= (i = nshown);	/* update ttcol on BS's		 */
628 	while (i--)
629 		ttputc('\b');	/* update ttcol again!		 */
630 	*nx = nxtra;
631 	return ((nhits > 0) ? TRUE : FALSE);
632 }
633 
634 /*
635  * Do completion on a list of objects, listing instead of completing.
636  */
637 static int
638 complt_list(int flags, char *buf, int cpos)
639 {
640 	struct list	*lh, *lh2, *lh3;
641 	struct list	*wholelist = NULL;
642 	struct buffer	*bp;
643 	int	 i, maxwidth, width;
644 	int	 preflen = 0;
645 	int	 oldrow = ttrow;
646 	int	 oldcol = ttcol;
647 	int	 oldhue = tthue;
648 	char	 *linebuf;
649 	size_t	 linesize, len;
650 	char *cp;
651 
652 	lh = NULL;
653 
654 	ttflush();
655 
656 	/* The results are put into a completion buffer. */
657 	bp = bfind("*Completions*", TRUE);
658 	if (bclear(bp) == FALSE)
659 		return (FALSE);
660 	bp->b_flag |= BFREADONLY;
661 
662 	/*
663 	 * First get the list of objects.  This list may contain only
664 	 * the ones that complete what has been typed, or may be the
665 	 * whole list of all objects of this type.  They are filtered
666 	 * later in any case.  Set wholelist if the list has been
667 	 * cons'ed up just for us, so we can free it later.  We have
668 	 * to copy the buffer list for this function even though we
669 	 * didn't for complt.  The sorting code does destructive
670 	 * changes to the list, which we don't want to happen to the
671 	 * main buffer list!
672 	 */
673 	if ((flags & EFBUF) != 0)
674 		wholelist = lh = copy_list(&(bheadp->b_list));
675 	else if ((flags & EFFUNC) != 0) {
676 		buf[cpos] = '\0';
677 		wholelist = lh = complete_function_list(buf);
678 	} else if ((flags & EFFILE) != 0) {
679 		buf[cpos] = '\0';
680 		wholelist = lh = make_file_list(buf);
681 		/*
682 		 * We don't want to display stuff up to the / for file
683 		 * names preflen is the list of a prefix of what the
684 		 * user typed that should not be displayed.
685 		 */
686 		cp = strrchr(buf, '/');
687 		if (cp)
688 			preflen = cp - buf + 1;
689 	} else
690 		panic("broken complt call: flags");
691 
692 	/*
693 	 * Sort the list, since users expect to see it in alphabetic
694 	 * order.
695 	 */
696 	lh2 = lh;
697 	while (lh2 != NULL) {
698 		lh3 = lh2->l_next;
699 		while (lh3 != NULL) {
700 			if (strcmp(lh2->l_name, lh3->l_name) > 0) {
701 				cp = lh2->l_name;
702 				lh2->l_name = lh3->l_name;
703 				lh3->l_name = cp;
704 			}
705 			lh3 = lh3->l_next;
706 		}
707 		lh2 = lh2->l_next;
708 	}
709 
710 	/*
711 	 * First find max width of object to be displayed, so we can
712 	 * put several on a line.
713 	 */
714 	maxwidth = 0;
715 	lh2 = lh;
716 	while (lh2 != NULL) {
717 		for (i = 0; i < cpos; ++i) {
718 			if (buf[i] != lh2->l_name[i])
719 				break;
720 		}
721 		if (i == cpos) {
722 			width = strlen(lh2->l_name);
723 			if (width > maxwidth)
724 				maxwidth = width;
725 		}
726 		lh2 = lh2->l_next;
727 	}
728 	maxwidth += 1 - preflen;
729 
730 	/*
731 	 * Now do the display.  Objects are written into linebuf until
732 	 * it fills, and then put into the help buffer.
733 	 */
734 	linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
735 	if ((linebuf = malloc(linesize)) == NULL) {
736 		free_file_list(wholelist);
737 		return (FALSE);
738 	}
739 	width = 0;
740 
741 	/*
742 	 * We're going to strlcat() into the buffer, so it has to be
743 	 * NUL terminated.
744 	 */
745 	linebuf[0] = '\0';
746 	for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
747 		for (i = 0; i < cpos; ++i) {
748 			if (buf[i] != lh2->l_name[i])
749 				break;
750 		}
751 		/* if we have a match */
752 		if (i == cpos) {
753 			/* if it wraps */
754 			if ((width + maxwidth) > ncol) {
755 				addline(bp, linebuf);
756 				linebuf[0] = '\0';
757 				width = 0;
758 			}
759 			len = strlcat(linebuf, lh2->l_name + preflen,
760 			    linesize);
761 			width += maxwidth;
762 			if (len < width && width < linesize) {
763 				/* pad so the objects nicely line up */
764 				memset(linebuf + len, ' ',
765 				    maxwidth - strlen(lh2->l_name + preflen));
766 				linebuf[width] = '\0';
767 			}
768 		}
769 	}
770 	if (width > 0)
771 		addline(bp, linebuf);
772 	free(linebuf);
773 
774 	/*
775 	 * Note that we free lists only if they are put in wholelist lists
776 	 * that were built just for us should be freed.  However when we use
777 	 * the buffer list, obviously we don't want it freed.
778 	 */
779 	free_file_list(wholelist);
780 	popbuftop(bp, WEPHEM);	/* split the screen and put up the help
781 				 * buffer */
782 	update(CMODE);		/* needed to make the new stuff actually
783 				 * appear */
784 	ttmove(oldrow, oldcol);	/* update leaves cursor in arbitrary place */
785 	ttcolor(oldhue);	/* with arbitrary color */
786 	ttflush();
787 	return (0);
788 }
789 
790 /*
791  * The "lp1" and "lp2" point to list structures.  The "cpos" is a horizontal
792  * position in the name.  Return the longest block of characters that can be
793  * autocompleted at this point.  Sometimes the two symbols are the same, but
794  * this is normal.
795  */
796 int
797 getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
798 {
799 	int	i;
800 
801 	i = cpos;
802 	for (;;) {
803 		if (lp1->l_name[i] != lp2->l_name[i])
804 			break;
805 		if (lp1->l_name[i] == '\0')
806 			break;
807 		++i;
808 		if (wflag && !ISWORD(lp1->l_name[i - 1]))
809 			break;
810 	}
811 	return (i - cpos);
812 }
813 
814 /*
815  * Special "printf" for the echo line.  Each call to "ewprintf" starts a
816  * new line in the echo area, and ends with an erase to end of the echo
817  * line.  The formatting is done by a call to the standard formatting
818  * routine.
819  */
820 void
821 ewprintf(const char *fmt, ...)
822 {
823 	va_list	 ap;
824 
825 	if (inmacro)
826 		return;
827 
828 	va_start(ap, fmt);
829 	ttcolor(CTEXT);
830 	ttmove(nrow - 1, 0);
831 	eformat(fmt, ap);
832 	va_end(ap);
833 	tteeol();
834 	ttflush();
835 	epresf = TRUE;
836 }
837 
838 /*
839  * Printf style formatting. This is called by "ewprintf" to provide
840  * formatting services to its clients.  The move to the start of the
841  * echo line, and the erase to the end of the echo line, is done by
842  * the caller.
843  * %c prints the "name" of the supplied character.
844  * %k prints the name of the current key (and takes no arguments).
845  * %d prints a decimal integer
846  * %o prints an octal integer
847  * %p prints a pointer
848  * %s prints a string
849  * %ld prints a long word
850  * Anything else is echoed verbatim
851  */
852 static void
853 eformat(const char *fp, va_list ap)
854 {
855 	char	kname[NKNAME], tmp[100], *cp;
856 	int	c;
857 
858 	while ((c = *fp++) != '\0') {
859 		if (c != '%')
860 			eputc(c);
861 		else {
862 			c = *fp++;
863 			switch (c) {
864 			case 'c':
865 				getkeyname(kname, sizeof(kname),
866 				    va_arg(ap, int));
867 				eputs(kname);
868 				break;
869 
870 			case 'k':
871 				for (cp = kname, c = 0; c < key.k_count; c++) {
872 					if (c)
873 						*cp++ = ' ';
874 					cp = getkeyname(cp, sizeof(kname) -
875 					    (cp - kname) - 1, key.k_chars[c]);
876 				}
877 				eputs(kname);
878 				break;
879 
880 			case 'd':
881 				eputi(va_arg(ap, int), 10);
882 				break;
883 
884 			case 'o':
885 				eputi(va_arg(ap, int), 8);
886 				break;
887 
888 			case 'p':
889 				snprintf(tmp, sizeof(tmp), "%p",
890 				    va_arg(ap, void *));
891 				eputs(tmp);
892 				break;
893 
894 			case 's':
895 				eputs(va_arg(ap, char *));
896 				break;
897 
898 			case 'l':
899 				/* explicit longword */
900 				c = *fp++;
901 				switch (c) {
902 				case 'd':
903 					eputl(va_arg(ap, long), 10);
904 					break;
905 				default:
906 					eputc(c);
907 					break;
908 				}
909 				break;
910 
911 			default:
912 				eputc(c);
913 			}
914 		}
915 	}
916 }
917 
918 /*
919  * Put integer, in radix "r".
920  */
921 static void
922 eputi(int i, int r)
923 {
924 	int	 q;
925 
926 	if (i < 0) {
927 		eputc('-');
928 		i = -i;
929 	}
930 	if ((q = i / r) != 0)
931 		eputi(q, r);
932 	eputc(i % r + '0');
933 }
934 
935 /*
936  * Put long, in radix "r".
937  */
938 static void
939 eputl(long l, int r)
940 {
941 	long	 q;
942 
943 	if (l < 0) {
944 		eputc('-');
945 		l = -l;
946 	}
947 	if ((q = l / r) != 0)
948 		eputl(q, r);
949 	eputc((int)(l % r) + '0');
950 }
951 
952 /*
953  * Put string.
954  */
955 static void
956 eputs(const char *s)
957 {
958 	int	 c;
959 
960 	while ((c = *s++) != '\0')
961 		eputc(c);
962 }
963 
964 /*
965  * Put character.  Watch for control characters, and for the line getting
966  * too long.
967  */
968 static void
969 eputc(char c)
970 {
971 	if (ttcol + 2 < ncol) {
972 		if (ISCTRL(c)) {
973 			eputc('^');
974 			c = CCHR(c);
975 		}
976 		ttputc(c);
977 		++ttcol;
978 	}
979 }
980 
981 void
982 free_file_list(struct list *lp)
983 {
984 	struct list	*next;
985 
986 	while (lp) {
987 		next = lp->l_next;
988 		free(lp->l_name);
989 		free(lp);
990 		lp = next;
991 	}
992 }
993 
994 static struct list *
995 copy_list(struct list *lp)
996 {
997 	struct list	*current, *last, *nxt;
998 
999 	last = NULL;
1000 	while (lp) {
1001 		current = malloc(sizeof(struct list));
1002 		if (current == NULL) {
1003 			/* Free what we have allocated so far */
1004 			for (current = last; current; current = nxt) {
1005 				nxt = current->l_next;
1006 				free(current->l_name);
1007 				free(current);
1008 			}
1009 			return (NULL);
1010 		}
1011 		current->l_next = last;
1012 		current->l_name = strdup(lp->l_name);
1013 		last = current;
1014 		lp = lp->l_next;
1015 	}
1016 	return (last);
1017 }
1018