Bug Summary

File:src/bin/ksh/misc.c
Warning:line 437, column 20
Dereference of null pointer (loaded from variable 'array')

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 misc.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/bin/ksh/obj -resource-dir /usr/local/lib/clang/13.0.0 -D EMACS -D VI -I . -I /usr/src/bin/ksh -I /usr/src/bin/ksh/../../lib/libc/gen -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/ksh/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/bin/ksh/misc.c
1/* $OpenBSD: misc.c,v 1.78 2021/12/24 22:08:37 deraadt Exp $ */
2
3/*
4 * Miscellaneous functions
5 */
6
7#include <ctype.h>
8#include <errno(*__errno()).h>
9#include <fcntl.h>
10#include <limits.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include "sh.h"
16#include "charclass.h"
17
18short ctypes [UCHAR_MAX(127*2 +1)+1]; /* type bits for unsigned char */
19static int dropped_privileges;
20
21static int do_gmatch(const unsigned char *, const unsigned char *,
22 const unsigned char *, const unsigned char *);
23static const unsigned char *cclass(const unsigned char *, int);
24
25/*
26 * Fast character classes
27 */
28void
29setctypes(const char *s, int t)
30{
31 int i;
32
33 if (t & C_IFS(1<<(7))) {
34 for (i = 0; i < UCHAR_MAX(127*2 +1)+1; i++)
35 ctypes[i] &= ~C_IFS(1<<(7));
36 ctypes[0] |= C_IFS(1<<(7)); /* include \0 in C_IFS */
37 }
38 while (*s != 0)
39 ctypes[(unsigned char) *s++] |= t;
40}
41
42void
43initctypes(void)
44{
45 int c;
46
47 for (c = 'a'; c <= 'z'; c++)
48 ctypes[c] |= C_ALPHA(1<<(0));
49 for (c = 'A'; c <= 'Z'; c++)
50 ctypes[c] |= C_ALPHA(1<<(0));
51 ctypes['_'] |= C_ALPHA(1<<(0));
52 setctypes(" \t\n|&;<>()", C_LEX1(1<<(2))); /* \0 added automatically */
53 setctypes("*@#!$-?", C_VAR1(1<<(3)));
54 setctypes(" \t\n", C_IFSWS(1<<(4)));
55 setctypes("=-+?", C_SUBOP1(1<<(5)));
56 setctypes("#%", C_SUBOP2(1<<(6)));
57 setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE(1<<(8)));
58}
59
60/* convert uint64_t to base N string */
61
62char *
63u64ton(uint64_t n, int base)
64{
65 char *p;
66 static char buf [20];
67
68 p = &buf[sizeof(buf)];
69 *--p = '\0';
70 do {
71 *--p = "0123456789ABCDEF"[n%base];
72 n /= base;
73 } while (n != 0);
74 return p;
75}
76
77char *
78str_save(const char *s, Area *ap)
79{
80 size_t len;
81 char *p;
82
83 if (!s)
84 return NULL((void*)0);
85 len = strlen(s)+1;
86 p = alloc(len, ap);
87 strlcpy(p, s, len);
88 return (p);
89}
90
91/* Allocate a string of size n+1 and copy upto n characters from the possibly
92 * null terminated string s into it. Always returns a null terminated string
93 * (unless n < 0).
94 */
95char *
96str_nsave(const char *s, int n, Area *ap)
97{
98 char *ns;
99
100 if (n < 0)
101 return 0;
102 ns = alloc(n + 1, ap);
103 ns[0] = '\0';
104 return strncat(ns, s, n);
105}
106
107/* called from expand.h:XcheckN() to grow buffer */
108char *
109Xcheck_grow_(XString *xsp, char *xp, size_t more)
110{
111 char *old_beg = xsp->beg;
112
113 xsp->len += more > xsp->len ? more : xsp->len;
114 xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap);
115 xsp->end = xsp->beg + xsp->len;
116 return xsp->beg + (xp - old_beg);
117}
118
119const struct option sh_options[] = {
120 /* Special cases (see parse_args()): -A, -o, -s.
121 * Options are sorted by their longnames - the order of these
122 * entries MUST match the order of sh_flag F* enumerations in sh.h.
123 */
124 { "allexport", 'a', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
125 { "braceexpand", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
126 { "bgnice", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
127 { NULL((void*)0), 'c', OF_CMDLINE0x01 },
128 { "csh-history", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
129#ifdef EMACS1
130 { "emacs", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
131#endif
132 { "errexit", 'e', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
133#ifdef EMACS1
134 { "gmacs", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
135#endif
136 { "ignoreeof", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
137 { "interactive",'i', OF_CMDLINE0x01 },
138 { "keyword", 'k', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
139 { "login", 'l', OF_CMDLINE0x01 },
140 { "markdirs", 'X', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
141 { "monitor", 'm', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
142 { "noclobber", 'C', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
143 { "noexec", 'n', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
144 { "noglob", 'f', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
145 { "nohup", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
146 { "nolog", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* no effect */
147 { "notify", 'b', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
148 { "nounset", 'u', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
149 { "physical", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
150 { "pipefail", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
151 { "posix", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
152 { "privileged", 'p', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
153 { "restricted", 'r', OF_CMDLINE0x01 },
154 { "sh", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
155 { "stdin", 's', OF_CMDLINE0x01 }, /* pseudo non-standard */
156 { "trackall", 'h', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
157 { "verbose", 'v', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
158#ifdef VI1
159 { "vi", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
160 { "viraw", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* no effect */
161 { "vi-show8", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
162 { "vi-tabcomplete", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
163 { "vi-esccomplete", 0, OF_ANY(0x01 | 0x02 | 0x04 | 0x08) }, /* non-standard */
164#endif
165 { "xtrace", 'x', OF_ANY(0x01 | 0x02 | 0x04 | 0x08) },
166 /* Anonymous flags: used internally by shell only
167 * (not visible to user)
168 */
169 { NULL((void*)0), 0, OF_INTERNAL0x08 }, /* FTALKING_I */
170};
171
172/*
173 * translate -o option into F* constant (also used for test -o option)
174 */
175int
176option(const char *n)
177{
178 unsigned int ele;
179
180 for (ele = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++)
181 if (sh_options[ele].name && strcmp(sh_options[ele].name, n) == 0)
182 return ele;
183
184 return -1;
185}
186
187struct options_info {
188 int opt_width;
189 struct {
190 const char *name;
191 int flag;
192 } opts[NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0]))];
193};
194
195static char *options_fmt_entry(void *arg, int i, char *buf, int buflen);
196static void printoptions(int verbose);
197
198/* format a single select menu item */
199static char *
200options_fmt_entry(void *arg, int i, char *buf, int buflen)
201{
202 struct options_info *oi = (struct options_info *) arg;
203
204 shf_snprintf(buf, buflen, "%-*s %s",
205 oi->opt_width, oi->opts[i].name,
206 Flag(oi->opts[i].flag)(shell_flags[(int) (oi->opts[i].flag)]) ? "on" : "off");
207 return buf;
208}
209
210static void
211printoptions(int verbose)
212{
213 unsigned int ele;
214
215 if (verbose) {
216 struct options_info oi;
217 unsigned int n;
218 int len;
219
220 /* verbose version */
221 shprintf("Current option settings\n");
222
223 for (ele = n = oi.opt_width = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++) {
224 if (sh_options[ele].name) {
225 len = strlen(sh_options[ele].name);
226 oi.opts[n].name = sh_options[ele].name;
227 oi.opts[n++].flag = ele;
228 if (len > oi.opt_width)
229 oi.opt_width = len;
230 }
231 }
232 print_columns(shl_stdout(&shf_iob[1]), n, options_fmt_entry, &oi,
233 oi.opt_width + 5, 1);
234 } else {
235 /* short version ala ksh93 */
236 shprintf("set");
237 for (ele = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++) {
238 if (sh_options[ele].name)
239 shprintf(" %co %s",
240 Flag(ele)(shell_flags[(int) (ele)]) ? '-' : '+',
241 sh_options[ele].name);
242 }
243 shprintf("\n");
244 }
245}
246
247char *
248getoptions(void)
249{
250 unsigned int ele;
251 char m[(int) FNFLAGS + 1];
252 char *cp = m;
253
254 for (ele = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++)
255 if (sh_options[ele].c && Flag(ele)(shell_flags[(int) (ele)]))
256 *cp++ = sh_options[ele].c;
257 *cp = 0;
258 return str_save(m, ATEMP&genv->area);
259}
260
261/* change a Flag(*) value; takes care of special actions */
262void
263change_flag(enum sh_flag f,
264 int what, /* flag to change */
265 int newval) /* what is changing the flag (command line vs set) */
266{
267 int oldval;
268
269 oldval = Flag(f)(shell_flags[(int) (f)]);
270 Flag(f)(shell_flags[(int) (f)]) = newval;
271 if (f == FMONITOR) {
272 if (what != OF_CMDLINE0x01 && newval != oldval)
273 j_change();
274 } else
275 if (0
276#ifdef VI1
277 || f == FVI
278#endif /* VI */
279#ifdef EMACS1
280 || f == FEMACS || f == FGMACS
281#endif /* EMACS */
282 )
283 {
284 if (newval) {
285#ifdef VI1
286 Flag(FVI)(shell_flags[(int) (FVI)]) = 0;
287#endif /* VI */
288#ifdef EMACS1
289 Flag(FEMACS)(shell_flags[(int) (FEMACS)]) = Flag(FGMACS)(shell_flags[(int) (FGMACS)]) = 0;
290#endif /* EMACS */
291 Flag(f)(shell_flags[(int) (f)]) = newval;
292 }
293 } else
294 /* Turning off -p? */
295 if (f == FPRIVILEGED && oldval && !newval && issetugid() &&
296 !dropped_privileges) {
297 gid_t gid = getgid();
298
299 setresgid(gid, gid, gid);
300 setgroups(1, &gid);
301 setresuid(ksheuid, ksheuid, ksheuid);
302
303 if (pledge("stdio rpath wpath cpath fattr flock getpw proc "
304 "exec tty", NULL((void*)0)) == -1)
305 bi_errorf("pledge fail");
306 dropped_privileges = 1;
307 } else if (f == FPOSIX && newval) {
308 Flag(FBRACEEXPAND)(shell_flags[(int) (FBRACEEXPAND)]) = 0;
309 }
310 /* Changing interactive flag? */
311 if (f == FTALKING) {
312 if ((what == OF_CMDLINE0x01 || what == OF_SET0x02) && procpid == kshpid)
313 Flag(FTALKING_I)(shell_flags[(int) (FTALKING_I)]) = newval;
314 }
315}
316
317/* parse command line & set command arguments. returns the index of
318 * non-option arguments, -1 if there is an error.
319 */
320int
321parse_args(char **argv,
322 int what, /* OF_CMDLINE or OF_SET */
323 int *setargsp)
324{
325 static char cmd_opts[NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])) + 3]; /* o:\0 */
326 static char set_opts[NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])) + 5]; /* Ao;s\0 */
327 char *opts;
328 char *array = NULL((void*)0);
329 Getopt go;
330 int i, optc, sortargs = 0, arrayset = 0;
331 unsigned int ele;
332
333 /* First call? Build option strings... */
334 if (cmd_opts[0] == '\0') {
1
Assuming the condition is false
2
Taking false branch
335 char *p, *q;
336
337 /* see cmd_opts[] declaration */
338 strlcpy(cmd_opts, "o:", sizeof cmd_opts);
339 p = cmd_opts + strlen(cmd_opts);
340 /* see set_opts[] declaration */
341 strlcpy(set_opts, "A:o;s", sizeof set_opts);
342 q = set_opts + strlen(set_opts);
343 for (ele = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++) {
344 if (sh_options[ele].c) {
345 if (sh_options[ele].flags & OF_CMDLINE0x01)
346 *p++ = sh_options[ele].c;
347 if (sh_options[ele].flags & OF_SET0x02)
348 *q++ = sh_options[ele].c;
349 }
350 }
351 *p = '\0';
352 *q = '\0';
353 }
354
355 if (what == OF_CMDLINE0x01) {
3
Assuming 'what' is not equal to OF_CMDLINE
4
Taking false branch
356 char *p;
357 /* Set FLOGIN before parsing options so user can clear
358 * flag using +l.
359 */
360 Flag(FLOGIN)(shell_flags[(int) (FLOGIN)]) = (argv[0][0] == '-' ||
361 ((p = strrchr(argv[0], '/')) && *++p == '-'));
362 opts = cmd_opts;
363 } else
364 opts = set_opts;
365 ksh_getopt_reset(&go, GF_ERROR(1<<(0))|GF_PLUSOPT(1<<(1)));
5
Calling 'ksh_getopt_reset'
7
Returning from 'ksh_getopt_reset'
366 while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
8
Calling 'ksh_getopt'
32
Returning from 'ksh_getopt'
33
Assuming the condition is true
34
Loop condition is true. Entering loop body
40
Loop condition is false. Execution continues on line 423
367 int set = (go.info & GI_PLUS(1<<(1))) ? 0 : 1;
35
'?' condition is true
368 switch (optc) {
36
Control jumps to 'case 65:' at line 369
369 case 'A':
370 arrayset = set
36.1
'set' is 0
? 1 : -1;
37
'?' condition is false
371 array = go.optarg;
38
Null pointer value stored to 'array'
372 break;
39
Execution continues on line 366
373
374 case 'o':
375 if (go.optarg == NULL((void*)0)) {
376 /* lone -o: print options
377 *
378 * Note that on the command line, -o requires
379 * an option (ie, can't get here if what is
380 * OF_CMDLINE).
381 */
382 printoptions(set);
383 break;
384 }
385 i = option(go.optarg);
386 if (i != -1 && set == Flag(i)(shell_flags[(int) (i)]))
387 /* Don't check the context if the flag
388 * isn't changing - makes "set -o interactive"
389 * work if you're already interactive. Needed
390 * if the output of "set +o" is to be used.
391 */
392 ;
393 else if (i != -1 && (sh_options[i].flags & what))
394 change_flag((enum sh_flag) i, what, set);
395 else {
396 bi_errorf("%s: bad option", go.optarg);
397 return -1;
398 }
399 break;
400
401 case '?':
402 return -1;
403
404 default:
405 /* -s: sort positional params (at&t ksh stupidity) */
406 if (what == OF_SET0x02 && optc == 's') {
407 sortargs = 1;
408 break;
409 }
410 for (ele = 0; ele < NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0])); ele++)
411 if (optc == sh_options[ele].c &&
412 (what & sh_options[ele].flags)) {
413 change_flag((enum sh_flag) ele, what,
414 set);
415 break;
416 }
417 if (ele == NELEM(sh_options)(sizeof(sh_options) / sizeof((sh_options)[0]))) {
418 internal_errorf("%s: `%c'", __func__, optc);
419 return -1; /* not reached */
420 }
421 }
422 }
423 if (!(go.info & GI_MINUSMINUS(1<<(2))) && argv[go.optind] &&
424 (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
425 argv[go.optind][1] == '\0') {
426 /* lone - clears -v and -x flags */
427 if (argv[go.optind][0] == '-' && !Flag(FPOSIX)(shell_flags[(int) (FPOSIX)]))
428 Flag(FVERBOSE)(shell_flags[(int) (FVERBOSE)]) = Flag(FXTRACE)(shell_flags[(int) (FXTRACE)]) = 0;
429 /* set skips lone - or + option */
430 go.optind++;
431 }
432 if (setargsp)
41
Assuming 'setargsp' is null
42
Taking false branch
433 /* -- means set $#/$* even if there are no arguments */
434 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS(1<<(2))) ||
435 argv[go.optind]);
436
437 if (arrayset
42.1
'arrayset' is -1
&& (!*array || *skip_varname(array, false0))) {
43
Dereference of null pointer (loaded from variable 'array')
438 bi_errorf("%s: is not an identifier", array);
439 return -1;
440 }
441 if (sortargs) {
442 for (i = go.optind; argv[i]; i++)
443 ;
444 qsortp((void **) &argv[go.optind], (size_t) (i - go.optind),
445 xstrcmp);
446 }
447 if (arrayset) {
448 set_array(array, arrayset, argv + go.optind);
449 for (; argv[go.optind]; go.optind++)
450 ;
451 }
452
453 return go.optind;
454}
455
456/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
457int
458getn(const char *as, int *ai)
459{
460 char *p;
461 long n;
462
463 n = strtol(as, &p, 10);
464
465 if (!*as || *p || INT_MIN(-2147483647 -1) >= n || n >= INT_MAX2147483647)
466 return 0;
467
468 *ai = (int)n;
469 return 1;
470}
471
472/* getn() that prints error */
473int
474bi_getn(const char *as, int *ai)
475{
476 int rv = getn(as, ai);
477
478 if (!rv)
479 bi_errorf("%s: bad number", as);
480 return rv;
481}
482
483/* -------- gmatch.c -------- */
484
485/*
486 * int gmatch(string, pattern)
487 * char *string, *pattern;
488 *
489 * Match a pattern as in sh(1).
490 * pattern character are prefixed with MAGIC by expand.
491 */
492
493int
494gmatch(const char *s, const char *p, int isfile)
495{
496 const char *se, *pe;
497
498 if (s == NULL((void*)0) || p == NULL((void*)0))
499 return 0;
500 se = s + strlen(s);
501 pe = p + strlen(p);
502 /* isfile is false iff no syntax check has been done on
503 * the pattern. If check fails, just to a strcmp().
504 */
505 if (!isfile && !has_globbing(p, pe)) {
506 size_t len = pe - p + 1;
507 char tbuf[64];
508 char *t = len <= sizeof(tbuf) ? tbuf :
509 alloc(len, ATEMP&genv->area);
510 debunk(t, p, len);
511 return !strcmp(t, s);
512 }
513 return do_gmatch((const unsigned char *) s, (const unsigned char *) se,
514 (const unsigned char *) p, (const unsigned char *) pe);
515}
516
517/* Returns if p is a syntacticly correct globbing pattern, false
518 * if it contains no pattern characters or if there is a syntax error.
519 * Syntax errors are:
520 * - [ with no closing ]
521 * - imbalanced $(...) expression
522 * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
523 */
524/*XXX
525- if no magic,
526 if dest given, copy to dst
527 return ?
528- if magic && (no globbing || syntax error)
529 debunk to dst
530 return ?
531- return ?
532*/
533int
534has_globbing(const char *xp, const char *xpe)
535{
536 const unsigned char *p = (const unsigned char *) xp;
537 const unsigned char *pe = (const unsigned char *) xpe;
538 int c;
539 int nest = 0, bnest = 0;
540 int saw_glob = 0;
541 int in_bracket = 0; /* inside [...] */
542
543 for (; p < pe; p++) {
544 if (!ISMAGIC(*p)((unsigned char)(*p) == (7)))
545 continue;
546 if ((c = *++p) == '*' || c == '?')
547 saw_glob = 1;
548 else if (c == '[') {
549 if (!in_bracket) {
550 saw_glob = 1;
551 in_bracket = 1;
552 if (ISMAGIC(p[1])((unsigned char)(p[1]) == (7)) && p[2] == '!')
553 p += 2;
554 if (ISMAGIC(p[1])((unsigned char)(p[1]) == (7)) && p[2] == ']')
555 p += 2;
556 }
557 /* XXX Do we need to check ranges here? POSIX Q */
558 } else if (c == ']') {
559 if (in_bracket) {
560 if (bnest) /* [a*(b]) */
561 return 0;
562 in_bracket = 0;
563 }
564 } else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) {
565 saw_glob = 1;
566 if (in_bracket)
567 bnest++;
568 else
569 nest++;
570 } else if (c == '|') {
571 if (in_bracket && !bnest) /* *(a[foo|bar]) */
572 return 0;
573 } else if (c == /*(*/ ')') {
574 if (in_bracket) {
575 if (!bnest--) /* *(a[b)c] */
576 return 0;
577 } else if (nest)
578 nest--;
579 }
580 /* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-]
581 MAGIC-{, MAGIC-,, MAGIC-} */
582 }
583 return saw_glob && !in_bracket && !nest;
584}
585
586/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
587static int
588do_gmatch(const unsigned char *s, const unsigned char *se,
589 const unsigned char *p, const unsigned char *pe)
590{
591 int sc, pc;
592 const unsigned char *prest, *psub, *pnext;
593 const unsigned char *srest;
594
595 if (s == NULL((void*)0) || p == NULL((void*)0))
596 return 0;
597 while (p < pe) {
598 pc = *p++;
599 sc = s < se ? *s : '\0';
600 s++;
601 if (!ISMAGIC(pc)((unsigned char)(pc) == (7))) {
602 if (sc != pc)
603 return 0;
604 continue;
605 }
606 switch (*p++) {
607 case '[':
608 if (sc == 0 || (p = cclass(p, sc)) == NULL((void*)0))
609 return 0;
610 break;
611
612 case '?':
613 if (sc == 0)
614 return 0;
615 break;
616
617 case '*':
618 /* collapse consecutive stars */
619 while (ISMAGIC(p[0])((unsigned char)(p[0]) == (7)) && p[1] == '*')
620 p += 2;
621 if (p == pe)
622 return 1;
623 s--;
624 do {
625 if (do_gmatch(s, se, p, pe))
626 return 1;
627 } while (s++ < se);
628 return 0;
629
630 /*
631 * [*+?@!](pattern|pattern|..)
632 *
633 * Not ifdef'd KSH as this is needed for ${..%..}, etc.
634 */
635 case 0x80|'+': /* matches one or more times */
636 case 0x80|'*': /* matches zero or more times */
637 if (!(prest = pat_scan(p, pe, 0)))
638 return 0;
639 s--;
640 /* take care of zero matches */
641 if (p[-1] == (0x80 | '*') &&
642 do_gmatch(s, se, prest, pe))
643 return 1;
644 for (psub = p; ; psub = pnext) {
645 pnext = pat_scan(psub, pe, 1);
646 for (srest = s; srest <= se; srest++) {
647 if (do_gmatch(s, srest, psub, pnext - 2) &&
648 (do_gmatch(srest, se, prest, pe) ||
649 (s != srest && do_gmatch(srest,
650 se, p - 2, pe))))
651 return 1;
652 }
653 if (pnext == prest)
654 break;
655 }
656 return 0;
657
658 case 0x80|'?': /* matches zero or once */
659 case 0x80|'@': /* matches one of the patterns */
660 case 0x80|' ': /* simile for @ */
661 if (!(prest = pat_scan(p, pe, 0)))
662 return 0;
663 s--;
664 /* Take care of zero matches */
665 if (p[-1] == (0x80 | '?') &&
666 do_gmatch(s, se, prest, pe))
667 return 1;
668 for (psub = p; ; psub = pnext) {
669 pnext = pat_scan(psub, pe, 1);
670 srest = prest == pe ? se : s;
671 for (; srest <= se; srest++) {
672 if (do_gmatch(s, srest, psub, pnext - 2) &&
673 do_gmatch(srest, se, prest, pe))
674 return 1;
675 }
676 if (pnext == prest)
677 break;
678 }
679 return 0;
680
681 case 0x80|'!': /* matches none of the patterns */
682 if (!(prest = pat_scan(p, pe, 0)))
683 return 0;
684 s--;
685 for (srest = s; srest <= se; srest++) {
686 int matched = 0;
687
688 for (psub = p; ; psub = pnext) {
689 pnext = pat_scan(psub, pe, 1);
690 if (do_gmatch(s, srest, psub,
691 pnext - 2)) {
692 matched = 1;
693 break;
694 }
695 if (pnext == prest)
696 break;
697 }
698 if (!matched &&
699 do_gmatch(srest, se, prest, pe))
700 return 1;
701 }
702 return 0;
703
704 default:
705 if (sc != p[-1])
706 return 0;
707 break;
708 }
709 }
710 return s == se;
711}
712
713static int
714posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep)
715{
716 const struct cclass *cc;
717 const unsigned char *colon;
718 size_t len;
719 int rval = 0;
720
721 if ((colon = strchr(pattern, ':')) == NULL((void*)0) || colon[1] != MAGIC(7)) {
722 *ep = pattern - 2;
723 return -1;
724 }
725 *ep = colon + 3; /* skip MAGIC */
726 len = (size_t)(colon - pattern);
727
728 for (cc = cclasses; cc->name != NULL((void*)0); cc++) {
729 if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
730 if (cc->isctype(test))
731 rval = 1;
732 break;
733 }
734 }
735 if (cc->name == NULL((void*)0)) {
736 rval = -2; /* invalid character class */
737 }
738 return rval;
739}
740
741static const unsigned char *
742cclass(const unsigned char *p, int sub)
743{
744 int c, d, rv, not, found = 0;
745 const unsigned char *orig_p = p;
746
747 if ((not = (ISMAGIC(*p)((unsigned char)(*p) == (7)) && *++p == '!')))
748 p++;
749 do {
750 /* check for POSIX character class (e.g. [[:alpha:]]) */
751 if ((p[0] == MAGIC(7) && p[1] == '[' && p[2] == ':') ||
752 (p[0] == '[' && p[1] == ':')) {
753 do {
754 const char *pp = p + (*p == MAGIC(7)) + 2;
755 rv = posix_cclass(pp, sub, &p);
756 switch (rv) {
757 case 1:
758 found = 1;
759 break;
760 case -2:
761 return NULL((void*)0);
762 }
763 } while (rv != -1 && p[0] == MAGIC(7) && p[1] == '[' && p[2] == ':');
764 if (p[0] == MAGIC(7) && p[1] == ']')
765 break;
766 }
767
768 c = *p++;
769 if (ISMAGIC(c)((unsigned char)(c) == (7))) {
770 c = *p++;
771 if ((c & 0x80) && !ISMAGIC(c)((unsigned char)(c) == (7))) {
772 c &= 0x7f;/* extended pattern matching: *+?@! */
773 /* XXX the ( char isn't handled as part of [] */
774 if (c == ' ') /* simile for @: plain (..) */
775 c = '(' /*)*/;
776 }
777 }
778 if (c == '\0')
779 /* No closing ] - act as if the opening [ was quoted */
780 return sub == '[' ? orig_p : NULL((void*)0);
781 if (ISMAGIC(p[0])((unsigned char)(p[0]) == (7)) && p[1] == '-' &&
782 (!ISMAGIC(p[2])((unsigned char)(p[2]) == (7)) || p[3] != ']')) {
783 p += 2; /* MAGIC- */
784 d = *p++;
785 if (ISMAGIC(d)((unsigned char)(d) == (7))) {
786 d = *p++;
787 if ((d & 0x80) && !ISMAGIC(d)((unsigned char)(d) == (7)))
788 d &= 0x7f;
789 }
790 /* POSIX says this is an invalid expression */
791 if (c > d)
792 return NULL((void*)0);
793 } else
794 d = c;
795 if (c == sub || (c <= sub && sub <= d))
796 found = 1;
797 } while (!(ISMAGIC(p[0])((unsigned char)(p[0]) == (7)) && p[1] == ']'));
798
799 return (found != not) ? p+2 : NULL((void*)0);
800}
801
802/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
803const unsigned char *
804pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep)
805{
806 int nest = 0;
807
808 for (; p < pe; p++) {
809 if (!ISMAGIC(*p)((unsigned char)(*p) == (7)))
810 continue;
811 if ((*++p == /*(*/ ')' && nest-- == 0) ||
812 (*p == '|' && match_sep && nest == 0))
813 return ++p;
814 if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f))
815 nest++;
816 }
817 return NULL((void*)0);
818}
819
820/*
821 * quick sort of array of generic pointers to objects.
822 */
823void
824qsortp(void **base, /* base address */
825 size_t n, /* elements */
826 int (*f) (const void *, const void *)) /* compare function */
827{
828 qsort(base, n, sizeof(char *), f);
829}
830
831int
832xstrcmp(const void *p1, const void *p2)
833{
834 return (strcmp(*(char **)p1, *(char **)p2));
835}
836
837/* Initialize a Getopt structure */
838void
839ksh_getopt_reset(Getopt *go, int flags)
840{
841 go->optind = 1;
842 go->optarg = NULL((void*)0);
6
Null pointer value stored to 'go.optarg'
843 go->p = 0;
844 go->flags = flags;
845 go->info = 0;
846 go->buf[1] = '\0';
847}
848
849
850/* getopt() used for shell built-in commands, the getopts command, and
851 * command line options.
852 * A leading ':' in options means don't print errors, instead return '?'
853 * or ':' and set go->optarg to the offending option character.
854 * If GF_ERROR is set (and option doesn't start with :), errors result in
855 * a call to bi_errorf().
856 *
857 * Non-standard features:
858 * - ';' is like ':' in options, except the argument is optional
859 * (if it isn't present, optarg is set to 0).
860 * Used for 'set -o'.
861 * - ',' is like ':' in options, except the argument always immediately
862 * follows the option character (optarg is set to the null string if
863 * the option is missing).
864 * Used for 'read -u2', 'print -u2' and fc -40.
865 * - '#' is like ':' in options, expect that the argument is optional
866 * and must start with a digit or be the string "unlimited". If the
867 * argument doesn't match, it is assumed to be missing and normal option
868 * processing continues (optarg is set to 0 if the option is missing).
869 * Used for 'typeset -LZ4' and 'ulimit -adunlimited'.
870 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
871 * option starting with + is accepted, the GI_PLUS flag will be set
872 * in go->info.
873 */
874int
875ksh_getopt(char **argv, Getopt *go, const char *options)
876{
877 char c;
878 char *o;
879
880 if (go->p
8.1
Field 'p' is equal to 0
== 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
881 char *arg = argv[go->optind], flag = arg ? *arg : '\0';
9
Assuming 'arg' is non-null
10
'?' condition is true
882
883 go->p = 1;
884 if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
11
Assuming the condition is false
885 go->optind++;
886 go->p = 0;
887 go->info |= GI_MINUSMINUS(1<<(2));
888 return -1;
889 }
890 if (arg
11.1
'arg' is not equal to NULL
== NULL((void*)0) ||
14
Taking false branch
891 ((flag != '-' ) && /* neither a - nor a + (if + allowed) */
892 (!(go->flags & GF_PLUSOPT(1<<(1))) || flag != '+')) ||
12
Assuming the condition is false
893 (c = arg[1]) == '\0') {
13
Assuming the condition is false
894 go->p = 0;
895 return -1;
896 }
897 go->optind++;
898 go->info &= ~(GI_MINUS(1<<(0))|GI_PLUS(1<<(1)));
899 go->info |= flag == '-' ? GI_MINUS(1<<(0)) : GI_PLUS(1<<(1));
15
'?' condition is false
900 }
901 go->p++;
902 if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
16
Assuming the condition is false
17
Assuming the condition is false
18
Assuming the condition is false
19
Assuming the condition is false
20
Assuming the condition is false
22
Taking false branch
903 !(o = strchr(options, c))) {
21
Assuming 'o' is non-null
904 if (options[0] == ':') {
905 go->buf[0] = c;
906 go->optarg = go->buf;
907 } else {
908 warningf(false0, "%s%s-%c: unknown option",
909 (go->flags & GF_NONAME(1<<(2))) ? "" : argv[0],
910 (go->flags & GF_NONAME(1<<(2))) ? "" : ": ", c);
911 if (go->flags & GF_ERROR(1<<(0)))
912 bi_errorf(NULL((void*)0));
913 }
914 return '?';
915 }
916 /* : means argument must be present, may be part of option argument
917 * or the next argument
918 * ; same as : but argument may be missing
919 * , means argument is part of option argument, and may be null.
920 */
921 if (*++o == ':' || *o == ';') {
23
Assuming the condition is false
24
Assuming the condition is false
25
Taking false branch
922 if (argv[go->optind - 1][go->p])
923 go->optarg = argv[go->optind - 1] + go->p;
924 else if (argv[go->optind])
925 go->optarg = argv[go->optind++];
926 else if (*o == ';')
927 go->optarg = NULL((void*)0);
928 else {
929 if (options[0] == ':') {
930 go->buf[0] = c;
931 go->optarg = go->buf;
932 return ':';
933 }
934 warningf(false0, "%s%s-`%c' requires argument",
935 (go->flags & GF_NONAME(1<<(2))) ? "" : argv[0],
936 (go->flags & GF_NONAME(1<<(2))) ? "" : ": ", c);
937 if (go->flags & GF_ERROR(1<<(0)))
938 bi_errorf(NULL((void*)0));
939 return '?';
940 }
941 go->p = 0;
942 } else if (*o == ',') {
26
Assuming the condition is false
27
Taking false branch
943 /* argument is attached to option character, even if null */
944 go->optarg = argv[go->optind - 1] + go->p;
945 go->p = 0;
946 } else if (*o == '#') {
28
Assuming the condition is false
29
Taking false branch
947 /* argument is optional and may be attached or unattached
948 * but must start with a digit. optarg is set to 0 if the
949 * argument is missing.
950 */
951 if (argv[go->optind - 1][go->p]) {
952 if (digit(argv[go->optind - 1][go->p])isdigit((unsigned char)(argv[go->optind - 1][go->p])) ||
953 !strcmp(&argv[go->optind - 1][go->p], "unlimited")) {
954 go->optarg = argv[go->optind - 1] + go->p;
955 go->p = 0;
956 } else
957 go->optarg = NULL((void*)0);
958 } else {
959 if (argv[go->optind] && (digit(argv[go->optind][0])isdigit((unsigned char)(argv[go->optind][0])) ||
960 !strcmp(argv[go->optind], "unlimited"))) {
961 go->optarg = argv[go->optind++];
962 go->p = 0;
963 } else
964 go->optarg = NULL((void*)0);
965 }
966 }
967 return c;
30
Returning without writing to 'go->optarg'
31
Returning value (loaded from 'c'), which participates in a condition later
968}
969
970/* print variable/alias value using necessary quotes
971 * (POSIX says they should be suitable for re-entry...)
972 * No trailing newline is printed.
973 */
974void
975print_value_quoted(const char *s)
976{
977 const char *p;
978 int inquote = 0;
979
980 /* Test if any quotes are needed */
981 for (p = s; *p; p++)
982 if (ctype(*p, C_QUOTE)!!(ctypes[(unsigned char)(*p)]&((1<<(8)))))
983 break;
984 if (!*p) {
985 shprintf("%s", s);
986 return;
987 }
988 for (p = s; *p; p++) {
989 if (*p == '\'') {
990 shprintf(inquote ? "'\\'" : "\\'");
991 inquote = 0;
992 } else {
993 if (!inquote) {
994 shprintf("'");
995 inquote = 1;
996 }
997 shf_putc(*p, shl_stdout)(((&shf_iob[1]))->wnleft == 0 ? shf_putchar((*p), ((&
shf_iob[1]))) : (((&shf_iob[1]))->wnleft--, *((&shf_iob
[1]))->wp++ = (*p)))
;
998 }
999 }
1000 if (inquote)
1001 shprintf("'");
1002}
1003
1004/* Print things in columns and rows - func() is called to format the ith
1005 * element
1006 */
1007void
1008print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int),
1009 void *arg, int max_width, int prefcol)
1010{
1011 char *str = alloc(max_width + 1, ATEMP&genv->area);
1012 int i;
1013 int r, c;
1014 int rows, cols;
1015 int nspace;
1016 int col_width;
1017
1018 /* max_width + 1 for the space. Note that no space
1019 * is printed after the last column to avoid problems
1020 * with terminals that have auto-wrap.
1021 */
1022 cols = x_cols / (max_width + 1);
1023 if (!cols)
1024 cols = 1;
1025 rows = (n + cols - 1) / cols;
1026 if (prefcol && n && cols > rows) {
1027 int tmp = rows;
1028
1029 rows = cols;
1030 cols = tmp;
1031 if (rows > n)
1032 rows = n;
1033 }
1034
1035 col_width = max_width;
1036 if (cols == 1)
1037 col_width = 0; /* Don't pad entries in single column output. */
1038 nspace = (x_cols - max_width * cols) / cols;
1039 if (nspace <= 0)
1040 nspace = 1;
1041 for (r = 0; r < rows; r++) {
1042 for (c = 0; c < cols; c++) {
1043 i = c * rows + r;
1044 if (i < n) {
1045 shf_fprintf(shf, "%-*s",
1046 col_width,
1047 (*func)(arg, i, str, max_width + 1));
1048 if (c + 1 < cols)
1049 shf_fprintf(shf, "%*s", nspace, "");
1050 }
1051 }
1052 shf_putchar('\n', shf);
1053 }
1054 afree(str, ATEMP&genv->area);
1055}
1056
1057/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
1058int
1059strip_nuls(char *buf, int nbytes)
1060{
1061 char *dst;
1062
1063 if ((dst = memchr(buf, '\0', nbytes))) {
1064 char *end = buf + nbytes;
1065 char *p, *q;
1066
1067 for (p = dst; p < end; p = q) {
1068 /* skip a block of nulls */
1069 while (++p < end && *p == '\0')
1070 ;
1071 /* find end of non-null block */
1072 if (!(q = memchr(p, '\0', end - p)))
1073 q = end;
1074 memmove(dst, p, q - p);
1075 dst += q - p;
1076 }
1077 *dst = '\0';
1078 return dst - buf;
1079 }
1080 return nbytes;
1081}
1082
1083/* Like read(2), but if read fails due to non-blocking flag, resets flag
1084 * and restarts read.
1085 */
1086int
1087blocking_read(int fd, char *buf, int nbytes)
1088{
1089 int ret;
1090 int tried_reset = 0;
1091
1092 while ((ret = read(fd, buf, nbytes)) == -1) {
1093 if (!tried_reset && errno(*__errno()) == EAGAIN35) {
1094 int oerrno = errno(*__errno());
1095 if (reset_nonblock(fd) > 0) {
1096 tried_reset = 1;
1097 continue;
1098 }
1099 errno(*__errno()) = oerrno;
1100 }
1101 break;
1102 }
1103 return ret;
1104}
1105
1106/* Reset the non-blocking flag on the specified file descriptor.
1107 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1108 * 1 if it was.
1109 */
1110int
1111reset_nonblock(int fd)
1112{
1113 int flags;
1114
1115 if ((flags = fcntl(fd, F_GETFL3)) == -1)
1116 return -1;
1117 if (!(flags & O_NONBLOCK0x0004))
1118 return 0;
1119 flags &= ~O_NONBLOCK0x0004;
1120 if (fcntl(fd, F_SETFL4, flags) == -1)
1121 return -1;
1122 return 1;
1123}
1124
1125
1126/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */
1127char *
1128ksh_get_wd(char *buf, int bsize)
1129{
1130 char *b;
1131 char *ret;
1132
1133 /* Note: we could just use plain getcwd(), but then we'd had to
1134 * inject possibly allocated space into the ATEMP area. */
1135 /* Assume getcwd() available */
1136 if (!buf) {
1137 bsize = PATH_MAX1024;
1138 b = alloc(bsize, ATEMP&genv->area);
1139 } else
1140 b = buf;
1141
1142 ret = getcwd(b, bsize);
1143
1144 if (!buf) {
1145 if (ret)
1146 ret = aresize(b, strlen(b) + 1, ATEMP&genv->area);
1147 else
1148 afree(b, ATEMP&genv->area);
1149 }
1150
1151 return ret;
1152}