xref: /openbsd/usr.bin/mg/echo.c (revision 750a99bf)
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 
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
eerase(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
eyorn(const char * sp)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 			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  */
92 int
eynorr(const char * sp)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 			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  */
128 int
eyesno(const char * sp)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 			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  */
174 char *
eread(const char * fmt,char * buf,size_t nbuf,int flag,...)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 *
veread(const char * fp,char * buf,size_t nbuf,int flag,va_list ap)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 != *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 
511 skipkey:	/* ignore key press */
512 ;
513 	}
514 done:
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);
528 memfail:
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  */
543 static int
complt(int flags,int c,char * buf,size_t nbuf,int cpos,int * nx)544 complt(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  */
640 static int
complt_list(int flags,char * buf,int cpos)641 complt_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  */
799 int
getxtra(struct list * lp1,struct list * lp2,int cpos,int wflag)800 getxtra(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  */
823 void
ewprintf(const char * fmt,...)824 ewprintf(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  */
855 static void
eformat(const char * fp,va_list ap)856 eformat(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  */
924 static void
eputi(int i,int r)925 eputi(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  */
941 static void
eputl(long l,int r)942 eputl(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  */
958 static void
eputs(const char * s)959 eputs(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  */
971 static void
eputc(char c)972 eputc(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 
984 void
free_file_list(struct list * lp)985 free_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 
997 static struct list *
copy_list(struct list * lp)998 copy_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