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