GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/echo.c Lines: 0 498 0.0 %
Date: 2017-11-13 Branches: 0 346 0.0 %

Line Branch Exec Source
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
}