File: | src/usr.bin/mg/echo.c |
Warning: | line 963, column 12 Assigned value is garbage or undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
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 = FALSE0; /* stuff in echo line flag */ | |||
38 | ||||
39 | /* | |||
40 | * Erase the echo line. | |||
41 | */ | |||
42 | void | |||
43 | eerase(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 | */ | |||
58 | int | |||
59 | eyorn(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 | */ | |||
92 | int | |||
93 | eynorr(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 | */ | |||
128 | int | |||
129 | eyesno(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 | */ | |||
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)__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 | ||||
186 | static char * | |||
187 | veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap) | |||
188 | { | |||
189 | int dynbuf = (buf == NULL((void *)0)); | |||
| ||||
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) { | |||
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) { | |||
220 | ttcolor(CTEXT1); | |||
221 | ttmove(nrow - 1, 0); | |||
222 | epresf = TRUE1; | |||
223 | } else | |||
224 | eputc(' '); | |||
225 | eformat(fp, ap); | |||
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 | ||||
511 | skipkey: /* ignore key press */ | |||
512 | ; | |||
513 | } | |||
514 | done: | |||
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); | |||
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 | |||
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((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 | */ | |||
640 | static int | |||
641 | complt_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 | */ | |||
799 | int | |||
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])((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 | */ | |||
823 | void | |||
824 | ewprintf(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 | */ | |||
855 | static void | |||
856 | eformat(const char *fp, va_list ap) | |||
857 | { | |||
858 | char kname[NKNAME20], 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)__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++) { | |||
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)__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 | */ | |||
924 | static void | |||
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 | |||
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 | |||
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 | |||
972 | eputc(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 | ||||
984 | void | |||
985 | free_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 | ||||
997 | static struct list * | |||
998 | copy_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 | } |