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 |
520986 |
sold = source; |
|
70 |
260493 |
s = pushs(SWSTR, ATEMP); |
|
71 |
260493 |
s->start = s->str = cp; |
|
72 |
260493 |
source = s; |
|
73 |
✗✓ | 260493 |
if (yylex(ONEWORD) != LWORD) |
74 |
internal_errorf(1, "substitute"); |
||
75 |
260493 |
source = sold; |
|
76 |
260493 |
afree(s, ATEMP); |
|
77 |
260493 |
return evalstr(yylval.cp, f); |
|
78 |
} |
||
79 |
|||
80 |
/* |
||
81 |
* expand arg-list |
||
82 |
*/ |
||
83 |
char ** |
||
84 |
eval(char **ap, int f) |
||
85 |
{ |
||
86 |
11557742 |
XPtrV w; |
|
87 |
|||
88 |
✓✓ | 5778871 |
if (*ap == NULL) |
89 |
2681125 |
return ap; |
|
90 |
3097746 |
XPinit(w, 32); |
|
91 |
✗✓ | 6195492 |
XPput(w, NULL); /* space for shell name */ |
92 |
✓✓ | 29912361 |
while (*ap != NULL) |
93 |
11858715 |
expand(*ap++, &w, f); |
|
94 |
✓✓ | 6194387 |
XPput(w, NULL); |
95 |
3097185 |
return (char **) XPclose(w) + 1; |
|
96 |
5778310 |
} |
|
97 |
|||
98 |
/* |
||
99 |
* expand string |
||
100 |
*/ |
||
101 |
char * |
||
102 |
evalstr(char *cp, int f) |
||
103 |
{ |
||
104 |
6028654 |
XPtrV w; |
|
105 |
|||
106 |
3009039 |
XPinit(w, 1); |
|
107 |
3009039 |
expand(cp, &w, f); |
|
108 |
✓✗ | 9027117 |
cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w); |
109 |
3009039 |
XPfree(w); |
|
110 |
3009039 |
return cp; |
|
111 |
3009039 |
} |
|
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 |
230488 |
XPtrV w; |
|
121 |
|||
122 |
115244 |
XPinit(w, 1); |
|
123 |
115244 |
expand(cp, &w, f); |
|
124 |
✗✓✗ | 115244 |
switch (XPsize(w)) { |
125 |
case 0: |
||
126 |
cp = null; |
||
127 |
break; |
||
128 |
case 1: |
||
129 |
115244 |
cp = (char*) *XPptrv(w); |
|
130 |
115244 |
break; |
|
131 |
default: |
||
132 |
cp = evalstr(cp, f&~DOGLOB); |
||
133 |
break; |
||
134 |
} |
||
135 |
115244 |
XPfree(w); |
|
136 |
115244 |
return cp; |
|
137 |
115244 |
} |
|
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 |
29987154 |
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 |
14993577 |
Expand x = { |
|
163 |
/* expansion variables */ |
||
164 |
NULL, { NULL }, NULL, 0 |
||
165 |
}; |
||
166 |
14993577 |
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 |
✗✓ | 14993577 |
if (cp == NULL) |
173 |
internal_errorf(1, "expand(NULL)"); |
||
174 |
/* for alias, readonly, set, typeset commands */ |
||
175 |
✓✓✓✓ |
15761986 |
if ((f & DOVACHECK) && is_wdvarassign(cp)) { |
176 |
377391 |
f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); |
|
177 |
377391 |
f |= DOASNTILDE; |
|
178 |
377391 |
} |
|
179 |
✓✓ | 14993577 |
if (Flag(FNOGLOB)) |
180 |
8734 |
f &= ~DOGLOB; |
|
181 |
✗✓ | 14993577 |
if (Flag(FMARKDIRS)) |
182 |
f |= DOMARKDIRS; |
||
183 |
#ifdef BRACE_EXPAND |
||
184 |
✓✓✓✓ |
29986875 |
if (Flag(FBRACEEXPAND) && (f & DOGLOB)) |
185 |
11472740 |
f |= DOBRACE_; |
|
186 |
#endif /* BRACE_EXPAND */ |
||
187 |
|||
188 |
14993577 |
Xinit(ds, dp, 128, ATEMP); /* init dest. string */ |
|
189 |
type = XBASE; |
||
190 |
sp = cp; |
||
191 |
fdo = 0; |
||
192 |
saw_eq = 0; |
||
193 |
14993577 |
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ |
|
194 |
doblank = 0; |
||
195 |
make_magic = 0; |
||
196 |
14993577 |
word = (f&DOBLANK) ? IFS_WS : IFS_WORD; |
|
197 |
14993577 |
st_head.next = NULL; |
|
198 |
st = &st_head; |
||
199 |
|||
200 |
221406844 |
while (1) { |
|
201 |
✓✓ | 243331821 |
Xcheck(ds, dp); |
202 |
|||
203 |
✓✓✗✓ ✓✓✓✓ |
390194941 |
switch (type) { |
204 |
case XBASE: /* original prefixed string */ |
||
205 |
164542664 |
c = *sp++; |
|
206 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓ |
164542664 |
switch (c) { |
207 |
case EOS: |
||
208 |
c = 0; |
||
209 |
14982440 |
break; |
|
210 |
case CHAR: |
||
211 |
58168971 |
c = *sp++; |
|
212 |
58168971 |
break; |
|
213 |
case QCHAR: |
||
214 |
1320448 |
quote |= 2; /* temporary quote */ |
|
215 |
1320448 |
c = *sp++; |
|
216 |
1320448 |
break; |
|
217 |
case OQUOTE: |
||
218 |
word = IFS_WORD; |
||
219 |
tilde_ok = 0; |
||
220 |
quote = 1; |
||
221 |
3994399 |
continue; |
|
222 |
case CQUOTE: |
||
223 |
quote = 0; |
||
224 |
3993909 |
continue; |
|
225 |
case COMSUB: |
||
226 |
tilde_ok = 0; |
||
227 |
✗✓ | 1369030 |
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 |
1357987 |
type = comsub(&x, sp); |
|
237 |
✓✗✓✓ |
2715974 |
if (type == XCOM && (f&DOBLANK)) |
238 |
5677 |
doblank++; |
|
239 |
1357987 |
sp = strchr(sp, 0) + 1; |
|
240 |
newlines = 0; |
||
241 |
} |
||
242 |
1357987 |
continue; |
|
243 |
case EXPRSUB: |
||
244 |
word = IFS_WORD; |
||
245 |
tilde_ok = 0; |
||
246 |
✗✓ | 130202 |
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 |
130202 |
struct tbl v; |
|
255 |
char *p; |
||
256 |
|||
257 |
130202 |
v.flag = DEFINED|ISSET|INTEGER; |
|
258 |
130202 |
v.type = 10; /* not default */ |
|
259 |
130202 |
v.name[0] = '\0'; |
|
260 |
130202 |
v_evaluate(&v, substitute(sp, 0), |
|
261 |
KSH_UNWIND_ERROR, true); |
||
262 |
130202 |
sp = strchr(sp, 0) + 1; |
|
263 |
✓✓ | 671958 |
for (p = str_val(&v); *p; ) { |
264 |
✗✓ | 205782 |
Xcheck(ds, dp); |
265 |
205782 |
*dp++ = *p++; |
|
266 |
} |
||
267 |
130192 |
} |
|
268 |
130192 |
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 |
5365548 |
char *varname = ++sp; /* skip the { or x (}) */ |
|
277 |
5365548 |
int stype; |
|
278 |
5365548 |
int slen = 0; |
|
279 |
|||
280 |
5365548 |
sp = strchr(sp, '\0') + 1; /* skip variable */ |
|
281 |
5365548 |
type = varsub(&x, varname, sp, &stype, &slen); |
|
282 |
✓✓ | 5365548 |
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 |
✓✓ | 5365476 |
if (f&DOBLANK) |
296 |
3392643 |
doblank++; |
|
297 |
tilde_ok = 0; |
||
298 |
✓✓ | 5365476 |
if (type == XBASE) { /* expand? */ |
299 |
✓✓ | 651685 |
if (!st->next) { |
300 |
SubType *newst; |
||
301 |
|||
302 |
442159 |
newst = alloc( |
|
303 |
442159 |
sizeof(SubType), ATEMP); |
|
304 |
442159 |
newst->next = NULL; |
|
305 |
442159 |
newst->prev = st; |
|
306 |
442159 |
st->next = newst; |
|
307 |
442159 |
} |
|
308 |
651685 |
st = st->next; |
|
309 |
651685 |
st->stype = stype; |
|
310 |
651685 |
st->base = Xsavepos(ds, dp); |
|
311 |
651685 |
st->f = f; |
|
312 |
651685 |
st->var = x.var; |
|
313 |
651685 |
st->quote = quote; |
|
314 |
/* skip qualifier(s) */ |
||
315 |
✓✗ | 651685 |
if (stype) |
316 |
651685 |
sp += slen; |
|
317 |
✗✓✓✓ ✓ |
651691 |
switch (stype & 0x7f) { |
318 |
case '#': |
||
319 |
case '%': |
||
320 |
/* ! DOBLANK,DOBRACE_,DOTILDE */ |
||
321 |
20716 |
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 |
20716 |
*dp++ = MAGIC; |
|
329 |
20716 |
*dp++ = '@' + 0x80U; |
|
330 |
20716 |
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 |
✓✗ | 314305 |
if (!(x.var->flag & INTEGER)) |
346 |
314305 |
f |= DOASNTILDE|DOTILDE; |
|
347 |
314305 |
f |= DOTEMP_; |
|
348 |
/* These will be done after the |
||
349 |
* value has been assigned. |
||
350 |
*/ |
||
351 |
314305 |
f &= ~(DOBLANK|DOGLOB|DOBRACE_); |
|
352 |
tilde_ok = 1; |
||
353 |
314305 |
break; |
|
354 |
case '?': |
||
355 |
6 |
f &= ~DOBLANK; |
|
356 |
6 |
f |= DOTEMP_; |
|
357 |
/* FALLTHROUGH */ |
||
358 |
default: |
||
359 |
/* Enable tilde expansion */ |
||
360 |
tilde_ok = 1; |
||
361 |
316664 |
f |= DOTILDE; |
|
362 |
316664 |
} |
|
363 |
} else |
||
364 |
/* skip word */ |
||
365 |
4713791 |
sp = (char *) wdscan(sp, CSUBST); |
|
366 |
continue; |
||
367 |
5365476 |
} |
|
368 |
case CSUBST: /* only get here if expanding word */ |
||
369 |
651661 |
sp++; /* ({) skip the } or x */ |
|
370 |
tilde_ok = 0; /* in case of ${unset:-} */ |
||
371 |
651661 |
*dp = '\0'; |
|
372 |
651661 |
quote = st->quote; |
|
373 |
651661 |
f = st->f; |
|
374 |
✓✓ | 651661 |
if (f&DOBLANK) |
375 |
764 |
doblank--; |
|
376 |
✗✓✓✓ ✓ |
651661 |
switch (st->stype&0x7f) { |
377 |
case '#': |
||
378 |
case '%': |
||
379 |
/* Append end-pattern */ |
||
380 |
20716 |
*dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; |
|
381 |
20716 |
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 |
41432 |
x.str = trimsub(str_val(st->var), |
|
387 |
20716 |
dp, st->stype); |
|
388 |
✓✓✓✓ |
22030 |
if (x.str[0] != '\0' || st->quote) |
389 |
19502 |
type = XSUB; |
|
390 |
else |
||
391 |
type = XNULLSUB; |
||
392 |
✓✓ | 20716 |
if (f&DOBLANK) |
393 |
588 |
doblank++; |
|
394 |
20716 |
st = st->prev; |
|
395 |
20716 |
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 |
314305 |
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 |
314305 |
len = strlen(dp) + 1; |
|
415 |
628610 |
setstr(st->var, |
|
416 |
314305 |
debunk(alloc(len, ATEMP), |
|
417 |
dp, len), KSH_UNWIND_ERROR); |
||
418 |
314305 |
x.str = str_val(st->var); |
|
419 |
type = XSUB; |
||
420 |
✓✓ | 314305 |
if (f&DOBLANK) |
421 |
16 |
doblank++; |
|
422 |
314305 |
st = st->prev; |
|
423 |
314305 |
continue; |
|
424 |
case '?': |
||
425 |
{ |
||
426 |
6 |
char *s = Xrestpos(ds, dp, st->base); |
|
427 |
|||
428 |
6 |
errorf("%s: %s", st->var->name, |
|
429 |
✗✓ | 6 |
dp == s ? |
430 |
"parameter null or not set" : |
||
431 |
(debunk(s, s, strlen(s) + 1), s)); |
||
432 |
} |
||
433 |
} |
||
434 |
316634 |
st = st->prev; |
|
435 |
type = XBASE; |
||
436 |
316634 |
continue; |
|
437 |
|||
438 |
case OPAT: /* open pattern: *(foo|bar) */ |
||
439 |
/* Next char is the type of pattern */ |
||
440 |
make_magic = 1; |
||
441 |
17658 |
c = *sp++ + 0x80; |
|
442 |
17658 |
break; |
|
443 |
|||
444 |
case SPAT: /* pattern separator (|) */ |
||
445 |
make_magic = 1; |
||
446 |
c = '|'; |
||
447 |
11772 |
break; |
|
448 |
|||
449 |
case CPAT: /* close pattern */ |
||
450 |
make_magic = 1; |
||
451 |
c = /*(*/ ')'; |
||
452 |
17658 |
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 |
✓✓ | 1324 |
if (f&DOBLANK) { |
463 |
134 |
doblank--; |
|
464 |
/* not really correct: x=; "$x$@" should |
||
465 |
* generate a null argument and |
||
466 |
* set A; "${@:+}" shouldn't. |
||
467 |
*/ |
||
468 |
✓✓ | 134 |
if (dp == Xstring(ds, dp)) |
469 |
128 |
word = IFS_WS; |
|
470 |
} |
||
471 |
1324 |
continue; |
|
472 |
|||
473 |
case XSUB: |
||
474 |
case XSUBMID: |
||
475 |
✓✓ | 88175875 |
if ((c = *x.str++) == 0) { |
476 |
type = XBASE; |
||
477 |
✓✓ | 5045194 |
if (f&DOBLANK) |
478 |
3390423 |
doblank--; |
|
479 |
5045194 |
continue; |
|
480 |
} |
||
481 |
break; |
||
482 |
|||
483 |
case XARGSEP: |
||
484 |
type = XARG; |
||
485 |
10923 |
quote = 1; |
|
486 |
case XARG: |
||
487 |
✓✓ | 181352 |
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 |
✓✓✓✓ |
34626 |
if (quote && x.split) |
493 |
12675 |
word = IFS_WORD; |
|
494 |
✓✓ | 17433 |
if ((x.str = *x.u.strv++) == NULL) { |
495 |
type = XBASE; |
||
496 |
✓✓ | 2294 |
if (f&DOBLANK) |
497 |
1902 |
doblank--; |
|
498 |
2294 |
continue; |
|
499 |
} |
||
500 |
15139 |
c = ifs0; |
|
501 |
✓✓ | 15139 |
if (c == 0) { |
502 |
✓✓✓✓ |
72 |
if (quote && !x.split) |
503 |
12 |
continue; |
|
504 |
c = ' '; |
||
505 |
36 |
} |
|
506 |
✓✓✓✓ |
30146 |
if (quote && x.split) { |
507 |
/* terminate word for "$@" */ |
||
508 |
type = XARGSEP; |
||
509 |
quote = 0; |
||
510 |
10923 |
} |
|
511 |
} |
||
512 |
break; |
||
513 |
|||
514 |
case XCOM: |
||
515 |
✓✓ | 64925005 |
if (x.u.shf == NULL) /* $(< ...) failed, fake EOF */ |
516 |
12 |
c = EOF; |
|
517 |
✓✓ | 64924993 |
else if (newlines) { /* Spit out saved nl's */ |
518 |
c = '\n'; |
||
519 |
13845 |
--newlines; |
|
520 |
13845 |
} else { |
|
521 |
✓✓✓✓ |
269013304 |
while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') |
522 |
✓✓ | 2342178 |
if (c == '\n') |
523 |
2341353 |
newlines++; /* Save newlines */ |
|
524 |
✓✓ | 64911148 |
if (newlines && c != EOF) { |
525 |
1332158 |
shf_ungetc(c, x.u.shf); |
|
526 |
c = '\n'; |
||
527 |
1332158 |
--newlines; |
|
528 |
1332158 |
} |
|
529 |
} |
||
530 |
✓✓ | 64925005 |
if (c == EOF) { |
531 |
newlines = 0; |
||
532 |
✓✓ | 1357987 |
if (x.u.shf != NULL) |
533 |
1357975 |
shf_close(x.u.shf); |
|
534 |
✓✓ | 1357987 |
if (x.split) |
535 |
1357975 |
subst_exstat = waitlast(); |
|
536 |
else |
||
537 |
12 |
subst_exstat = (x.u.shf == NULL); |
|
538 |
type = XBASE; |
||
539 |
✓✓ | 1357987 |
if (f&DOBLANK) |
540 |
5677 |
doblank--; |
|
541 |
1357987 |
continue; |
|
542 |
} |
||
543 |
break; |
||
544 |
} |
||
545 |
|||
546 |
/* check for end of word or IFS separation */ |
||
547 |
✓✓✓✓ ✓✓✓✓ |
670459405 |
if (c == 0 || (!quote && (f & DOBLANK) && doblank && |
548 |
124205082 |
!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 |
✓✓ | 15120650 |
if (word == IFS_WORD || |
561 |
✓✓ | 11425 |
(!ctype(c, C_IFSWS) && c && word == IFS_NWS)) { |
562 |
char *p; |
||
563 |
|||
564 |
15109333 |
*dp++ = '\0'; |
|
565 |
15109333 |
p = Xclose(ds, dp); |
|
566 |
#ifdef BRACE_EXPAND |
||
567 |
✓✓ | 15109333 |
if (fdo & DOBRACE_) |
568 |
/* also does globbing */ |
||
569 |
1018 |
alt_expand(wp, p, p, |
|
570 |
1018 |
p + Xlength(ds, (dp - 1)), |
|
571 |
1018 |
fdo | (f & DOMARKDIRS)); |
|
572 |
else |
||
573 |
#endif /* BRACE_EXPAND */ |
||
574 |
✓✓ | 15108315 |
if (fdo & DOGLOB) |
575 |
1339205 |
glob(p, wp, f & DOMARKDIRS); |
|
576 |
✓✓✓✓ |
27499960 |
else if ((f & DOPAT) || !(fdo & DOMAGIC_)) |
577 |
✓✓ | 21035110 |
XPput(*wp, p); |
578 |
else |
||
579 |
✓✓ | 6508047 |
XPput(*wp, debunk(p, p, strlen(p) + 1)); |
580 |
fdo = 0; |
||
581 |
saw_eq = 0; |
||
582 |
15109333 |
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; |
|
583 |
✓✓ | 15109333 |
if (c != 0) |
584 |
133834 |
Xinit(ds, dp, 128, ATEMP); |
|
585 |
15109333 |
} |
|
586 |
✓✓ | 15120650 |
if (c == 0) |
587 |
return; |
||
588 |
✓✓ | 138210 |
if (word != IFS_NWS) |
589 |
138030 |
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; |
|
590 |
} else { |
||
591 |
✓✓ | 206275057 |
if (type == XSUB) { |
592 |
✓✓✓✗ |
4744444 |
if (word == IFS_NWS && |
593 |
48 |
Xlength(ds, dp) == 0) { |
|
594 |
char *p; |
||
595 |
|||
596 |
✗✓ | 48 |
if ((p = strdup("")) == NULL) |
597 |
internal_errorf(1, "unable " |
||
598 |
"to allocate memory"); |
||
599 |
✗✓ | 96 |
XPput(*wp, p); |
600 |
48 |
} |
|
601 |
type = XSUBMID; |
||
602 |
4744396 |
} |
|
603 |
|||
604 |
/* age tilde_ok info - ~ code tests second bit */ |
||
605 |
206275057 |
tilde_ok <<= 1; |
|
606 |
/* mark any special second pass chars */ |
||
607 |
✓✓ | 206275057 |
if (!quote) |
608 |
✗✗✗✓ ✗✓✗✗ ✓✓✓✓ ✓ |
127428185 |
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 |
✓✓ | 4848599 |
if (f & (DOPAT | DOGLOB)) { |
618 |
4682720 |
fdo |= DOMAGIC_; |
|
619 |
✓✓ | 4682720 |
if (c == '[') |
620 |
1354813 |
fdo |= f & DOGLOB; |
|
621 |
4682720 |
*dp++ = MAGIC; |
|
622 |
4682720 |
} |
|
623 |
break; |
||
624 |
case '*': |
||
625 |
case '?': |
||
626 |
✓✓ | 273210 |
if (f & (DOPAT | DOGLOB)) { |
627 |
41436 |
fdo |= DOMAGIC_ | (f & DOGLOB); |
|
628 |
41436 |
*dp++ = MAGIC; |
|
629 |
41436 |
} |
|
630 |
break; |
||
631 |
#ifdef BRACE_EXPAND |
||
632 |
case OBRACE: |
||
633 |
case ',': |
||
634 |
case CBRACE: |
||
635 |
✓✓✓✓ ✓✓ |
21334 |
if ((f & DOBRACE_) && (c == OBRACE || |
636 |
2577 |
(fdo & DOBRACE_))) { |
|
637 |
2886 |
fdo |= DOBRACE_|DOMAGIC_; |
|
638 |
2886 |
*dp++ = MAGIC; |
|
639 |
2886 |
} |
|
640 |
break; |
||
641 |
#endif /* BRACE_EXPAND */ |
||
642 |
case '=': |
||
643 |
/* Note first unquoted = for ~ */ |
||
644 |
✓✓ | 3919804 |
if (!(f & DOTEMP_) && !saw_eq) { |
645 |
saw_eq = 1; |
||
646 |
tilde_ok = 1; |
||
647 |
3895795 |
} |
|
648 |
break; |
||
649 |
case ':': /* : */ |
||
650 |
/* Note unquoted : for ~ */ |
||
651 |
✓✓✓✓ |
107634 |
if (!(f & DOTEMP_) && (f & DOASNTILDE)) |
652 |
34392 |
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 |
✓✓✓✓ |
1755 |
if (type == XBASE && |
661 |
✓✓ | 457 |
(f & (DOTILDE|DOASNTILDE)) && |
662 |
8 |
(tilde_ok & 2)) { |
|
663 |
6 |
char *p, *dp_x; |
|
664 |
|||
665 |
6 |
dp_x = dp; |
|
666 |
6 |
p = maybe_expand_tilde(sp, |
|
667 |
&ds, &dp_x, |
||
668 |
6 |
f & DOASNTILDE); |
|
669 |
✗✓ | 6 |
if (p) { |
670 |
if (dp != dp_x) |
||
671 |
word = IFS_WORD; |
||
672 |
dp = dp_x; |
||
673 |
sp = p; |
||
674 |
continue; |
||
675 |
} |
||
676 |
✗✓ | 12 |
} |
677 |
break; |
||
678 |
} |
||
679 |
else |
||
680 |
87967918 |
quote &= ~2; /* undo temporary */ |
|
681 |
|||
682 |
✓✓ | 206275057 |
if (make_magic) { |
683 |
make_magic = 0; |
||
684 |
47088 |
fdo |= DOMAGIC_ | (f & DOGLOB); |
|
685 |
47088 |
*dp++ = MAGIC; |
|
686 |
✓✓ | 206275057 |
} else if (ISMAGIC(c)) { |
687 |
10671 |
fdo |= DOMAGIC_; |
|
688 |
10671 |
*dp++ = MAGIC; |
|
689 |
10671 |
} |
|
690 |
206332816 |
*dp++ = c; /* save output char */ |
|
691 |
word = IFS_WORD; |
||
692 |
} |
||
693 |
} |
||
694 |
14982440 |
} |
|
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 |
✓✓ | 10731108 |
if (sp[0] == '\0') /* Bad variable name */ |
713 |
6 |
return -1; |
|
714 |
|||
715 |
5365548 |
xp->var = NULL; |
|
716 |
|||
717 |
/* ${#var}, string length or array size */ |
||
718 |
✓✓✓✓ |
5511939 |
if (sp[0] == '#' && (c = sp[1]) != '\0') { |
719 |
/* Can't have any modifiers for ${#...} */ |
||
720 |
✗✓ | 96 |
if (*word != CSUBST) |
721 |
return -1; |
||
722 |
96 |
sp++; |
|
723 |
/* Check for size of array */ |
||
724 |
✓✗✓✓ ✓✗✓✗ |
378 |
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { |
725 |
int n = 0; |
||
726 |
|||
727 |
96 |
vp = global(arrayname(sp)); |
|
728 |
✓✓ | 96 |
if (vp->flag & (ISSET|ARRAY)) |
729 |
51 |
zero_ok = 1; |
|
730 |
✓✓ | 408 |
for (; vp; vp = vp->u.array) |
731 |
✓✓ | 108 |
if (vp->flag & ISSET) |
732 |
57 |
n++; |
|
733 |
c = n; /* ksh88/ksh93 go for number, not max index */ |
||
734 |
✗✗ | 96 |
} 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 |
✗✓ | 96 |
if (Flag(FNOUNSET) && c == 0 && !zero_ok) |
742 |
errorf("%s: parameter not set", sp); |
||
743 |
96 |
*stypep = 0; /* unqualified variable/string substitution */ |
|
744 |
96 |
xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); |
|
745 |
96 |
return XSUB; |
|
746 |
} |
||
747 |
|||
748 |
/* Check for qualifiers in word part */ |
||
749 |
stype = 0; |
||
750 |
✓✓ | 11386441 |
c = word[slen = 0] == CHAR ? word[1] : 0; |
751 |
✓✓ | 5365452 |
if (c == ':') { |
752 |
slen += 2; |
||
753 |
stype = 0x80; |
||
754 |
✓✗ | 10515 |
c = word[slen + 0] == CHAR ? word[slen + 1] : 0; |
755 |
3505 |
} |
|
756 |
✓✓ | 5365452 |
if (ctype(c, C_SUBOP1)) { |
757 |
634755 |
slen += 2; |
|
758 |
634755 |
stype |= c; |
|
759 |
✓✓ | 5365452 |
} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ |
760 |
20716 |
slen += 2; |
|
761 |
stype = c; |
||
762 |
✓✓✓✓ |
32934 |
if (word[slen + 0] == CHAR && c == word[slen + 1]) { |
763 |
11676 |
stype |= 0x80; |
|
764 |
11676 |
slen += 2; |
|
765 |
11676 |
} |
|
766 |
✗✓ | 4709981 |
} else if (stype) /* : is not ok */ |
767 |
return -1; |
||
768 |
✓✓✓✓ |
10075433 |
if (!stype && *word != CSUBST) |
769 |
66 |
return -1; |
|
770 |
5365386 |
*stypep = stype; |
|
771 |
5365386 |
*slenp = slen; |
|
772 |
|||
773 |
5365386 |
c = sp[0]; |
|
774 |
✓✓ | 5365386 |
if (c == '*' || c == '@') { |
775 |
✗✗✗✓ |
1968 |
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 |
✓✓ | 1968 |
if (genv->loc->argc == 0) { |
782 |
138 |
xp->str = null; |
|
783 |
138 |
xp->var = global(sp); |
|
784 |
138 |
state = c == '@' ? XNULLSUB : XSUB; |
|
785 |
138 |
} else { |
|
786 |
1830 |
xp->u.strv = (const char **) genv->loc->argv + 1; |
|
787 |
1830 |
xp->str = *xp->u.strv++; |
|
788 |
1830 |
xp->split = c == '@'; /* $@ */ |
|
789 |
state = XARG; |
||
790 |
} |
||
791 |
zero_ok = 1; /* exempt "$@" and "$*" from 'set -u' */ |
||
792 |
1968 |
} else { |
|
793 |
✓✓✓✓ ✓✓✓✗ |
5364684 |
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { |
794 |
XPtrV wv; |
||
795 |
|||
796 |
✗✗✗✗ ✓ |
464 |
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 |
464 |
XPinit(wv, 32); |
|
804 |
464 |
vp = global(arrayname(sp)); |
|
805 |
✓✓ | 10078 |
for (; vp; vp = vp->u.array) { |
806 |
✓✗ | 4575 |
if (!(vp->flag&ISSET)) |
807 |
continue; |
||
808 |
✗✓ | 9150 |
XPput(wv, str_val(vp)); |
809 |
4575 |
} |
|
810 |
✗✓ | 464 |
if (XPsize(wv) == 0) { |
811 |
xp->str = null; |
||
812 |
state = p[1] == '@' ? XNULLSUB : XSUB; |
||
813 |
XPfree(wv); |
||
814 |
} else { |
||
815 |
✗✓ | 928 |
XPput(wv, 0); |
816 |
464 |
xp->u.strv = (const char **) XPptrv(wv); |
|
817 |
464 |
xp->str = *xp->u.strv++; |
|
818 |
464 |
xp->split = p[1] == '@'; /* ${foo[@]} */ |
|
819 |
state = XARG; |
||
820 |
} |
||
821 |
✓✗ | 464 |
} else { |
822 |
/* Can't assign things like $! or $1 */ |
||
823 |
✓✓✗✓ |
5677627 |
if ((stype & 0x7f) == '=' && |
824 |
✓✗ | 629346 |
(ctype(*sp, C_VAR1) || digit(*sp))) |
825 |
return -1; |
||
826 |
5362954 |
xp->var = global(sp); |
|
827 |
5362954 |
xp->str = str_val(xp->var); |
|
828 |
state = XSUB; |
||
829 |
} |
||
830 |
} |
||
831 |
|||
832 |
5365386 |
c = stype&0x7f; |
|
833 |
/* test the compiler's code generator */ |
||
834 |
✓✓✓✓ ✓✓✓✓ |
20671162 |
if (ctype(c, C_SUBOP2) || |
835 |
✓✓ | 10689340 |
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ |
836 |
✓✓ | 5344670 |
c == '=' || c == '-' || c == '?' : c == '+')) |
837 |
651685 |
state = XBASE; /* expand word instead of variable value */ |
|
838 |
✓✓✓✓ |
5365404 |
if (Flag(FNOUNSET) && xp->str == null && !zero_ok && |
839 |
✓✗✓✗ |
12 |
(ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) |
840 |
errorf("%s: parameter not set", sp); |
||
841 |
5365380 |
return state; |
|
842 |
5365548 |
} |
|
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 |
2738060 |
s = pushs(SSTRING, ATEMP); |
|
855 |
1369030 |
s->start = s->str = cp; |
|
856 |
1369030 |
sold = source; |
|
857 |
1369030 |
t = compile(s); |
|
858 |
1369030 |
afree(s, ATEMP); |
|
859 |
1369030 |
source = sold; |
|
860 |
|||
861 |
✗✓ | 1369030 |
if (t == NULL) |
862 |
return XBASE; |
||
863 |
|||
864 |
✓✗✓✓ ✓✗ |
2738072 |
if (t != NULL && t->type == TCOM && /* $(<file) */ |
865 |
✓✓✓✗ |
193120 |
*t->args == NULL && *t->vars == NULL && t->ioact != NULL) { |
866 |
12 |
struct ioword *io = *t->ioact; |
|
867 |
char *name; |
||
868 |
|||
869 |
✗✓ | 12 |
if ((io->flag&IOTYPE) != IOREAD) |
870 |
errorf("funny $() command: %s", |
||
871 |
snptreef(NULL, 32, "%R", io)); |
||
872 |
12 |
shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, |
|
873 |
SHF_MAPHI|SHF_CLEXEC); |
||
874 |
✓✗ | 12 |
if (shf == NULL) |
875 |
12 |
warningf(!Flag(FTALKING), |
|
876 |
"%s: cannot open $(<) input", name); |
||
877 |
12 |
xp->split = 0; /* no waitlast() */ |
|
878 |
12 |
} else { |
|
879 |
1369018 |
int ofd1, pv[2]; |
|
880 |
1369018 |
openpipe(pv); |
|
881 |
1369018 |
shf = shf_fdopen(pv[0], SHF_RD, NULL); |
|
882 |
1369018 |
ofd1 = savefd(1); |
|
883 |
✓✗ | 1369018 |
if (pv[1] != 1) { |
884 |
1369018 |
ksh_dup2(pv[1], 1, false); |
|
885 |
1369018 |
close(pv[1]); |
|
886 |
1369018 |
} |
|
887 |
1369018 |
execute(t, XFORK|XXCOM|XPIPEO, NULL); |
|
888 |
1369018 |
restfd(1, ofd1); |
|
889 |
1369018 |
startlast(); |
|
890 |
1369018 |
xp->split = 1; /* waitlast() */ |
|
891 |
1369018 |
} |
|
892 |
|||
893 |
1357987 |
xp->u.shf = shf; |
|
894 |
1357987 |
return XCOM; |
|
895 |
1357987 |
} |
|
896 |
|||
897 |
/* |
||
898 |
* perform #pattern and %pattern substitution in ${} |
||
899 |
*/ |
||
900 |
|||
901 |
static char * |
||
902 |
trimsub(char *str, char *pat, int how) |
||
903 |
{ |
||
904 |
52001 |
char *end = strchr(str, 0); |
|
905 |
char *p, c; |
||
906 |
|||
907 |
✓✓✓✓ ✓ |
31285 |
switch (how&0xff) { /* UCHAR_MAX maybe? */ |
908 |
case '#': /* shortest at beginning */ |
||
909 |
✓✓ | 203518 |
for (p = str; p <= end; p++) { |
910 |
93435 |
c = *p; *p = '\0'; |
|
911 |
✓✓ | 93435 |
if (gmatch(str, pat, false)) { |
912 |
*p = c; |
||
913 |
272 |
return p; |
|
914 |
} |
||
915 |
*p = c; |
||
916 |
} |
||
917 |
break; |
||
918 |
case '#'|0x80: /* longest match at beginning */ |
||
919 |
✓✓ | 86980 |
for (p = end; p >= str; p--) { |
920 |
43478 |
c = *p; *p = '\0'; |
|
921 |
✓✓ | 43478 |
if (gmatch(str, pat, false)) { |
922 |
*p = c; |
||
923 |
2924 |
return p; |
|
924 |
} |
||
925 |
*p = c; |
||
926 |
} |
||
927 |
break; |
||
928 |
case '%': /* shortest match at end */ |
||
929 |
✓✓ | 5036 |
for (p = end; p >= str; p--) { |
930 |
✓✓ | 2297 |
if (gmatch(p, pat, false)) |
931 |
223 |
return str_nsave(str, p - str, ATEMP); |
|
932 |
} |
||
933 |
break; |
||
934 |
case '%'|0x80: /* longest match at end */ |
||
935 |
✓✓ | 113652 |
for (p = str; p <= end; p++) { |
936 |
✓✓ | 54814 |
if (gmatch(p, pat, false)) |
937 |
6728 |
return str_nsave(str, p - str, ATEMP); |
|
938 |
} |
||
939 |
break; |
||
940 |
} |
||
941 |
|||
942 |
10569 |
return str; /* no match, return string */ |
|
943 |
20716 |
} |
|
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 |
2678410 |
int oldsize = XPsize(*wp); |
|
955 |
|||
956 |
✓✓ | 1339205 |
if (glob_str(cp, wp, markdirs) == 0) |
957 |
✓✓ | 8639 |
XPput(*wp, debunk(cp, cp, strlen(cp) + 1)); |
958 |
else |
||
959 |
1334888 |
qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), |
|
960 |
xstrcmp); |
||
961 |
1339205 |
} |
|
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 |
2678452 |
int oldsize = XPsize(*wp); |
|
975 |
1339226 |
XString xs; |
|
976 |
1339226 |
char *xp; |
|
977 |
|||
978 |
1339226 |
Xinit(xs, xp, 256, ATEMP); |
|
979 |
1339226 |
globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); |
|
980 |
1339226 |
Xfree(xs, xp); |
|
981 |
|||
982 |
2678452 |
return XPsize(*wp) - oldsize; |
|
983 |
1339226 |
} |
|
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 |
11190884 |
char *xp = *xpp; |
|
994 |
char *se; |
||
995 |
char odirsep; |
||
996 |
|||
997 |
/* This to allow long expansions to be interrupted */ |
||
998 |
5595442 |
intrcheck(); |
|
999 |
|||
1000 |
✓✓ | 5595442 |
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 |
✓✗✓✗ |
4237173 |
if ((check & GF_EXCHECK) || |
1008 |
✓✓ | 4237173 |
((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 |
7 |
struct stat lstatb, statb; |
|
1013 |
int stat_done = 0; /* -1: failed, 1 ok */ |
||
1014 |
|||
1015 |
✗✓ | 7 |
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 |
✗✓✗✗ ✗✗ |
7 |
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 |
✓✗✓✗ ✗✗ |
14 |
if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) && |
1031 |
✓✗✓✗ |
14 |
xp > Xstring(*xs, xp) && xp[-1] != '/' && |
1032 |
✓✓ | 7 |
(S_ISDIR(lstatb.st_mode) || |
1033 |
✗✓✗✗ ✗✗ |
3 |
(S_ISLNK(lstatb.st_mode) && stat_check() > 0 && |
1034 |
S_ISDIR(statb.st_mode)))) { |
||
1035 |
4 |
*xp++ = '/'; |
|
1036 |
4 |
*xp = '\0'; |
|
1037 |
4 |
} |
|
1038 |
✓✗ | 14 |
} |
1039 |
✓✓ | 8496083 |
XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp), ATEMP)); |
1040 |
4237166 |
return; |
|
1041 |
} |
||
1042 |
|||
1043 |
✓✓ | 1358276 |
if (xp > Xstring(*xs, xp)) |
1044 |
19050 |
*xp++ = '/'; |
|
1045 |
✓✓ | 2723986 |
while (*sp == '/') { |
1046 |
✗✓ | 3717 |
Xcheck(*xs, xp); |
1047 |
3717 |
*xp++ = *sp++; |
|
1048 |
} |
||
1049 |
1358276 |
np = strchr(sp, '/'); |
|
1050 |
✓✓ | 1358276 |
if (np != NULL) { |
1051 |
se = np; |
||
1052 |
18770 |
odirsep = *np; /* don't assume '/', can be multiple kinds */ |
|
1053 |
18770 |
*np++ = '\0'; |
|
1054 |
18770 |
} else { |
|
1055 |
odirsep = '\0'; /* keep gcc quiet */ |
||
1056 |
1339506 |
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 |
✓✓ | 1358276 |
if (!has_globbing(sp, se)) { |
1067 |
✗✓ | 1348221 |
XcheckN(*xs, xp, se - sp + 1); |
1068 |
1348221 |
debunk(xp, sp, Xnleft(*xs, xp)); |
|
1069 |
1348221 |
xp += strlen(xp); |
|
1070 |
1348221 |
*xpp = xp; |
|
1071 |
1348221 |
globit(xs, xpp, np, wp, check); |
|
1072 |
1348221 |
} else { |
|
1073 |
DIR *dirp; |
||
1074 |
struct dirent *d; |
||
1075 |
char *name; |
||
1076 |
int len; |
||
1077 |
int prefix_len; |
||
1078 |
|||
1079 |
10055 |
*xp = '\0'; |
|
1080 |
10055 |
prefix_len = Xlength(*xs, xp); |
|
1081 |
✓✓ | 24309 |
dirp = opendir(prefix_len ? Xstring(*xs, xp) : "."); |
1082 |
✓✓ | 10055 |
if (dirp == NULL) |
1083 |
goto Nodir; |
||
1084 |
✓✓ | 6182878 |
while ((d = readdir(dirp)) != NULL) { |
1085 |
3255347 |
name = d->d_name; |
|
1086 |
✓✓✓✗ |
3265115 |
if (name[0] == '.' && |
1087 |
✓✓✓✓ |
39216 |
(name[1] == 0 || (name[1] == '.' && name[2] == 0))) |
1088 |
19536 |
continue; /* always ignore . and .. */ |
|
1089 |
✓✓✗✓ ✓✓ |
6471622 |
if ((*name == '.' && *sp != '.') || |
1090 |
3235739 |
!gmatch(name, sp, true)) |
|
1091 |
327816 |
continue; |
|
1092 |
|||
1093 |
2907995 |
len = strlen(d->d_name) + 1; |
|
1094 |
✗✓ | 2907995 |
XcheckN(*xs, xp, len); |
1095 |
2907995 |
memcpy(xp, name, len); |
|
1096 |
2907995 |
*xpp = xp + len - 1; |
|
1097 |
2907995 |
globit(xs, xpp, np, wp, |
|
1098 |
2907995 |
(check & GF_MARKDIR) | GF_GLOBBED |
|
1099 |
2907995 |
| (np ? GF_EXCHECK : GF_NONE)); |
|
1100 |
2907995 |
xp = Xstring(*xs, xp) + prefix_len; |
|
1101 |
} |
||
1102 |
9768 |
closedir(dirp); |
|
1103 |
Nodir:; |
||
1104 |
} |
||
1105 |
|||
1106 |
✓✓ | 1358276 |
if (np != NULL) |
1107 |
18770 |
*--np = odirsep; |
|
1108 |
6953718 |
} |
|
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 |
✓✓ | 9883824 |
if ((s = strchr(sp, MAGIC))) { |
1117 |
✗✓ | 4576689 |
if (s - sp >= dlen) |
1118 |
return dp; |
||
1119 |
4576689 |
memcpy(dp, sp, s - sp); |
|
1120 |
✓✓✓✗ |
37451133 |
for (d = dp + (s - sp); *s && (d - dp < dlen); s++) |
1121 |
✓✓✓✓ ✗✓ |
14070246 |
if (!ISMAGIC(*s) || !(*++s & 0x80) || |
1122 |
96 |
!strchr("*+?@! ", *s & 0x7f)) |
|
1123 |
9432489 |
*d++ = *s; |
|
1124 |
else { |
||
1125 |
/* extended pattern operators: *+?@! */ |
||
1126 |
✓✓ | 96 |
if ((*s & 0x7f) != ' ') |
1127 |
78 |
*d++ = *s & 0x7f; |
|
1128 |
✓✗ | 96 |
if (d - dp < dlen) |
1129 |
96 |
*d++ = '('; |
|
1130 |
} |
||
1131 |
4576689 |
*d = '\0'; |
|
1132 |
✓✓ | 4941912 |
} else if (dp != sp) |
1133 |
351277 |
strlcpy(dp, sp, dlen); |
|
1134 |
4941912 |
return dp; |
|
1135 |
4941912 |
} |
|
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 |
12 |
XString ts; |
|
1145 |
6 |
char *dp = *dpp; |
|
1146 |
char *tp, *r; |
||
1147 |
|||
1148 |
6 |
Xinit(ts, tp, 16, ATEMP); |
|
1149 |
/* : only for DOASNTILDE form */ |
||
1150 |
✓✓✓✗ ✓✗✓✗ |
42 |
while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':')) |
1151 |
{ |
||
1152 |
✗✓ | 6 |
Xcheck(ts, tp); |
1153 |
6 |
*tp++ = p[1]; |
|
1154 |
6 |
p += 2; |
|
1155 |
} |
||
1156 |
6 |
*tp = '\0'; |
|
1157 |
✗✓✗✗ ✗✗ |
18 |
r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? |
1158 |
6 |
tilde(Xstring(ts, tp)) : NULL; |
|
1159 |
6 |
Xfree(ts, tp); |
|
1160 |
✗✓ | 6 |
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 |
6 |
return r; |
|
1171 |
6 |
} |
|
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 |
✗✓ | 12 |
if (cp[0] == '\0') |
1185 |
dp = str_val(global("HOME")); |
||
1186 |
✗✓✗✗ |
6 |
else if (cp[0] == '+' && cp[1] == '\0') |
1187 |
dp = str_val(global("PWD")); |
||
1188 |
✗✓✗✗ |
6 |
else if (cp[0] == '-' && cp[1] == '\0') |
1189 |
dp = str_val(global("OLDPWD")); |
||
1190 |
else |
||
1191 |
6 |
dp = homedir(cp); |
|
1192 |
/* If HOME, PWD or OLDPWD are not set, don't expand ~ */ |
||
1193 |
✗✓ | 6 |
if (dp == null) |
1194 |
dp = NULL; |
||
1195 |
6 |
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 |
12 |
ap = ktenter(&homedirs, name, hash(name)); |
|
1211 |
✓✗ | 6 |
if (!(ap->flag & ISSET)) { |
1212 |
struct passwd *pw; |
||
1213 |
|||
1214 |
6 |
pw = getpwnam(name); |
|
1215 |
✓✗ | 6 |
if (pw == NULL) |
1216 |
6 |
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 |
6 |
} |
|
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 |
✓✓✗✓ |
9676 |
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 |
✓✓ | 2886 |
if (p) { |
1239 |
comma = NULL; |
||
1240 |
count = 1; |
||
1241 |
✓✓ | 13356 |
for (p += 2; *p && count; p++) { |
1242 |
✓✓ | 5660 |
if (ISMAGIC(*p)) { |
1243 |
✗✓ | 1868 |
if (*++p == OBRACE) |
1244 |
count++; |
||
1245 |
✓✓ | 1868 |
else if (*p == CBRACE) |
1246 |
1018 |
--count; |
|
1247 |
✓✗ | 850 |
else if (*p == ',' && count == 1) |
1248 |
850 |
comma = p; |
|
1249 |
} |
||
1250 |
} |
||
1251 |
} |
||
1252 |
/* no valid expansions... */ |
||
1253 |
✓✓ | 2886 |
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 |
✗✓ | 1868 |
if (fdo & DOGLOB) |
1259 |
glob(start, wp, fdo & DOMARKDIRS); |
||
1260 |
else |
||
1261 |
✗✓ | 3736 |
XPput(*wp, debunk(start, start, end - start)); |
1262 |
1868 |
return; |
|
1263 |
} |
||
1264 |
brace_end = p; |
||
1265 |
✓✓ | 1018 |
if (!comma) { |
1266 |
168 |
alt_expand(wp, start, brace_end, end, fdo); |
|
1267 |
168 |
return; |
|
1268 |
} |
||
1269 |
|||
1270 |
/* expand expression */ |
||
1271 |
850 |
field_start = brace_start + 2; |
|
1272 |
count = 1; |
||
1273 |
✓✓ | 8500 |
for (p = brace_start + 2; p != brace_end; p++) { |
1274 |
✓✓ | 3400 |
if (ISMAGIC(*p)) { |
1275 |
✗✓ | 1700 |
if (*++p == OBRACE) |
1276 |
count++; |
||
1277 |
✓✓✗✓ |
2550 |
else if ((*p == CBRACE && --count == 0) || |
1278 |
✓✗ | 850 |
(*p == ',' && count == 1)) { |
1279 |
char *new; |
||
1280 |
int l1, l2, l3; |
||
1281 |
|||
1282 |
1700 |
l1 = brace_start - start; |
|
1283 |
1700 |
l2 = (p - 1) - field_start; |
|
1284 |
1700 |
l3 = end - brace_end; |
|
1285 |
1700 |
new = alloc(l1 + l2 + l3 + 1, ATEMP); |
|
1286 |
1700 |
memcpy(new, start, l1); |
|
1287 |
1700 |
memcpy(new + l1, field_start, l2); |
|
1288 |
1700 |
memcpy(new + l1 + l2, brace_end, l3); |
|
1289 |
1700 |
new[l1 + l2 + l3] = '\0'; |
|
1290 |
1700 |
alt_expand(wp, new, new + l1, |
|
1291 |
1700 |
new + l1 + l2 + l3, fdo); |
|
1292 |
1700 |
field_start = p + 1; |
|
1293 |
1700 |
} |
|
1294 |
} |
||
1295 |
} |
||
1296 |
850 |
return; |
|
1297 |
2886 |
} |
|
1298 |
#endif /* BRACE_EXPAND */ |
Generated by: GCOVR (Version 3.3) |