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