Bug Summary

File:src/usr.bin/mg/echo.c
Warning:line 963, column 12
Assigned value is garbage or undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name echo.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/mg/obj -resource-dir /usr/local/lib/clang/13.0.0 -D REGEX -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/mg/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/mg/echo.c
1/* $OpenBSD: echo.c,v 1.68 2021/03/02 15:03:35 lum Exp $ */
2
3/* This file is in the public domain. */
4
5/*
6 * Echo line reading and writing.
7 *
8 * Common routines for reading and writing characters in the echo line area
9 * of the display screen. Used by the entire known universe.
10 */
11
12#include <sys/queue.h>
13#include <signal.h>
14#include <stdarg.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <term.h>
19
20#include "def.h"
21#include "funmap.h"
22#include "key.h"
23#include "macro.h"
24
25static char *veread(const char *, char *, size_t, int, va_list)
26 __attribute__((__format__ (printf, 1, 0)));
27static int complt(int, int, char *, size_t, int, int *);
28static int complt_list(int, char *, int);
29static void eformat(const char *, va_list)
30 __attribute__((__format__ (printf, 1, 0)));
31static void eputi(int, int);
32static void eputl(long, int);
33static void eputs(const char *);
34static void eputc(char);
35static struct list *copy_list(struct list *);
36
37int epresf = FALSE0; /* stuff in echo line flag */
38
39/*
40 * Erase the echo line.
41 */
42void
43eerase(void)
44{
45 ttcolor(CTEXT1);
46 ttmove(nrow - 1, 0);
47 tteeol();
48 ttflush();
49 epresf = FALSE0;
50}
51
52/*
53 * Ask a "yes" or "no" question. Return ABORT if the user answers the
54 * question with the abort ("^G") character. Return FALSE for "no" and
55 * TRUE for "yes". No formatting services are available. No newline
56 * required.
57 */
58int
59eyorn(const char *sp)
60{
61 int s;
62
63 if (inmacro)
64 return (TRUE1);
65
66 ewprintf("%s? (y or n) ", sp);
67 for (;;) {
68 s = getkey(FALSE0);
69 if (s == 'y' || s == 'Y' || s == ' ') {
70 ewprintf("");
71 return (TRUE1);
72 }
73 if (s == 'n' || s == 'N' || s == CCHR('M')(('M') ^ 0x40)) {
74 ewprintf("");
75 return (FALSE0);
76 }
77 if (s == CCHR('G')(('G') ^ 0x40)) {
78 ewprintf("");
79 return (ctrlg(FFRAND8, 1));
80 }
81 ewprintf("Please answer y or n. %s? (y or n) ", sp);
82 }
83 /* NOTREACHED */
84}
85
86/*
87 * Ask a "yes", "no" or "revert" question. Return ABORT if the user answers
88 * the question with the abort ("^G") character. Return FALSE for "no",
89 * TRUE for "yes" and REVERT for "revert". No formatting services are
90 * available. No newline required.
91 */
92int
93eynorr(const char *sp)
94{
95 int s;
96
97 if (inmacro)
98 return (TRUE1);
99
100 ewprintf("%s? (y, n or r) ", sp);
101 for (;;) {
102 s = getkey(FALSE0);
103 if (s == 'y' || s == 'Y' || s == ' ') {
104 ewprintf("");
105 return (TRUE1);
106 }
107 if (s == 'n' || s == 'N' || s == CCHR('M')(('M') ^ 0x40)) {
108 ewprintf("");
109 return (FALSE0);
110 }
111 if (s == 'r' || s == 'R') {
112 ewprintf("");
113 return (REVERT4);
114 }
115 if (s == CCHR('G')(('G') ^ 0x40)) {
116 ewprintf("");
117 return (ctrlg(FFRAND8, 1));
118 }
119 ewprintf("Please answer y, n or r.");
120 }
121 /* NOTREACHED */
122}
123
124/*
125 * Like eyorn, but for more important questions. User must type all of
126 * "yes" or "no" and the trailing newline.
127 */
128int
129eyesno(const char *sp)
130{
131 char buf[64], *rep;
132
133 if (inmacro)
134 return (TRUE1);
135
136 rep = eread("%s? (yes or no) ", buf, sizeof(buf),
137 EFNUL0x0040 | EFNEW0x0008 | EFCR0x0010, sp);
138 for (;;) {
139 if (rep == NULL((void *)0)) {
140 ewprintf("");
141 return (ABORT2);
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 (TRUE1);
154 }
155 if (strcasecmp(rep, "no") == 0) {
156 ewprintf("");
157 return (FALSE0);
158 }
159 }
160 rep = eread("Please answer yes or no. %s? (yes or no) ",
161 buf, sizeof(buf), EFNUL0x0040 | EFNEW0x0008 | EFCR0x0010, sp);
162 }
163 /* NOTREACHED */
164}
165
166/*
167 * This is the general "read input from the echo line" routine. The basic
168 * idea is that the prompt string "prompt" is written to the echo line, and
169 * a one line reply is read back into the supplied "buf" (with maximum
170 * length "len").
171 * XXX: When checking for an empty return value, always check rep, *not* buf
172 * as buf may be freed in pathological cases.
173 */
174char *
175eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
176{
177 va_list ap;
178 char *rep;
179
180 va_start(ap, flag)__builtin_va_start(ap, flag);
181 rep = veread(fmt, buf, nbuf, flag, ap);
182 va_end(ap)__builtin_va_end(ap);
183 return (rep);
184}
185
186static char *
187veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
188{
189 int dynbuf = (buf == NULL((void *)0));
1
Assuming 'buf' is not equal to NULL
190 int cpos, epos; /* cursor, end position in buf */
191 int c, i, y;
192 int cplflag; /* display completion list */
193 int cwin = FALSE0; /* 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) {
2
Assuming 'inmacro' is 0
3
Taking false branch
205 if (dynbuf) {
206 if ((buf = malloc(maclcur->l_used + 1)) == NULL((void *)0))
207 return (NULL((void *)0));
208 } else if (maclcur->l_used >= nbuf)
209 return (NULL((void *)0));
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 = FALSE0;
218
219 if ((flag & EFNEW0x0008) != 0 || ttrow != nrow - 1) {
4
Assuming the condition is false
5
Assuming the condition is false
6
Taking false branch
220 ttcolor(CTEXT1);
221 ttmove(nrow - 1, 0);
222 epresf = TRUE1;
223 } else
224 eputc(' ');
225 eformat(fp, ap);
7
Calling 'eformat'
226 if ((flag & EFDEF0x0020) != 0) {
227 if (buf == NULL((void *)0))
228 return (NULL((void *)0));
229 eputs(buf);
230 epos = cpos += strlen(buf);
231 }
232 tteeol();
233 ttflush();
234 for (;;) {
235 c = getkey(FALSE0);
236 if ((flag & EFAUTO0x0007) != 0 && c == CCHR('I')(('I') ^ 0x40)) {
237 if (cplflag == TRUE1) {
238 complt_list(flag, buf, cpos);
239 cwin = TRUE1;
240 } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE1) {
241 cplflag = TRUE1;
242 epos += i;
243 cpos = epos;
244 }
245 continue;
246 }
247 cplflag = FALSE0;
248
249 if (esc > 0) { /* ESC sequence started */
250 match = 0;
251 if (ml == esc && key_leftcur_term->type. Strings[79][ml] && c == key_leftcur_term->type. Strings[79][ml]) {
252 match++;
253 if (key_leftcur_term->type. Strings[79][++ml] == '\0') {
254 c = CCHR('B')(('B') ^ 0x40);
255 esc = 0;
256 }
257 }
258 if (mr == esc && key_rightcur_term->type. Strings[83][mr] && c == key_rightcur_term->type. Strings[83][mr]) {
259 match++;
260 if (key_rightcur_term->type. Strings[83][++mr] == '\0') {
261 c = CCHR('F')(('F') ^ 0x40);
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')(('A') ^ 0x40): /* start of line */
277 while (cpos > 0) {
278 if (ISCTRL(buf[--cpos])((cinfo[((unsigned char) (buf[--cpos]))]&0x08)!=0) != FALSE0) {
279 ttputc('\b');
280 --ttcol;
281 }
282 ttputc('\b');
283 --ttcol;
284 }
285 ttflush();
286 break;
287 case CCHR('D')(('D') ^ 0x40):
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')(('E') ^ 0x40): /* end of line */
302 while (cpos < epos) {
303 eputc(buf[cpos++]);
304 }
305 ttflush();
306 break;
307 case CCHR('B')(('B') ^ 0x40): /* back */
308 if (cpos > 0) {
309 if (ISCTRL(buf[--cpos])((cinfo[((unsigned char) (buf[--cpos]))]&0x08)!=0) != FALSE0) {
310 ttputc('\b');
311 --ttcol;
312 }
313 ttputc('\b');
314 --ttcol;
315 ttflush();
316 }
317 break;
318 case CCHR('F')(('F') ^ 0x40): /* forw */
319 if (cpos < epos) {
320 eputc(buf[cpos++]);
321 ttflush();
322 }
323 break;
324 case CCHR('Y')(('Y') ^ 0x40): /* 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((void *)0))
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')(('K') ^ 0x40): /* copy here-EOL to kill buffer */
356 kdelete();
357 for (i = cpos; i < epos; i++)
358 kinsert(buf[i], KFORW0x01);
359 tteeol();
360 epos = cpos;
361 ttflush();
362 break;
363 case CCHR('[')(('[') ^ 0x40):
364 ml = mr = esc = 1;
365 break;
366 case CCHR('J')(('J') ^ 0x40):
367 c = CCHR('M')(('M') ^ 0x40);
368 /* FALLTHROUGH */
369 case CCHR('M')(('M') ^ 0x40): /* return, done */
370 /* if there's nothing in the minibuffer, abort */
371 if (epos == 0 && !(flag & EFNUL0x0040)) {
372 (void)ctrlg(FFRAND8, 0);
373 ttflush();
374 return (NULL((void *)0));
375 }
376 if ((flag & EFFUNC0x0001) != 0) {
377 if (complt(flag, c, buf, nbuf, epos, &i)
378 == FALSE0)
379 continue;
380 if (i > 0)
381 epos += i;
382 }
383 buf[epos] = '\0';
384 if ((flag & EFCR0x0010) != 0) {
385 ttputc(CCHR('M')(('M') ^ 0x40));
386 ttflush();
387 }
388 if (macrodef) {
389 struct line *lp;
390
391 if ((lp = lalloc(cpos)) == NULL((void *)0))
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')(('G') ^ 0x40): /* bell, abort */
402 eputc(CCHR('G')(('G') ^ 0x40));
403 (void)ctrlg(FFRAND8, 0);
404 ttflush();
405 ret = NULL((void *)0);
406 goto done;
407 case CCHR('H')(('H') ^ 0x40): /* rubout, erase */
408 case CCHR('?')(('?') ^ 0x40):
409 if (cpos != 0) {
410 y = buf[--cpos];
411 epos--;
412 ttputc('\b');
413 ttcol--;
414 if (ISCTRL(y)((cinfo[((unsigned char) (y))]&0x08)!=0) != FALSE0) {
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)((cinfo[((unsigned char) (y))]&0x08)!=0) != FALSE0) {
426 ttputc(' ');
427 ttputc('\b');
428 }
429 ttputc('\b');
430 ttmove(rr, cc);
431 ttflush();
432 }
433 break;
434 case CCHR('X')(('X') ^ 0x40): /* kill line */
435 case CCHR('U')(('U') ^ 0x40):
436 while (cpos != 0) {
437 ttputc('\b');
438 ttputc(' ');
439 ttputc('\b');
440 --ttcol;
441 if (ISCTRL(buf[--cpos])((cinfo[((unsigned char) (buf[--cpos]))]&0x08)!=0) != FALSE0) {
442 ttputc('\b');
443 ttputc(' ');
444 ttputc('\b');
445 --ttcol;
446 }
447 epos--;
448 }
449 ttflush();
450 break;
451 case CCHR('W')(('W') ^ 0x40): /* kill to beginning of word */
452 while ((cpos > 0) && !ISWORD(buf[cpos - 1])((cinfo[((unsigned char) (buf[cpos - 1]))]&0x01)!=0)) {
453 ttputc('\b');
454 ttputc(' ');
455 ttputc('\b');
456 --ttcol;
457 if (ISCTRL(buf[--cpos])((cinfo[((unsigned char) (buf[--cpos]))]&0x08)!=0) != FALSE0) {
458 ttputc('\b');
459 ttputc(' ');
460 ttputc('\b');
461 --ttcol;
462 }
463 epos--;
464 }
465 while ((cpos > 0) && ISWORD(buf[cpos - 1])((cinfo[((unsigned char) (buf[cpos - 1]))]&0x01)!=0)) {
466 ttputc('\b');
467 ttputc(' ');
468 ttputc('\b');
469 --ttcol;
470 if (ISCTRL(buf[--cpos])((cinfo[((unsigned char) (buf[--cpos]))]&0x08)!=0) != FALSE0) {
471 ttputc('\b');
472 ttputc(' ');
473 ttputc('\b');
474 --ttcol;
475 }
476 epos--;
477 }
478 ttflush();
479 break;
480 case CCHR('\\')(('\\') ^ 0x40):
481 case CCHR('Q')(('Q') ^ 0x40): /* quote next */
482 c = getkey(FALSE0);
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((void *)0))
489 goto memfail;
490 buf = newp;
491 nbuf = newsize;
492 }
493 if (!dynbuf && epos + 1 >= nbuf) {
494 dobeep();
495 ewprintf("Line too long. Press Control-g to escape.");
496 goto skipkey;
497 }
498 for (i = epos; i > cpos; i--)
499 buf[i] = buf[i - 1];
500 buf[cpos++] = (char)c;
501 epos++;
502 eputc((char)c);
503 cc = ttcol;
504 rr = ttrow;
505 for (i = cpos; i < epos; i++)
506 eputc(buf[i]);
507 ttmove(rr, cc);
508 ttflush();
509 }
510
511skipkey: /* ignore key press */
512;
513 }
514done:
515 if (cwin == TRUE1) {
516 /* blow away cpltion window */
517 bp = bfind("*Completions*", TRUE1);
518 if ((wp = popbuf(bp, WEPHEM0x01)) != NULL((void *)0)) {
519 if (wp->w_flag & WEPHEM0x01) {
520 curwp = wp;
521 delwind(FFRAND8, 1);
522 } else {
523 killbuffer(bp);
524 }
525 }
526 }
527 return (ret);
528memfail:
529 if (dynbuf && buf)
530 free(buf);
531 dobeep();
532 ewprintf("Out of memory");
533 return (emptyval);
534}
535
536/*
537 * Do completion on a list of objects.
538 * c is SPACE, TAB, or CR
539 * return TRUE if matched (or partially matched)
540 * FALSE is result is ambiguous,
541 * ABORT on error.
542 */
543static int
544complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
545{
546 struct list *lh, *lh2;
547 struct list *wholelist = NULL((void *)0);
548 int i, nxtra, nhits, bxtra, msglen, nshown;
549 int wflag = FALSE0;
550 char *msg;
551
552 lh = lh2 = NULL((void *)0);
553
554 if ((flags & EFFUNC0x0001) != 0) {
555 buf[cpos] = '\0';
556 wholelist = lh = complete_function_list(buf);
557 } else if ((flags & EFBUF0x0002) != 0) {
558 lh = &(bheadp->b_list);
559 } else if ((flags & EFFILE0x0004) != 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 = TRUE1;
567 else if (c != '\t' && c != CCHR('M')(('M') ^ 0x40))
568 panic("broken complt call: c");
569
570 nhits = 0;
571 nxtra = HUGE1000;
572
573 for (; lh != NULL((void *)0); lh = lh->l_nextl_p.l_nxt) {
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')(('M') ^ 0x40)) /* exact */
608 *nx = 0;
609 return (TRUE1);
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) ? TRUE1 : FALSE0);
635}
636
637/*
638 * Do completion on a list of objects, listing instead of completing.
639 */
640static int
641complt_list(int flags, char *buf, int cpos)
642{
643 struct list *lh, *lh2, *lh3;
644 struct list *wholelist = NULL((void *)0);
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((void *)0);
656
657 ttflush();
658
659 /* The results are put into a completion buffer. */
660 bp = bfind("*Completions*", TRUE1);
661 if (bclear(bp) == FALSE0)
662 return (FALSE0);
663 bp->b_flag |= BFREADONLY0x10;
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 & EFBUF0x0002) != 0)
677 wholelist = lh = copy_list(&(bheadp->b_list));
678 else if ((flags & EFFUNC0x0001) != 0) {
679 buf[cpos] = '\0';
680 wholelist = lh = complete_function_list(buf);
681 } else if ((flags & EFFILE0x0004) != 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((void *)0)) {
701 lh3 = lh2->l_nextl_p.l_nxt;
702 while (lh3 != NULL((void *)0)) {
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_nextl_p.l_nxt;
709 }
710 lh2 = lh2->l_nextl_p.l_nxt;
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((void *)0)) {
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_nextl_p.l_nxt;
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((void *)0)) {
739 free_file_list(wholelist);
740 return (FALSE0);
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((void *)0); lh2 = lh2->l_nextl_p.l_nxt) {
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)addlinef(bp, "%s", 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)addlinef(bp, "%s", 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, WEPHEM0x01); /* split the screen and put up the help
784 * buffer */
785 update(CMODE2); /* needed to make the new stuff actually
786 * appear */
787 ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
788 ttcolor(oldhue); /* with arbitrary color */
789 ttflush();
790 return (0);
791}
792
793/*
794 * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
795 * position in the name. Return the longest block of characters that can be
796 * autocompleted at this point. Sometimes the two symbols are the same, but
797 * this is normal.
798 */
799int
800getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
801{
802 int i;
803
804 i = cpos;
805 for (;;) {
806 if (lp1->l_name[i] != lp2->l_name[i])
807 break;
808 if (lp1->l_name[i] == '\0')
809 break;
810 ++i;
811 if (wflag && !ISWORD(lp1->l_name[i - 1])((cinfo[((unsigned char) (lp1->l_name[i - 1]))]&0x01)!=
0)
)
812 break;
813 }
814 return (i - cpos);
815}
816
817/*
818 * Special "printf" for the echo line. Each call to "ewprintf" starts a
819 * new line in the echo area, and ends with an erase to end of the echo
820 * line. The formatting is done by a call to the standard formatting
821 * routine.
822 */
823void
824ewprintf(const char *fmt, ...)
825{
826 va_list ap;
827
828 if (inmacro)
829 return;
830
831 va_start(ap, fmt)__builtin_va_start(ap, fmt);
832 ttcolor(CTEXT1);
833 ttmove(nrow - 1, 0);
834 eformat(fmt, ap);
835 va_end(ap)__builtin_va_end(ap);
836 tteeol();
837 ttflush();
838 epresf = TRUE1;
839}
840
841/*
842 * Printf style formatting. This is called by "ewprintf" to provide
843 * formatting services to its clients. The move to the start of the
844 * echo line, and the erase to the end of the echo line, is done by
845 * the caller.
846 * %c prints the "name" of the supplied character.
847 * %k prints the name of the current key (and takes no arguments).
848 * %d prints a decimal integer
849 * %o prints an octal integer
850 * %p prints a pointer
851 * %s prints a string
852 * %ld prints a long word
853 * Anything else is echoed verbatim
854 */
855static void
856eformat(const char *fp, va_list ap)
857{
858 char kname[NKNAME20], tmp[100], *cp;
859 int c;
860
861 while ((c = *fp++) != '\0') {
8
Assuming the condition is true
9
Loop condition is true. Entering loop body
862 if (c != '%')
10
Assuming the condition is false
11
Taking false branch
863 eputc(c);
864 else {
865 c = *fp++;
866 switch (c) {
12
Control jumps to 'case 107:' at line 873
867 case 'c':
868 getkeyname(kname, sizeof(kname),
869 va_arg(ap, int)__builtin_va_arg(ap, int));
870 eputs(kname);
871 break;
872
873 case 'k':
874 for (cp = kname, c = 0; c < key.k_count; c++) {
13
Assuming 'c' is >= field 'k_count'
14
Loop condition is false. Execution continues on line 880
875 if (c)
876 *cp++ = ' ';
877 cp = getkeyname(cp, sizeof(kname) -
878 (cp - kname) - 1, key.k_chars[c]);
879 }
880 eputs(kname);
15
Calling 'eputs'
881 break;
882
883 case 'd':
884 eputi(va_arg(ap, int)__builtin_va_arg(ap, int), 10);
885 break;
886
887 case 'o':
888 eputi(va_arg(ap, int)__builtin_va_arg(ap, int), 8);
889 break;
890
891 case 'p':
892 snprintf(tmp, sizeof(tmp), "%p",
893 va_arg(ap, void *)__builtin_va_arg(ap, void *));
894 eputs(tmp);
895 break;
896
897 case 's':
898 eputs(va_arg(ap, char *)__builtin_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)__builtin_va_arg(ap, long), 10);
907 break;
908 default:
909 eputc(c);
910 break;
911 }
912 break;
913
914 default:
915 eputc(c);
916 }
917 }
918 }
919}
920
921/*
922 * Put integer, in radix "r".
923 */
924static void
925eputi(int i, int r)
926{
927 int q;
928
929 if (i < 0) {
930 eputc('-');
931 i = -i;
932 }
933 if ((q = i / r) != 0)
934 eputi(q, r);
935 eputc(i % r + '0');
936}
937
938/*
939 * Put long, in radix "r".
940 */
941static void
942eputl(long l, int r)
943{
944 long q;
945
946 if (l < 0) {
947 eputc('-');
948 l = -l;
949 }
950 if ((q = l / r) != 0)
951 eputl(q, r);
952 eputc((int)(l % r) + '0');
953}
954
955/*
956 * Put string.
957 */
958static void
959eputs(const char *s)
960{
961 int c;
962
963 while ((c = *s++) != '\0')
16
Assigned value is garbage or undefined
964 eputc(c);
965}
966
967/*
968 * Put character. Watch for control characters, and for the line getting
969 * too long.
970 */
971static void
972eputc(char c)
973{
974 if (ttcol + 2 < ncol) {
975 if (ISCTRL(c)((cinfo[((unsigned char) (c))]&0x08)!=0)) {
976 eputc('^');
977 c = CCHR(c)((c) ^ 0x40);
978 }
979 ttputc(c);
980 ++ttcol;
981 }
982}
983
984void
985free_file_list(struct list *lp)
986{
987 struct list *next;
988
989 while (lp) {
990 next = lp->l_nextl_p.l_nxt;
991 free(lp->l_name);
992 free(lp);
993 lp = next;
994 }
995}
996
997static struct list *
998copy_list(struct list *lp)
999{
1000 struct list *current, *last, *nxt;
1001
1002 last = NULL((void *)0);
1003 while (lp) {
1004 current = malloc(sizeof(struct list));
1005 if (current == NULL((void *)0)) {
1006 /* Free what we have allocated so far */
1007 for (current = last; current; current = nxt) {
1008 nxt = current->l_nextl_p.l_nxt;
1009 free(current->l_name);
1010 free(current);
1011 }
1012 return (NULL((void *)0));
1013 }
1014 current->l_nextl_p.l_nxt = last;
1015 current->l_name = strdup(lp->l_name);
1016 last = current;
1017 lp = lp->l_nextl_p.l_nxt;
1018 }
1019 return (last);
1020}