Bug Summary

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