1 |
/* $OpenBSD: eval.c,v 1.40 2013/09/14 20:09:30 millert Exp $ */ |
2 |
|
3 |
/*- |
4 |
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, |
5 |
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
6 |
* mirabilos <m@mirbsd.org> |
7 |
* |
8 |
* Provided that these terms and disclaimer and all copyright notices |
9 |
* are retained or reproduced in an accompanying document, permission |
10 |
* is granted to deal in this work without restriction, including un- |
11 |
* limited rights to use, publicly perform, distribute, sell, modify, |
12 |
* merge, give away, or sublicence. |
13 |
* |
14 |
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to |
15 |
* the utmost extent permitted by applicable law, neither express nor |
16 |
* implied; without malicious intent or gross negligence. In no event |
17 |
* may a licensor, author or contributor be held liable for indirect, |
18 |
* direct, other damage, loss, or other issues arising in any way out |
19 |
* of dealing in the work, even if advised of the possibility of such |
20 |
* damage or existence of a defect, except proven that it results out |
21 |
* of said person's immediate fault when using the work as intended. |
22 |
*/ |
23 |
|
24 |
#include "sh.h" |
25 |
|
26 |
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $"); |
27 |
|
28 |
/* |
29 |
* string expansion |
30 |
* |
31 |
* first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. |
32 |
* second pass: alternation ({,}), filename expansion (*?[]). |
33 |
*/ |
34 |
|
35 |
/* expansion generator state */ |
36 |
typedef struct { |
37 |
/* not including an "int type;" member, see expand() */ |
38 |
/* string */ |
39 |
const char *str; |
40 |
/* source */ |
41 |
union { |
42 |
/* string[] */ |
43 |
const char **strv; |
44 |
/* file */ |
45 |
struct shf *shf; |
46 |
} u; |
47 |
/* variable in ${var...} */ |
48 |
struct tbl *var; |
49 |
/* split "$@" / call waitlast in $() */ |
50 |
bool split; |
51 |
} Expand; |
52 |
|
53 |
#define XBASE 0 /* scanning original */ |
54 |
#define XSUB 1 /* expanding ${} string */ |
55 |
#define XARGSEP 2 /* ifs0 between "$*" */ |
56 |
#define XARG 3 /* expanding $*, $@ */ |
57 |
#define XCOM 4 /* expanding $() */ |
58 |
#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */ |
59 |
#define XSUBMID 6 /* middle of expanding ${} */ |
60 |
|
61 |
/* States used for field splitting */ |
62 |
#define IFS_WORD 0 /* word has chars (or quotes except "$@") */ |
63 |
#define IFS_WS 1 /* have seen IFS white-space */ |
64 |
#define IFS_NWS 2 /* have seen IFS non-white-space */ |
65 |
#define IFS_IWS 3 /* beginning of word, ignore IFS WS */ |
66 |
#define IFS_QUOTE 4 /* beg.w/quote, become IFS_WORD unless "$@" */ |
67 |
|
68 |
#define STYPE_CHAR 0xFF |
69 |
#define STYPE_DBL 0x100 |
70 |
#define STYPE_AT 0x200 |
71 |
#define STYPE_SINGLE 0x2FF |
72 |
#define STYPE_MASK 0x300 |
73 |
|
74 |
static int varsub(Expand *, const char *, const char *, int *, int *); |
75 |
static int comsub(Expand *, const char *, int); |
76 |
static char *valsub(struct op *, Area *); |
77 |
static char *trimsub(char *, char *, int); |
78 |
static void glob(char *, XPtrV *, bool); |
79 |
static void globit(XString *, char **, char *, XPtrV *, int); |
80 |
static const char *maybe_expand_tilde(const char *, XString *, char **, bool); |
81 |
#ifndef MKSH_NOPWNAM |
82 |
static char *homedir(char *); |
83 |
#endif |
84 |
static void alt_expand(XPtrV *, char *, char *, char *, int); |
85 |
static int utflen(const char *) MKSH_A_PURE; |
86 |
static void utfincptr(const char *, mksh_ari_t *); |
87 |
|
88 |
/* UTFMODE functions */ |
89 |
static int |
90 |
utflen(const char *s) |
91 |
{ |
92 |
size_t n; |
93 |
|
94 |
if (UTFMODE) { |
95 |
n = 0; |
96 |
while (*s) { |
97 |
s += utf_ptradj(s); |
98 |
++n; |
99 |
} |
100 |
} else |
101 |
n = strlen(s); |
102 |
|
103 |
if (n > 2147483647) |
104 |
n = 2147483647; |
105 |
return ((int)n); |
106 |
} |
107 |
|
108 |
static void |
109 |
utfincptr(const char *s, mksh_ari_t *lp) |
110 |
{ |
111 |
const char *cp = s; |
112 |
|
113 |
while ((*lp)--) |
114 |
cp += utf_ptradj(cp); |
115 |
*lp = cp - s; |
116 |
} |
117 |
|
118 |
/* compile and expand word */ |
119 |
char * |
120 |
substitute(const char *cp, int f) |
121 |
{ |
122 |
struct source *s, *sold; |
123 |
|
124 |
sold = source; |
125 |
s = pushs(SWSTR, ATEMP); |
126 |
s->start = s->str = cp; |
127 |
source = s; |
128 |
if (yylex(ONEWORD) != LWORD) |
129 |
internal_errorf(Tbadsubst); |
130 |
source = sold; |
131 |
afree(s, ATEMP); |
132 |
return (evalstr(yylval.cp, f)); |
133 |
} |
134 |
|
135 |
/* |
136 |
* expand arg-list |
137 |
*/ |
138 |
char ** |
139 |
eval(const char **ap, int f) |
140 |
{ |
141 |
XPtrV w; |
142 |
|
143 |
if (*ap == NULL) { |
144 |
union mksh_ccphack vap; |
145 |
|
146 |
vap.ro = ap; |
147 |
return (vap.rw); |
148 |
} |
149 |
XPinit(w, 32); |
150 |
/* space for shell name */ |
151 |
XPput(w, NULL); |
152 |
while (*ap != NULL) |
153 |
expand(*ap++, &w, f); |
154 |
XPput(w, NULL); |
155 |
return ((char **)XPclose(w) + 1); |
156 |
} |
157 |
|
158 |
/* |
159 |
* expand string |
160 |
*/ |
161 |
char * |
162 |
evalstr(const char *cp, int f) |
163 |
{ |
164 |
XPtrV w; |
165 |
char *dp = null; |
166 |
|
167 |
XPinit(w, 1); |
168 |
expand(cp, &w, f); |
169 |
if (XPsize(w)) |
170 |
dp = *XPptrv(w); |
171 |
XPfree(w); |
172 |
return (dp); |
173 |
} |
174 |
|
175 |
/* |
176 |
* expand string - return only one component |
177 |
* used from iosetup to expand redirection files |
178 |
*/ |
179 |
char * |
180 |
evalonestr(const char *cp, int f) |
181 |
{ |
182 |
XPtrV w; |
183 |
char *rv; |
184 |
|
185 |
XPinit(w, 1); |
186 |
expand(cp, &w, f); |
187 |
switch (XPsize(w)) { |
188 |
case 0: |
189 |
rv = null; |
190 |
break; |
191 |
case 1: |
192 |
rv = (char *) *XPptrv(w); |
193 |
break; |
194 |
default: |
195 |
rv = evalstr(cp, f & ~DOGLOB); |
196 |
break; |
197 |
} |
198 |
XPfree(w); |
199 |
return (rv); |
200 |
} |
201 |
|
202 |
/* for nested substitution: ${var:=$var2} */ |
203 |
typedef struct SubType { |
204 |
struct tbl *var; /* variable for ${var..} */ |
205 |
struct SubType *prev; /* old type */ |
206 |
struct SubType *next; /* poped type (to avoid re-allocating) */ |
207 |
size_t base; /* start position of expanded word */ |
208 |
short stype; /* [=+-?%#] action after expanded word */ |
209 |
short f; /* saved value of f (DOPAT, etc) */ |
210 |
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */ |
211 |
uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */ |
212 |
} SubType; |
213 |
|
214 |
void |
215 |
expand( |
216 |
/* input word */ |
217 |
const char *ccp, |
218 |
/* output words */ |
219 |
XPtrV *wp, |
220 |
/* DO* flags */ |
221 |
int f) |
222 |
{ |
223 |
int c = 0; |
224 |
/* expansion type */ |
225 |
int type; |
226 |
/* quoted */ |
227 |
int quote = 0; |
228 |
/* destination string and live pointer */ |
229 |
XString ds; |
230 |
char *dp; |
231 |
/* source */ |
232 |
const char *sp; |
233 |
/* second pass flags */ |
234 |
int fdo; |
235 |
/* have word */ |
236 |
int word; |
237 |
/* field splitting of parameter/command substitution */ |
238 |
int doblank; |
239 |
/* expansion variables */ |
240 |
Expand x = { |
241 |
NULL, { NULL }, NULL, 0 |
242 |
}; |
243 |
SubType st_head, *st; |
244 |
/* record number of trailing newlines in COMSUB */ |
245 |
int newlines = 0; |
246 |
bool saw_eq, make_magic; |
247 |
unsigned int tilde_ok; |
248 |
size_t len; |
249 |
char *cp; |
250 |
|
251 |
if (ccp == NULL) |
252 |
internal_errorf("expand(NULL)"); |
253 |
/* for alias, readonly, set, typeset commands */ |
254 |
if ((f & DOVACHECK) && is_wdvarassign(ccp)) { |
255 |
f &= ~(DOVACHECK | DOBLANK | DOGLOB | DOTILDE); |
256 |
f |= DOASNTILDE | DOSCALAR; |
257 |
} |
258 |
if (Flag(FNOGLOB)) |
259 |
f &= ~DOGLOB; |
260 |
if (Flag(FMARKDIRS)) |
261 |
f |= DOMARKDIRS; |
262 |
if (Flag(FBRACEEXPAND) && (f & DOGLOB)) |
263 |
f |= DOBRACE; |
264 |
|
265 |
/* init destination string */ |
266 |
Xinit(ds, dp, 128, ATEMP); |
267 |
type = XBASE; |
268 |
sp = ccp; |
269 |
fdo = 0; |
270 |
saw_eq = false; |
271 |
/* must be 1/0 */ |
272 |
tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0; |
273 |
doblank = 0; |
274 |
make_magic = false; |
275 |
word = (f&DOBLANK) ? IFS_WS : IFS_WORD; |
276 |
/* clang doesn't know OSUBST comes before CSUBST */ |
277 |
memset(&st_head, 0, sizeof(st_head)); |
278 |
st = &st_head; |
279 |
|
280 |
while (/* CONSTCOND */ 1) { |
281 |
Xcheck(ds, dp); |
282 |
|
283 |
switch (type) { |
284 |
case XBASE: |
285 |
/* original prefixed string */ |
286 |
c = ord(*sp++); |
287 |
switch (c) { |
288 |
case EOS: |
289 |
c = 0; |
290 |
break; |
291 |
case CHAR: |
292 |
c = ord(*sp++); |
293 |
break; |
294 |
case QCHAR: |
295 |
/* temporary quote */ |
296 |
quote |= 2; |
297 |
c = ord(*sp++); |
298 |
break; |
299 |
case OQUOTE: |
300 |
if (word != IFS_WORD) |
301 |
word = IFS_QUOTE; |
302 |
tilde_ok = 0; |
303 |
quote = 1; |
304 |
continue; |
305 |
case CQUOTE: |
306 |
if (word == IFS_QUOTE) |
307 |
word = IFS_WORD; |
308 |
quote = st->quotew; |
309 |
continue; |
310 |
case COMASUB: |
311 |
case COMSUB: |
312 |
case FUNASUB: |
313 |
case FUNSUB: |
314 |
case VALSUB: |
315 |
tilde_ok = 0; |
316 |
if (f & DONTRUNCOMMAND) { |
317 |
word = IFS_WORD; |
318 |
*dp++ = '$'; |
319 |
switch (c) { |
320 |
case COMASUB: |
321 |
case COMSUB: |
322 |
*dp++ = '('; |
323 |
c = ORD(')'); |
324 |
break; |
325 |
case FUNASUB: |
326 |
case FUNSUB: |
327 |
case VALSUB: |
328 |
*dp++ = '{'; |
329 |
*dp++ = c == VALSUB ? '|' : ' '; |
330 |
c = ORD('}'); |
331 |
break; |
332 |
} |
333 |
while (*sp != '\0') { |
334 |
Xcheck(ds, dp); |
335 |
*dp++ = *sp++; |
336 |
} |
337 |
if ((unsigned int)c == ORD('}')) |
338 |
*dp++ = ';'; |
339 |
*dp++ = c; |
340 |
} else { |
341 |
type = comsub(&x, sp, c); |
342 |
if (type != XBASE && (f & DOBLANK)) |
343 |
doblank++; |
344 |
sp = strnul(sp) + 1; |
345 |
newlines = 0; |
346 |
} |
347 |
continue; |
348 |
case EXPRSUB: |
349 |
tilde_ok = 0; |
350 |
if (f & DONTRUNCOMMAND) { |
351 |
word = IFS_WORD; |
352 |
*dp++ = '$'; *dp++ = '('; *dp++ = '('; |
353 |
while (*sp != '\0') { |
354 |
Xcheck(ds, dp); |
355 |
*dp++ = *sp++; |
356 |
} |
357 |
*dp++ = ')'; *dp++ = ')'; |
358 |
} else { |
359 |
struct tbl v; |
360 |
|
361 |
v.flag = DEFINED|ISSET|INTEGER; |
362 |
/* not default */ |
363 |
v.type = 10; |
364 |
v.name[0] = '\0'; |
365 |
v_evaluate(&v, substitute(sp, 0), |
366 |
KSH_UNWIND_ERROR, true); |
367 |
sp = strnul(sp) + 1; |
368 |
x.str = str_val(&v); |
369 |
type = XSUB; |
370 |
if (f & DOBLANK) |
371 |
doblank++; |
372 |
} |
373 |
continue; |
374 |
case OSUBST: { |
375 |
/* ${{#}var{:}[=+-?#%]word} */ |
376 |
/*- |
377 |
* format is: |
378 |
* OSUBST [{x] plain-variable-part \0 |
379 |
* compiled-word-part CSUBST [}x] |
380 |
* This is where all syntax checking gets done... |
381 |
*/ |
382 |
/* skip the { or x (}) */ |
383 |
const char *varname = ++sp; |
384 |
int stype; |
385 |
int slen = 0; |
386 |
|
387 |
/* skip variable */ |
388 |
sp = cstrchr(sp, '\0') + 1; |
389 |
type = varsub(&x, varname, sp, &stype, &slen); |
390 |
if (type < 0) { |
391 |
char *beg, *end, *str; |
392 |
unwind_substsyn: |
393 |
/* restore sp */ |
394 |
sp = varname - 2; |
395 |
beg = wdcopy(sp, ATEMP); |
396 |
end = (wdscan(cstrchr(sp, '\0') + 1, |
397 |
CSUBST) - sp) + beg; |
398 |
/* ({) the } or x is already skipped */ |
399 |
if (end < wdscan(beg, EOS)) |
400 |
*end = EOS; |
401 |
str = snptreef(NULL, 64, Tf_S, beg); |
402 |
afree(beg, ATEMP); |
403 |
errorf(Tf_sD_s, str, Tbadsubst); |
404 |
} |
405 |
if (f & DOBLANK) |
406 |
doblank++; |
407 |
tilde_ok = 0; |
408 |
if (word == IFS_QUOTE && type != XNULLSUB) |
409 |
word = IFS_WORD; |
410 |
if (type == XBASE) { |
411 |
/* expand? */ |
412 |
if (!st->next) { |
413 |
SubType *newst; |
414 |
|
415 |
newst = alloc(sizeof(SubType), ATEMP); |
416 |
newst->next = NULL; |
417 |
newst->prev = st; |
418 |
st->next = newst; |
419 |
} |
420 |
st = st->next; |
421 |
st->stype = stype; |
422 |
st->base = Xsavepos(ds, dp); |
423 |
st->f = f; |
424 |
if (x.var == vtemp) { |
425 |
st->var = tempvar(vtemp->name); |
426 |
st->var->flag &= ~INTEGER; |
427 |
/* can't fail here */ |
428 |
setstr(st->var, |
429 |
str_val(x.var), |
430 |
KSH_RETURN_ERROR | 0x4); |
431 |
} else |
432 |
st->var = x.var; |
433 |
|
434 |
st->quotew = st->quotep = quote; |
435 |
/* skip qualifier(s) */ |
436 |
if (stype) |
437 |
sp += slen; |
438 |
switch (stype & STYPE_SINGLE) { |
439 |
case ORD('#') | STYPE_AT: |
440 |
x.str = shf_smprintf("%08X", |
441 |
(unsigned int)hash(str_val(st->var))); |
442 |
break; |
443 |
case ORD('Q') | STYPE_AT: { |
444 |
struct shf shf; |
445 |
|
446 |
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf); |
447 |
print_value_quoted(&shf, str_val(st->var)); |
448 |
x.str = shf_sclose(&shf); |
449 |
break; |
450 |
} |
451 |
case ORD('0'): { |
452 |
char *beg, *mid, *end, *stg; |
453 |
mksh_ari_t from = 0, num = -1, flen, finc = 0; |
454 |
|
455 |
beg = wdcopy(sp, ATEMP); |
456 |
mid = beg + (wdscan(sp, ADELIM) - sp); |
457 |
stg = beg + (wdscan(sp, CSUBST) - sp); |
458 |
mid[-2] = EOS; |
459 |
if (ord(mid[-1]) == ORD(/*{*/ '}')) { |
460 |
sp += mid - beg - 1; |
461 |
end = NULL; |
462 |
} else { |
463 |
end = mid + |
464 |
(wdscan(mid, ADELIM) - mid); |
465 |
if (ord(end[-1]) != ORD(/*{*/ '}')) |
466 |
/* more than max delimiters */ |
467 |
goto unwind_substsyn; |
468 |
end[-2] = EOS; |
469 |
sp += end - beg - 1; |
470 |
} |
471 |
evaluate(substitute(stg = wdstrip(beg, 0), 0), |
472 |
&from, KSH_UNWIND_ERROR, true); |
473 |
afree(stg, ATEMP); |
474 |
if (end) { |
475 |
evaluate(substitute(stg = wdstrip(mid, 0), 0), |
476 |
&num, KSH_UNWIND_ERROR, true); |
477 |
afree(stg, ATEMP); |
478 |
} |
479 |
afree(beg, ATEMP); |
480 |
beg = str_val(st->var); |
481 |
flen = utflen(beg); |
482 |
if (from < 0) { |
483 |
if (-from < flen) |
484 |
finc = flen + from; |
485 |
} else |
486 |
finc = from < flen ? from : flen; |
487 |
if (UTFMODE) |
488 |
utfincptr(beg, &finc); |
489 |
beg += finc; |
490 |
flen = utflen(beg); |
491 |
if (num < 0 || num > flen) |
492 |
num = flen; |
493 |
if (UTFMODE) |
494 |
utfincptr(beg, &num); |
495 |
strndupx(x.str, beg, num, ATEMP); |
496 |
goto do_CSUBST; |
497 |
} |
498 |
case ORD('/') | STYPE_AT: |
499 |
case ORD('/'): { |
500 |
char *s, *p, *d, *sbeg, *end; |
501 |
char *pat = NULL, *rrep = null; |
502 |
char fpat = 0, *tpat1, *tpat2; |
503 |
char *ws, *wpat, *wrep; |
504 |
|
505 |
s = ws = wdcopy(sp, ATEMP); |
506 |
p = s + (wdscan(sp, ADELIM) - sp); |
507 |
d = s + (wdscan(sp, CSUBST) - sp); |
508 |
p[-2] = EOS; |
509 |
if (ord(p[-1]) == ORD(/*{*/ '}')) |
510 |
d = NULL; |
511 |
else |
512 |
d[-2] = EOS; |
513 |
sp += (d ? d : p) - s - 1; |
514 |
if (!(stype & STYPE_MASK) && |
515 |
s[0] == CHAR && |
516 |
ctype(s[1], C_SUB2)) |
517 |
fpat = s[1]; |
518 |
wpat = s + (fpat ? 2 : 0); |
519 |
wrep = d ? p : NULL; |
520 |
if (!(stype & STYPE_AT)) { |
521 |
rrep = wrep ? evalstr(wrep, |
522 |
DOTILDE | DOSCALAR) : |
523 |
null; |
524 |
} |
525 |
|
526 |
/* prepare string on which to work */ |
527 |
strdupx(s, str_val(st->var), ATEMP); |
528 |
sbeg = s; |
529 |
again_search: |
530 |
pat = evalstr(wpat, |
531 |
DOTILDE | DOSCALAR | DOPAT); |
532 |
/* check for special cases */ |
533 |
if (!*pat && !fpat) { |
534 |
/* |
535 |
* empty unanchored |
536 |
* pattern => reject |
537 |
*/ |
538 |
goto no_repl; |
539 |
} |
540 |
if ((stype & STYPE_MASK) && |
541 |
gmatchx(null, pat, false)) { |
542 |
/* |
543 |
* pattern matches empty |
544 |
* string => don't loop |
545 |
*/ |
546 |
stype &= ~STYPE_MASK; |
547 |
} |
548 |
|
549 |
/* first see if we have any match at all */ |
550 |
if (ord(fpat) == ORD('#')) { |
551 |
/* anchor at the beginning */ |
552 |
tpat1 = shf_smprintf("%s%c*", pat, MAGIC); |
553 |
tpat2 = tpat1; |
554 |
} else if (ord(fpat) == ORD('%')) { |
555 |
/* anchor at the end */ |
556 |
tpat1 = shf_smprintf("%c*%s", MAGIC, pat); |
557 |
tpat2 = pat; |
558 |
} else { |
559 |
/* float */ |
560 |
tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC); |
561 |
tpat2 = tpat1 + 2; |
562 |
} |
563 |
again_repl: |
564 |
/* |
565 |
* this would not be necessary if gmatchx would return |
566 |
* the start and end values of a match found, like re* |
567 |
*/ |
568 |
if (!gmatchx(sbeg, tpat1, false)) |
569 |
goto end_repl; |
570 |
end = strnul(s); |
571 |
/* now anchor the beginning of the match */ |
572 |
if (ord(fpat) != ORD('#')) |
573 |
while (sbeg <= end) { |
574 |
if (gmatchx(sbeg, tpat2, false)) |
575 |
break; |
576 |
else |
577 |
sbeg++; |
578 |
} |
579 |
/* now anchor the end of the match */ |
580 |
p = end; |
581 |
if (ord(fpat) != ORD('%')) |
582 |
while (p >= sbeg) { |
583 |
bool gotmatch; |
584 |
|
585 |
c = ord(*p); |
586 |
*p = '\0'; |
587 |
gotmatch = tobool(gmatchx(sbeg, pat, false)); |
588 |
*p = c; |
589 |
if (gotmatch) |
590 |
break; |
591 |
p--; |
592 |
} |
593 |
strndupx(end, sbeg, p - sbeg, ATEMP); |
594 |
record_match(end); |
595 |
afree(end, ATEMP); |
596 |
if (stype & STYPE_AT) { |
597 |
if (rrep != null) |
598 |
afree(rrep, ATEMP); |
599 |
rrep = wrep ? evalstr(wrep, |
600 |
DOTILDE | DOSCALAR) : |
601 |
null; |
602 |
} |
603 |
strndupx(end, s, sbeg - s, ATEMP); |
604 |
d = shf_smprintf(Tf_sss, end, rrep, p); |
605 |
afree(end, ATEMP); |
606 |
sbeg = d + (sbeg - s) + strlen(rrep); |
607 |
afree(s, ATEMP); |
608 |
s = d; |
609 |
if (stype & STYPE_AT) { |
610 |
afree(tpat1, ATEMP); |
611 |
afree(pat, ATEMP); |
612 |
goto again_search; |
613 |
} else if (stype & STYPE_DBL) |
614 |
goto again_repl; |
615 |
end_repl: |
616 |
afree(tpat1, ATEMP); |
617 |
x.str = s; |
618 |
no_repl: |
619 |
afree(pat, ATEMP); |
620 |
if (rrep != null) |
621 |
afree(rrep, ATEMP); |
622 |
afree(ws, ATEMP); |
623 |
goto do_CSUBST; |
624 |
} |
625 |
case ORD('#'): |
626 |
case ORD('%'): |
627 |
/* ! DOBLANK,DOBRACE */ |
628 |
f = (f & DONTRUNCOMMAND) | |
629 |
DOPAT | DOTILDE | |
630 |
DOTEMP | DOSCALAR; |
631 |
tilde_ok = 1; |
632 |
st->quotew = quote = 0; |
633 |
/* |
634 |
* Prepend open pattern (so | |
635 |
* in a trim will work as |
636 |
* expected) |
637 |
*/ |
638 |
if (!Flag(FSH)) { |
639 |
*dp++ = MAGIC; |
640 |
*dp++ = ORD(0x80 | '@'); |
641 |
} |
642 |
break; |
643 |
case ORD('='): |
644 |
/* |
645 |
* Tilde expansion for string |
646 |
* variables in POSIX mode is |
647 |
* governed by Austinbug 351. |
648 |
* In non-POSIX mode historic |
649 |
* ksh behaviour (enable it!) |
650 |
* us followed. |
651 |
* Not doing tilde expansion |
652 |
* for integer variables is a |
653 |
* non-POSIX thing - makes |
654 |
* sense though, since ~ is |
655 |
* a arithmetic operator. |
656 |
*/ |
657 |
if (!(x.var->flag & INTEGER)) |
658 |
f |= DOASNTILDE | DOTILDE; |
659 |
f |= DOTEMP | DOSCALAR; |
660 |
/* |
661 |
* These will be done after the |
662 |
* value has been assigned. |
663 |
*/ |
664 |
f &= ~(DOBLANK|DOGLOB|DOBRACE); |
665 |
tilde_ok = 1; |
666 |
break; |
667 |
case ORD('?'): |
668 |
if (*sp == CSUBST) |
669 |
errorf("%s: parameter null or not set", |
670 |
st->var->name); |
671 |
f &= ~DOBLANK; |
672 |
f |= DOTEMP; |
673 |
/* FALLTHROUGH */ |
674 |
default: |
675 |
/* '-' '+' '?' */ |
676 |
if (quote) |
677 |
word = IFS_WORD; |
678 |
else if (dp == Xstring(ds, dp)) |
679 |
word = IFS_IWS; |
680 |
/* Enable tilde expansion */ |
681 |
tilde_ok = 1; |
682 |
f |= DOTILDE; |
683 |
} |
684 |
} else |
685 |
/* skip word */ |
686 |
sp += wdscan(sp, CSUBST) - sp; |
687 |
continue; |
688 |
} |
689 |
case CSUBST: |
690 |
/* only get here if expanding word */ |
691 |
do_CSUBST: |
692 |
/* ({) skip the } or x */ |
693 |
sp++; |
694 |
/* in case of ${unset:-} */ |
695 |
tilde_ok = 0; |
696 |
*dp = '\0'; |
697 |
quote = st->quotep; |
698 |
f = st->f; |
699 |
if (f & DOBLANK) |
700 |
doblank--; |
701 |
switch (st->stype & STYPE_SINGLE) { |
702 |
case ORD('#'): |
703 |
case ORD('%'): |
704 |
if (!Flag(FSH)) { |
705 |
/* Append end-pattern */ |
706 |
*dp++ = MAGIC; |
707 |
*dp++ = ')'; |
708 |
} |
709 |
*dp = '\0'; |
710 |
dp = Xrestpos(ds, dp, st->base); |
711 |
/* |
712 |
* Must use st->var since calling |
713 |
* global would break things |
714 |
* like x[i+=1]. |
715 |
*/ |
716 |
x.str = trimsub(str_val(st->var), |
717 |
dp, st->stype); |
718 |
if (x.str[0] != '\0') { |
719 |
word = IFS_IWS; |
720 |
type = XSUB; |
721 |
} else if (quote) { |
722 |
word = IFS_WORD; |
723 |
type = XSUB; |
724 |
} else { |
725 |
if (dp == Xstring(ds, dp)) |
726 |
word = IFS_IWS; |
727 |
type = XNULLSUB; |
728 |
} |
729 |
if (f & DOBLANK) |
730 |
doblank++; |
731 |
st = st->prev; |
732 |
continue; |
733 |
case ORD('='): |
734 |
/* |
735 |
* Restore our position and substitute |
736 |
* the value of st->var (may not be |
737 |
* the assigned value in the presence |
738 |
* of integer/right-adj/etc attributes). |
739 |
*/ |
740 |
dp = Xrestpos(ds, dp, st->base); |
741 |
/* |
742 |
* Must use st->var since calling |
743 |
* global would cause with things |
744 |
* like x[i+=1] to be evaluated twice. |
745 |
*/ |
746 |
/* |
747 |
* Note: not exported by FEXPORT |
748 |
* in AT&T ksh. |
749 |
*/ |
750 |
/* |
751 |
* XXX POSIX says readonly is only |
752 |
* fatal for special builtins (setstr |
753 |
* does readonly check). |
754 |
*/ |
755 |
len = strlen(dp) + 1; |
756 |
setstr(st->var, |
757 |
debunk(alloc(len, ATEMP), |
758 |
dp, len), KSH_UNWIND_ERROR); |
759 |
x.str = str_val(st->var); |
760 |
type = XSUB; |
761 |
if (f & DOBLANK) |
762 |
doblank++; |
763 |
st = st->prev; |
764 |
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS; |
765 |
continue; |
766 |
case ORD('?'): |
767 |
dp = Xrestpos(ds, dp, st->base); |
768 |
|
769 |
errorf(Tf_sD_s, st->var->name, |
770 |
debunk(dp, dp, strlen(dp) + 1)); |
771 |
break; |
772 |
case ORD('0'): |
773 |
case ORD('/') | STYPE_AT: |
774 |
case ORD('/'): |
775 |
case ORD('#') | STYPE_AT: |
776 |
case ORD('Q') | STYPE_AT: |
777 |
dp = Xrestpos(ds, dp, st->base); |
778 |
type = XSUB; |
779 |
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS; |
780 |
if (f & DOBLANK) |
781 |
doblank++; |
782 |
st = st->prev; |
783 |
continue; |
784 |
/* default: '-' '+' */ |
785 |
} |
786 |
st = st->prev; |
787 |
type = XBASE; |
788 |
continue; |
789 |
|
790 |
case OPAT: |
791 |
/* open pattern: *(foo|bar) */ |
792 |
/* Next char is the type of pattern */ |
793 |
make_magic = true; |
794 |
c = ord(*sp++) | 0x80U; |
795 |
break; |
796 |
|
797 |
case SPAT: |
798 |
/* pattern separator (|) */ |
799 |
make_magic = true; |
800 |
c = ORD('|'); |
801 |
break; |
802 |
|
803 |
case CPAT: |
804 |
/* close pattern */ |
805 |
make_magic = true; |
806 |
c = ORD(/*(*/ ')'); |
807 |
break; |
808 |
} |
809 |
break; |
810 |
|
811 |
case XNULLSUB: |
812 |
/* |
813 |
* Special case for "$@" (and "${foo[@]}") - no |
814 |
* word is generated if $# is 0 (unless there is |
815 |
* other stuff inside the quotes). |
816 |
*/ |
817 |
type = XBASE; |
818 |
if (f & DOBLANK) { |
819 |
doblank--; |
820 |
if (dp == Xstring(ds, dp) && word != IFS_WORD) |
821 |
word = IFS_IWS; |
822 |
} |
823 |
continue; |
824 |
|
825 |
case XSUB: |
826 |
case XSUBMID: |
827 |
if ((c = ord(*x.str++)) == 0) { |
828 |
type = XBASE; |
829 |
if (f & DOBLANK) |
830 |
doblank--; |
831 |
continue; |
832 |
} |
833 |
break; |
834 |
|
835 |
case XARGSEP: |
836 |
type = XARG; |
837 |
quote = 1; |
838 |
/* FALLTHROUGH */ |
839 |
case XARG: |
840 |
if ((c = ord(*x.str++)) == '\0') { |
841 |
/* |
842 |
* force null words to be created so |
843 |
* set -- "" 2 ""; echo "$@" will do |
844 |
* the right thing |
845 |
*/ |
846 |
if (quote && x.split) |
847 |
word = IFS_WORD; |
848 |
if ((x.str = *x.u.strv++) == NULL) { |
849 |
type = XBASE; |
850 |
if (f & DOBLANK) |
851 |
doblank--; |
852 |
continue; |
853 |
} |
854 |
c = ord(ifs0); |
855 |
if ((f & DOHEREDOC)) { |
856 |
/* pseudo-field-split reliably */ |
857 |
if (c == 0) |
858 |
c = ORD(' '); |
859 |
break; |
860 |
} |
861 |
if ((f & DOSCALAR)) { |
862 |
/* do not field-split */ |
863 |
if (x.split) { |
864 |
c = ORD(' '); |
865 |
break; |
866 |
} |
867 |
if (c == 0) |
868 |
continue; |
869 |
} |
870 |
if (c == 0) { |
871 |
if (quote && !x.split) |
872 |
continue; |
873 |
if (!quote && word == IFS_WS) |
874 |
continue; |
875 |
/* this is so we don't terminate */ |
876 |
c = ORD(' '); |
877 |
/* now force-emit a word */ |
878 |
goto emit_word; |
879 |
} |
880 |
if (quote && x.split) { |
881 |
/* terminate word for "$@" */ |
882 |
type = XARGSEP; |
883 |
quote = 0; |
884 |
} |
885 |
} |
886 |
break; |
887 |
|
888 |
case XCOM: |
889 |
if (x.u.shf == NULL) { |
890 |
/* $(<...) failed */ |
891 |
subst_exstat = 1; |
892 |
/* fake EOF */ |
893 |
c = -1; |
894 |
} else if (newlines) { |
895 |
/* spit out saved NLs */ |
896 |
c = ORD('\n'); |
897 |
--newlines; |
898 |
} else { |
899 |
while ((c = shf_getc(x.u.shf)) == 0 || |
900 |
cinttype(c, C_NL)) { |
901 |
#ifdef MKSH_WITH_TEXTMODE |
902 |
if (c == ORD('\r')) { |
903 |
c = shf_getc(x.u.shf); |
904 |
switch (c) { |
905 |
case ORD('\n'): |
906 |
break; |
907 |
default: |
908 |
shf_ungetc(c, x.u.shf); |
909 |
/* FALLTHROUGH */ |
910 |
case -1: |
911 |
c = ORD('\r'); |
912 |
break; |
913 |
} |
914 |
} |
915 |
#endif |
916 |
if (c == ORD('\n')) |
917 |
/* save newlines */ |
918 |
newlines++; |
919 |
} |
920 |
if (newlines && c != -1) { |
921 |
shf_ungetc(c, x.u.shf); |
922 |
c = ORD('\n'); |
923 |
--newlines; |
924 |
} |
925 |
} |
926 |
if (c == -1) { |
927 |
newlines = 0; |
928 |
if (x.u.shf) |
929 |
shf_close(x.u.shf); |
930 |
if (x.split) |
931 |
subst_exstat = waitlast(); |
932 |
type = XBASE; |
933 |
if (f & DOBLANK) |
934 |
doblank--; |
935 |
continue; |
936 |
} |
937 |
break; |
938 |
} |
939 |
|
940 |
/* check for end of word or IFS separation */ |
941 |
if (c == 0 || (!quote && (f & DOBLANK) && doblank && |
942 |
!make_magic && ctype(c, C_IFS))) { |
943 |
/*- |
944 |
* How words are broken up: |
945 |
* | value of c |
946 |
* word | ws nws 0 |
947 |
* ----------------------------------- |
948 |
* IFS_WORD w/WS w/NWS w |
949 |
* IFS_WS -/WS -/NWS - |
950 |
* IFS_NWS -/NWS w/NWS - |
951 |
* IFS_IWS -/WS w/NWS - |
952 |
* (w means generate a word) |
953 |
*/ |
954 |
if ((word == IFS_WORD) || (word == IFS_QUOTE) || (c && |
955 |
(word == IFS_IWS || word == IFS_NWS) && |
956 |
!ctype(c, C_IFSWS))) { |
957 |
emit_word: |
958 |
if (f & DOHERESTR) |
959 |
*dp++ = '\n'; |
960 |
*dp++ = '\0'; |
961 |
cp = Xclose(ds, dp); |
962 |
if (fdo & DOBRACE) |
963 |
/* also does globbing */ |
964 |
alt_expand(wp, cp, cp, |
965 |
cp + Xlength(ds, (dp - 1)), |
966 |
fdo | (f & DOMARKDIRS)); |
967 |
else if (fdo & DOGLOB) |
968 |
glob(cp, wp, tobool(f & DOMARKDIRS)); |
969 |
else if ((f & DOPAT) || !(fdo & DOMAGIC)) |
970 |
XPput(*wp, cp); |
971 |
else |
972 |
XPput(*wp, debunk(cp, cp, |
973 |
strlen(cp) + 1)); |
974 |
fdo = 0; |
975 |
saw_eq = false; |
976 |
/* must be 1/0 */ |
977 |
tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0; |
978 |
if (c == 0) |
979 |
return; |
980 |
Xinit(ds, dp, 128, ATEMP); |
981 |
} else if (c == 0) { |
982 |
return; |
983 |
} else if (type == XSUB && ctype(c, C_IFS) && |
984 |
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) { |
985 |
*(cp = alloc(1, ATEMP)) = '\0'; |
986 |
XPput(*wp, cp); |
987 |
type = XSUBMID; |
988 |
} |
989 |
if (word != IFS_NWS) |
990 |
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; |
991 |
} else { |
992 |
if (type == XSUB) { |
993 |
if (word == IFS_NWS && |
994 |
Xlength(ds, dp) == 0) { |
995 |
*(cp = alloc(1, ATEMP)) = '\0'; |
996 |
XPput(*wp, cp); |
997 |
} |
998 |
type = XSUBMID; |
999 |
} |
1000 |
|
1001 |
/* age tilde_ok info - ~ code tests second bit */ |
1002 |
tilde_ok <<= 1; |
1003 |
/* mark any special second pass chars */ |
1004 |
if (!quote) |
1005 |
switch (ord(c)) { |
1006 |
case ORD('['): |
1007 |
case ORD('!'): |
1008 |
case ORD('-'): |
1009 |
case ORD(']'): |
1010 |
/* |
1011 |
* For character classes - doesn't hurt |
1012 |
* to have magic !,-,]s outside of |
1013 |
* [...] expressions. |
1014 |
*/ |
1015 |
if (f & (DOPAT | DOGLOB)) { |
1016 |
fdo |= DOMAGIC; |
1017 |
if ((unsigned int)c == ORD('[')) |
1018 |
fdo |= f & DOGLOB; |
1019 |
*dp++ = MAGIC; |
1020 |
} |
1021 |
break; |
1022 |
case ORD('*'): |
1023 |
case ORD('?'): |
1024 |
if (f & (DOPAT | DOGLOB)) { |
1025 |
fdo |= DOMAGIC | (f & DOGLOB); |
1026 |
*dp++ = MAGIC; |
1027 |
} |
1028 |
break; |
1029 |
case ORD('{'): |
1030 |
case ORD('}'): |
1031 |
case ORD(','): |
1032 |
if ((f & DOBRACE) && |
1033 |
(ord(c) == ORD('{' /*}*/) || |
1034 |
(fdo & DOBRACE))) { |
1035 |
fdo |= DOBRACE|DOMAGIC; |
1036 |
*dp++ = MAGIC; |
1037 |
} |
1038 |
break; |
1039 |
case ORD('='): |
1040 |
/* Note first unquoted = for ~ */ |
1041 |
if (!(f & DOTEMP) && (!Flag(FPOSIX) || |
1042 |
(f & DOASNTILDE)) && !saw_eq) { |
1043 |
saw_eq = true; |
1044 |
tilde_ok = 1; |
1045 |
} |
1046 |
break; |
1047 |
case ORD(':'): |
1048 |
/* : */ |
1049 |
/* Note unquoted : for ~ */ |
1050 |
if (!(f & DOTEMP) && (f & DOASNTILDE)) |
1051 |
tilde_ok = 1; |
1052 |
break; |
1053 |
case ORD('~'): |
1054 |
/* |
1055 |
* tilde_ok is reset whenever |
1056 |
* any of ' " $( $(( ${ } are seen. |
1057 |
* Note that tilde_ok must be preserved |
1058 |
* through the sequence ${A=a=}~ |
1059 |
*/ |
1060 |
if (type == XBASE && |
1061 |
(f & (DOTILDE | DOASNTILDE)) && |
1062 |
(tilde_ok & 2)) { |
1063 |
const char *tcp; |
1064 |
char *tdp = dp; |
1065 |
|
1066 |
tcp = maybe_expand_tilde(sp, |
1067 |
&ds, &tdp, |
1068 |
tobool(f & DOASNTILDE)); |
1069 |
if (tcp) { |
1070 |
if (dp != tdp) |
1071 |
word = IFS_WORD; |
1072 |
dp = tdp; |
1073 |
sp = tcp; |
1074 |
continue; |
1075 |
} |
1076 |
} |
1077 |
break; |
1078 |
} |
1079 |
else |
1080 |
/* undo temporary */ |
1081 |
quote &= ~2; |
1082 |
|
1083 |
if (make_magic) { |
1084 |
make_magic = false; |
1085 |
fdo |= DOMAGIC | (f & DOGLOB); |
1086 |
*dp++ = MAGIC; |
1087 |
} else if (ISMAGIC(c)) { |
1088 |
fdo |= DOMAGIC; |
1089 |
*dp++ = MAGIC; |
1090 |
} |
1091 |
/* save output char */ |
1092 |
*dp++ = c; |
1093 |
word = IFS_WORD; |
1094 |
} |
1095 |
} |
1096 |
} |
1097 |
|
1098 |
static bool |
1099 |
hasnonempty(const char **strv) |
1100 |
{ |
1101 |
size_t i = 0; |
1102 |
|
1103 |
while (strv[i]) |
1104 |
if (*strv[i++]) |
1105 |
return (true); |
1106 |
return (false); |
1107 |
} |
1108 |
|
1109 |
/* |
1110 |
* Prepare to generate the string returned by ${} substitution. |
1111 |
*/ |
1112 |
static int |
1113 |
varsub(Expand *xp, const char *sp, const char *word, |
1114 |
int *stypep, /* becomes qualifier type */ |
1115 |
int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */ |
1116 |
{ |
1117 |
int c; |
1118 |
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */ |
1119 |
int stype; /* substitution type */ |
1120 |
int slen = 0; |
1121 |
const char *p; |
1122 |
struct tbl *vp; |
1123 |
bool zero_ok = false; |
1124 |
|
1125 |
if ((stype = ord(sp[0])) == '\0') |
1126 |
/* Bad variable name */ |
1127 |
return (-1); |
1128 |
|
1129 |
xp->var = NULL; |
1130 |
|
1131 |
/*- |
1132 |
* ${#var}, string length (-U: characters, +U: octets) or array size |
1133 |
* ${%var}, string width (-U: screen columns, +U: octets) |
1134 |
*/ |
1135 |
c = ord(sp[1]); |
1136 |
if ((unsigned int)stype == ORD('%') && c == '\0') |
1137 |
return (-1); |
1138 |
if (ctype(stype, C_SUB2) && c != '\0') { |
1139 |
/* Can't have any modifiers for ${#...} or ${%...} */ |
1140 |
if (*word != CSUBST) |
1141 |
return (-1); |
1142 |
sp++; |
1143 |
/* Check for size of array */ |
1144 |
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') || |
1145 |
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) { |
1146 |
int n = 0; |
1147 |
|
1148 |
if ((unsigned int)stype != ORD('#')) |
1149 |
return (-1); |
1150 |
vp = global(arrayname(sp)); |
1151 |
if (vp->flag & (ISSET|ARRAY)) |
1152 |
zero_ok = true; |
1153 |
for (; vp; vp = vp->u.array) |
1154 |
if (vp->flag & ISSET) |
1155 |
n++; |
1156 |
c = n; |
1157 |
} else if ((unsigned int)c == ORD('*') || |
1158 |
(unsigned int)c == ORD('@')) { |
1159 |
if ((unsigned int)stype != ORD('#')) |
1160 |
return (-1); |
1161 |
c = e->loc->argc; |
1162 |
} else { |
1163 |
p = str_val(global(sp)); |
1164 |
zero_ok = p != null; |
1165 |
if ((unsigned int)stype == ORD('#')) |
1166 |
c = utflen(p); |
1167 |
else { |
1168 |
/* partial utf_mbswidth reimplementation */ |
1169 |
const char *s = p; |
1170 |
unsigned int wc; |
1171 |
size_t len; |
1172 |
int cw; |
1173 |
|
1174 |
c = 0; |
1175 |
while (*s) { |
1176 |
if (!UTFMODE || (len = utf_mbtowc(&wc, |
1177 |
s)) == (size_t)-1) |
1178 |
/* not UTFMODE or not UTF-8 */ |
1179 |
wc = rtt2asc(*s++); |
1180 |
else |
1181 |
/* UTFMODE and UTF-8 */ |
1182 |
s += len; |
1183 |
/* wc == char or wchar at s++ */ |
1184 |
if ((cw = utf_wcwidth(wc)) == -1) { |
1185 |
/* 646, 8859-1, 10646 C0/C1 */ |
1186 |
c = -1; |
1187 |
break; |
1188 |
} |
1189 |
c += cw; |
1190 |
} |
1191 |
} |
1192 |
} |
1193 |
if (Flag(FNOUNSET) && c == 0 && !zero_ok) |
1194 |
errorf(Tf_parm, sp); |
1195 |
/* unqualified variable/string substitution */ |
1196 |
*stypep = 0; |
1197 |
xp->str = shf_smprintf(Tf_d, c); |
1198 |
return (XSUB); |
1199 |
} |
1200 |
if ((unsigned int)stype == ORD('!') && c != '\0' && *word == CSUBST) { |
1201 |
sp++; |
1202 |
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') || |
1203 |
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) { |
1204 |
c = ORD('!'); |
1205 |
stype = 0; |
1206 |
goto arraynames; |
1207 |
} |
1208 |
xp->var = global(sp); |
1209 |
xp->str = p ? shf_smprintf("%s[%lu]", |
1210 |
xp->var->name, arrayindex(xp->var)) : xp->var->name; |
1211 |
*stypep = 0; |
1212 |
return (XSUB); |
1213 |
} |
1214 |
|
1215 |
/* Check for qualifiers in word part */ |
1216 |
stype = 0; |
1217 |
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0; |
1218 |
if ((unsigned int)c == ORD(':')) { |
1219 |
slen += 2; |
1220 |
stype = STYPE_DBL; |
1221 |
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0; |
1222 |
} |
1223 |
if (!stype && (unsigned int)c == ORD('/')) { |
1224 |
slen += 2; |
1225 |
stype = c; |
1226 |
if (word[slen] == ADELIM && |
1227 |
ord(word[slen + 1]) == (unsigned int)c) { |
1228 |
slen += 2; |
1229 |
stype |= STYPE_DBL; |
1230 |
} |
1231 |
} else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') || |
1232 |
(unsigned int)c == ORD('0'))) { |
1233 |
stype |= ORD('0'); |
1234 |
} else if (ctype(c, C_SUB1)) { |
1235 |
slen += 2; |
1236 |
stype |= c; |
1237 |
} else if (ctype(c, C_SUB2)) { |
1238 |
/* Note: ksh88 allows :%, :%%, etc */ |
1239 |
slen += 2; |
1240 |
stype = c; |
1241 |
if (word[slen + 0] == CHAR && |
1242 |
ord(word[slen + 1]) == (unsigned int)c) { |
1243 |
stype |= STYPE_DBL; |
1244 |
slen += 2; |
1245 |
} |
1246 |
} else if ((unsigned int)c == ORD('@')) { |
1247 |
/* @x where x is command char */ |
1248 |
switch (c = ord(word[slen + 2]) == CHAR ? |
1249 |
ord(word[slen + 3]) : 0) { |
1250 |
case ORD('#'): |
1251 |
case ORD('/'): |
1252 |
case ORD('Q'): |
1253 |
break; |
1254 |
default: |
1255 |
return (-1); |
1256 |
} |
1257 |
stype |= STYPE_AT | c; |
1258 |
slen += 4; |
1259 |
} else if (stype) |
1260 |
/* : is not ok */ |
1261 |
return (-1); |
1262 |
if (!stype && *word != CSUBST) |
1263 |
return (-1); |
1264 |
|
1265 |
c = ord(sp[0]); |
1266 |
if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) { |
1267 |
switch (stype & STYPE_SINGLE) { |
1268 |
/* can't assign to a vector */ |
1269 |
case ORD('='): |
1270 |
/* can't trim a vector (yet) */ |
1271 |
case ORD('%'): |
1272 |
case ORD('#'): |
1273 |
case ORD('?'): |
1274 |
case ORD('0'): |
1275 |
case ORD('/') | STYPE_AT: |
1276 |
case ORD('/'): |
1277 |
case ORD('#') | STYPE_AT: |
1278 |
case ORD('Q') | STYPE_AT: |
1279 |
return (-1); |
1280 |
} |
1281 |
if (e->loc->argc == 0) { |
1282 |
xp->str = null; |
1283 |
xp->var = global(sp); |
1284 |
state = (unsigned int)c == ORD('@') ? XNULLSUB : XSUB; |
1285 |
} else { |
1286 |
xp->u.strv = (const char **)e->loc->argv + 1; |
1287 |
xp->str = *xp->u.strv++; |
1288 |
/* $@ */ |
1289 |
xp->split = tobool((unsigned int)c == ORD('@')); |
1290 |
state = XARG; |
1291 |
} |
1292 |
/* POSIX 2009? */ |
1293 |
zero_ok = true; |
1294 |
} else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') || |
1295 |
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) { |
1296 |
XPtrV wv; |
1297 |
|
1298 |
switch (stype & STYPE_SINGLE) { |
1299 |
/* can't assign to a vector */ |
1300 |
case ORD('='): |
1301 |
/* can't trim a vector (yet) */ |
1302 |
case ORD('%'): |
1303 |
case ORD('#'): |
1304 |
case ORD('?'): |
1305 |
case ORD('0'): |
1306 |
case ORD('/') | STYPE_AT: |
1307 |
case ORD('/'): |
1308 |
case ORD('#') | STYPE_AT: |
1309 |
case ORD('Q') | STYPE_AT: |
1310 |
return (-1); |
1311 |
} |
1312 |
c = 0; |
1313 |
arraynames: |
1314 |
XPinit(wv, 32); |
1315 |
vp = global(arrayname(sp)); |
1316 |
for (; vp; vp = vp->u.array) { |
1317 |
if (!(vp->flag&ISSET)) |
1318 |
continue; |
1319 |
XPput(wv, (unsigned int)c == ORD('!') ? |
1320 |
shf_smprintf(Tf_lu, arrayindex(vp)) : |
1321 |
str_val(vp)); |
1322 |
} |
1323 |
if (XPsize(wv) == 0) { |
1324 |
xp->str = null; |
1325 |
state = ord(p[1]) == ORD('@') ? XNULLSUB : XSUB; |
1326 |
XPfree(wv); |
1327 |
} else { |
1328 |
XPput(wv, 0); |
1329 |
xp->u.strv = (const char **)XPptrv(wv); |
1330 |
xp->str = *xp->u.strv++; |
1331 |
/* ${foo[@]} */ |
1332 |
xp->split = tobool(ord(p[1]) == ORD('@')); |
1333 |
state = XARG; |
1334 |
} |
1335 |
} else { |
1336 |
xp->var = global(sp); |
1337 |
xp->str = str_val(xp->var); |
1338 |
/* can't assign things like $! or $1 */ |
1339 |
if ((unsigned int)(stype & STYPE_SINGLE) == ORD('=') && |
1340 |
!*xp->str && ctype(*sp, C_VAR1 | C_DIGIT)) |
1341 |
return (-1); |
1342 |
state = XSUB; |
1343 |
} |
1344 |
|
1345 |
c = stype & STYPE_CHAR; |
1346 |
/* test the compiler's code generator */ |
1347 |
if ((!(stype & STYPE_AT) && (ctype(c, C_SUB2) || |
1348 |
(((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) && |
1349 |
(state != XARG || (ifs0 || xp->split ? |
1350 |
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ? |
1351 |
ctype(c, C_EQUAL | C_MINUS | C_QUEST) : (unsigned int)c == ORD('+')))) || |
1352 |
(unsigned int)stype == (ORD('0') | STYPE_DBL) || |
1353 |
(unsigned int)stype == (ORD('#') | STYPE_AT) || |
1354 |
(unsigned int)stype == (ORD('Q') | STYPE_AT) || |
1355 |
(unsigned int)(stype & STYPE_CHAR) == ORD('/')) |
1356 |
/* expand word instead of variable value */ |
1357 |
state = XBASE; |
1358 |
if (Flag(FNOUNSET) && xp->str == null && !zero_ok && |
1359 |
(ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+')))) |
1360 |
errorf(Tf_parm, sp); |
1361 |
*stypep = stype; |
1362 |
*slenp = slen; |
1363 |
return (state); |
1364 |
} |
1365 |
|
1366 |
/* |
1367 |
* Run the command in $(...) and read its output. |
1368 |
*/ |
1369 |
static int |
1370 |
comsub(Expand *xp, const char *cp, int fn) |
1371 |
{ |
1372 |
Source *s, *sold; |
1373 |
struct op *t; |
1374 |
struct shf *shf; |
1375 |
bool doalias = false; |
1376 |
uint8_t old_utfmode = UTFMODE; |
1377 |
|
1378 |
switch (fn) { |
1379 |
case COMASUB: |
1380 |
fn = COMSUB; |
1381 |
if (0) |
1382 |
/* FALLTHROUGH */ |
1383 |
case FUNASUB: |
1384 |
fn = FUNSUB; |
1385 |
doalias = true; |
1386 |
} |
1387 |
|
1388 |
s = pushs(SSTRING, ATEMP); |
1389 |
s->start = s->str = cp; |
1390 |
sold = source; |
1391 |
t = compile(s, true, doalias); |
1392 |
afree(s, ATEMP); |
1393 |
source = sold; |
1394 |
|
1395 |
UTFMODE = old_utfmode; |
1396 |
|
1397 |
if (t == NULL) |
1398 |
return (XBASE); |
1399 |
|
1400 |
/* no waitlast() unless specifically enabled later */ |
1401 |
xp->split = false; |
1402 |
|
1403 |
if (t->type == TCOM && |
1404 |
*t->args == NULL && *t->vars == NULL && t->ioact != NULL) { |
1405 |
/* $(<file) */ |
1406 |
struct ioword *io = *t->ioact; |
1407 |
char *name; |
1408 |
|
1409 |
switch (io->ioflag & IOTYPE) { |
1410 |
case IOREAD: |
1411 |
shf = shf_open(name = evalstr(io->ioname, DOTILDE), |
1412 |
O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); |
1413 |
if (shf == NULL) |
1414 |
warningf(!Flag(FTALKING), Tf_sD_s_sD_s, |
1415 |
name, Tcant_open, "$(<...) input", |
1416 |
cstrerror(errno)); |
1417 |
break; |
1418 |
case IOHERE: |
1419 |
if (!herein(io, &name)) { |
1420 |
xp->str = name; |
1421 |
/* as $(…) requires, trim trailing newlines */ |
1422 |
name = strnul(name); |
1423 |
while (name > xp->str && name[-1] == '\n') |
1424 |
--name; |
1425 |
*name = '\0'; |
1426 |
return (XSUB); |
1427 |
} |
1428 |
shf = NULL; |
1429 |
break; |
1430 |
default: |
1431 |
errorf(Tf_sD_s, T_funny_command, |
1432 |
snptreef(NULL, 32, Tft_R, io)); |
1433 |
} |
1434 |
} else if (fn == FUNSUB) { |
1435 |
int ofd1; |
1436 |
struct temp *tf = NULL; |
1437 |
|
1438 |
/* |
1439 |
* create a temporary file, open for reading and writing, |
1440 |
* with an shf open for reading (buffered) but yet unused |
1441 |
*/ |
1442 |
maketemp(ATEMP, TT_FUNSUB, &tf); |
1443 |
if (!tf->shf) { |
1444 |
errorf(Tf_temp, |
1445 |
Tcreate, tf->tffn, cstrerror(errno)); |
1446 |
} |
1447 |
/* extract shf from temporary file, unlink and free it */ |
1448 |
shf = tf->shf; |
1449 |
unlink(tf->tffn); |
1450 |
afree(tf, ATEMP); |
1451 |
/* save stdout and let it point to the tempfile */ |
1452 |
ofd1 = savefd(1); |
1453 |
ksh_dup2(shf_fileno(shf), 1, false); |
1454 |
/* |
1455 |
* run tree, with output thrown into the tempfile, |
1456 |
* in a new function block |
1457 |
*/ |
1458 |
valsub(t, NULL); |
1459 |
subst_exstat = exstat & 0xFF; |
1460 |
/* rewind the tempfile and restore regular stdout */ |
1461 |
lseek(shf_fileno(shf), (off_t)0, SEEK_SET); |
1462 |
restfd(1, ofd1); |
1463 |
} else if (fn == VALSUB) { |
1464 |
xp->str = valsub(t, ATEMP); |
1465 |
subst_exstat = exstat & 0xFF; |
1466 |
return (XSUB); |
1467 |
} else { |
1468 |
int ofd1, pv[2]; |
1469 |
|
1470 |
openpipe(pv); |
1471 |
shf = shf_fdopen(pv[0], SHF_RD, NULL); |
1472 |
ofd1 = savefd(1); |
1473 |
if (pv[1] != 1) { |
1474 |
ksh_dup2(pv[1], 1, false); |
1475 |
close(pv[1]); |
1476 |
} |
1477 |
execute(t, XXCOM | XPIPEO | XFORK, NULL); |
1478 |
restfd(1, ofd1); |
1479 |
startlast(); |
1480 |
/* waitlast() */ |
1481 |
xp->split = true; |
1482 |
} |
1483 |
|
1484 |
xp->u.shf = shf; |
1485 |
return (XCOM); |
1486 |
} |
1487 |
|
1488 |
/* |
1489 |
* perform #pattern and %pattern substitution in ${} |
1490 |
*/ |
1491 |
static char * |
1492 |
trimsub(char *str, char *pat, int how) |
1493 |
{ |
1494 |
char *end = strnul(str); |
1495 |
char *p, c; |
1496 |
|
1497 |
switch (how & (STYPE_CHAR | STYPE_DBL)) { |
1498 |
case ORD('#'): |
1499 |
/* shortest match at beginning */ |
1500 |
for (p = str; p <= end; p += utf_ptradj(p)) { |
1501 |
c = *p; *p = '\0'; |
1502 |
if (gmatchx(str, pat, false)) { |
1503 |
record_match(str); |
1504 |
*p = c; |
1505 |
return (p); |
1506 |
} |
1507 |
*p = c; |
1508 |
} |
1509 |
break; |
1510 |
case ORD('#') | STYPE_DBL: |
1511 |
/* longest match at beginning */ |
1512 |
for (p = end; p >= str; p--) { |
1513 |
c = *p; *p = '\0'; |
1514 |
if (gmatchx(str, pat, false)) { |
1515 |
record_match(str); |
1516 |
*p = c; |
1517 |
return (p); |
1518 |
} |
1519 |
*p = c; |
1520 |
} |
1521 |
break; |
1522 |
case ORD('%'): |
1523 |
/* shortest match at end */ |
1524 |
p = end; |
1525 |
while (p >= str) { |
1526 |
if (gmatchx(p, pat, false)) |
1527 |
goto trimsub_match; |
1528 |
if (UTFMODE) { |
1529 |
char *op = p; |
1530 |
while ((p-- > str) && ((rtt2asc(*p) & 0xC0) == 0x80)) |
1531 |
; |
1532 |
if ((p < str) || (p + utf_ptradj(p) != op)) |
1533 |
p = op - 1; |
1534 |
} else |
1535 |
--p; |
1536 |
} |
1537 |
break; |
1538 |
case ORD('%') | STYPE_DBL: |
1539 |
/* longest match at end */ |
1540 |
for (p = str; p <= end; p++) |
1541 |
if (gmatchx(p, pat, false)) { |
1542 |
trimsub_match: |
1543 |
record_match(p); |
1544 |
strndupx(end, str, p - str, ATEMP); |
1545 |
return (end); |
1546 |
} |
1547 |
break; |
1548 |
} |
1549 |
|
1550 |
/* no match, return string */ |
1551 |
return (str); |
1552 |
} |
1553 |
|
1554 |
/* |
1555 |
* glob |
1556 |
* Name derived from V6's /etc/glob, the program that expanded filenames. |
1557 |
*/ |
1558 |
|
1559 |
/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */ |
1560 |
static void |
1561 |
glob(char *cp, XPtrV *wp, bool markdirs) |
1562 |
{ |
1563 |
int oldsize = XPsize(*wp); |
1564 |
|
1565 |
if (glob_str(cp, wp, markdirs) == 0) |
1566 |
XPput(*wp, debunk(cp, cp, strlen(cp) + 1)); |
1567 |
else |
1568 |
qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize, |
1569 |
sizeof(void *), ascpstrcmp); |
1570 |
} |
1571 |
|
1572 |
#define GF_NONE 0 |
1573 |
#define GF_EXCHECK BIT(0) /* do existence check on file */ |
1574 |
#define GF_GLOBBED BIT(1) /* some globbing has been done */ |
1575 |
#define GF_MARKDIR BIT(2) /* add trailing / to directories */ |
1576 |
|
1577 |
/* |
1578 |
* Apply file globbing to cp and store the matching files in wp. Returns |
1579 |
* the number of matches found. |
1580 |
*/ |
1581 |
int |
1582 |
glob_str(char *cp, XPtrV *wp, bool markdirs) |
1583 |
{ |
1584 |
int oldsize = XPsize(*wp); |
1585 |
XString xs; |
1586 |
char *xp; |
1587 |
|
1588 |
Xinit(xs, xp, 256, ATEMP); |
1589 |
globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); |
1590 |
Xfree(xs, xp); |
1591 |
|
1592 |
return (XPsize(*wp) - oldsize); |
1593 |
} |
1594 |
|
1595 |
static void |
1596 |
globit(XString *xs, /* dest string */ |
1597 |
char **xpp, /* ptr to dest end */ |
1598 |
char *sp, /* source path */ |
1599 |
XPtrV *wp, /* output list */ |
1600 |
int check) /* GF_* flags */ |
1601 |
{ |
1602 |
char *np; /* next source component */ |
1603 |
char *xp = *xpp; |
1604 |
char *se; |
1605 |
char odirsep; |
1606 |
|
1607 |
/* This to allow long expansions to be interrupted */ |
1608 |
intrcheck(); |
1609 |
|
1610 |
if (sp == NULL) { |
1611 |
/* end of source path */ |
1612 |
/* |
1613 |
* We only need to check if the file exists if a pattern |
1614 |
* is followed by a non-pattern (eg, foo*x/bar; no check |
1615 |
* is needed for foo* since the match must exist) or if |
1616 |
* any patterns were expanded and the markdirs option is set. |
1617 |
* Symlinks make things a bit tricky... |
1618 |
*/ |
1619 |
if ((check & GF_EXCHECK) || |
1620 |
((check & GF_MARKDIR) && (check & GF_GLOBBED))) { |
1621 |
#define stat_check() (stat_done ? stat_done : (stat_done = \ |
1622 |
stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1)) |
1623 |
struct stat lstatb, statb; |
1624 |
/* -1: failed, 1 ok, 0 not yet done */ |
1625 |
int stat_done = 0; |
1626 |
|
1627 |
if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0) |
1628 |
return; |
1629 |
/* |
1630 |
* special case for systems which strip trailing |
1631 |
* slashes from regular files (eg, /etc/passwd/). |
1632 |
* SunOS 4.1.3 does this... |
1633 |
*/ |
1634 |
if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) && |
1635 |
mksh_cdirsep(xp[-1]) && !S_ISDIR(lstatb.st_mode) && |
1636 |
(!S_ISLNK(lstatb.st_mode) || |
1637 |
stat_check() < 0 || !S_ISDIR(statb.st_mode))) |
1638 |
return; |
1639 |
/* |
1640 |
* Possibly tack on a trailing / if there isn't already |
1641 |
* one and if the file is a directory or a symlink to a |
1642 |
* directory |
1643 |
*/ |
1644 |
if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) && |
1645 |
xp > Xstring(*xs, xp) && !mksh_cdirsep(xp[-1]) && |
1646 |
(S_ISDIR(lstatb.st_mode) || |
1647 |
(S_ISLNK(lstatb.st_mode) && stat_check() > 0 && |
1648 |
S_ISDIR(statb.st_mode)))) { |
1649 |
*xp++ = '/'; |
1650 |
*xp = '\0'; |
1651 |
} |
1652 |
} |
1653 |
strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP); |
1654 |
XPput(*wp, np); |
1655 |
return; |
1656 |
} |
1657 |
|
1658 |
if (xp > Xstring(*xs, xp)) |
1659 |
*xp++ = '/'; |
1660 |
while (mksh_cdirsep(*sp)) { |
1661 |
Xcheck(*xs, xp); |
1662 |
*xp++ = *sp++; |
1663 |
} |
1664 |
np = mksh_sdirsep(sp); |
1665 |
if (np != NULL) { |
1666 |
se = np; |
1667 |
/* don't assume '/', can be multiple kinds */ |
1668 |
odirsep = *np; |
1669 |
*np++ = '\0'; |
1670 |
} else { |
1671 |
odirsep = '\0'; /* keep gcc quiet */ |
1672 |
se = strnul(sp); |
1673 |
} |
1674 |
|
1675 |
|
1676 |
/* |
1677 |
* Check if sp needs globbing - done to avoid pattern checks for strings |
1678 |
* containing MAGIC characters, open [s without the matching close ], |
1679 |
* etc. (otherwise opendir() will be called which may fail because the |
1680 |
* directory isn't readable - if no globbing is needed, only execute |
1681 |
* permission should be required (as per POSIX)). |
1682 |
*/ |
1683 |
if (!has_globbing(sp)) { |
1684 |
XcheckN(*xs, xp, se - sp + 1); |
1685 |
debunk(xp, sp, Xnleft(*xs, xp)); |
1686 |
xp = strnul(xp); |
1687 |
*xpp = xp; |
1688 |
globit(xs, xpp, np, wp, check); |
1689 |
} else { |
1690 |
DIR *dirp; |
1691 |
struct dirent *d; |
1692 |
char *name; |
1693 |
size_t len, prefix_len; |
1694 |
|
1695 |
/* xp = *xpp; copy_non_glob() may have re-alloc'd xs */ |
1696 |
*xp = '\0'; |
1697 |
prefix_len = Xlength(*xs, xp); |
1698 |
dirp = opendir(prefix_len ? Xstring(*xs, xp) : Tdot); |
1699 |
if (dirp == NULL) |
1700 |
goto Nodir; |
1701 |
while ((d = readdir(dirp)) != NULL) { |
1702 |
name = d->d_name; |
1703 |
if (name[0] == '.' && |
1704 |
(name[1] == 0 || (name[1] == '.' && name[2] == 0))) |
1705 |
/* always ignore . and .. */ |
1706 |
continue; |
1707 |
if ((*name == '.' && *sp != '.') || |
1708 |
!gmatchx(name, sp, true)) |
1709 |
continue; |
1710 |
|
1711 |
len = strlen(d->d_name) + 1; |
1712 |
XcheckN(*xs, xp, len); |
1713 |
memcpy(xp, name, len); |
1714 |
*xpp = xp + len - 1; |
1715 |
globit(xs, xpp, np, wp, (check & GF_MARKDIR) | |
1716 |
GF_GLOBBED | (np ? GF_EXCHECK : GF_NONE)); |
1717 |
xp = Xstring(*xs, xp) + prefix_len; |
1718 |
} |
1719 |
closedir(dirp); |
1720 |
Nodir: |
1721 |
; |
1722 |
} |
1723 |
|
1724 |
if (np != NULL) |
1725 |
*--np = odirsep; |
1726 |
} |
1727 |
|
1728 |
/* remove MAGIC from string */ |
1729 |
char * |
1730 |
debunk(char *dp, const char *sp, size_t dlen) |
1731 |
{ |
1732 |
char *d; |
1733 |
const char *s; |
1734 |
|
1735 |
if ((s = cstrchr(sp, MAGIC))) { |
1736 |
if (s - sp >= (ssize_t)dlen) |
1737 |
return (dp); |
1738 |
memmove(dp, sp, s - sp); |
1739 |
for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++) |
1740 |
if (!ISMAGIC(*s) || !(*++s & 0x80) || |
1741 |
!ctype(*s & 0x7F, C_PATMO | C_SPC)) |
1742 |
*d++ = *s; |
1743 |
else { |
1744 |
/* extended pattern operators: *+?@! */ |
1745 |
if ((*s & 0x7f) != ' ') |
1746 |
*d++ = *s & 0x7f; |
1747 |
if (d - dp < (ssize_t)dlen) |
1748 |
*d++ = '('; |
1749 |
} |
1750 |
*d = '\0'; |
1751 |
} else if (dp != sp) |
1752 |
strlcpy(dp, sp, dlen); |
1753 |
return (dp); |
1754 |
} |
1755 |
|
1756 |
/* |
1757 |
* Check if p is an unquoted name, possibly followed by a / or :. If so |
1758 |
* puts the expanded version in *dcp,dp and returns a pointer in p just |
1759 |
* past the name, otherwise returns 0. |
1760 |
*/ |
1761 |
static const char * |
1762 |
maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign) |
1763 |
{ |
1764 |
XString ts; |
1765 |
char *dp = *dpp; |
1766 |
char *tp; |
1767 |
const char *r; |
1768 |
|
1769 |
Xinit(ts, tp, 16, ATEMP); |
1770 |
/* : only for DOASNTILDE form */ |
1771 |
while (p[0] == CHAR && /* not cdirsep */ p[1] != '/' && |
1772 |
(!isassign || p[1] != ':')) { |
1773 |
Xcheck(ts, tp); |
1774 |
*tp++ = p[1]; |
1775 |
p += 2; |
1776 |
} |
1777 |
*tp = '\0'; |
1778 |
r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? |
1779 |
do_tilde(Xstring(ts, tp)) : NULL; |
1780 |
Xfree(ts, tp); |
1781 |
if (r) { |
1782 |
while (*r) { |
1783 |
Xcheck(*dsp, dp); |
1784 |
if (ISMAGIC(*r)) |
1785 |
*dp++ = MAGIC; |
1786 |
*dp++ = *r++; |
1787 |
} |
1788 |
*dpp = dp; |
1789 |
r = p; |
1790 |
} |
1791 |
return (r); |
1792 |
} |
1793 |
|
1794 |
/* |
1795 |
* tilde expansion |
1796 |
* |
1797 |
* based on a version by Arnold Robbins |
1798 |
*/ |
1799 |
char * |
1800 |
do_tilde(char *cp) |
1801 |
{ |
1802 |
char *dp = null; |
1803 |
#ifndef MKSH_NOPWNAM |
1804 |
bool do_simplify = true; |
1805 |
#endif |
1806 |
|
1807 |
if (cp[0] == '\0') |
1808 |
dp = str_val(global("HOME")); |
1809 |
else if (cp[0] == '+' && cp[1] == '\0') |
1810 |
dp = str_val(global(TPWD)); |
1811 |
else if (ksh_isdash(cp)) |
1812 |
dp = str_val(global(TOLDPWD)); |
1813 |
#ifndef MKSH_NOPWNAM |
1814 |
else { |
1815 |
dp = homedir(cp); |
1816 |
do_simplify = false; |
1817 |
} |
1818 |
#endif |
1819 |
|
1820 |
/* if parameters aren't set, don't expand ~ */ |
1821 |
if (dp == NULL || dp == null) |
1822 |
return (NULL); |
1823 |
|
1824 |
/* simplify parameters as if cwd upon entry */ |
1825 |
#ifndef MKSH_NOPWNAM |
1826 |
if (do_simplify) |
1827 |
#endif |
1828 |
{ |
1829 |
strdupx(dp, dp, ATEMP); |
1830 |
simplify_path(dp); |
1831 |
} |
1832 |
return (dp); |
1833 |
} |
1834 |
|
1835 |
#ifndef MKSH_NOPWNAM |
1836 |
/* |
1837 |
* map userid to user's home directory. |
1838 |
* note that 4.3's getpw adds more than 6K to the shell, |
1839 |
* and the YP version probably adds much more. |
1840 |
* we might consider our own version of getpwnam() to keep the size down. |
1841 |
*/ |
1842 |
static char * |
1843 |
homedir(char *name) |
1844 |
{ |
1845 |
struct tbl *ap; |
1846 |
|
1847 |
ap = ktenter(&homedirs, name, hash(name)); |
1848 |
if (!(ap->flag & ISSET)) { |
1849 |
struct passwd *pw; |
1850 |
|
1851 |
pw = getpwnam(name); |
1852 |
if (pw == NULL) |
1853 |
return (NULL); |
1854 |
strdupx(ap->val.s, pw->pw_dir, APERM); |
1855 |
ap->flag |= DEFINED|ISSET|ALLOC; |
1856 |
} |
1857 |
return (ap->val.s); |
1858 |
} |
1859 |
#endif |
1860 |
|
1861 |
static void |
1862 |
alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo) |
1863 |
{ |
1864 |
unsigned int count = 0; |
1865 |
char *brace_start, *brace_end, *comma = NULL; |
1866 |
char *field_start; |
1867 |
char *p = exp_start; |
1868 |
|
1869 |
/* search for open brace */ |
1870 |
while ((p = strchr(p, MAGIC)) && ord(p[1]) != ORD('{' /*}*/)) |
1871 |
p += 2; |
1872 |
brace_start = p; |
1873 |
|
1874 |
/* find matching close brace, if any */ |
1875 |
if (p) { |
1876 |
comma = NULL; |
1877 |
count = 1; |
1878 |
p += 2; |
1879 |
while (*p && count) { |
1880 |
if (ISMAGIC(*p++)) { |
1881 |
if (ord(*p) == ORD('{' /*}*/)) |
1882 |
++count; |
1883 |
else if (ord(*p) == ORD(/*{*/ '}')) |
1884 |
--count; |
1885 |
else if (*p == ',' && count == 1) |
1886 |
comma = p; |
1887 |
++p; |
1888 |
} |
1889 |
} |
1890 |
} |
1891 |
/* no valid expansions... */ |
1892 |
if (!p || count != 0) { |
1893 |
/* |
1894 |
* Note that given a{{b,c} we do not expand anything (this is |
1895 |
* what AT&T ksh does. This may be changed to do the {b,c} |
1896 |
* expansion. } |
1897 |
*/ |
1898 |
if (fdo & DOGLOB) |
1899 |
glob(start, wp, tobool(fdo & DOMARKDIRS)); |
1900 |
else |
1901 |
XPput(*wp, debunk(start, start, end - start)); |
1902 |
return; |
1903 |
} |
1904 |
brace_end = p; |
1905 |
if (!comma) { |
1906 |
alt_expand(wp, start, brace_end, end, fdo); |
1907 |
return; |
1908 |
} |
1909 |
|
1910 |
/* expand expression */ |
1911 |
field_start = brace_start + 2; |
1912 |
count = 1; |
1913 |
for (p = brace_start + 2; p != brace_end; p++) { |
1914 |
if (ISMAGIC(*p)) { |
1915 |
if (ord(*++p) == ORD('{' /*}*/)) |
1916 |
++count; |
1917 |
else if ((ord(*p) == ORD(/*{*/ '}') && --count == 0) || |
1918 |
(*p == ',' && count == 1)) { |
1919 |
char *news; |
1920 |
int l1, l2, l3; |
1921 |
|
1922 |
/* |
1923 |
* addition safe since these operate on |
1924 |
* one string (separate substrings) |
1925 |
*/ |
1926 |
l1 = brace_start - start; |
1927 |
l2 = (p - 1) - field_start; |
1928 |
l3 = end - brace_end; |
1929 |
news = alloc(l1 + l2 + l3 + 1, ATEMP); |
1930 |
memcpy(news, start, l1); |
1931 |
memcpy(news + l1, field_start, l2); |
1932 |
memcpy(news + l1 + l2, brace_end, l3); |
1933 |
news[l1 + l2 + l3] = '\0'; |
1934 |
alt_expand(wp, news, news + l1, |
1935 |
news + l1 + l2 + l3, fdo); |
1936 |
field_start = p + 1; |
1937 |
} |
1938 |
} |
1939 |
} |
1940 |
return; |
1941 |
} |
1942 |
|
1943 |
/* helper function due to setjmp/longjmp woes */ |
1944 |
static char * |
1945 |
valsub(struct op *t, Area *ap) |
1946 |
{ |
1947 |
char * volatile cp = NULL; |
1948 |
struct tbl * volatile vp = NULL; |
1949 |
|
1950 |
newenv(E_FUNC); |
1951 |
newblock(); |
1952 |
if (ap) |
1953 |
vp = local("REPLY", false); |
1954 |
if (!kshsetjmp(e->jbuf)) |
1955 |
execute(t, XXCOM | XERROK, NULL); |
1956 |
if (vp) |
1957 |
strdupx(cp, str_val(vp), ap); |
1958 |
quitenv(NULL); |
1959 |
|
1960 |
return (cp); |
1961 |
} |