clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name filecomplete.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/lib/libedit/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I . -I /usr/src/lib/libedit -I . -I /usr/src/lib/libedit -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libedit/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/lib/libedit/filecomplete.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | #include "config.h" |
34 | |
35 | #include <sys/types.h> |
36 | #include <sys/stat.h> |
37 | #include <dirent.h> |
38 | #include <errno.h> |
39 | #include <fcntl.h> |
40 | #include <limits.h> |
41 | #include <pwd.h> |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | #include <string.h> |
45 | #include <unistd.h> |
46 | |
47 | #include "el.h" |
48 | #include "filecomplete.h" |
49 | |
50 | static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{("; |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | char * |
63 | fn_tilde_expand(const char *txt) |
64 | { |
65 | struct passwd pwres, *pass; |
66 | char *temp; |
67 | size_t tempsz, len = 0; |
68 | char pwbuf[1024]; |
69 | |
70 | if (txt[0] != '~') |
71 | return strdup(txt); |
72 | |
73 | temp = strchr(txt + 1, '/'); |
74 | if (temp == NULL) { |
75 | temp = strdup(txt + 1); |
76 | if (temp == NULL) |
77 | return NULL; |
78 | } else { |
79 | len = temp - txt + 1; |
80 | temp = malloc(len); |
81 | if (temp == NULL) |
82 | return NULL; |
83 | (void)strncpy(temp, txt + 1, len - 2); |
84 | temp[len - 2] = '\0'; |
85 | } |
86 | if (temp[0] == 0) { |
87 | if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) |
88 | pass = NULL; |
89 | } else { |
90 | if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) |
91 | pass = NULL; |
92 | } |
93 | free(temp); |
94 | if (pass == NULL) |
95 | return strdup(txt); |
96 | |
97 | |
98 | |
99 | txt += len; |
100 | |
101 | tempsz = strlen(pass->pw_dir) + 1 + strlen(txt) + 1; |
102 | temp = malloc(tempsz); |
103 | if (temp == NULL) |
104 | return NULL; |
105 | (void)snprintf(temp, tempsz, "%s/%s", pass->pw_dir, txt); |
106 | |
107 | return temp; |
108 | } |
109 | |
110 | |
111 | |
112 | |
113 | |
114 | |
115 | |
116 | |
117 | |
118 | char * |
119 | fn_filename_completion_function(const char *text, int state) |
120 | { |
121 | static DIR *dir = NULL; |
122 | static char *filename = NULL, *dirname = NULL, *dirpath = NULL; |
123 | static size_t filename_len = 0; |
124 | struct dirent *entry; |
125 | char *temp; |
126 | size_t tempsz, len; |
127 | |
128 | if (state == 0 || dir == NULL) { |
129 | temp = strrchr(text, '/'); |
130 | if (temp) { |
| 13 | | Assuming 'temp' is null | |
|
| |
131 | size_t sz = strlen(temp + 1) + 1; |
132 | char *nptr; |
133 | temp++; |
134 | nptr = realloc(filename, sz); |
135 | if (nptr == NULL) { |
136 | free(filename); |
137 | filename = NULL; |
138 | return NULL; |
139 | } |
140 | filename = nptr; |
141 | (void)strlcpy(filename, temp, sz); |
142 | len = temp - text; |
143 | |
144 | nptr = realloc(dirname, len + 1); |
145 | if (nptr == NULL) { |
146 | free(dirname); |
147 | dirname = NULL; |
148 | return NULL; |
149 | } |
150 | dirname = nptr; |
151 | (void)strncpy(dirname, text, len); |
152 | dirname[len] = '\0'; |
153 | } else { |
154 | free(filename); |
155 | if (*text == 0) |
| 15 | | Assuming the condition is true | |
|
| |
156 | filename = NULL; |
157 | else { |
158 | filename = strdup(text); |
159 | if (filename == NULL) |
160 | return NULL; |
161 | } |
162 | free(dirname); |
163 | dirname = NULL; |
164 | } |
165 | |
166 | if (dir != NULL) { |
| |
167 | (void)closedir(dir); |
168 | dir = NULL; |
169 | } |
170 | |
171 | |
172 | |
173 | free(dirpath); |
174 | dirpath = NULL; |
175 | if (dirname == NULL) { |
| |
176 | if ((dirname = strdup("")) == NULL) |
| 19 | | Assuming the condition is false | |
|
| |
177 | return NULL; |
178 | dirpath = strdup("./"); |
179 | } else if (*dirname == '~') |
180 | dirpath = fn_tilde_expand(dirname); |
181 | else |
182 | dirpath = strdup(dirname); |
183 | |
184 | if (dirpath == NULL) |
| 21 | | Assuming 'dirpath' is not equal to NULL | |
|
| |
185 | return NULL; |
186 | |
187 | dir = opendir(dirpath); |
188 | if (!dir) |
| 23 | | Assuming 'dir' is non-null | |
|
189 | return NULL; |
190 | |
191 | |
192 | filename_len = filename ? strlen(filename) : 0; |
| |
| |
193 | } |
194 | |
195 | |
196 | while ((entry = readdir(dir)) != NULL) { |
| 26 | | Assuming the condition is true | |
|
197 | |
198 | if (entry->d_name[0] == '.' && (!entry->d_name[1] |
| 27 | | Assuming the condition is false | |
|
199 | || (entry->d_name[1] == '.' && !entry->d_name[2]))) |
200 | continue; |
201 | if (filename_len == 0) |
| |
202 | break; |
203 | |
204 | |
205 | if (entry->d_name[0] == filename[0] |
206 | #if HAVE_STRUCT_DIRENT_D_NAMLEN |
207 | && entry->d_namlen >= filename_len |
208 | #else |
209 | && strlen(entry->d_name) >= filename_len |
210 | #endif |
211 | && strncmp(entry->d_name, filename, |
212 | filename_len) == 0) |
213 | break; |
214 | } |
215 | |
216 | if (entry) { |
| 29 | | Execution continues on line 216 | |
|
| |
217 | |
218 | #if HAVE_STRUCT_DIRENT_D_NAMLEN |
219 | len = entry->d_namlen; |
220 | #else |
221 | len = strlen(entry->d_name); |
222 | #endif |
223 | |
224 | tempsz = strlen(dirname) + len + 1; |
225 | temp = malloc(tempsz); |
| |
226 | if (temp == NULL) |
| 32 | | Assuming 'temp' is not equal to NULL | |
|
| |
227 | return NULL; |
228 | (void)snprintf(temp, tempsz, "%s%s", dirname, entry->d_name); |
229 | } else { |
230 | (void)closedir(dir); |
231 | dir = NULL; |
232 | temp = NULL; |
233 | } |
234 | |
235 | return temp; |
236 | } |
237 | |
238 | |
239 | static const char * |
240 | append_char_function(const char *name) |
241 | { |
242 | struct stat stbuf; |
243 | char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; |
244 | const char *rs = " "; |
245 | |
246 | if (stat(expname ? expname : name, &stbuf) == -1) |
247 | goto out; |
248 | if (S_ISDIR(stbuf.st_mode)) |
249 | rs = "/"; |
250 | out: |
251 | if (expname) |
252 | free(expname); |
253 | return rs; |
254 | } |
255 | |
256 | |
257 | |
258 | |
259 | char ** completion_matches(const char *, char *(*)(const char *, int)); |
260 | char ** |
261 | completion_matches(const char *text, char *(*genfunc)(const char *, int)) |
262 | { |
263 | char **match_list = NULL, *retstr, *prevstr; |
264 | size_t match_list_len, max_equal, which, i; |
265 | size_t matches; |
266 | |
267 | matches = 0; |
268 | match_list_len = 1; |
269 | while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { |
| 12 | | Calling 'fn_filename_completion_function' | |
|
| 34 | | Returned allocated memory | |
|
| 35 | | Loop condition is true. Entering loop body | |
|
270 | |
271 | if (matches + 3 >= match_list_len) { |
| |
272 | char **nmatch_list; |
273 | while (matches + 3 >= match_list_len) |
| 37 | | Loop condition is true. Entering loop body | |
|
| 38 | | Loop condition is true. Entering loop body | |
|
| 39 | | Loop condition is false. Execution continues on line 275 | |
|
274 | match_list_len <<= 1; |
275 | nmatch_list = reallocarray(match_list, |
276 | match_list_len, sizeof(char *)); |
277 | if (nmatch_list == NULL) { |
| 40 | | Assuming 'nmatch_list' is equal to NULL | |
|
| |
278 | free(match_list); |
| 42 | | Potential leak of memory pointed to by 'retstr' |
|
279 | return NULL; |
280 | } |
281 | match_list = nmatch_list; |
282 | |
283 | } |
284 | match_list[++matches] = retstr; |
285 | } |
286 | |
287 | if (!match_list) |
288 | return NULL; |
289 | |
290 | |
291 | which = 2; |
292 | prevstr = match_list[1]; |
293 | max_equal = strlen(prevstr); |
294 | for (; which <= matches; which++) { |
295 | for (i = 0; i < max_equal && |
296 | prevstr[i] == match_list[which][i]; i++) |
297 | continue; |
298 | max_equal = i; |
299 | } |
300 | |
301 | retstr = malloc(max_equal + 1); |
302 | if (retstr == NULL) { |
303 | free(match_list); |
304 | return NULL; |
305 | } |
306 | (void)strncpy(retstr, match_list[1], max_equal); |
307 | retstr[max_equal] = '\0'; |
308 | match_list[0] = retstr; |
309 | |
310 | |
311 | match_list[matches + 1] = NULL; |
312 | |
313 | return match_list; |
314 | } |
315 | |
316 | |
317 | |
318 | |
319 | static int |
320 | _fn_qsort_string_compare(const void *i1, const void *i2) |
321 | { |
322 | const char *s1 = ((const char * const *)i1)[0]; |
323 | const char *s2 = ((const char * const *)i2)[0]; |
324 | |
325 | return strcasecmp(s1, s2); |
326 | } |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | |
333 | |
334 | |
335 | |
336 | void |
337 | fn_display_match_list (EditLine *el, char **matches, size_t num, size_t width) |
338 | { |
339 | size_t line, lines, col, cols, thisguy; |
340 | int screenwidth = el->el_terminal.t_size.h; |
341 | |
342 | |
343 | matches++; |
344 | num--; |
345 | |
346 | |
347 | |
348 | |
349 | |
350 | cols = screenwidth / (width + 1); |
351 | if (cols == 0) |
352 | cols = 1; |
353 | |
354 | |
355 | lines = (num + cols - 1) / cols; |
356 | |
357 | |
358 | qsort(matches, num, sizeof(char *), _fn_qsort_string_compare); |
359 | |
360 | |
361 | |
362 | |
363 | for (line = 0; line < lines; line++) { |
364 | for (col = 0; col < cols; col++) { |
365 | thisguy = line + col * lines; |
366 | if (thisguy >= num) |
367 | break; |
368 | (void)fprintf(el->el_outfile, "%s%-*s", |
369 | col == 0 ? "" : " ", (int)width, matches[thisguy]); |
370 | } |
371 | (void)fprintf(el->el_outfile, "\n"); |
372 | } |
373 | } |
374 | |
375 | |
376 | |
377 | |
378 | |
379 | |
380 | |
381 | |
382 | |
383 | |
384 | |
385 | |
386 | |
387 | int |
388 | fn_complete(EditLine *el, |
389 | char *(*complet_func)(const char *, int), |
390 | char **(*attempted_completion_function)(const char *, int, int), |
391 | const wchar_t *word_break, const wchar_t *special_prefixes, |
392 | const char *(*app_func)(const char *), size_t query_items, |
393 | int *completion_type, int *over, int *point, int *end) |
394 | { |
395 | const LineInfoW *li; |
396 | wchar_t *temp; |
397 | char **matches; |
398 | const wchar_t *ctemp; |
399 | size_t len; |
400 | int what_to_do = '\t'; |
401 | int retval = CC_NORM; |
402 | |
403 | if (el->el_state.lastcmd == el->el_state.thiscmd) |
| 2 | | Assuming field 'lastcmd' is not equal to field 'thiscmd' | |
|
| |
404 | what_to_do = '?'; |
405 | |
406 | |
407 | if (completion_type != NULL) |
| |
408 | *completion_type = what_to_do; |
409 | |
410 | if (!complet_func) |
| |
411 | complet_func = fn_filename_completion_function; |
412 | if (!app_func) |
| |
413 | app_func = append_char_function; |
414 | |
415 | |
416 | li = el_wline(el); |
417 | ctemp = li->cursor; |
418 | while (ctemp > li->buffer |
| 7 | | Assuming 'ctemp' is <= field 'buffer' | |
|
419 | && !wcschr(word_break, ctemp[-1]) |
420 | && (!special_prefixes || !wcschr(special_prefixes, ctemp[-1]) ) ) |
421 | ctemp--; |
422 | |
423 | len = li->cursor - ctemp; |
424 | temp = reallocarray(NULL, len + 1, sizeof(*temp)); |
425 | (void)wcsncpy(temp, ctemp, len); |
426 | temp[len] = '\0'; |
427 | |
428 | |
429 | |
430 | if (point != NULL) |
| |
431 | *point = (int)(li->cursor - li->buffer); |
432 | if (end != NULL) |
| |
433 | *end = (int)(li->lastchar - li->buffer); |
434 | |
435 | if (attempted_completion_function) { |
| |
436 | int cur_off = (int)(li->cursor - li->buffer); |
437 | matches = (*attempted_completion_function) ( |
438 | ct_encode_string(temp, &el->el_scratch), |
439 | (int)(cur_off - len), cur_off); |
440 | } else |
441 | matches = NULL; |
442 | if (!attempted_completion_function || |
443 | (over != NULL && !*over && !matches)) |
444 | matches = completion_matches( |
| 11 | | Calling 'completion_matches' | |
|
445 | ct_encode_string(temp, &el->el_scratch), complet_func); |
446 | |
447 | if (over != NULL) |
448 | *over = 0; |
449 | |
450 | if (matches) { |
451 | int i; |
452 | size_t matches_num, maxlen, match_len, match_display=1; |
453 | |
454 | retval = CC_REFRESH; |
455 | |
456 | |
457 | |
458 | |
459 | if (matches[0][0] != '\0') { |
460 | el_deletestr(el, (int) len); |
461 | el_winsertstr(el, |
462 | ct_decode_string(matches[0], &el->el_scratch)); |
463 | } |
464 | |
465 | if (what_to_do == '?') |
466 | goto display_matches; |
467 | |
468 | if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { |
469 | |
470 | |
471 | |
472 | |
473 | |
474 | el_winsertstr(el, |
475 | ct_decode_string((*app_func)(matches[0]), |
476 | &el->el_scratch)); |
477 | } else if (what_to_do == '!') { |
478 | display_matches: |
479 | |
480 | |
481 | |
482 | |
483 | |
484 | for(i = 1, maxlen = 0; matches[i]; i++) { |
485 | match_len = strlen(matches[i]); |
486 | if (match_len > maxlen) |
487 | maxlen = match_len; |
488 | } |
489 | |
490 | matches_num = i - 1; |
491 | |
492 | |
493 | (void)fprintf(el->el_outfile, "\n"); |
494 | |
495 | |
496 | |
497 | |
498 | |
499 | if (matches_num > query_items) { |
500 | (void)fprintf(el->el_outfile, |
501 | "Display all %zu possibilities? (y or n) ", |
502 | matches_num); |
503 | (void)fflush(el->el_outfile); |
504 | if (getc(stdin) != 'y') |
505 | match_display = 0; |
506 | (void)fprintf(el->el_outfile, "\n"); |
507 | } |
508 | |
509 | if (match_display) { |
510 | |
511 | |
512 | |
513 | |
514 | |
515 | |
516 | |
517 | fn_display_match_list(el, matches, |
518 | matches_num+1, maxlen); |
519 | } |
520 | retval = CC_REDISPLAY; |
521 | } else if (matches[0][0]) { |
522 | |
523 | |
524 | |
525 | |
526 | |
527 | el_beep(el); |
528 | } else { |
529 | |
530 | |
531 | el_beep(el); |
532 | retval = CC_NORM; |
533 | } |
534 | |
535 | |
536 | for (i = 0; matches[i]; i++) |
537 | free(matches[i]); |
538 | free(matches); |
539 | matches = NULL; |
540 | } |
541 | free(temp); |
542 | return retval; |
543 | } |
544 | |
545 | |
546 | |
547 | |
548 | unsigned char |
549 | _el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) |
550 | { |
551 | return (unsigned char)fn_complete(el, NULL, NULL, |
| |
552 | break_chars, NULL, NULL, 100, |
553 | NULL, NULL, NULL, NULL); |
554 | } |