GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: eval.c,v 1.50 2016/03/05 12:30:17 czarkoff 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 |
3952 |
{ |
|
67 |
struct source *s, *sold; |
||
68 |
|||
69 |
3952 |
sold = source; |
|
70 |
3952 |
s = pushs(SWSTR, ATEMP); |
|
71 |
3952 |
s->start = s->str = cp; |
|
72 |
3952 |
source = s; |
|
73 |
✗✓ | 3952 |
if (yylex(ONEWORD) != LWORD) |
74 |
internal_errorf(1, "substitute"); |
||
75 |
3952 |
source = sold; |
|
76 |
3952 |
afree(s, ATEMP); |
|
77 |
3952 |
return evalstr(yylval.cp, f); |
|
78 |
} |
||
79 |
|||
80 |
/* |
||
81 |
* expand arg-list |
||
82 |
*/ |
||
83 |
char ** |
||
84 |
eval(char **ap, int f) |
||
85 |
173294 |
{ |
|
86 |
XPtrV w; |
||
87 |
|||
88 |
✓✓ | 173294 |
if (*ap == NULL) |
89 |
20182 |
return ap; |
|
90 |
153112 |
XPinit(w, 32); |
|
91 |
✗✓ | 153112 |
XPput(w, NULL); /* space for shell name */ |
92 |
✓✓ | 688247 |
while (*ap != NULL) |
93 |
382031 |
expand(*ap++, &w, f); |
|
94 |
✓✓ | 153104 |
XPput(w, NULL); |
95 |
153104 |
return (char **) XPclose(w) + 1; |
|
96 |
} |
||
97 |
|||
98 |
/* |
||
99 |
* expand string |
||
100 |
*/ |
||
101 |
char * |
||
102 |
evalstr(char *cp, int f) |
||
103 |
51190 |
{ |
|
104 |
XPtrV w; |
||
105 |
|||
106 |
51190 |
XPinit(w, 1); |
|
107 |
51190 |
expand(cp, &w, f); |
|
108 |
✓✗ | 51188 |
cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w); |
109 |
51188 |
XPfree(w); |
|
110 |
51188 |
return cp; |
|
111 |
} |
||
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 |
7018 |
{ |
|
120 |
XPtrV w; |
||
121 |
|||
122 |
7018 |
XPinit(w, 1); |
|
123 |
7018 |
expand(cp, &w, f); |
|
124 |
✗✓✗ | 7018 |
switch (XPsize(w)) { |
125 |
case 0: |
||
126 |
cp = null; |
||
127 |
break; |
||
128 |
case 1: |
||
129 |
7018 |
cp = (char*) *XPptrv(w); |
|
130 |
7018 |
break; |
|
131 |
default: |
||
132 |
cp = evalstr(cp, f&~DOGLOB); |
||
133 |
break; |
||
134 |
} |
||
135 |
7018 |
XPfree(w); |
|
136 |
7018 |
return cp; |
|
137 |
} |
||
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 |
440241 |
{ |
|
155 |
440241 |
int c = 0; |
|
156 |
int type; /* expansion type */ |
||
157 |
440241 |
int quote = 0; /* quoted */ |
|
158 |
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 |
Expand x = { |
||
163 |
/* expansion variables */ |
||
164 |
NULL, { NULL }, NULL, 0 |
||
165 |
440241 |
}; |
|
166 |
SubType st_head, *st; |
||
167 |
440241 |
int newlines = 0; /* For trailing newlines in COMSUB */ |
|
168 |
int saw_eq, tilde_ok; |
||
169 |
int make_magic; |
||
170 |
size_t len; |
||
171 |
|||
172 |
✗✓ | 440241 |
if (cp == NULL) |
173 |
internal_errorf(1, "expand(NULL)"); |
||
174 |
/* for alias, readonly, set, typeset commands */ |
||
175 |
✓✓✓✓ |
440241 |
if ((f & DOVACHECK) && is_wdvarassign(cp)) { |
176 |
10684 |
f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); |
|
177 |
10684 |
f |= DOASNTILDE; |
|
178 |
} |
||
179 |
✓✓ | 440241 |
if (Flag(FNOGLOB)) |
180 |
559 |
f &= ~DOGLOB; |
|
181 |
✗✓ | 440241 |
if (Flag(FMARKDIRS)) |
182 |
f |= DOMARKDIRS; |
||
183 |
#ifdef BRACE_EXPAND |
||
184 |
✓✓✓✓ |
440241 |
if (Flag(FBRACEEXPAND) && (f & DOGLOB)) |
185 |
328503 |
f |= DOBRACE_; |
|
186 |
#endif /* BRACE_EXPAND */ |
||
187 |
|||
188 |
440241 |
Xinit(ds, dp, 128, ATEMP); /* init dest. string */ |
|
189 |
440241 |
type = XBASE; |
|
190 |
440241 |
sp = cp; |
|
191 |
440241 |
fdo = 0; |
|
192 |
440241 |
saw_eq = 0; |
|
193 |
440241 |
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ |
|
194 |
440241 |
doblank = 0; |
|
195 |
440241 |
make_magic = 0; |
|
196 |
440241 |
word = (f&DOBLANK) ? IFS_WS : IFS_WORD; |
|
197 |
440241 |
st_head.next = NULL; |
|
198 |
440241 |
st = &st_head; |
|
199 |
|||
200 |
while (1) { |
||
201 |
✓✓ | 6336676 |
Xcheck(ds, dp); |
202 |
|||
203 |
✓✓✓✓ ✓✓✗ |
6336676 |
switch (type) { |
204 |
case XBASE: /* original prefixed string */ |
||
205 |
2794427 |
c = *sp++; |
|
206 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓ |
2794427 |
switch (c) { |
207 |
case EOS: |
||
208 |
440231 |
c = 0; |
|
209 |
440231 |
break; |
|
210 |
case CHAR: |
||
211 |
1979735 |
c = *sp++; |
|
212 |
1979735 |
break; |
|
213 |
case QCHAR: |
||
214 |
103195 |
quote |= 2; /* temporary quote */ |
|
215 |
103195 |
c = *sp++; |
|
216 |
103195 |
break; |
|
217 |
case OQUOTE: |
||
218 |
41207 |
word = IFS_WORD; |
|
219 |
41207 |
tilde_ok = 0; |
|
220 |
41207 |
quote = 1; |
|
221 |
41207 |
continue; |
|
222 |
case CQUOTE: |
||
223 |
41205 |
quote = 0; |
|
224 |
41205 |
continue; |
|
225 |
case COMSUB: |
||
226 |
2446 |
tilde_ok = 0; |
|
227 |
✗✓ | 2446 |
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 |
2446 |
type = comsub(&x, sp); |
|
237 |
✓✗✓✓ |
2446 |
if (type == XCOM && (f&DOBLANK)) |
238 |
925 |
doblank++; |
|
239 |
2446 |
sp = strchr(sp, 0) + 1; |
|
240 |
2446 |
newlines = 0; |
|
241 |
} |
||
242 |
continue; |
||
243 |
case EXPRSUB: |
||
244 |
42 |
word = IFS_WORD; |
|
245 |
42 |
tilde_ok = 0; |
|
246 |
✗✓ | 42 |
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 |
struct tbl v; |
||
255 |
char *p; |
||
256 |
|||
257 |
42 |
v.flag = DEFINED|ISSET|INTEGER; |
|
258 |
42 |
v.type = 10; /* not default */ |
|
259 |
42 |
v.name[0] = '\0'; |
|
260 |
42 |
v_evaluate(&v, substitute(sp, 0), |
|
261 |
KSH_UNWIND_ERROR, true); |
||
262 |
40 |
sp = strchr(sp, 0) + 1; |
|
263 |
✓✓ | 140 |
for (p = str_val(&v); *p; ) { |
264 |
✗✓ | 60 |
Xcheck(ds, dp); |
265 |
60 |
*dp++ = *p++; |
|
266 |
} |
||
267 |
} |
||
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 |
160870 |
char *varname = ++sp; /* skip the { or x (}) */ |
|
277 |
int stype; |
||
278 |
160870 |
int slen = 0; |
|
279 |
|||
280 |
160870 |
sp = strchr(sp, '\0') + 1; /* skip variable */ |
|
281 |
160870 |
type = varsub(&x, varname, sp, &stype, &slen); |
|
282 |
✓✓ | 160868 |
if (type < 0) { |
283 |
char endc; |
||
284 |
char *str, *end; |
||
285 |
|||
286 |
4 |
sp = varname - 2; /* restore sp */ |
|
287 |
4 |
end = (char *) wdscan(sp, CSUBST); |
|
288 |
/* ({) the } or x is already skipped */ |
||
289 |
4 |
endc = *end; |
|
290 |
4 |
*end = EOS; |
|
291 |
4 |
str = snptreef(NULL, 64, "%S", sp); |
|
292 |
4 |
*end = endc; |
|
293 |
4 |
errorf("%s: bad substitution", str); |
|
294 |
} |
||
295 |
✓✓ | 160864 |
if (f&DOBLANK) |
296 |
109340 |
doblank++; |
|
297 |
160864 |
tilde_ok = 0; |
|
298 |
✓✓ | 160864 |
if (type == XBASE) { /* expand? */ |
299 |
✓✓ | 25321 |
if (!st->next) { |
300 |
SubType *newst; |
||
301 |
|||
302 |
18271 |
newst = alloc( |
|
303 |
sizeof(SubType), ATEMP); |
||
304 |
18271 |
newst->next = NULL; |
|
305 |
18271 |
newst->prev = st; |
|
306 |
18271 |
st->next = newst; |
|
307 |
} |
||
308 |
25321 |
st = st->next; |
|
309 |
25321 |
st->stype = stype; |
|
310 |
25321 |
st->base = Xsavepos(ds, dp); |
|
311 |
25321 |
st->f = f; |
|
312 |
25321 |
st->var = x.var; |
|
313 |
25321 |
st->quote = quote; |
|
314 |
/* skip qualifier(s) */ |
||
315 |
✓✗ | 25321 |
if (stype) |
316 |
25321 |
sp += slen; |
|
317 |
✓✓✓✓ |
25321 |
switch (stype & 0x7f) { |
318 |
case '#': |
||
319 |
case '%': |
||
320 |
/* ! DOBLANK,DOBRACE_,DOTILDE */ |
||
321 |
3553 |
f = DOPAT | (f&DONTRUNCOMMAND) | |
|
322 |
DOTEMP_; |
||
323 |
3553 |
quote = 0; |
|
324 |
/* Prepend open pattern (so | |
||
325 |
* in a trim will work as |
||
326 |
* expected) |
||
327 |
*/ |
||
328 |
3553 |
*dp++ = MAGIC; |
|
329 |
3553 |
*dp++ = '@' + 0x80; |
|
330 |
3553 |
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 |
✓✗ | 10577 |
if (!(x.var->flag & INTEGER)) |
346 |
10577 |
f |= DOASNTILDE|DOTILDE; |
|
347 |
10577 |
f |= DOTEMP_; |
|
348 |
/* These will be done after the |
||
349 |
* value has been assigned. |
||
350 |
*/ |
||
351 |
10577 |
f &= ~(DOBLANK|DOGLOB|DOBRACE_); |
|
352 |
10577 |
tilde_ok = 1; |
|
353 |
10577 |
break; |
|
354 |
case '?': |
||
355 |
2 |
f &= ~DOBLANK; |
|
356 |
2 |
f |= DOTEMP_; |
|
357 |
/* FALLTHROUGH */ |
||
358 |
default: |
||
359 |
/* Enable tilde expansion */ |
||
360 |
11191 |
tilde_ok = 1; |
|
361 |
11191 |
f |= DOTILDE; |
|
362 |
} |
||
363 |
} else |
||
364 |
/* skip word */ |
||
365 |
135543 |
sp = (char *) wdscan(sp, CSUBST); |
|
366 |
continue; |
||
367 |
} |
||
368 |
case CSUBST: /* only get here if expanding word */ |
||
369 |
25321 |
sp++; /* ({) skip the } or x */ |
|
370 |
25321 |
tilde_ok = 0; /* in case of ${unset:-} */ |
|
371 |
25321 |
*dp = '\0'; |
|
372 |
25321 |
quote = st->quote; |
|
373 |
25321 |
f = st->f; |
|
374 |
✓✓ | 25321 |
if (f&DOBLANK) |
375 |
584 |
doblank--; |
|
376 |
✓✓✓✓ |
25321 |
switch (st->stype&0x7f) { |
377 |
case '#': |
||
378 |
case '%': |
||
379 |
/* Append end-pattern */ |
||
380 |
3553 |
*dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; |
|
381 |
3553 |
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 |
3553 |
x.str = trimsub(str_val(st->var), |
|
387 |
dp, st->stype); |
||
388 |
✓✓✓✓ |
7098 |
if (x.str[0] != '\0' || st->quote) |
389 |
3545 |
type = XSUB; |
|
390 |
else |
||
391 |
8 |
type = XNULLSUB; |
|
392 |
✓✓ | 3553 |
if (f&DOBLANK) |
393 |
165 |
doblank++; |
|
394 |
3553 |
st = st->prev; |
|
395 |
3553 |
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 |
10577 |
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 |
10577 |
len = strlen(dp) + 1; |
|
415 |
10577 |
setstr(st->var, |
|
416 |
debunk(alloc(len, ATEMP), |
||
417 |
dp, len), KSH_UNWIND_ERROR); |
||
418 |
10577 |
x.str = str_val(st->var); |
|
419 |
10577 |
type = XSUB; |
|
420 |
✓✓ | 10577 |
if (f&DOBLANK) |
421 |
2 |
doblank++; |
|
422 |
10577 |
st = st->prev; |
|
423 |
10577 |
continue; |
|
424 |
case '?': |
||
425 |
{ |
||
426 |
2 |
char *s = Xrestpos(ds, dp, st->base); |
|
427 |
|||
428 |
✗✓ | 2 |
errorf("%s: %s", st->var->name, |
429 |
dp == s ? |
||
430 |
"parameter null or not set" : |
||
431 |
(debunk(s, s, strlen(s) + 1), s)); |
||
432 |
} |
||
433 |
} |
||
434 |
11189 |
st = st->prev; |
|
435 |
11189 |
type = XBASE; |
|
436 |
11189 |
continue; |
|
437 |
|||
438 |
case OPAT: /* open pattern: *(foo|bar) */ |
||
439 |
/* Next char is the type of pattern */ |
||
440 |
58 |
make_magic = 1; |
|
441 |
58 |
c = *sp++ + 0x80; |
|
442 |
58 |
break; |
|
443 |
|||
444 |
case SPAT: /* pattern separator (|) */ |
||
445 |
40 |
make_magic = 1; |
|
446 |
40 |
c = '|'; |
|
447 |
40 |
break; |
|
448 |
|||
449 |
case CPAT: /* close pattern */ |
||
450 |
58 |
make_magic = 1; |
|
451 |
58 |
c = /*(*/ ')'; |
|
452 |
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 |
28 |
type = XBASE; |
|
462 |
✓✓ | 28 |
if (f&DOBLANK) { |
463 |
26 |
doblank--; |
|
464 |
/* not really correct: x=; "$x$@" should |
||
465 |
* generate a null argument and |
||
466 |
* set A; "${@:+}" shouldn't. |
||
467 |
*/ |
||
468 |
✓✓ | 26 |
if (dp == Xstring(ds, dp)) |
469 |
24 |
word = IFS_WS; |
|
470 |
} |
||
471 |
continue; |
||
472 |
|||
473 |
case XSUB: |
||
474 |
case XSUBMID: |
||
475 |
✓✓ | 3003546 |
if ((c = *x.str++) == 0) { |
476 |
149494 |
type = XBASE; |
|
477 |
✓✓ | 149494 |
if (f&DOBLANK) |
478 |
108763 |
doblank--; |
|
479 |
continue; |
||
480 |
} |
||
481 |
break; |
||
482 |
|||
483 |
case XARGSEP: |
||
484 |
227 |
type = XARG; |
|
485 |
227 |
quote = 1; |
|
486 |
case XARG: |
||
487 |
✓✓ | 6353 |
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 |
✓✓✓✓ |
476 |
if (quote && x.split) |
493 |
268 |
word = IFS_WORD; |
|
494 |
✓✓ | 476 |
if ((x.str = *x.u.strv++) == NULL) { |
495 |
151 |
type = XBASE; |
|
496 |
✓✓ | 151 |
if (f&DOBLANK) |
497 |
134 |
doblank--; |
|
498 |
continue; |
||
499 |
} |
||
500 |
325 |
c = ifs0; |
|
501 |
✓✓ | 325 |
if (c == 0) { |
502 |
✓✓✓✓ |
16 |
if (quote && !x.split) |
503 |
4 |
continue; |
|
504 |
12 |
c = ' '; |
|
505 |
} |
||
506 |
✓✓✓✓ |
321 |
if (quote && x.split) { |
507 |
/* terminate word for "$@" */ |
||
508 |
227 |
type = XARGSEP; |
|
509 |
227 |
quote = 0; |
|
510 |
} |
||
511 |
} |
||
512 |
break; |
||
513 |
|||
514 |
case XCOM: |
||
515 |
✓✓ | 532322 |
if (x.u.shf == NULL) /* $(< ...) failed, fake EOF */ |
516 |
4 |
c = EOF; |
|
517 |
✓✓ | 532318 |
else if (newlines) { /* Spit out saved nl's */ |
518 |
930 |
c = '\n'; |
|
519 |
930 |
--newlines; |
|
520 |
} else { |
||
521 |
✓✓✗✓ ✓✓ |
565075 |
while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') |
522 |
✓✗ | 33687 |
if (c == '\n') |
523 |
33687 |
newlines++; /* Save newlines */ |
|
524 |
✓✓ | 531388 |
if (newlines && c != EOF) { |
525 |
30618 |
shf_ungetc(c, x.u.shf); |
|
526 |
30618 |
c = '\n'; |
|
527 |
30618 |
--newlines; |
|
528 |
} |
||
529 |
} |
||
530 |
✓✓ | 532322 |
if (c == EOF) { |
531 |
2446 |
newlines = 0; |
|
532 |
✓✓ | 2446 |
if (x.u.shf != NULL) |
533 |
2442 |
shf_close(x.u.shf); |
|
534 |
✓✓ | 2446 |
if (x.split) |
535 |
2442 |
subst_exstat = waitlast(); |
|
536 |
else |
||
537 |
4 |
subst_exstat = (x.u.shf == NULL); |
|
538 |
2446 |
type = XBASE; |
|
539 |
✓✓ | 2446 |
if (f&DOBLANK) |
540 |
925 |
doblank--; |
|
541 |
continue; |
||
542 |
} |
||
543 |
break; |
||
544 |
} |
||
545 |
|||
546 |
/* check for end of word or IFS separation */ |
||
547 |
✓✓✓✓ ✓✓✓✗ ✓✓ |
5913462 |
if (c == 0 || (!quote && (f & DOBLANK) && doblank && |
548 |
!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 |
✓✓✓✓ ✓✓ |
471266 |
if (word == IFS_WORD || |
561 |
(!ctype(c, C_IFSWS) && c && word == IFS_NWS)) { |
||
562 |
char *p; |
||
563 |
|||
564 |
468222 |
*dp++ = '\0'; |
|
565 |
468222 |
p = Xclose(ds, dp); |
|
566 |
#ifdef BRACE_EXPAND |
||
567 |
✓✓ | 468222 |
if (fdo & DOBRACE_) |
568 |
/* also does globbing */ |
||
569 |
2 |
alt_expand(wp, p, p, |
|
570 |
p + Xlength(ds, (dp - 1)), |
||
571 |
fdo | (f & DOMARKDIRS)); |
||
572 |
else |
||
573 |
#endif /* BRACE_EXPAND */ |
||
574 |
✓✓ | 468220 |
if (fdo & DOGLOB) |
575 |
7785 |
glob(p, wp, f & DOMARKDIRS); |
|
576 |
✓✓✓✓ |
876972 |
else if ((f & DOPAT) || !(fdo & DOMAGIC_)) |
577 |
✓✓ | 416537 |
XPput(*wp, p); |
578 |
else |
||
579 |
✓✓ | 43898 |
XPput(*wp, debunk(p, p, strlen(p) + 1)); |
580 |
468222 |
fdo = 0; |
|
581 |
468222 |
saw_eq = 0; |
|
582 |
468222 |
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; |
|
583 |
✓✓ | 468222 |
if (c != 0) |
584 |
30458 |
Xinit(ds, dp, 128, ATEMP); |
|
585 |
} |
||
586 |
✓✓ | 471266 |
if (c == 0) |
587 |
440231 |
return; |
|
588 |
✓✓ | 31035 |
if (word != IFS_NWS) |
589 |
✓✓ | 31011 |
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; |
590 |
} else { |
||
591 |
✓✓ | 5442196 |
if (type == XSUB) { |
592 |
✓✓✓✗ |
141140 |
if (word == IFS_NWS && |
593 |
Xlength(ds, dp) == 0) { |
||
594 |
char *p; |
||
595 |
|||
596 |
✗✓ | 5 |
if ((p = strdup("")) == NULL) |
597 |
internal_errorf(1, "unable " |
||
598 |
"to allocate memory"); |
||
599 |
✗✓ | 5 |
XPput(*wp, p); |
600 |
} |
||
601 |
141140 |
type = XSUBMID; |
|
602 |
} |
||
603 |
|||
604 |
/* age tilde_ok info - ~ code tests second bit */ |
||
605 |
5442196 |
tilde_ok <<= 1; |
|
606 |
/* mark any special second pass chars */ |
||
607 |
✓✓ | 5442196 |
if (!quote) |
608 |
✓✓✓✓ ✓✓✓ |
3768629 |
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 |
✓✓ | 86993 |
if (f & (DOPAT | DOGLOB)) { |
618 |
77910 |
fdo |= DOMAGIC_; |
|
619 |
✓✓ | 77910 |
if (c == '[') |
620 |
8304 |
fdo |= f & DOGLOB; |
|
621 |
77910 |
*dp++ = MAGIC; |
|
622 |
} |
||
623 |
break; |
||
624 |
case '*': |
||
625 |
case '?': |
||
626 |
✓✓ | 28563 |
if (f & (DOPAT | DOGLOB)) { |
627 |
17391 |
fdo |= DOMAGIC_ | (f & DOGLOB); |
|
628 |
17391 |
*dp++ = MAGIC; |
|
629 |
} |
||
630 |
break; |
||
631 |
#ifdef BRACE_EXPAND |
||
632 |
case OBRACE: |
||
633 |
case ',': |
||
634 |
case CBRACE: |
||
635 |
✓✓✓✓ ✓✓ |
5846 |
if ((f & DOBRACE_) && (c == OBRACE || |
636 |
(fdo & DOBRACE_))) { |
||
637 |
4 |
fdo |= DOBRACE_|DOMAGIC_; |
|
638 |
4 |
*dp++ = MAGIC; |
|
639 |
} |
||
640 |
break; |
||
641 |
#endif /* BRACE_EXPAND */ |
||
642 |
case '=': |
||
643 |
/* Note first unquoted = for ~ */ |
||
644 |
✓✓ | 57346 |
if (!(f & DOTEMP_) && !saw_eq) { |
645 |
51862 |
saw_eq = 1; |
|
646 |
51862 |
tilde_ok = 1; |
|
647 |
} |
||
648 |
break; |
||
649 |
case ':': /* : */ |
||
650 |
/* Note unquoted : for ~ */ |
||
651 |
✓✓ | 3575 |
if (!(f & DOTEMP_) && (f & DOASNTILDE)) |
652 |
480 |
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 |
✓✓✓✗ ✓✗ |
5 |
if (type == XBASE && |
661 |
(f & (DOTILDE|DOASNTILDE)) && |
||
662 |
(tilde_ok & 2)) { |
||
663 |
char *p, *dp_x; |
||
664 |
|||
665 |
2 |
dp_x = dp; |
|
666 |
2 |
p = maybe_expand_tilde(sp, |
|
667 |
&ds, &dp_x, |
||
668 |
f & DOASNTILDE); |
||
669 |
✗✓ | 2 |
if (p) { |
670 |
if (dp != dp_x) |
||
671 |
word = IFS_WORD; |
||
672 |
dp = dp_x; |
||
673 |
sp = p; |
||
674 |
continue; |
||
675 |
} |
||
676 |
} |
||
677 |
break; |
||
678 |
} |
||
679 |
else |
||
680 |
1673567 |
quote &= ~2; /* undo temporary */ |
|
681 |
|||
682 |
✓✓ | 5442196 |
if (make_magic) { |
683 |
156 |
make_magic = 0; |
|
684 |
156 |
fdo |= DOMAGIC_ | (f & DOGLOB); |
|
685 |
156 |
*dp++ = MAGIC; |
|
686 |
✓✓ | 5442040 |
} else if (ISMAGIC(c)) { |
687 |
15 |
fdo |= DOMAGIC_; |
|
688 |
15 |
*dp++ = MAGIC; |
|
689 |
} |
||
690 |
5442196 |
*dp++ = c; /* save output char */ |
|
691 |
5442196 |
word = IFS_WORD; |
|
692 |
} |
||
693 |
} |
||
694 |
} |
||
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 |
160870 |
{ |
|
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 |
160870 |
int zero_ok = 0; |
|
711 |
|||
712 |
✓✓ | 160870 |
if (sp[0] == '\0') /* Bad variable name */ |
713 |
2 |
return -1; |
|
714 |
|||
715 |
160868 |
xp->var = NULL; |
|
716 |
|||
717 |
/* ${#var}, string length or array size */ |
||
718 |
✓✓✓✓ |
160868 |
if (sp[0] == '#' && (c = sp[1]) != '\0') { |
719 |
/* Can't have any modifiers for ${#...} */ |
||
720 |
✗✓ | 8 |
if (*word != CSUBST) |
721 |
return -1; |
||
722 |
8 |
sp++; |
|
723 |
/* Check for size of array */ |
||
724 |
✓✗✓✗ ✓✗ |
16 |
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { |
725 |
8 |
int n = 0; |
|
726 |
|||
727 |
8 |
vp = global(arrayname(sp)); |
|
728 |
✓✓ | 8 |
if (vp->flag & (ISSET|ARRAY)) |
729 |
5 |
zero_ok = 1; |
|
730 |
✓✓ | 12 |
for (; vp; vp = vp->u.array) |
731 |
✓✓ | 12 |
if (vp->flag & ISSET) |
732 |
7 |
n++; |
|
733 |
8 |
c = n; /* ksh88/ksh93 go for number, not max index */ |
|
734 |
} 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 |
✗✓✗✗ |
8 |
if (Flag(FNOUNSET) && c == 0 && !zero_ok) |
742 |
errorf("%s: parameter not set", sp); |
||
743 |
8 |
*stypep = 0; /* unqualified variable/string substitution */ |
|
744 |
8 |
xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); |
|
745 |
8 |
return XSUB; |
|
746 |
} |
||
747 |
|||
748 |
/* Check for qualifiers in word part */ |
||
749 |
160860 |
stype = 0; |
|
750 |
✓✓ | 160860 |
c = word[slen = 0] == CHAR ? word[1] : 0; |
751 |
✓✓ | 160860 |
if (c == ':') { |
752 |
343 |
slen += 2; |
|
753 |
343 |
stype = 0x80; |
|
754 |
✓✗ | 343 |
c = word[slen + 0] == CHAR ? word[slen + 1] : 0; |
755 |
} |
||
756 |
✓✓ | 160860 |
if (ctype(c, C_SUBOP1)) { |
757 |
23123 |
slen += 2; |
|
758 |
23123 |
stype |= c; |
|
759 |
✓✓ | 137737 |
} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ |
760 |
3553 |
slen += 2; |
|
761 |
3553 |
stype = c; |
|
762 |
✓✓✓✓ |
3553 |
if (word[slen + 0] == CHAR && c == word[slen + 1]) { |
763 |
24 |
stype |= 0x80; |
|
764 |
24 |
slen += 2; |
|
765 |
} |
||
766 |
✗✓ | 134184 |
} else if (stype) /* : is not ok */ |
767 |
return -1; |
||
768 |
✓✓✓✓ |
160860 |
if (!stype && *word != CSUBST) |
769 |
2 |
return -1; |
|
770 |
160858 |
*stypep = stype; |
|
771 |
160858 |
*slenp = slen; |
|
772 |
|||
773 |
160858 |
c = sp[0]; |
|
774 |
✓✓ | 160858 |
if (c == '*' || c == '@') { |
775 |
✗✓ | 171 |
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 |
✓✓ | 171 |
if (genv->loc->argc == 0) { |
782 |
28 |
xp->str = null; |
|
783 |
28 |
xp->var = global(sp); |
|
784 |
✓✓ | 28 |
state = c == '@' ? XNULLSUB : XSUB; |
785 |
} else { |
||
786 |
143 |
xp->u.strv = (const char **) genv->loc->argv + 1; |
|
787 |
143 |
xp->str = *xp->u.strv++; |
|
788 |
143 |
xp->split = c == '@'; /* $@ */ |
|
789 |
143 |
state = XARG; |
|
790 |
} |
||
791 |
171 |
zero_ok = 1; /* exempt "$@" and "$*" from 'set -u' */ |
|
792 |
} else { |
||
793 |
✓✓✓✓ ✓✗ |
160687 |
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { |
794 |
XPtrV wv; |
||
795 |
|||
796 |
✗✓ | 8 |
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 |
8 |
XPinit(wv, 32); |
|
804 |
8 |
vp = global(arrayname(sp)); |
|
805 |
✓✓ | 25 |
for (; vp; vp = vp->u.array) { |
806 |
✗✓ | 17 |
if (!(vp->flag&ISSET)) |
807 |
continue; |
||
808 |
✗✓ | 17 |
XPput(wv, str_val(vp)); |
809 |
} |
||
810 |
✗✓ | 8 |
if (XPsize(wv) == 0) { |
811 |
xp->str = null; |
||
812 |
state = p[1] == '@' ? XNULLSUB : XSUB; |
||
813 |
XPfree(wv); |
||
814 |
} else { |
||
815 |
✗✓ | 8 |
XPput(wv, 0); |
816 |
8 |
xp->u.strv = (const char **) XPptrv(wv); |
|
817 |
8 |
xp->str = *xp->u.strv++; |
|
818 |
8 |
xp->split = p[1] == '@'; /* ${foo[@]} */ |
|
819 |
8 |
state = XARG; |
|
820 |
} |
||
821 |
} else { |
||
822 |
/* Can't assign things like $! or $1 */ |
||
823 |
✓✓✓✗ ✗✓ |
160679 |
if ((stype & 0x7f) == '=' && |
824 |
(ctype(*sp, C_VAR1) || digit(*sp))) |
||
825 |
return -1; |
||
826 |
160679 |
xp->var = global(sp); |
|
827 |
160679 |
xp->str = str_val(xp->var); |
|
828 |
160679 |
state = XSUB; |
|
829 |
} |
||
830 |
} |
||
831 |
|||
832 |
160858 |
c = stype&0x7f; |
|
833 |
/* test the compiler's code generator */ |
||
834 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓ |
160858 |
if (ctype(c, C_SUBOP2) || |
835 |
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ |
||
836 |
c == '=' || c == '-' || c == '?' : c == '+')) |
||
837 |
25321 |
state = XBASE; /* expand word instead of variable value */ |
|
838 |
✓✓✓✓ ✓✗✓✗ |
160858 |
if (Flag(FNOUNSET) && xp->str == null && !zero_ok && |
839 |
(ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) |
||
840 |
2 |
errorf("%s: parameter not set", sp); |
|
841 |
160856 |
return state; |
|
842 |
} |
||
843 |
|||
844 |
/* |
||
845 |
* Run the command in $(...) and read its output. |
||
846 |
*/ |
||
847 |
static int |
||
848 |
comsub(Expand *xp, char *cp) |
||
849 |
2446 |
{ |
|
850 |
Source *s, *sold; |
||
851 |
struct op *t; |
||
852 |
struct shf *shf; |
||
853 |
|||
854 |
2446 |
s = pushs(SSTRING, ATEMP); |
|
855 |
2446 |
s->start = s->str = cp; |
|
856 |
2446 |
sold = source; |
|
857 |
2446 |
t = compile(s); |
|
858 |
2446 |
afree(s, ATEMP); |
|
859 |
2446 |
source = sold; |
|
860 |
|||
861 |
✗✓ | 2446 |
if (t == NULL) |
862 |
return XBASE; |
||
863 |
|||
864 |
✓✗✓✓ ✓✓✓✗ ✓✗ |
2450 |
if (t != NULL && t->type == TCOM && /* $(<file) */ |
865 |
*t->args == NULL && *t->vars == NULL && t->ioact != NULL) { |
||
866 |
4 |
struct ioword *io = *t->ioact; |
|
867 |
char *name; |
||
868 |
|||
869 |
✗✓ | 4 |
if ((io->flag&IOTYPE) != IOREAD) |
870 |
errorf("funny $() command: %s", |
||
871 |
snptreef(NULL, 32, "%R", io)); |
||
872 |
4 |
shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, |
|
873 |
SHF_MAPHI|SHF_CLEXEC); |
||
874 |
✓✗ | 4 |
if (shf == NULL) |
875 |
4 |
warningf(!Flag(FTALKING), |
|
876 |
"%s: cannot open $(<) input", name); |
||
877 |
4 |
xp->split = 0; /* no waitlast() */ |
|
878 |
} else { |
||
879 |
int ofd1, pv[2]; |
||
880 |
2442 |
openpipe(pv); |
|
881 |
2442 |
shf = shf_fdopen(pv[0], SHF_RD, NULL); |
|
882 |
2442 |
ofd1 = savefd(1); |
|
883 |
✓✗ | 2442 |
if (pv[1] != 1) { |
884 |
2442 |
ksh_dup2(pv[1], 1, false); |
|
885 |
2442 |
close(pv[1]); |
|
886 |
} |
||
887 |
2442 |
execute(t, XFORK|XXCOM|XPIPEO, NULL); |
|
888 |
2442 |
restfd(1, ofd1); |
|
889 |
2442 |
startlast(); |
|
890 |
2442 |
xp->split = 1; /* waitlast() */ |
|
891 |
} |
||
892 |
|||
893 |
2446 |
xp->u.shf = shf; |
|
894 |
2446 |
return XCOM; |
|
895 |
} |
||
896 |
|||
897 |
/* |
||
898 |
* perform #pattern and %pattern substitution in ${} |
||
899 |
*/ |
||
900 |
|||
901 |
static char * |
||
902 |
trimsub(char *str, char *pat, int how) |
||
903 |
3553 |
{ |
|
904 |
3553 |
char *end = strchr(str, 0); |
|
905 |
char *p, c; |
||
906 |
|||
907 |
✓✓✓✓ ✗ |
3553 |
switch (how&0xff) { /* UCHAR_MAX maybe? */ |
908 |
case '#': /* shortest at beginning */ |
||
909 |
✓✓ | 23810 |
for (p = str; p <= end; p++) { |
910 |
20416 |
c = *p; *p = '\0'; |
|
911 |
✓✓ | 20416 |
if (gmatch(str, pat, false)) { |
912 |
18 |
*p = c; |
|
913 |
18 |
return p; |
|
914 |
} |
||
915 |
20398 |
*p = c; |
|
916 |
} |
||
917 |
break; |
||
918 |
case '#'|0x80: /* longest match at beginning */ |
||
919 |
✓✓ | 46 |
for (p = end; p >= str; p--) { |
920 |
42 |
c = *p; *p = '\0'; |
|
921 |
✓✓ | 42 |
if (gmatch(str, pat, false)) { |
922 |
8 |
*p = c; |
|
923 |
8 |
return p; |
|
924 |
} |
||
925 |
34 |
*p = c; |
|
926 |
} |
||
927 |
break; |
||
928 |
case '%': /* shortest match at end */ |
||
929 |
✓✓ | 664 |
for (p = end; p >= str; p--) { |
930 |
✓✓ | 609 |
if (gmatch(p, pat, false)) |
931 |
62 |
return str_nsave(str, p - str, ATEMP); |
|
932 |
} |
||
933 |
break; |
||
934 |
case '%'|0x80: /* longest match at end */ |
||
935 |
✓✓ | 42 |
for (p = str; p <= end; p++) { |
936 |
✓✓ | 38 |
if (gmatch(p, pat, false)) |
937 |
8 |
return str_nsave(str, p - str, ATEMP); |
|
938 |
} |
||
939 |
break; |
||
940 |
} |
||
941 |
|||
942 |
3457 |
return str; /* no match, return string */ |
|
943 |
} |
||
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 |
7787 |
{ |
|
954 |
7787 |
int oldsize = XPsize(*wp); |
|
955 |
|||
956 |
✓✓ | 7787 |
if (glob_str(cp, wp, markdirs) == 0) |
957 |
✗✓ | 314 |
XPput(*wp, debunk(cp, cp, strlen(cp) + 1)); |
958 |
else |
||
959 |
7473 |
qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), |
|
960 |
xstrcmp); |
||
961 |
7787 |
} |
|
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 |
7815 |
{ |
|
974 |
7815 |
int oldsize = XPsize(*wp); |
|
975 |
XString xs; |
||
976 |
char *xp; |
||
977 |
|||
978 |
7815 |
Xinit(xs, xp, 256, ATEMP); |
|
979 |
✓✓ | 7815 |
globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); |
980 |
7815 |
Xfree(xs, xp); |
|
981 |
|||
982 |
7815 |
return XPsize(*wp) - oldsize; |
|
983 |
} |
||
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 |
15505 |
{ |
|
992 |
char *np; /* next source component */ |
||
993 |
15505 |
char *xp = *xpp; |
|
994 |
char *se; |
||
995 |
char odirsep; |
||
996 |
|||
997 |
/* This to allow long expansions to be interrupted */ |
||
998 |
15505 |
intrcheck(); |
|
999 |
|||
1000 |
✓✓ | 15505 |
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 |
✓✗✓✓ |
7582 |
if ((check & GF_EXCHECK) || |
1008 |
((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 |
struct stat lstatb, statb; |
||
1013 |
6 |
int stat_done = 0; /* -1: failed, 1 ok */ |
|
1014 |
|||
1015 |
✗✓ | 6 |
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 |
✗✓✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ |
6 |
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 |
✓✗✓✗ ✓✗✓✓ ✗✓✗✗ ✗✗✗✗ ✗✗✗✗ |
6 |
if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) && |
1031 |
xp > Xstring(*xs, xp) && xp[-1] != '/' && |
||
1032 |
(S_ISDIR(lstatb.st_mode) || |
||
1033 |
(S_ISLNK(lstatb.st_mode) && stat_check() > 0 && |
||
1034 |
S_ISDIR(statb.st_mode)))) { |
||
1035 |
2 |
*xp++ = '/'; |
|
1036 |
2 |
*xp = '\0'; |
|
1037 |
} |
||
1038 |
} |
||
1039 |
✗✓ | 7582 |
XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp), ATEMP)); |
1040 |
7582 |
return; |
|
1041 |
} |
||
1042 |
|||
1043 |
✓✓ | 7923 |
if (xp > Xstring(*xs, xp)) |
1044 |
108 |
*xp++ = '/'; |
|
1045 |
✓✓ | 7962 |
while (*sp == '/') { |
1046 |
✗✓ | 39 |
Xcheck(*xs, xp); |
1047 |
39 |
*xp++ = *sp++; |
|
1048 |
} |
||
1049 |
7923 |
np = strchr(sp, '/'); |
|
1050 |
✓✓ | 7923 |
if (np != NULL) { |
1051 |
108 |
se = np; |
|
1052 |
108 |
odirsep = *np; /* don't assume '/', can be multiple kinds */ |
|
1053 |
108 |
*np++ = '\0'; |
|
1054 |
} else { |
||
1055 |
7815 |
odirsep = '\0'; /* keep gcc quiet */ |
|
1056 |
7815 |
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 |
✓✓ | 7923 |
if (!has_globbing(sp, se)) { |
1067 |
✗✓ | 7505 |
XcheckN(*xs, xp, se - sp + 1); |
1068 |
7505 |
debunk(xp, sp, Xnleft(*xs, xp)); |
|
1069 |
7505 |
xp += strlen(xp); |
|
1070 |
7505 |
*xpp = xp; |
|
1071 |
7505 |
globit(xs, xpp, np, wp, check); |
|
1072 |
} else { |
||
1073 |
DIR *dirp; |
||
1074 |
struct dirent *d; |
||
1075 |
char *name; |
||
1076 |
int len; |
||
1077 |
int prefix_len; |
||
1078 |
|||
1079 |
/* xp = *xpp; copy_non_glob() may have re-alloc'd xs */ |
||
1080 |
418 |
*xp = '\0'; |
|
1081 |
418 |
prefix_len = Xlength(*xs, xp); |
|
1082 |
✓✓ | 418 |
dirp = opendir(prefix_len ? Xstring(*xs, xp) : "."); |
1083 |
✗✓ | 418 |
if (dirp == NULL) |
1084 |
goto Nodir; |
||
1085 |
✓✓ | 27860 |
while ((d = readdir(dirp)) != NULL) { |
1086 |
27442 |
name = d->d_name; |
|
1087 |
✓✓✓✓ ✓✓✗✓ |
27442 |
if (name[0] == '.' && |
1088 |
(name[1] == 0 || (name[1] == '.' && name[2] == 0))) |
||
1089 |
continue; /* always ignore . and .. */ |
||
1090 |
✓✓✗✓ ✓✓ |
26606 |
if ((*name == '.' && *sp != '.') || |
1091 |
!gmatch(name, sp, true)) |
||
1092 |
continue; |
||
1093 |
|||
1094 |
185 |
len = strlen(d->d_name) + 1; |
|
1095 |
✗✓ | 185 |
XcheckN(*xs, xp, len); |
1096 |
185 |
memcpy(xp, name, len); |
|
1097 |
185 |
*xpp = xp + len - 1; |
|
1098 |
185 |
globit(xs, xpp, np, wp, |
|
1099 |
(check & GF_MARKDIR) | GF_GLOBBED |
||
1100 |
| (np ? GF_EXCHECK : GF_NONE)); |
||
1101 |
185 |
xp = Xstring(*xs, xp) + prefix_len; |
|
1102 |
} |
||
1103 |
418 |
closedir(dirp); |
|
1104 |
7923 |
Nodir:; |
|
1105 |
} |
||
1106 |
|||
1107 |
✓✓ | 7923 |
if (np != NULL) |
1108 |
108 |
*--np = odirsep; |
|
1109 |
} |
||
1110 |
|||
1111 |
#if 0 |
||
1112 |
/* Check if p contains something that needs globbing; if it does, 0 is |
||
1113 |
* returned; if not, p is copied into xs/xp after stripping any MAGICs |
||
1114 |
*/ |
||
1115 |
static int copy_non_glob(XString *xs, char **xpp, char *p); |
||
1116 |
static int |
||
1117 |
copy_non_glob(XString *xs, char **xpp, char *p) |
||
1118 |
{ |
||
1119 |
char *xp; |
||
1120 |
int len = strlen(p); |
||
1121 |
|||
1122 |
XcheckN(*xs, *xpp, len); |
||
1123 |
xp = *xpp; |
||
1124 |
for (; *p; p++) { |
||
1125 |
if (ISMAGIC(*p)) { |
||
1126 |
int c = *++p; |
||
1127 |
|||
1128 |
if (c == '*' || c == '?') |
||
1129 |
return 0; |
||
1130 |
if (*p == '[') { |
||
1131 |
char *q = p + 1; |
||
1132 |
|||
1133 |
if (ISMAGIC(*q) && q[1] == '!') |
||
1134 |
q += 2; |
||
1135 |
if (ISMAGIC(*q) && q[1] == ']') |
||
1136 |
q += 2; |
||
1137 |
for (; *q; q++) |
||
1138 |
if (ISMAGIC(*q) && *++q == ']') |
||
1139 |
return 0; |
||
1140 |
/* pass a literal [ through */ |
||
1141 |
} |
||
1142 |
/* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */ |
||
1143 |
} |
||
1144 |
*xp++ = *p; |
||
1145 |
} |
||
1146 |
*xp = '\0'; |
||
1147 |
*xpp = xp; |
||
1148 |
return 1; |
||
1149 |
} |
||
1150 |
#endif /* 0 */ |
||
1151 |
|||
1152 |
/* remove MAGIC from string */ |
||
1153 |
char * |
||
1154 |
debunk(char *dp, const char *sp, size_t dlen) |
||
1155 |
69194 |
{ |
|
1156 |
char *d, *s; |
||
1157 |
|||
1158 |
✓✓ | 69194 |
if ((s = strchr(sp, MAGIC))) { |
1159 |
✗✓ | 57182 |
if (s - sp >= dlen) |
1160 |
return dp; |
||
1161 |
57182 |
memcpy(dp, sp, s - sp); |
|
1162 |
✓✓✓✗ |
275469 |
for (d = dp + (s - sp); *s && (d - dp < dlen); s++) |
1163 |
✓✓✓✓ ✗✓ |
436542 |
if (!ISMAGIC(*s) || !(*++s & 0x80) || |
1164 |
!strchr("*+?@! ", *s & 0x7f)) |
||
1165 |
218255 |
*d++ = *s; |
|
1166 |
else { |
||
1167 |
/* extended pattern operators: *+?@! */ |
||
1168 |
✓✓ | 32 |
if ((*s & 0x7f) != ' ') |
1169 |
26 |
*d++ = *s & 0x7f; |
|
1170 |
✓✗ | 32 |
if (d - dp < dlen) |
1171 |
32 |
*d++ = '('; |
|
1172 |
} |
||
1173 |
57182 |
*d = '\0'; |
|
1174 |
✓✓ | 12012 |
} else if (dp != sp) |
1175 |
11851 |
strlcpy(dp, sp, dlen); |
|
1176 |
69194 |
return dp; |
|
1177 |
} |
||
1178 |
|||
1179 |
/* Check if p is an unquoted name, possibly followed by a / or :. If so |
||
1180 |
* puts the expanded version in *dcp,dp and returns a pointer in p just |
||
1181 |
* past the name, otherwise returns 0. |
||
1182 |
*/ |
||
1183 |
static char * |
||
1184 |
maybe_expand_tilde(char *p, XString *dsp, char **dpp, int isassign) |
||
1185 |
2 |
{ |
|
1186 |
XString ts; |
||
1187 |
2 |
char *dp = *dpp; |
|
1188 |
char *tp, *r; |
||
1189 |
|||
1190 |
2 |
Xinit(ts, tp, 16, ATEMP); |
|
1191 |
/* : only for DOASNTILDE form */ |
||
1192 |
✓✓✓✗ ✗✓✓✗ |
6 |
while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':')) |
1193 |
{ |
||
1194 |
✗✓ | 2 |
Xcheck(ts, tp); |
1195 |
2 |
*tp++ = p[1]; |
|
1196 |
2 |
p += 2; |
|
1197 |
} |
||
1198 |
2 |
*tp = '\0'; |
|
1199 |
✓✗ | 2 |
r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? |
1200 |
tilde(Xstring(ts, tp)) : NULL; |
||
1201 |
2 |
Xfree(ts, tp); |
|
1202 |
✗✓ | 2 |
if (r) { |
1203 |
while (*r) { |
||
1204 |
Xcheck(*dsp, dp); |
||
1205 |
if (ISMAGIC(*r)) |
||
1206 |
*dp++ = MAGIC; |
||
1207 |
*dp++ = *r++; |
||
1208 |
} |
||
1209 |
*dpp = dp; |
||
1210 |
r = p; |
||
1211 |
} |
||
1212 |
2 |
return r; |
|
1213 |
} |
||
1214 |
|||
1215 |
/* |
||
1216 |
* tilde expansion |
||
1217 |
* |
||
1218 |
* based on a version by Arnold Robbins |
||
1219 |
*/ |
||
1220 |
|||
1221 |
static char * |
||
1222 |
tilde(char *cp) |
||
1223 |
2 |
{ |
|
1224 |
char *dp; |
||
1225 |
|||
1226 |
✗✓ | 2 |
if (cp[0] == '\0') |
1227 |
dp = str_val(global("HOME")); |
||
1228 |
✗✓✗✗ |
2 |
else if (cp[0] == '+' && cp[1] == '\0') |
1229 |
dp = str_val(global("PWD")); |
||
1230 |
✗✓✗✗ |
2 |
else if (cp[0] == '-' && cp[1] == '\0') |
1231 |
dp = str_val(global("OLDPWD")); |
||
1232 |
else |
||
1233 |
2 |
dp = homedir(cp); |
|
1234 |
/* If HOME, PWD or OLDPWD are not set, don't expand ~ */ |
||
1235 |
✗✓ | 2 |
if (dp == null) |
1236 |
dp = NULL; |
||
1237 |
2 |
return dp; |
|
1238 |
} |
||
1239 |
|||
1240 |
/* |
||
1241 |
* map userid to user's home directory. |
||
1242 |
* note that 4.3's getpw adds more than 6K to the shell, |
||
1243 |
* and the YP version probably adds much more. |
||
1244 |
* we might consider our own version of getpwnam() to keep the size down. |
||
1245 |
*/ |
||
1246 |
|||
1247 |
static char * |
||
1248 |
homedir(char *name) |
||
1249 |
2 |
{ |
|
1250 |
struct tbl *ap; |
||
1251 |
|||
1252 |
2 |
ap = ktenter(&homedirs, name, hash(name)); |
|
1253 |
✓✗ | 2 |
if (!(ap->flag & ISSET)) { |
1254 |
struct passwd *pw; |
||
1255 |
|||
1256 |
2 |
pw = getpwnam(name); |
|
1257 |
✓✗ | 2 |
if (pw == NULL) |
1258 |
2 |
return NULL; |
|
1259 |
ap->val.s = str_save(pw->pw_dir, APERM); |
||
1260 |
ap->flag |= DEFINED|ISSET|ALLOC; |
||
1261 |
} |
||
1262 |
return ap->val.s; |
||
1263 |
} |
||
1264 |
|||
1265 |
#ifdef BRACE_EXPAND |
||
1266 |
static void |
||
1267 |
alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo) |
||
1268 |
4 |
{ |
|
1269 |
4 |
int count = 0; |
|
1270 |
4 |
char *brace_start, *brace_end, *comma = NULL; |
|
1271 |
char *field_start; |
||
1272 |
char *p; |
||
1273 |
|||
1274 |
/* search for open brace */ |
||
1275 |
✓✓✓✓ |
4 |
for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2) |
1276 |
; |
||
1277 |
4 |
brace_start = p; |
|
1278 |
|||
1279 |
/* find matching close brace, if any */ |
||
1280 |
✓✓ | 4 |
if (p) { |
1281 |
2 |
comma = NULL; |
|
1282 |
2 |
count = 1; |
|
1283 |
✓✓ | 6 |
for (p += 2; *p && count; p++) { |
1284 |
✓✓ | 4 |
if (ISMAGIC(*p)) { |
1285 |
✗✓ | 2 |
if (*++p == OBRACE) |
1286 |
count++; |
||
1287 |
✓✗ | 2 |
else if (*p == CBRACE) |
1288 |
2 |
--count; |
|
1289 |
else if (*p == ',' && count == 1) |
||
1290 |
comma = p; |
||
1291 |
} |
||
1292 |
} |
||
1293 |
} |
||
1294 |
/* no valid expansions... */ |
||
1295 |
✓✓ | 4 |
if (!p || count != 0) { |
1296 |
/* Note that given a{{b,c} we do not expand anything (this is |
||
1297 |
* what at&t ksh does. This may be changed to do the {b,c} |
||
1298 |
* expansion. } |
||
1299 |
*/ |
||
1300 |
✓✗ | 2 |
if (fdo & DOGLOB) |
1301 |
2 |
glob(start, wp, fdo & DOMARKDIRS); |
|
1302 |
else |
||
1303 |
XPput(*wp, debunk(start, start, end - start)); |
||
1304 |
return; |
||
1305 |
} |
||
1306 |
2 |
brace_end = p; |
|
1307 |
✓✗ | 2 |
if (!comma) { |
1308 |
2 |
alt_expand(wp, start, brace_end, end, fdo); |
|
1309 |
2 |
return; |
|
1310 |
} |
||
1311 |
|||
1312 |
/* expand expression */ |
||
1313 |
field_start = brace_start + 2; |
||
1314 |
count = 1; |
||
1315 |
for (p = brace_start + 2; p != brace_end; p++) { |
||
1316 |
if (ISMAGIC(*p)) { |
||
1317 |
if (*++p == OBRACE) |
||
1318 |
count++; |
||
1319 |
else if ((*p == CBRACE && --count == 0) || |
||
1320 |
(*p == ',' && count == 1)) { |
||
1321 |
char *new; |
||
1322 |
int l1, l2, l3; |
||
1323 |
|||
1324 |
l1 = brace_start - start; |
||
1325 |
l2 = (p - 1) - field_start; |
||
1326 |
l3 = end - brace_end; |
||
1327 |
new = alloc(l1 + l2 + l3 + 1, ATEMP); |
||
1328 |
memcpy(new, start, l1); |
||
1329 |
memcpy(new + l1, field_start, l2); |
||
1330 |
memcpy(new + l1 + l2, brace_end, l3); |
||
1331 |
new[l1 + l2 + l3] = '\0'; |
||
1332 |
alt_expand(wp, new, new + l1, |
||
1333 |
new + l1 + l2 + l3, fdo); |
||
1334 |
field_start = p + 1; |
||
1335 |
} |
||
1336 |
} |
||
1337 |
} |
||
1338 |
return; |
||
1339 |
} |
||
1340 |
#endif /* BRACE_EXPAND */ |
Generated by: GCOVR (Version 3.3) |