Bug Summary

File:src/bin/ksh/eval.c
Warning:line 394, column 13
Access to field 'quote' results in a dereference of a null pointer (loaded from variable 'st')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name eval.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/bin/ksh/obj -resource-dir /usr/local/llvm16/lib/clang/16 -D EMACS -D VI -I . -I /usr/src/bin/ksh -I /usr/src/bin/ksh/../../lib/libc/gen -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/ksh/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/bin/ksh/eval.c
1/* $OpenBSD: eval.c,v 1.67 2023/05/24 14:20:33 millert Exp $ */
2
3/*
4 * Expansion - quoting, separation, substitution, globbing
5 */
6
7#include <sys/stat.h>
8
9#include <ctype.h>
10#include <dirent.h>
11#include <errno(*__errno()).h>
12#include <fcntl.h>
13#include <pwd.h>
14#include <stdio.h>
15#include <string.h>
16#include <unistd.h>
17
18#include "sh.h"
19
20/*
21 * string expansion
22 *
23 * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
24 * second pass: alternation ({,}), filename expansion (*?[]).
25 */
26
27/* expansion generator state */
28typedef struct Expand {
29 /* int type; */ /* see expand() */
30 const char *str; /* string */
31 union {
32 const char **strv;/* string[] */
33 struct shf *shf;/* file */
34 } u; /* source */
35 struct tbl *var; /* variable in ${var..} */
36 short split; /* split "$@" / call waitlast $() */
37} Expand;
38
39#define XBASE0 0 /* scanning original */
40#define XSUB1 1 /* expanding ${} string */
41#define XARGSEP2 2 /* ifs0 between "$*" */
42#define XARG3 3 /* expanding $*, $@ */
43#define XCOM4 4 /* expanding $() */
44#define XNULLSUB5 5 /* "$@" when $# is 0 (don't generate word) */
45#define XSUBMID6 6 /* middle of expanding ${} */
46
47/* States used for field splitting */
48#define IFS_WORD0 0 /* word has chars (or quotes) */
49#define IFS_WS1 1 /* have seen IFS white-space */
50#define IFS_NWS2 2 /* have seen IFS non-white-space */
51#define IFS_IWS3 3 /* beginning of word, ignore IFS white-space */
52#define IFS_QUOTE4 4 /* beg.w/quote, becomes IFS_WORD unless "$@" */
53
54static int varsub(Expand *, char *, char *, int *, int *);
55static int comsub(Expand *, char *);
56static char *trimsub(char *, char *, int);
57static void glob(char *, XPtrV *, int);
58static void globit(XString *, char **, char *, XPtrV *, int);
59static char *maybe_expand_tilde(char *, XString *, char **, int);
60static char *tilde(char *);
61static char *homedir(char *);
62static void alt_expand(XPtrV *, char *, char *, char *, int);
63
64static struct tbl *varcpy(struct tbl *);
65
66/* compile and expand word */
67char *
68substitute(const char *cp, int f)
69{
70 struct source *s, *sold;
71
72 if (disable_subst)
73 return str_save(cp, ATEMP&genv->area);
74
75 sold = source;
76 s = pushs(SWSTR4, ATEMP&genv->area);
77 s->start = s->str = cp;
78 source = s;
79 if (yylex(ONEWORD(1<<(1))) != LWORD256)
80 internal_errorf("substitute");
81 source = sold;
82 afree(s, ATEMP&genv->area);
83 return evalstr(yylval.cp, f);
84}
85
86/*
87 * expand arg-list
88 */
89char **
90eval(char **ap, int f)
91{
92 XPtrV w;
93
94 if (*ap == NULL((void *)0))
95 return ap;
96 XPinit(w, 32)do { void **vp__; vp__ = areallocarray(((void *)0), 32, sizeof
(void *), &genv->area); (w).cur = (w).beg = vp__; (w).
end = vp__ + 32; } while (0)
;
97 XPput(w, NULL)do { if ((w).cur >= (w).end) { int n = ((w).cur - (w).beg)
; (w).beg = areallocarray((w).beg, n, 2 * sizeof(void *), &
genv->area); (w).cur = (w).beg + n; (w).end = (w).cur + n;
} *(w).cur++ = (((void *)0)); } while (0)
; /* space for shell name */
98 while (*ap != NULL((void *)0))
99 expand(*ap++, &w, f);
100 XPput(w, NULL)do { if ((w).cur >= (w).end) { int n = ((w).cur - (w).beg)
; (w).beg = areallocarray((w).beg, n, 2 * sizeof(void *), &
genv->area); (w).cur = (w).beg + n; (w).end = (w).cur + n;
} *(w).cur++ = (((void *)0)); } while (0)
;
101 return (char **) XPclose(w)areallocarray((w).beg, ((w).cur - (w).beg), sizeof(void *), &
genv->area)
+ 1;
102}
103
104/*
105 * expand string
106 */
107char *
108evalstr(char *cp, int f)
109{
110 XPtrV w;
111
112 XPinit(w, 1)do { void **vp__; vp__ = areallocarray(((void *)0), 1, sizeof
(void *), &genv->area); (w).cur = (w).beg = vp__; (w).
end = vp__ + 1; } while (0)
;
113 expand(cp, &w, f);
114 cp = (XPsize(w)((w).cur - (w).beg) == 0) ? null : (char*) *XPptrv(w)((w).beg);
115 XPfree(w)afree((w).beg, &genv->area);
116 return cp;
117}
118
119/*
120 * expand string - return only one component
121 * used from iosetup to expand redirection files
122 */
123char *
124evalonestr(char *cp, int f)
125{
126 XPtrV w;
127
128 XPinit(w, 1)do { void **vp__; vp__ = areallocarray(((void *)0), 1, sizeof
(void *), &genv->area); (w).cur = (w).beg = vp__; (w).
end = vp__ + 1; } while (0)
;
129 expand(cp, &w, f);
130 switch (XPsize(w)((w).cur - (w).beg)) {
131 case 0:
132 cp = null;
133 break;
134 case 1:
135 cp = (char*) *XPptrv(w)((w).beg);
136 break;
137 default:
138 cp = evalstr(cp, f&~DOGLOB(1<<(1)));
139 break;
140 }
141 XPfree(w)afree((w).beg, &genv->area);
142 return cp;
143}
144
145/* for nested substitution: ${var:=$var2} */
146typedef struct SubType {
147 short stype; /* [=+-?%#] action after expanded word */
148 short base; /* begin position of expanded word */
149 short f; /* saved value of f (DOPAT, etc) */
150 struct tbl *var; /* variable for ${var..} */
151 short quote; /* saved value of quote (for ${..[%#]..}) */
152 struct SubType *prev; /* old type */
153 struct SubType *next; /* poped type (to avoid re-allocating) */
154} SubType;
155
156void
157expand(char *cp, /* input word */
158 XPtrV *wp, /* output words */
159 int f) /* DO* flags */
160{
161 int c = 0;
162 int type; /* expansion type */
163 int quote = 0; /* quoted */
164 XString ds; /* destination string */
165 char *dp, *sp; /* dest., source */
166 int fdo, word; /* second pass flags; have word */
167 int doblank; /* field splitting of parameter/command subst */
168 Expand x = {
169 /* expansion variables */
170 NULL((void *)0), { NULL((void *)0) }, NULL((void *)0), 0
171 };
172 SubType st_head, *st;
173 int newlines = 0; /* For trailing newlines in COMSUB */
174 int saw_eq, tilde_ok;
175 int make_magic;
176 size_t len;
177
178 if (cp == NULL((void *)0))
1
Assuming 'cp' is not equal to NULL
179 internal_errorf("expand(NULL)");
180 /* for alias, readonly, set, typeset commands */
181 if ((f & DOVACHECK(1<<(9))) && is_wdvarassign(cp)) {
2
Assuming the condition is false
182 f &= ~(DOVACHECK(1<<(9))|DOBLANK(1<<(0))|DOGLOB(1<<(1))|DOTILDE(1<<(3)));
183 f |= DOASNTILDE(1<<(5));
184 }
185 if (Flag(FNOGLOB)(shell_flags[(int) (FNOGLOB)]))
3
Assuming the condition is false
4
Taking false branch
186 f &= ~DOGLOB(1<<(1));
187 if (Flag(FMARKDIRS)(shell_flags[(int) (FMARKDIRS)]))
5
Assuming the condition is false
188 f |= DOMARKDIRS(1<<(10));
189 if (Flag(FBRACEEXPAND)(shell_flags[(int) (FBRACEEXPAND)]) && (f & DOGLOB(1<<(1))))
6
Taking false branch
7
Assuming the condition is false
190 f |= DOBRACE_(1<<(6));
191
192 Xinit(ds, dp, 128, ATEMP)do { (ds).len = 128; (ds).areap = (&genv->area); (ds).
beg = alloc((ds).len + 8, (ds).areap); (ds).end = (ds).beg + (
ds).len; dp = (ds).beg; } while (0)
; /* init dest. string */
8
Loop condition is false. Exiting loop
193 type = XBASE0;
194 sp = cp;
195 fdo = 0;
196 saw_eq = 0;
197 tilde_ok = (f & (DOTILDE(1<<(3))|DOASNTILDE(1<<(5)))) ? 1 : 0; /* must be 1/0 */
9
Assuming the condition is false
10
'?' condition is false
198 doblank = 0;
199 make_magic = 0;
200 word = (f&DOBLANK(1<<(0))) ? IFS_WS1 : IFS_WORD0;
11
Assuming the condition is false
12
'?' condition is false
201
202 memset(&st_head, 0, sizeof(st_head));
13
Null pointer value stored to 'st_head.prev'
203 st = &st_head;
204
205 while (1) {
14
Loop condition is true. Entering loop body
22
Execution continues on line 205
23
Loop condition is true. Entering loop body
206 Xcheck(ds, dp)do { ptrdiff_t more = ((dp) + (1)) - (ds).end; if (more > 0
) dp = Xcheck_grow_(&ds, dp, more); } while (0)
;
15
Taking false branch
16
Loop condition is false. Exiting loop
24
Taking false branch
25
Loop condition is false. Exiting loop
207
208 switch (type) {
17
Control jumps to 'case 0:' at line 209
26
Control jumps to 'case 0:' at line 209
209 case XBASE0: /* original prefixed string */
210 c = *sp++;
211 switch (c) {
18
Control jumps to 'case 8:' at line 390
27
Control jumps to 'case 8:' at line 390
212 case EOS0:
213 c = 0;
214 break;
215 case CHAR1:
216 c = *sp++;
217 break;
218 case QCHAR2:
219 quote |= 2; /* temporary quote */
220 c = *sp++;
221 break;
222 case OQUOTE5:
223 switch (word) {
224 case IFS_QUOTE4:
225 /* """something */
226 word = IFS_WORD0;
227 break;
228 case IFS_WORD0:
229 break;
230 default:
231 word = IFS_QUOTE4;
232 break;
233 }
234 tilde_ok = 0;
235 quote = 1;
236 continue;
237 case CQUOTE6:
238 quote = 0;
239 continue;
240 case COMSUB3:
241 tilde_ok = 0;
242 if (f & DONTRUNCOMMAND(1<<(4))) {
243 word = IFS_WORD0;
244 *dp++ = '$'; *dp++ = '(';
245 while (*sp != '\0') {
246 Xcheck(ds, dp)do { ptrdiff_t more = ((dp) + (1)) - (ds).end; if (more > 0
) dp = Xcheck_grow_(&ds, dp, more); } while (0)
;
247 *dp++ = *sp++;
248 }
249 *dp++ = ')';
250 } else {
251 type = comsub(&x, sp);
252 if (type == XCOM4 && (f&DOBLANK(1<<(0))))
253 doblank++;
254 sp = strchr(sp, 0) + 1;
255 newlines = 0;
256 }
257 continue;
258 case EXPRSUB4:
259 word = IFS_WORD0;
260 tilde_ok = 0;
261 if (f & DONTRUNCOMMAND(1<<(4))) {
262 *dp++ = '$'; *dp++ = '('; *dp++ = '(';
263 while (*sp != '\0') {
264 Xcheck(ds, dp)do { ptrdiff_t more = ((dp) + (1)) - (ds).end; if (more > 0
) dp = Xcheck_grow_(&ds, dp, more); } while (0)
;
265 *dp++ = *sp++;
266 }
267 *dp++ = ')'; *dp++ = ')';
268 } else {
269 struct tbl v;
270 char *p;
271
272 v.flag = DEFINED(1<<(1))|ISSET(1<<(2))|INTEGER(1<<(9));
273 v.type = 10; /* not default */
274 v.name[0] = '\0';
275 v_evaluate(&v, substitute(sp, 0),
276 KSH_UNWIND_ERROR0x0, true1);
277 sp = strchr(sp, 0) + 1;
278 for (p = str_val(&v); *p; ) {
279 Xcheck(ds, dp)do { ptrdiff_t more = ((dp) + (1)) - (ds).end; if (more > 0
) dp = Xcheck_grow_(&ds, dp, more); } while (0)
;
280 *dp++ = *p++;
281 }
282 }
283 continue;
284 case OSUBST7: /* ${{#}var{:}[=+-?#%]word} */
285 /* format is:
286 * OSUBST [{x] plain-variable-part \0
287 * compiled-word-part CSUBST [}x]
288 * This is where all syntax checking gets done...
289 */
290 {
291 char *varname = ++sp; /* skip the { or x (}) */
292 int stype;
293 int slen = 0;
294
295 sp = strchr(sp, '\0') + 1; /* skip variable */
296 type = varsub(&x, varname, sp, &stype, &slen);
297 if (type < 0) {
298 char endc;
299 char *str, *end;
300
301 sp = varname - 2; /* restore sp */
302 end = (char *) wdscan(sp, CSUBST8);
303 /* ({) the } or x is already skipped */
304 endc = *end;
305 *end = EOS0;
306 str = snptreef(NULL((void *)0), 64, "%S", sp);
307 *end = endc;
308 errorf("%s: bad substitution", str);
309 }
310 if (f&DOBLANK(1<<(0)))
311 doblank++;
312 tilde_ok = 0;
313 if (word == IFS_QUOTE4 && type != XNULLSUB5)
314 word = IFS_WORD0;
315 if (type == XBASE0) { /* expand? */
316 if (!st->next) {
317 SubType *newst;
318
319 newst = alloc(
320 sizeof(SubType), ATEMP&genv->area);
321 newst->next = NULL((void *)0);
322 newst->prev = st;
323 st->next = newst;
324 }
325 st = st->next;
326 st->stype = stype;
327 st->base = Xsavepos(ds, dp)((dp) - (ds).beg);
328 st->f = f;
329 st->var = varcpy(x.var);
330 st->quote = quote;
331 /* skip qualifier(s) */
332 if (stype)
333 sp += slen;
334 switch (stype & 0x7f) {
335 case '#':
336 case '%':
337 /* ! DOBLANK,DOBRACE_,DOTILDE */
338 f = DOPAT(1<<(2)) | (f&DONTRUNCOMMAND(1<<(4))) |
339 DOTEMP_(1<<(8));
340 quote = 0;
341 /* Prepend open pattern (so |
342 * in a trim will work as
343 * expected)
344 */
345 *dp++ = MAGIC(7);
346 *dp++ = '@' + 0x80U;
347 break;
348 case '=':
349 /* Enabling tilde expansion
350 * after :'s here is
351 * non-standard ksh, but is
352 * consistent with rules for
353 * other assignments. Not
354 * sure what POSIX thinks of
355 * this.
356 * Not doing tilde expansion
357 * for integer variables is a
358 * non-POSIX thing - makes
359 * sense though, since ~ is
360 * a arithmetic operator.
361 */
362 if (!(x.var->flag & INTEGER(1<<(9))))
363 f |= DOASNTILDE(1<<(5))|DOTILDE(1<<(3));
364 f |= DOTEMP_(1<<(8));
365 /* These will be done after the
366 * value has been assigned.
367 */
368 f &= ~(DOBLANK(1<<(0))|DOGLOB(1<<(1))|DOBRACE_(1<<(6)));
369 tilde_ok = 1;
370 break;
371 case '?':
372 f &= ~DOBLANK(1<<(0));
373 f |= DOTEMP_(1<<(8));
374 /* FALLTHROUGH */
375 default:
376 /* '-' '+' '?' */
377 if (quote)
378 word = IFS_WORD0;
379 else if (dp == Xstring(ds, dp)((ds).beg))
380 word = IFS_IWS3;
381 /* Enable tilde expansion */
382 tilde_ok = 1;
383 f |= DOTILDE(1<<(3));
384 }
385 } else
386 /* skip word */
387 sp = (char *) wdscan(sp, CSUBST8);
388 continue;
389 }
390 case CSUBST8: /* only get here if expanding word */
391 sp++; /* ({) skip the } or x */
392 tilde_ok = 0; /* in case of ${unset:-} */
393 *dp = '\0';
394 quote = st->quote;
28
Access to field 'quote' results in a dereference of a null pointer (loaded from variable 'st')
395 f = st->f;
396 if (f&DOBLANK(1<<(0)))
19
Taking false branch
397 doblank--;
398 switch (st->stype&0x7f) {
20
'Default' branch taken. Execution continues on line 467
399 case '#':
400 case '%':
401 /* Append end-pattern */
402 *dp++ = MAGIC(7); *dp++ = ')'; *dp = '\0';
403 dp = Xrestpos(ds, dp, st->base)((ds).beg + (st->base));
404 /* Must use st->var since calling
405 * global would break things
406 * like x[i+=1].
407 */
408 x.str = trimsub(str_val(st->var),
409 dp, st->stype);
410 if (x.str[0] != '\0') {
411 word = IFS_IWS3;
412 type = XSUB1;
413 } else if (quote) {
414 word = IFS_WORD0;
415 type = XSUB1;
416 } else {
417 if (dp == Xstring(ds, dp)((ds).beg))
418 word = IFS_IWS3;
419 type = XNULLSUB5;
420 }
421 if (f&DOBLANK(1<<(0)))
422 doblank++;
423 st = st->prev;
424 continue;
425 case '=':
426 /* Restore our position and substitute
427 * the value of st->var (may not be
428 * the assigned value in the presence
429 * of integer/right-adj/etc attributes).
430 */
431 dp = Xrestpos(ds, dp, st->base)((ds).beg + (st->base));
432 /* Must use st->var since calling
433 * global would cause with things
434 * like x[i+=1] to be evaluated twice.
435 */
436 /* Note: not exported by FEXPORT
437 * in at&t ksh.
438 */
439 /* XXX POSIX says readonly is only
440 * fatal for special builtins (setstr
441 * does readonly check).
442 */
443 len = strlen(dp) + 1;
444 setstr(st->var,
445 debunk(alloc(len, ATEMP&genv->area),
446 dp, len), KSH_UNWIND_ERROR0x0);
447 x.str = str_val(st->var);
448 type = XSUB1;
449 if (f&DOBLANK(1<<(0)))
450 doblank++;
451 st = st->prev;
452 if (quote || !*x.str)
453 word = IFS_WORD0;
454 else
455 word = IFS_IWS3;
456 continue;
457 case '?':
458 {
459 char *s = Xrestpos(ds, dp, st->base)((ds).beg + (st->base));
460
461 errorf("%s: %s", st->var->name,
462 dp == s ?
463 "parameter null or not set" :
464 (debunk(s, s, strlen(s) + 1), s));
465 }
466 }
467 st = st->prev;
21
Null pointer value stored to 'st'
468 type = XBASE0;
469 continue;
470
471 case OPAT9: /* open pattern: *(foo|bar) */
472 /* Next char is the type of pattern */
473 make_magic = 1;
474 c = *sp++ + 0x80;
475 break;
476
477 case SPAT10: /* pattern separator (|) */
478 make_magic = 1;
479 c = '|';
480 break;
481
482 case CPAT11: /* close pattern */
483 make_magic = 1;
484 c = /*(*/ ')';
485 break;
486 }
487 break;
488
489 case XNULLSUB5:
490 /* Special case for "$@" (and "${foo[@]}") - no
491 * word is generated if $# is 0 (unless there is
492 * other stuff inside the quotes).
493 */
494 type = XBASE0;
495 if (f&DOBLANK(1<<(0))) {
496 doblank--;
497 if (dp == Xstring(ds, dp)((ds).beg) && word != IFS_WORD0)
498 word = IFS_IWS3;
499 }
500 continue;
501
502 case XSUB1:
503 case XSUBMID6:
504 if ((c = *x.str++) == 0) {
505 type = XBASE0;
506 if (f&DOBLANK(1<<(0)))
507 doblank--;
508 continue;
509 }
510 break;
511
512 case XARGSEP2:
513 type = XARG3;
514 quote = 1;
515 case XARG3:
516 if ((c = *x.str++) == '\0') {
517 /* force null words to be created so
518 * set -- '' 2 ''; foo "$@" will do
519 * the right thing
520 */
521 if (quote && x.split)
522 word = IFS_WORD0;
523 if ((x.str = *x.u.strv++) == NULL((void *)0)) {
524 type = XBASE0;
525 if (f&DOBLANK(1<<(0)))
526 doblank--;
527 continue;
528 }
529 c = ifs0;
530 if (c == 0) {
531 if (quote && !x.split)
532 continue;
533 if (!quote && word == IFS_WS1)
534 continue;
535 /* this is so we don't terminate */
536 c = ' ';
537 /* now force-emit a word */
538 goto emit_word;
539 }
540 if (quote && x.split) {
541 /* terminate word for "$@" */
542 type = XARGSEP2;
543 quote = 0;
544 }
545 }
546 break;
547
548 case XCOM4:
549 if (x.u.shf == NULL((void *)0)) /* $(< ...) failed, fake EOF */
550 c = EOF(-1);
551 else if (newlines) { /* Spit out saved nl's */
552 c = '\n';
553 --newlines;
554 } else {
555 while ((c = shf_getc(x.u.shf)((x.u.shf)->rnleft > 0 ? (x.u.shf)->rnleft--, *(x.u.
shf)->rp++ : shf_getchar(x.u.shf))
) == 0 || c == '\n')
556 if (c == '\n')
557 newlines++; /* Save newlines */
558 if (newlines && c != EOF(-1)) {
559 shf_ungetc(c, x.u.shf);
560 c = '\n';
561 --newlines;
562 }
563 }
564 if (c == EOF(-1)) {
565 newlines = 0;
566 if (x.u.shf != NULL((void *)0))
567 shf_close(x.u.shf);
568 if (x.split)
569 subst_exstat = waitlast();
570 else
571 subst_exstat = (x.u.shf == NULL((void *)0));
572 type = XBASE0;
573 if (f&DOBLANK(1<<(0)))
574 doblank--;
575 continue;
576 }
577 break;
578 }
579
580 /* check for end of word or IFS separation */
581 if (c == 0 || (!quote && (f & DOBLANK(1<<(0))) && doblank &&
582 !make_magic && ctype(c, C_IFS)!!(ctypes[(unsigned char)(c)]&((1<<(7)))))) {
583 /* How words are broken up:
584 * | value of c
585 * word | ws nws 0
586 * -----------------------------------
587 * IFS_WORD w/WS w/NWS w
588 * IFS_WS -/WS w/NWS -
589 * IFS_NWS -/NWS w/NWS -
590 * IFS_IWS -/WS w/NWS -
591 * (w means generate a word)
592 */
593 if ((word == IFS_WORD0) || (word == IFS_QUOTE4) || (c &&
594 (word == IFS_IWS3 || word == IFS_NWS2) &&
595 !ctype(c, C_IFSWS)!!(ctypes[(unsigned char)(c)]&((1<<(4)))))) {
596 char *p;
597 emit_word:
598 *dp++ = '\0';
599 p = Xclose(ds, dp)aresize((ds).beg, ((dp) - (ds).beg), (ds).areap);
600 if (fdo & DOBRACE_(1<<(6)))
601 /* also does globbing */
602 alt_expand(wp, p, p,
603 p + Xlength(ds, (dp - 1))(((dp - 1)) - (ds).beg),
604 fdo | (f & DOMARKDIRS(1<<(10))));
605 else if (fdo & DOGLOB(1<<(1)))
606 glob(p, wp, f & DOMARKDIRS(1<<(10)));
607 else if ((f & DOPAT(1<<(2))) || !(fdo & DOMAGIC_(1<<(7))))
608 XPput(*wp, p)do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (p); } while (0)
;
609 else
610 XPput(*wp, debunk(p, p, strlen(p) + 1))do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (debunk(p, p, strlen(
p) + 1)); } while (0)
;
611 fdo = 0;
612 saw_eq = 0;
613 tilde_ok = (f & (DOTILDE(1<<(3))|DOASNTILDE(1<<(5)))) ? 1 : 0;
614 if (c != 0)
615 Xinit(ds, dp, 128, ATEMP)do { (ds).len = 128; (ds).areap = (&genv->area); (ds).
beg = alloc((ds).len + 8, (ds).areap); (ds).end = (ds).beg + (
ds).len; dp = (ds).beg; } while (0)
;
616 }
617 if (c == 0)
618 goto done;
619 if (word != IFS_NWS2)
620 word = ctype(c, C_IFSWS)!!(ctypes[(unsigned char)(c)]&((1<<(4)))) ? IFS_WS1 : IFS_NWS2;
621 } else {
622 if (type == XSUB1) {
623 if (word == IFS_NWS2 &&
624 Xlength(ds, dp)((dp) - (ds).beg) == 0) {
625 char *p;
626
627 if ((p = strdup("")) == NULL((void *)0))
628 internal_errorf("unable "
629 "to allocate memory");
630 XPput(*wp, p)do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (p); } while (0)
;
631 }
632 type = XSUBMID6;
633 }
634
635 /* age tilde_ok info - ~ code tests second bit */
636 tilde_ok <<= 1;
637 /* mark any special second pass chars */
638 if (!quote)
639 switch (c) {
640 case '[':
641 case '!':
642 case '-':
643 case ']':
644 /* For character classes - doesn't hurt
645 * to have magic !,-,]'s outside of
646 * [...] expressions.
647 */
648 if (f & (DOPAT(1<<(2)) | DOGLOB(1<<(1)))) {
649 fdo |= DOMAGIC_(1<<(7));
650 if (c == '[')
651 fdo |= f & DOGLOB(1<<(1));
652 *dp++ = MAGIC(7);
653 }
654 break;
655 case '*':
656 case '?':
657 if (f & (DOPAT(1<<(2)) | DOGLOB(1<<(1)))) {
658 fdo |= DOMAGIC_(1<<(7)) | (f & DOGLOB(1<<(1)));
659 *dp++ = MAGIC(7);
660 }
661 break;
662 case OBRACE'{':
663 case ',':
664 case CBRACE'}':
665 if ((f & DOBRACE_(1<<(6))) && (c == OBRACE'{' ||
666 (fdo & DOBRACE_(1<<(6))))) {
667 fdo |= DOBRACE_(1<<(6))|DOMAGIC_(1<<(7));
668 *dp++ = MAGIC(7);
669 }
670 break;
671 case '=':
672 /* Note first unquoted = for ~ */
673 if (!(f & DOTEMP_(1<<(8))) && !saw_eq) {
674 saw_eq = 1;
675 tilde_ok = 1;
676 }
677 break;
678 case ':': /* : */
679 /* Note unquoted : for ~ */
680 if (!(f & DOTEMP_(1<<(8))) && (f & DOASNTILDE(1<<(5))))
681 tilde_ok = 1;
682 break;
683 case '~':
684 /* tilde_ok is reset whenever
685 * any of ' " $( $(( ${ } are seen.
686 * Note that tilde_ok must be preserved
687 * through the sequence ${A=a=}~
688 */
689 if (type == XBASE0 &&
690 (f & (DOTILDE(1<<(3))|DOASNTILDE(1<<(5)))) &&
691 (tilde_ok & 2)) {
692 char *p, *dp_x;
693
694 dp_x = dp;
695 p = maybe_expand_tilde(sp,
696 &ds, &dp_x,
697 f & DOASNTILDE(1<<(5)));
698 if (p) {
699 if (dp != dp_x)
700 word = IFS_WORD0;
701 dp = dp_x;
702 sp = p;
703 continue;
704 }
705 }
706 break;
707 }
708 else
709 quote &= ~2; /* undo temporary */
710
711 if (make_magic) {
712 make_magic = 0;
713 fdo |= DOMAGIC_(1<<(7)) | (f & DOGLOB(1<<(1)));
714 *dp++ = MAGIC(7);
715 } else if (ISMAGIC(c)((unsigned char)(c) == (7))) {
716 fdo |= DOMAGIC_(1<<(7));
717 *dp++ = MAGIC(7);
718 }
719 *dp++ = c; /* save output char */
720 word = IFS_WORD0;
721 }
722 }
723
724done:
725 for (st = &st_head; st != NULL((void *)0); st = st->next) {
726 if (st->var == NULL((void *)0) || (st->var->flag & RDONLY(1<<(10))) == 0)
727 continue;
728
729 afree(st->var, ATEMP&genv->area);
730 }
731}
732
733/*
734 * Prepare to generate the string returned by ${} substitution.
735 */
736static int
737varsub(Expand *xp, char *sp, char *word,
738 int *stypep, /* becomes qualifier type */
739 int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
740{
741 int c;
742 int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
743 int stype; /* substitution type */
744 int slen;
745 char *p;
746 struct tbl *vp;
747 int zero_ok = 0;
748
749 if (sp[0] == '\0') /* Bad variable name */
750 return -1;
751
752 xp->var = NULL((void *)0);
753
754 /* ${#var}, string length or array size */
755 if (sp[0] == '#' && (c = sp[1]) != '\0') {
756 /* Can't have any modifiers for ${#...} */
757 if (*word != CSUBST8)
758 return -1;
759 sp++;
760 /* Check for size of array */
761 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
762 int n = 0;
763
764 vp = global(arrayname(sp));
765 if (vp->flag & (ISSET(1<<(2))|ARRAY(1<<(13))))
766 zero_ok = 1;
767 for (; vp; vp = vp->u.array)
768 if (vp->flag & ISSET(1<<(2)))
769 n++;
770 c = n; /* ksh88/ksh93 go for number, not max index */
771 } else if (c == '*' || c == '@')
772 c = genv->loc->argc;
773 else {
774 p = str_val(global(sp));
775 zero_ok = p != null;
776 c = strlen(p);
777 }
778 if (Flag(FNOUNSET)(shell_flags[(int) (FNOUNSET)]) && c == 0 && !zero_ok)
779 errorf("%s: parameter not set", sp);
780 *stypep = 0; /* unqualified variable/string substitution */
781 xp->str = str_save(u64ton((uint64_t)c, 10), ATEMP&genv->area);
782 return XSUB1;
783 }
784
785 /* Check for qualifiers in word part */
786 stype = 0;
787 c = word[slen = 0] == CHAR1 ? word[1] : 0;
788 if (c == ':') {
789 slen += 2;
790 stype = 0x80;
791 c = word[slen + 0] == CHAR1 ? word[slen + 1] : 0;
792 }
793 if (ctype(c, C_SUBOP1)!!(ctypes[(unsigned char)(c)]&((1<<(5))))) {
794 slen += 2;
795 stype |= c;
796 } else if (ctype(c, C_SUBOP2)!!(ctypes[(unsigned char)(c)]&((1<<(6))))) { /* Note: ksh88 allows :%, :%%, etc */
797 slen += 2;
798 stype = c;
799 if (word[slen + 0] == CHAR1 && c == word[slen + 1]) {
800 stype |= 0x80;
801 slen += 2;
802 }
803 } else if (stype) /* : is not ok */
804 return -1;
805 if (!stype && *word != CSUBST8)
806 return -1;
807 *stypep = stype;
808 *slenp = slen;
809
810 c = sp[0];
811 if (c == '*' || c == '@') {
812 switch (stype & 0x7f) {
813 case '=': /* can't assign to a vector */
814 case '%': /* can't trim a vector (yet) */
815 case '#':
816 return -1;
817 }
818 if (genv->loc->argc == 0) {
819 xp->str = null;
820 xp->var = global(sp);
821 state = c == '@' ? XNULLSUB5 : XSUB1;
822 } else {
823 xp->u.strv = (const char **) genv->loc->argv + 1;
824 xp->str = *xp->u.strv++;
825 xp->split = c == '@'; /* $@ */
826 state = XARG3;
827 }
828 zero_ok = 1; /* exempt "$@" and "$*" from 'set -u' */
829 } else {
830 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
831 XPtrV wv;
832
833 switch (stype & 0x7f) {
834 case '=': /* can't assign to a vector */
835 case '%': /* can't trim a vector (yet) */
836 case '#':
837 case '?':
838 return -1;
839 }
840 XPinit(wv, 32)do { void **vp__; vp__ = areallocarray(((void *)0), 32, sizeof
(void *), &genv->area); (wv).cur = (wv).beg = vp__; (wv
).end = vp__ + 32; } while (0)
;
841 vp = global(arrayname(sp));
842 for (; vp; vp = vp->u.array) {
843 if (!(vp->flag&ISSET(1<<(2))))
844 continue;
845 XPput(wv, str_val(vp))do { if ((wv).cur >= (wv).end) { int n = ((wv).cur - (wv).
beg); (wv).beg = areallocarray((wv).beg, n, 2 * sizeof(void *
), &genv->area); (wv).cur = (wv).beg + n; (wv).end = (
wv).cur + n; } *(wv).cur++ = (str_val(vp)); } while (0)
;
846 }
847 if (XPsize(wv)((wv).cur - (wv).beg) == 0) {
848 xp->str = null;
849 state = p[1] == '@' ? XNULLSUB5 : XSUB1;
850 XPfree(wv)afree((wv).beg, &genv->area);
851 } else {
852 XPput(wv, 0)do { if ((wv).cur >= (wv).end) { int n = ((wv).cur - (wv).
beg); (wv).beg = areallocarray((wv).beg, n, 2 * sizeof(void *
), &genv->area); (wv).cur = (wv).beg + n; (wv).end = (
wv).cur + n; } *(wv).cur++ = (0); } while (0)
;
853 xp->u.strv = (const char **) XPptrv(wv)((wv).beg);
854 xp->str = *xp->u.strv++;
855 xp->split = p[1] == '@'; /* ${foo[@]} */
856 state = XARG3;
857 }
858 } else {
859 /* Can't assign things like $! or $1 */
860 if ((stype & 0x7f) == '=' &&
861 (ctype(*sp, C_VAR1)!!(ctypes[(unsigned char)(*sp)]&((1<<(3)))) || digit(*sp)isdigit((unsigned char)(*sp))))
862 return -1;
863 xp->var = global(sp);
864 xp->str = str_val(xp->var);
865 state = XSUB1;
866 }
867 }
868
869 c = stype&0x7f;
870 /* test the compiler's code generator */
871 if (ctype(c, C_SUBOP2)!!(ctypes[(unsigned char)(c)]&((1<<(6)))) ||
872 (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
873 c == '=' || c == '-' || c == '?' : c == '+'))
874 state = XBASE0; /* expand word instead of variable value */
875 if (Flag(FNOUNSET)(shell_flags[(int) (FNOUNSET)]) && xp->str == null && !zero_ok &&
876 (ctype(c, C_SUBOP2)!!(ctypes[(unsigned char)(c)]&((1<<(6)))) || (state != XBASE0 && c != '+')))
877 errorf("%s: parameter not set", sp);
878 return state;
879}
880
881/*
882 * Run the command in $(...) and read its output.
883 */
884static int
885comsub(Expand *xp, char *cp)
886{
887 Source *s, *sold;
888 struct op *t;
889 struct shf *shf;
890
891 s = pushs(SSTRING3, ATEMP&genv->area);
892 s->start = s->str = cp;
893 sold = source;
894 t = compile(s);
895 afree(s, ATEMP&genv->area);
896 source = sold;
897
898 if (t == NULL((void *)0))
899 return XBASE0;
900
901 if (t != NULL((void *)0) && t->type == TCOM1 && /* $(<file) */
902 *t->args == NULL((void *)0) && *t->vars == NULL((void *)0) && t->ioact != NULL((void *)0)) {
903 struct ioword *io = *t->ioact;
904 char *name;
905
906 if ((io->flag&IOTYPE0xF) != IOREAD0x1)
907 errorf("funny $() command: %s",
908 snptreef(NULL((void *)0), 32, "%R", io));
909 shf = shf_open(name = evalstr(io->name, DOTILDE(1<<(3))), O_RDONLY0x0000, 0,
910 SHF_MAPHI0x0020|SHF_CLEXEC0x0010);
911 if (shf == NULL((void *)0))
912 warningf(!Flag(FTALKING)(shell_flags[(int) (FTALKING)]),
913 "%s: %s", name, strerror(errno(*__errno())));
914 xp->split = 0; /* no waitlast() */
915 } else {
916 int ofd1, pv[2];
917 openpipe(pv);
918 shf = shf_fdopen(pv[0], SHF_RD0x0001, NULL((void *)0));
919 ofd1 = savefd(1);
920 if (pv[1] != 1) {
921 ksh_dup2(pv[1], 1, false0);
922 close(pv[1]);
923 }
924 execute(t, XFORK(1<<(1))|XXCOM(1<<(5))|XPIPEO(1<<(4)), NULL((void *)0));
925 restfd(1, ofd1);
926 startlast();
927 xp->split = 1; /* waitlast() */
928 }
929
930 xp->u.shf = shf;
931 return XCOM4;
932}
933
934/*
935 * perform #pattern and %pattern substitution in ${}
936 */
937
938static char *
939trimsub(char *str, char *pat, int how)
940{
941 char *end = strchr(str, 0);
942 char *p, c;
943
944 switch (how&0xff) { /* UCHAR_MAX maybe? */
945 case '#': /* shortest at beginning */
946 for (p = str; p <= end; p++) {
947 c = *p; *p = '\0';
948 if (gmatch(str, pat, false0)) {
949 *p = c;
950 return p;
951 }
952 *p = c;
953 }
954 break;
955 case '#'|0x80: /* longest match at beginning */
956 for (p = end; p >= str; p--) {
957 c = *p; *p = '\0';
958 if (gmatch(str, pat, false0)) {
959 *p = c;
960 return p;
961 }
962 *p = c;
963 }
964 break;
965 case '%': /* shortest match at end */
966 for (p = end; p >= str; p--) {
967 if (gmatch(p, pat, false0))
968 return str_nsave(str, p - str, ATEMP&genv->area);
969 }
970 break;
971 case '%'|0x80: /* longest match at end */
972 for (p = str; p <= end; p++) {
973 if (gmatch(p, pat, false0))
974 return str_nsave(str, p - str, ATEMP&genv->area);
975 }
976 break;
977 }
978
979 return str; /* no match, return string */
980}
981
982/*
983 * glob
984 * Name derived from V6's /etc/glob, the program that expanded filenames.
985 */
986
987/* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
988static void
989glob(char *cp, XPtrV *wp, int markdirs)
990{
991 int oldsize = XPsize(*wp)((*wp).cur - (*wp).beg);
992
993 if (glob_str(cp, wp, markdirs) == 0)
994 XPput(*wp, debunk(cp, cp, strlen(cp) + 1))do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (debunk(cp, cp, strlen
(cp) + 1)); } while (0)
;
995 else
996 qsortp(XPptrv(*wp)((*wp).beg) + oldsize, (size_t)(XPsize(*wp)((*wp).cur - (*wp).beg) - oldsize),
997 xstrcmp);
998}
999
1000#define GF_NONE0 0
1001#define GF_EXCHECK(1<<(0)) BIT(0)(1<<(0)) /* do existence check on file */
1002#define GF_GLOBBED(1<<(1)) BIT(1)(1<<(1)) /* some globbing has been done */
1003#define GF_MARKDIR(1<<(2)) BIT(2)(1<<(2)) /* add trailing / to directories */
1004
1005/* Apply file globbing to cp and store the matching files in wp. Returns
1006 * the number of matches found.
1007 */
1008int
1009glob_str(char *cp, XPtrV *wp, int markdirs)
1010{
1011 int oldsize = XPsize(*wp)((*wp).cur - (*wp).beg);
1012 XString xs;
1013 char *xp;
1014
1015 Xinit(xs, xp, 256, ATEMP)do { (xs).len = 256; (xs).areap = (&genv->area); (xs).
beg = alloc((xs).len + 8, (xs).areap); (xs).end = (xs).beg + (
xs).len; xp = (xs).beg; } while (0)
;
1016 globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR(1<<(2)) : GF_NONE0);
1017 Xfree(xs, xp)afree((xs).beg, (xs).areap);
1018
1019 return XPsize(*wp)((*wp).cur - (*wp).beg) - oldsize;
1020}
1021
1022static void
1023globit(XString *xs, /* dest string */
1024 char **xpp, /* ptr to dest end */
1025 char *sp, /* source path */
1026 XPtrV *wp, /* output list */
1027 int check) /* GF_* flags */
1028{
1029 char *np; /* next source component */
1030 char *xp = *xpp;
1031 char *se;
1032 char odirsep;
1033
1034 /* This to allow long expansions to be interrupted */
1035 intrcheck();
1036
1037 if (sp == NULL((void *)0)) { /* end of source path */
1038 /* We only need to check if the file exists if a pattern
1039 * is followed by a non-pattern (eg, foo*x/bar; no check
1040 * is needed for foo* since the match must exist) or if
1041 * any patterns were expanded and the markdirs option is set.
1042 * Symlinks make things a bit tricky...
1043 */
1044 if ((check & GF_EXCHECK(1<<(0))) ||
1045 ((check & GF_MARKDIR(1<<(2))) && (check & GF_GLOBBED(1<<(1))))) {
1046#define stat_check()(stat_done ? stat_done : (stat_done = stat(((*xs).beg), &
statb) == -1 ? -1 : 1))
(stat_done ? stat_done : \
1047 (stat_done = stat(Xstring(*xs, xp)((*xs).beg), &statb) == -1 \
1048 ? -1 : 1))
1049 struct stat lstatb, statb;
1050 int stat_done = 0; /* -1: failed, 1 ok */
1051
1052 if (lstat(Xstring(*xs, xp)((*xs).beg), &lstatb) == -1)
1053 return;
1054 /* special case for systems which strip trailing
1055 * slashes from regular files (eg, /etc/passwd/).
1056 * SunOS 4.1.3 does this...
1057 */
1058 if ((check & GF_EXCHECK(1<<(0))) && xp > Xstring(*xs, xp)((*xs).beg) &&
1059 xp[-1] == '/' && !S_ISDIR(lstatb.st_mode)((lstatb.st_mode & 0170000) == 0040000) &&
1060 (!S_ISLNK(lstatb.st_mode)((lstatb.st_mode & 0170000) == 0120000) ||
1061 stat_check()(stat_done ? stat_done : (stat_done = stat(((*xs).beg), &
statb) == -1 ? -1 : 1))
< 0 || !S_ISDIR(statb.st_mode)((statb.st_mode & 0170000) == 0040000)))
1062 return;
1063 /* Possibly tack on a trailing / if there isn't already
1064 * one and if the file is a directory or a symlink to a
1065 * directory
1066 */
1067 if (((check & GF_MARKDIR(1<<(2))) && (check & GF_GLOBBED(1<<(1)))) &&
1068 xp > Xstring(*xs, xp)((*xs).beg) && xp[-1] != '/' &&
1069 (S_ISDIR(lstatb.st_mode)((lstatb.st_mode & 0170000) == 0040000) ||
1070 (S_ISLNK(lstatb.st_mode)((lstatb.st_mode & 0170000) == 0120000) && stat_check()(stat_done ? stat_done : (stat_done = stat(((*xs).beg), &
statb) == -1 ? -1 : 1))
> 0 &&
1071 S_ISDIR(statb.st_mode)((statb.st_mode & 0170000) == 0040000)))) {
1072 *xp++ = '/';
1073 *xp = '\0';
1074 }
1075 }
1076 XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp), ATEMP))do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (str_nsave(((*xs).beg
), ((xp) - (*xs).beg), &genv->area)); } while (0)
;
1077 return;
1078 }
1079
1080 if (xp > Xstring(*xs, xp)((*xs).beg))
1081 *xp++ = '/';
1082 while (*sp == '/') {
1083 Xcheck(*xs, xp)do { ptrdiff_t more = ((xp) + (1)) - (*xs).end; if (more >
0) xp = Xcheck_grow_(&*xs, xp, more); } while (0)
;
1084 *xp++ = *sp++;
1085 }
1086 np = strchr(sp, '/');
1087 if (np != NULL((void *)0)) {
1088 se = np;
1089 odirsep = *np; /* don't assume '/', can be multiple kinds */
1090 *np++ = '\0';
1091 } else {
1092 odirsep = '\0'; /* keep gcc quiet */
1093 se = sp + strlen(sp);
1094 }
1095
1096
1097 /* Check if sp needs globbing - done to avoid pattern checks for strings
1098 * containing MAGIC characters, open ['s without the matching close ],
1099 * etc. (otherwise opendir() will be called which may fail because the
1100 * directory isn't readable - if no globbing is needed, only execute
1101 * permission should be required (as per POSIX)).
1102 */
1103 if (!has_globbing(sp, se)) {
1104 XcheckN(*xs, xp, se - sp + 1)do { ptrdiff_t more = ((xp) + (se - sp + 1)) - (*xs).end; if (
more > 0) xp = Xcheck_grow_(&*xs, xp, more); } while (
0)
;
1105 debunk(xp, sp, Xnleft(*xs, xp)((*xs).end - (xp)));
1106 xp += strlen(xp);
1107 *xpp = xp;
1108 globit(xs, xpp, np, wp, check);
1109 } else {
1110 DIR *dirp;
1111 struct dirent *d;
1112 char *name;
1113 int len;
1114 int prefix_len;
1115
1116 *xp = '\0';
1117 prefix_len = Xlength(*xs, xp)((xp) - (*xs).beg);
1118 dirp = opendir(prefix_len ? Xstring(*xs, xp)((*xs).beg) : ".");
1119 if (dirp == NULL((void *)0))
1120 goto Nodir;
1121 while ((d = readdir(dirp)) != NULL((void *)0)) {
1122 name = d->d_name;
1123 if (name[0] == '.' &&
1124 (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
1125 continue; /* always ignore . and .. */
1126 if ((*name == '.' && *sp != '.') ||
1127 !gmatch(name, sp, true1))
1128 continue;
1129
1130 len = strlen(d->d_name) + 1;
1131 XcheckN(*xs, xp, len)do { ptrdiff_t more = ((xp) + (len)) - (*xs).end; if (more >
0) xp = Xcheck_grow_(&*xs, xp, more); } while (0)
;
1132 memcpy(xp, name, len);
1133 *xpp = xp + len - 1;
1134 globit(xs, xpp, np, wp,
1135 (check & GF_MARKDIR(1<<(2))) | GF_GLOBBED(1<<(1))
1136 | (np ? GF_EXCHECK(1<<(0)) : GF_NONE0));
1137 xp = Xstring(*xs, xp)((*xs).beg) + prefix_len;
1138 }
1139 closedir(dirp);
1140 Nodir:;
1141 }
1142
1143 if (np != NULL((void *)0))
1144 *--np = odirsep;
1145}
1146
1147/* remove MAGIC from string */
1148char *
1149debunk(char *dp, const char *sp, size_t dlen)
1150{
1151 char *d, *s;
1152
1153 if ((s = strchr(sp, MAGIC(7)))) {
1154 size_t slen = s - sp;
1155 if (slen >= dlen)
1156 return dp;
1157 memcpy(dp, sp, slen);
1158 for (d = dp + slen; *s && (d < dp + dlen); s++)
1159 if (!ISMAGIC(*s)((unsigned char)(*s) == (7)) || !(*++s & 0x80) ||
1160 !strchr("*+?@! ", *s & 0x7f))
1161 *d++ = *s;
1162 else {
1163 /* extended pattern operators: *+?@! */
1164 if ((*s & 0x7f) != ' ')
1165 *d++ = *s & 0x7f;
1166 if (d < dp + dlen)
1167 *d++ = '(';
1168 }
1169 *d = '\0';
1170 } else if (dp != sp)
1171 strlcpy(dp, sp, dlen);
1172 return dp;
1173}
1174
1175/* Check if p is an unquoted name, possibly followed by a / or :. If so
1176 * puts the expanded version in *dcp,dp and returns a pointer in p just
1177 * past the name, otherwise returns 0.
1178 */
1179static char *
1180maybe_expand_tilde(char *p, XString *dsp, char **dpp, int isassign)
1181{
1182 XString ts;
1183 char *dp = *dpp;
1184 char *tp, *r;
1185
1186 Xinit(ts, tp, 16, ATEMP)do { (ts).len = 16; (ts).areap = (&genv->area); (ts).beg
= alloc((ts).len + 8, (ts).areap); (ts).end = (ts).beg + (ts
).len; tp = (ts).beg; } while (0)
;
1187 /* : only for DOASNTILDE form */
1188 while (p[0] == CHAR1 && p[1] != '/' && (!isassign || p[1] != ':'))
1189 {
1190 Xcheck(ts, tp)do { ptrdiff_t more = ((tp) + (1)) - (ts).end; if (more > 0
) tp = Xcheck_grow_(&ts, tp, more); } while (0)
;
1191 *tp++ = p[1];
1192 p += 2;
1193 }
1194 *tp = '\0';
1195 r = (p[0] == EOS0 || p[0] == CHAR1 || p[0] == CSUBST8) ?
1196 tilde(Xstring(ts, tp)((ts).beg)) : NULL((void *)0);
1197 Xfree(ts, tp)afree((ts).beg, (ts).areap);
1198 if (r) {
1199 while (*r) {
1200 Xcheck(*dsp, dp)do { ptrdiff_t more = ((dp) + (1)) - (*dsp).end; if (more >
0) dp = Xcheck_grow_(&*dsp, dp, more); } while (0)
;
1201 if (ISMAGIC(*r)((unsigned char)(*r) == (7)))
1202 *dp++ = MAGIC(7);
1203 *dp++ = *r++;
1204 }
1205 *dpp = dp;
1206 r = p;
1207 }
1208 return r;
1209}
1210
1211/*
1212 * tilde expansion
1213 *
1214 * based on a version by Arnold Robbins
1215 */
1216
1217static char *
1218tilde(char *cp)
1219{
1220 char *dp;
1221
1222 if (cp[0] == '\0')
1223 dp = str_val(global("HOME"));
1224 else if (cp[0] == '+' && cp[1] == '\0')
1225 dp = str_val(global("PWD"));
1226 else if (cp[0] == '-' && cp[1] == '\0')
1227 dp = str_val(global("OLDPWD"));
1228 else
1229 dp = homedir(cp);
1230 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1231 if (dp == null)
1232 dp = NULL((void *)0);
1233 return dp;
1234}
1235
1236/*
1237 * map userid to user's home directory.
1238 * note that 4.3's getpw adds more than 6K to the shell,
1239 * and the YP version probably adds much more.
1240 * we might consider our own version of getpwnam() to keep the size down.
1241 */
1242
1243static char *
1244homedir(char *name)
1245{
1246 struct tbl *ap;
1247
1248 ap = ktenter(&homedirs, name, hash(name));
1249 if (!(ap->flag & ISSET(1<<(2)))) {
1250 struct passwd *pw;
1251
1252 pw = getpwnam(name);
1253 if (pw == NULL((void *)0))
1254 return NULL((void *)0);
1255 ap->val.s = str_save(pw->pw_dir, APERM&aperm);
1256 ap->flag |= DEFINED(1<<(1))|ISSET(1<<(2))|ALLOC(1<<(0));
1257 }
1258 return ap->val.s;
1259}
1260
1261static void
1262alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
1263{
1264 int count = 0;
1265 char *brace_start, *brace_end, *comma = NULL((void *)0);
1266 char *field_start;
1267 char *p;
1268
1269 /* search for open brace */
1270 for (p = exp_start; (p = strchr(p, MAGIC(7))) && p[1] != OBRACE'{'; p += 2)
1271 ;
1272 brace_start = p;
1273
1274 /* find matching close brace, if any */
1275 if (p) {
1276 comma = NULL((void *)0);
1277 count = 1;
1278 for (p += 2; *p && count; p++) {
1279 if (ISMAGIC(*p)((unsigned char)(*p) == (7))) {
1280 if (*++p == OBRACE'{')
1281 count++;
1282 else if (*p == CBRACE'}')
1283 --count;
1284 else if (*p == ',' && count == 1)
1285 comma = p;
1286 }
1287 }
1288 }
1289 /* no valid expansions... */
1290 if (!p || count != 0) {
1291 /* Note that given a{{b,c} we do not expand anything (this is
1292 * what at&t ksh does. This may be changed to do the {b,c}
1293 * expansion. }
1294 */
1295 if (fdo & DOGLOB(1<<(1)))
1296 glob(start, wp, fdo & DOMARKDIRS(1<<(10)));
1297 else
1298 XPput(*wp, debunk(start, start, end - start))do { if ((*wp).cur >= (*wp).end) { int n = ((*wp).cur - (*
wp).beg); (*wp).beg = areallocarray((*wp).beg, n, 2 * sizeof(
void *), &genv->area); (*wp).cur = (*wp).beg + n; (*wp
).end = (*wp).cur + n; } *(*wp).cur++ = (debunk(start, start,
end - start)); } while (0)
;
1299 return;
1300 }
1301 brace_end = p;
1302 if (!comma) {
1303 alt_expand(wp, start, brace_end, end, fdo);
1304 return;
1305 }
1306
1307 /* expand expression */
1308 field_start = brace_start + 2;
1309 count = 1;
1310 for (p = brace_start + 2; p != brace_end; p++) {
1311 if (ISMAGIC(*p)((unsigned char)(*p) == (7))) {
1312 if (*++p == OBRACE'{')
1313 count++;
1314 else if ((*p == CBRACE'}' && --count == 0) ||
1315 (*p == ',' && count == 1)) {
1316 char *new;
1317 int l1, l2, l3;
1318
1319 l1 = brace_start - start;
1320 l2 = (p - 1) - field_start;
1321 l3 = end - brace_end;
1322 new = alloc(l1 + l2 + l3 + 1, ATEMP&genv->area);
1323 memcpy(new, start, l1);
1324 memcpy(new + l1, field_start, l2);
1325 memcpy(new + l1 + l2, brace_end, l3);
1326 new[l1 + l2 + l3] = '\0';
1327 alt_expand(wp, new, new + l1,
1328 new + l1 + l2 + l3, fdo);
1329 field_start = p + 1;
1330 }
1331 }
1332 }
1333 return;
1334}
1335
1336/*
1337 * Copy the given variable if it's flagged as read-only.
1338 * Such variables have static storage and only one can therefore be referenced
1339 * at a time.
1340 * This is necessary in order to allow variable expansion expressions to refer
1341 * to multiple read-only variables.
1342 */
1343static struct tbl *
1344varcpy(struct tbl *vp)
1345{
1346 struct tbl *cpy;
1347
1348 if (vp == NULL((void *)0) || (vp->flag & RDONLY(1<<(10))) == 0)
1349 return vp;
1350
1351 cpy = alloc(sizeof(struct tbl), ATEMP&genv->area);
1352 memcpy(cpy, vp, sizeof(struct tbl));
1353 return cpy;
1354}