]>
Commit | Line | Data |
---|---|---|
71aad674 A |
1 | /*- |
2 | * Copyright (c) 1991, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * Copyright (c) 1997-2005 | |
5 | * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. | |
deb63bfb A |
6 | * Copyright (c) 2010-2015 |
7 | * Jilles Tjoelker <jilles@stack.nl>. All rights reserved. | |
71aad674 A |
8 | * |
9 | * This code is derived from software contributed to Berkeley by | |
10 | * Kenneth Almquist. | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
254f12f7 | 20 | * 3. Neither the name of the University nor the names of its contributors |
71aad674 A |
21 | * may be used to endorse or promote products derived from this software |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | */ | |
36 | ||
37 | #ifndef lint | |
38 | #if 0 | |
39 | static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; | |
40 | #endif | |
41 | #endif /* not lint */ | |
42 | #include <sys/cdefs.h> | |
254f12f7 | 43 | __FBSDID("$FreeBSD: head/bin/sh/expand.c 318269 2017-05-14 13:14:19Z jilles $"); |
71aad674 A |
44 | |
45 | #include <sys/types.h> | |
46 | #include <sys/time.h> | |
47 | #include <sys/stat.h> | |
48 | #include <dirent.h> | |
49 | #include <errno.h> | |
50 | #include <inttypes.h> | |
51 | #include <limits.h> | |
52 | #include <pwd.h> | |
53 | #include <stdio.h> | |
54 | #include <stdlib.h> | |
55 | #include <string.h> | |
56 | #include <unistd.h> | |
57 | #include <wchar.h> | |
58 | #include <wctype.h> | |
59 | ||
60 | /* | |
61 | * Routines to expand arguments to commands. We have to deal with | |
62 | * backquotes, shell variables, and file metacharacters. | |
63 | */ | |
64 | ||
65 | #include "shell.h" | |
66 | #include "main.h" | |
67 | #include "nodes.h" | |
68 | #include "eval.h" | |
69 | #include "expand.h" | |
70 | #include "syntax.h" | |
71 | #include "parser.h" | |
72 | #include "jobs.h" | |
73 | #include "options.h" | |
74 | #include "var.h" | |
75 | #include "input.h" | |
76 | #include "output.h" | |
77 | #include "memalloc.h" | |
78 | #include "error.h" | |
79 | #include "mystring.h" | |
80 | #include "arith.h" | |
81 | #include "show.h" | |
82 | #include "builtins.h" | |
83 | ||
deb63bfb | 84 | enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK }; |
71aad674 | 85 | |
deb63bfb A |
86 | struct worddest { |
87 | struct arglist *list; | |
88 | enum wordstate state; | |
71aad674 A |
89 | }; |
90 | ||
71aad674 | 91 | static char *expdest; /* output of current string */ |
deb63bfb | 92 | |
254f12f7 A |
93 | static const char *argstr(const char *, struct nodelist **restrict, int, |
94 | struct worddest *); | |
deb63bfb | 95 | static const char *exptilde(const char *, int); |
254f12f7 A |
96 | static const char *expari(const char *, struct nodelist **restrict, int, |
97 | struct worddest *); | |
deb63bfb | 98 | static void expbackq(union node *, int, int, struct worddest *); |
254f12f7 A |
99 | static const char *subevalvar_trim(const char *, struct nodelist **restrict, |
100 | int, int, int); | |
101 | static const char *subevalvar_misc(const char *, struct nodelist **restrict, | |
102 | const char *, int, int, int); | |
103 | static const char *evalvar(const char *, struct nodelist **restrict, int, | |
104 | struct worddest *); | |
71aad674 | 105 | static int varisset(const char *, int); |
deb63bfb A |
106 | static void strtodest(const char *, int, int, int, struct worddest *); |
107 | static void reprocess(int, int, int, int, struct worddest *); | |
108 | static void varvalue(const char *, int, int, int, struct worddest *); | |
109 | static void expandmeta(char *, struct arglist *); | |
110 | static void expmeta(char *, char *, struct arglist *); | |
111 | static int expsortcmp(const void *, const void *); | |
112 | static int patmatch(const char *, const char *); | |
113 | static void cvtnum(int, char *); | |
71aad674 A |
114 | static int collate_range_cmp(wchar_t, wchar_t); |
115 | ||
deb63bfb A |
116 | void |
117 | emptyarglist(struct arglist *list) | |
118 | { | |
119 | ||
120 | list->args = list->smallarg; | |
121 | list->count = 0; | |
122 | list->capacity = sizeof(list->smallarg) / sizeof(list->smallarg[0]); | |
123 | } | |
124 | ||
125 | void | |
126 | appendarglist(struct arglist *list, char *str) | |
127 | { | |
128 | char **newargs; | |
129 | int newcapacity; | |
130 | ||
131 | if (list->count >= list->capacity) { | |
132 | newcapacity = list->capacity * 2; | |
133 | if (newcapacity < 16) | |
134 | newcapacity = 16; | |
135 | if (newcapacity > INT_MAX / (int)sizeof(newargs[0])) | |
136 | error("Too many entries in arglist"); | |
137 | newargs = stalloc(newcapacity * sizeof(newargs[0])); | |
138 | memcpy(newargs, list->args, list->count * sizeof(newargs[0])); | |
139 | list->args = newargs; | |
140 | list->capacity = newcapacity; | |
141 | } | |
142 | list->args[list->count++] = str; | |
143 | } | |
144 | ||
71aad674 A |
145 | static int |
146 | collate_range_cmp(wchar_t c1, wchar_t c2) | |
147 | { | |
254f12f7 | 148 | wchar_t s1[2], s2[2]; |
71aad674 A |
149 | |
150 | s1[0] = c1; | |
254f12f7 | 151 | s1[1] = L'\0'; |
71aad674 | 152 | s2[0] = c2; |
254f12f7 | 153 | s2[1] = L'\0'; |
71aad674 A |
154 | return (wcscoll(s1, s2)); |
155 | } | |
156 | ||
157 | static char * | |
158 | stputs_quotes(const char *data, const char *syntax, char *p) | |
159 | { | |
160 | while (*data) { | |
161 | CHECKSTRSPACE(2, p); | |
162 | if (syntax[(int)*data] == CCTL) | |
163 | USTPUTC(CTLESC, p); | |
164 | USTPUTC(*data++, p); | |
165 | } | |
166 | return (p); | |
167 | } | |
168 | #define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) | |
169 | ||
deb63bfb A |
170 | static char * |
171 | nextword(char c, int flag, char *p, struct worddest *dst) | |
172 | { | |
173 | int is_ws; | |
174 | ||
175 | is_ws = c == '\t' || c == '\n' || c == ' '; | |
176 | if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK : | |
177 | dst->state != WORD_WS_DELIMITED) || c == '\0') { | |
178 | STPUTC('\0', p); | |
179 | if (flag & EXP_GLOB) | |
180 | expandmeta(grabstackstr(p), dst->list); | |
181 | else | |
182 | appendarglist(dst->list, grabstackstr(p)); | |
183 | dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE; | |
184 | } else if (!is_ws && dst->state == WORD_WS_DELIMITED) | |
185 | dst->state = WORD_IDLE; | |
186 | /* Reserve space while the stack string is empty. */ | |
187 | appendarglist(dst->list, NULL); | |
188 | dst->list->count--; | |
189 | STARTSTACKSTR(p); | |
190 | return p; | |
191 | } | |
192 | #define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist) | |
193 | ||
194 | static char * | |
195 | stputs_split(const char *data, const char *syntax, int flag, char *p, | |
196 | struct worddest *dst) | |
197 | { | |
198 | const char *ifs; | |
199 | char c; | |
200 | ||
201 | ifs = ifsset() ? ifsval() : " \t\n"; | |
202 | while (*data) { | |
203 | CHECKSTRSPACE(2, p); | |
204 | c = *data++; | |
205 | if (strchr(ifs, c) != NULL) { | |
206 | NEXTWORD(c, flag, p, dst); | |
207 | continue; | |
208 | } | |
209 | if (flag & EXP_GLOB && syntax[(int)c] == CCTL) | |
210 | USTPUTC(CTLESC, p); | |
211 | USTPUTC(c, p); | |
212 | } | |
213 | return (p); | |
214 | } | |
215 | #define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst) | |
216 | ||
71aad674 A |
217 | /* |
218 | * Perform expansions on an argument, placing the resulting list of arguments | |
219 | * in arglist. Parameter expansion, command substitution and arithmetic | |
220 | * expansion are always performed; additional expansions can be requested | |
221 | * via flag (EXP_*). | |
222 | * The result is left in the stack string. | |
223 | * When arglist is NULL, perform here document expansion. | |
224 | * | |
254f12f7 A |
225 | * When doing something that may cause this to be re-entered, make sure |
226 | * the stack string is empty via grabstackstr() and do not assume expdest | |
227 | * remains valid. | |
71aad674 A |
228 | */ |
229 | void | |
230 | expandarg(union node *arg, struct arglist *arglist, int flag) | |
231 | { | |
deb63bfb | 232 | struct worddest exparg; |
254f12f7 | 233 | struct nodelist *argbackq; |
71aad674 | 234 | |
deb63bfb A |
235 | if (fflag) |
236 | flag &= ~EXP_GLOB; | |
71aad674 | 237 | argbackq = arg->narg.backquote; |
deb63bfb A |
238 | exparg.list = arglist; |
239 | exparg.state = WORD_IDLE; | |
71aad674 | 240 | STARTSTACKSTR(expdest); |
254f12f7 | 241 | argstr(arg->narg.text, &argbackq, flag, &exparg); |
71aad674 A |
242 | if (arglist == NULL) { |
243 | STACKSTRNUL(expdest); | |
244 | return; /* here document expanded */ | |
245 | } | |
deb63bfb A |
246 | if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() || |
247 | exparg.state == WORD_QUOTEMARK) { | |
248 | STPUTC('\0', expdest); | |
249 | if (flag & EXP_SPLIT) { | |
250 | if (flag & EXP_GLOB) | |
251 | expandmeta(grabstackstr(expdest), exparg.list); | |
252 | else | |
253 | appendarglist(exparg.list, grabstackstr(expdest)); | |
254 | } | |
71aad674 | 255 | } |
deb63bfb A |
256 | if ((flag & EXP_SPLIT) == 0) |
257 | appendarglist(arglist, grabstackstr(expdest)); | |
71aad674 A |
258 | } |
259 | ||
260 | ||
261 | ||
262 | /* | |
263 | * Perform parameter expansion, command substitution and arithmetic | |
264 | * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. | |
265 | * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. | |
266 | * This is used to expand word in ${var+word} etc. | |
deb63bfb | 267 | * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC |
71aad674 | 268 | * characters to allow for further processing. |
deb63bfb A |
269 | * |
270 | * If EXP_SPLIT is set, dst receives any complete words produced. | |
71aad674 | 271 | */ |
deb63bfb | 272 | static const char * |
254f12f7 A |
273 | argstr(const char *p, struct nodelist **restrict argbackq, int flag, |
274 | struct worddest *dst) | |
71aad674 A |
275 | { |
276 | char c; | |
deb63bfb | 277 | int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */ |
71aad674 A |
278 | int firsteq = 1; |
279 | int split_lit; | |
280 | int lit_quoted; | |
281 | ||
282 | split_lit = flag & EXP_SPLIT_LIT; | |
283 | lit_quoted = flag & EXP_LIT_QUOTED; | |
284 | flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); | |
285 | if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) | |
286 | p = exptilde(p, flag); | |
287 | for (;;) { | |
288 | CHECKSTRSPACE(2, expdest); | |
289 | switch (c = *p++) { | |
290 | case '\0': | |
291 | return (p - 1); | |
292 | case CTLENDVAR: | |
293 | case CTLENDARI: | |
294 | return (p); | |
295 | case CTLQUOTEMARK: | |
296 | lit_quoted = 1; | |
297 | /* "$@" syntax adherence hack */ | |
deb63bfb A |
298 | if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && |
299 | p[2] == '@' && p[3] == '=') | |
71aad674 | 300 | break; |
deb63bfb A |
301 | if ((flag & EXP_SPLIT) != 0 && expdest == stackblock()) |
302 | dst->state = WORD_QUOTEMARK; | |
71aad674 A |
303 | break; |
304 | case CTLQUOTEEND: | |
305 | lit_quoted = 0; | |
306 | break; | |
307 | case CTLESC: | |
71aad674 | 308 | c = *p++; |
deb63bfb A |
309 | if (split_lit && !lit_quoted && |
310 | strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { | |
311 | NEXTWORD(c, flag, expdest, dst); | |
312 | break; | |
313 | } | |
314 | if (quotes) | |
315 | USTPUTC(CTLESC, expdest); | |
71aad674 | 316 | USTPUTC(c, expdest); |
71aad674 A |
317 | break; |
318 | case CTLVAR: | |
254f12f7 | 319 | p = evalvar(p, argbackq, flag, dst); |
71aad674 A |
320 | break; |
321 | case CTLBACKQ: | |
322 | case CTLBACKQ|CTLQUOTE: | |
254f12f7 A |
323 | expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst); |
324 | *argbackq = (*argbackq)->next; | |
71aad674 A |
325 | break; |
326 | case CTLARI: | |
254f12f7 | 327 | p = expari(p, argbackq, flag, dst); |
71aad674 A |
328 | break; |
329 | case ':': | |
330 | case '=': | |
331 | /* | |
332 | * sort of a hack - expand tildes in variable | |
333 | * assignments (after the first '=' and after ':'s). | |
334 | */ | |
deb63bfb A |
335 | if (split_lit && !lit_quoted && |
336 | strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { | |
337 | NEXTWORD(c, flag, expdest, dst); | |
338 | break; | |
339 | } | |
71aad674 | 340 | USTPUTC(c, expdest); |
71aad674 A |
341 | if (flag & EXP_VARTILDE && *p == '~' && |
342 | (c != '=' || firsteq)) { | |
343 | if (c == '=') | |
344 | firsteq = 0; | |
345 | p = exptilde(p, flag); | |
346 | } | |
347 | break; | |
348 | default: | |
deb63bfb A |
349 | if (split_lit && !lit_quoted && |
350 | strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { | |
351 | NEXTWORD(c, flag, expdest, dst); | |
352 | break; | |
353 | } | |
71aad674 | 354 | USTPUTC(c, expdest); |
71aad674 A |
355 | } |
356 | } | |
357 | } | |
358 | ||
359 | /* | |
360 | * Perform tilde expansion, placing the result in the stack string and | |
361 | * returning the next position in the input string to process. | |
362 | */ | |
deb63bfb A |
363 | static const char * |
364 | exptilde(const char *p, int flag) | |
71aad674 | 365 | { |
deb63bfb A |
366 | char c; |
367 | const char *startp = p; | |
368 | const char *user; | |
71aad674 A |
369 | struct passwd *pw; |
370 | char *home; | |
deb63bfb | 371 | int len; |
71aad674 A |
372 | |
373 | for (;;) { | |
374 | c = *p; | |
375 | switch(c) { | |
376 | case CTLESC: /* This means CTL* are always considered quoted. */ | |
377 | case CTLVAR: | |
378 | case CTLBACKQ: | |
379 | case CTLBACKQ | CTLQUOTE: | |
380 | case CTLARI: | |
381 | case CTLENDARI: | |
382 | case CTLQUOTEMARK: | |
383 | return (startp); | |
384 | case ':': | |
385 | if ((flag & EXP_VARTILDE) == 0) | |
386 | break; | |
387 | /* FALLTHROUGH */ | |
388 | case '\0': | |
389 | case '/': | |
390 | case CTLENDVAR: | |
deb63bfb A |
391 | len = p - startp - 1; |
392 | STPUTBIN(startp + 1, len, expdest); | |
393 | STACKSTRNUL(expdest); | |
394 | user = expdest - len; | |
395 | if (*user == '\0') { | |
71aad674 A |
396 | home = lookupvar("HOME"); |
397 | } else { | |
deb63bfb | 398 | pw = getpwnam(user); |
71aad674 A |
399 | home = pw != NULL ? pw->pw_dir : NULL; |
400 | } | |
deb63bfb | 401 | STADJUST(-len, expdest); |
71aad674 A |
402 | if (home == NULL || *home == '\0') |
403 | return (startp); | |
deb63bfb | 404 | strtodest(home, flag, VSNORMAL, 1, NULL); |
71aad674 A |
405 | return (p); |
406 | } | |
407 | p++; | |
408 | } | |
409 | } | |
410 | ||
411 | ||
71aad674 A |
412 | /* |
413 | * Expand arithmetic expression. | |
71aad674 | 414 | */ |
deb63bfb | 415 | static const char * |
254f12f7 A |
416 | expari(const char *p, struct nodelist **restrict argbackq, int flag, |
417 | struct worddest *dst) | |
71aad674 A |
418 | { |
419 | char *q, *start; | |
420 | arith_t result; | |
421 | int begoff; | |
422 | int quoted; | |
423 | int adj; | |
424 | ||
425 | quoted = *p++ == '"'; | |
426 | begoff = expdest - stackblock(); | |
254f12f7 | 427 | p = argstr(p, argbackq, 0, NULL); |
71aad674 A |
428 | STPUTC('\0', expdest); |
429 | start = stackblock() + begoff; | |
430 | ||
431 | q = grabstackstr(expdest); | |
432 | result = arith(start); | |
433 | ungrabstackstr(q, expdest); | |
434 | ||
435 | start = stackblock() + begoff; | |
436 | adj = start - expdest; | |
437 | STADJUST(adj, expdest); | |
438 | ||
439 | CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); | |
440 | fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); | |
441 | adj = strlen(expdest); | |
442 | STADJUST(adj, expdest); | |
254f12f7 A |
443 | /* |
444 | * If this is quoted, a '-' must not indicate a range in [...]. | |
445 | * If this is not quoted, splitting may occur. | |
446 | */ | |
447 | if (quoted ? | |
448 | result < 0 && begoff > 1 && flag & (EXP_GLOB | EXP_CASE) : | |
449 | flag & EXP_SPLIT) | |
450 | reprocess(expdest - adj - stackblock(), flag, VSNORMAL, quoted, | |
451 | dst); | |
71aad674 A |
452 | return p; |
453 | } | |
454 | ||
455 | ||
456 | /* | |
457 | * Perform command substitution. | |
458 | */ | |
459 | static void | |
deb63bfb | 460 | expbackq(union node *cmd, int quoted, int flag, struct worddest *dst) |
71aad674 A |
461 | { |
462 | struct backcmd in; | |
463 | int i; | |
464 | char buf[128]; | |
465 | char *p; | |
466 | char *dest = expdest; | |
71aad674 | 467 | char lastc; |
71aad674 | 468 | char const *syntax = quoted? DQSYNTAX : BASESYNTAX; |
deb63bfb | 469 | int quotes = flag & (EXP_GLOB | EXP_CASE); |
71aad674 | 470 | size_t nnl; |
deb63bfb | 471 | const char *ifs; |
254f12f7 | 472 | int startloc; |
71aad674 A |
473 | |
474 | INTOFF; | |
71aad674 A |
475 | p = grabstackstr(dest); |
476 | evalbackcmd(cmd, &in); | |
477 | ungrabstackstr(p, dest); | |
71aad674 A |
478 | |
479 | p = in.buf; | |
254f12f7 | 480 | startloc = dest - stackblock(); |
71aad674 | 481 | nnl = 0; |
deb63bfb A |
482 | if (!quoted && flag & EXP_SPLIT) |
483 | ifs = ifsset() ? ifsval() : " \t\n"; | |
484 | else | |
485 | ifs = ""; | |
254f12f7 | 486 | /* Remove trailing newlines */ |
71aad674 A |
487 | for (;;) { |
488 | if (--in.nleft < 0) { | |
489 | if (in.fd < 0) | |
490 | break; | |
deb63bfb A |
491 | while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) |
492 | ; | |
71aad674 A |
493 | TRACE(("expbackq: read returns %d\n", i)); |
494 | if (i <= 0) | |
495 | break; | |
496 | p = buf; | |
497 | in.nleft = i - 1; | |
498 | } | |
499 | lastc = *p++; | |
deb63bfb A |
500 | if (lastc == '\0') |
501 | continue; | |
254f12f7 A |
502 | if (nnl > 0 && lastc != '\n') { |
503 | NEXTWORD('\n', flag, dest, dst); | |
504 | nnl = 0; | |
505 | } | |
506 | if (strchr(ifs, lastc) != NULL) { | |
507 | if (lastc == '\n') | |
508 | nnl++; | |
509 | else | |
deb63bfb | 510 | NEXTWORD(lastc, flag, dest, dst); |
254f12f7 A |
511 | } else { |
512 | CHECKSTRSPACE(2, dest); | |
513 | if (quotes && syntax[(int)lastc] == CCTL) | |
514 | USTPUTC(CTLESC, dest); | |
515 | USTPUTC(lastc, dest); | |
71aad674 A |
516 | } |
517 | } | |
254f12f7 A |
518 | while (dest > stackblock() + startloc && STTOPC(dest) == '\n') |
519 | STUNPUTC(dest); | |
71aad674 A |
520 | |
521 | if (in.fd >= 0) | |
522 | close(in.fd); | |
523 | if (in.buf) | |
524 | ckfree(in.buf); | |
254f12f7 A |
525 | if (in.jp) { |
526 | p = grabstackstr(dest); | |
71aad674 | 527 | exitstatus = waitforjob(in.jp, (int *)NULL); |
254f12f7 A |
528 | ungrabstackstr(p, dest); |
529 | } | |
530 | TRACE(("expbackq: done\n")); | |
71aad674 A |
531 | expdest = dest; |
532 | INTON; | |
533 | } | |
534 | ||
535 | ||
536 | ||
537 | static void | |
538 | recordleft(const char *str, const char *loc, char *startp) | |
539 | { | |
540 | int amount; | |
541 | ||
542 | amount = ((str - 1) - (loc - startp)) - expdest; | |
543 | STADJUST(amount, expdest); | |
544 | while (loc != str - 1) | |
545 | *startp++ = *loc++; | |
546 | } | |
547 | ||
254f12f7 A |
548 | static const char * |
549 | subevalvar_trim(const char *p, struct nodelist **restrict argbackq, int strloc, | |
550 | int subtype, int startloc) | |
71aad674 A |
551 | { |
552 | char *startp; | |
553 | char *loc = NULL; | |
deb63bfb | 554 | char *str; |
71aad674 | 555 | int c = 0; |
71aad674 A |
556 | int amount; |
557 | ||
254f12f7 | 558 | p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL); |
71aad674 | 559 | STACKSTRNUL(expdest); |
71aad674 | 560 | startp = stackblock() + startloc; |
deb63bfb | 561 | str = stackblock() + strloc; |
71aad674 A |
562 | |
563 | switch (subtype) { | |
71aad674 A |
564 | case VSTRIMLEFT: |
565 | for (loc = startp; loc < str; loc++) { | |
566 | c = *loc; | |
567 | *loc = '\0'; | |
deb63bfb | 568 | if (patmatch(str, startp)) { |
71aad674 A |
569 | *loc = c; |
570 | recordleft(str, loc, startp); | |
254f12f7 | 571 | return p; |
71aad674 A |
572 | } |
573 | *loc = c; | |
71aad674 | 574 | } |
deb63bfb | 575 | break; |
71aad674 A |
576 | |
577 | case VSTRIMLEFTMAX: | |
578 | for (loc = str - 1; loc >= startp;) { | |
579 | c = *loc; | |
580 | *loc = '\0'; | |
deb63bfb | 581 | if (patmatch(str, startp)) { |
71aad674 A |
582 | *loc = c; |
583 | recordleft(str, loc, startp); | |
254f12f7 | 584 | return p; |
71aad674 A |
585 | } |
586 | *loc = c; | |
587 | loc--; | |
71aad674 | 588 | } |
deb63bfb | 589 | break; |
71aad674 A |
590 | |
591 | case VSTRIMRIGHT: | |
592 | for (loc = str - 1; loc >= startp;) { | |
deb63bfb | 593 | if (patmatch(str, loc)) { |
71aad674 A |
594 | amount = loc - expdest; |
595 | STADJUST(amount, expdest); | |
254f12f7 | 596 | return p; |
71aad674 A |
597 | } |
598 | loc--; | |
71aad674 | 599 | } |
deb63bfb | 600 | break; |
71aad674 A |
601 | |
602 | case VSTRIMRIGHTMAX: | |
603 | for (loc = startp; loc < str - 1; loc++) { | |
deb63bfb | 604 | if (patmatch(str, loc)) { |
71aad674 A |
605 | amount = loc - expdest; |
606 | STADJUST(amount, expdest); | |
254f12f7 | 607 | return p; |
71aad674 | 608 | } |
71aad674 | 609 | } |
deb63bfb A |
610 | break; |
611 | ||
612 | ||
613 | default: | |
614 | abort(); | |
615 | } | |
616 | amount = (expdest - stackblock() - strloc) + 1; | |
617 | STADJUST(-amount, expdest); | |
254f12f7 | 618 | return p; |
deb63bfb A |
619 | } |
620 | ||
621 | ||
254f12f7 A |
622 | static const char * |
623 | subevalvar_misc(const char *p, struct nodelist **restrict argbackq, | |
624 | const char *var, int subtype, int startloc, int varflags) | |
deb63bfb A |
625 | { |
626 | char *startp; | |
deb63bfb | 627 | int amount; |
71aad674 | 628 | |
254f12f7 | 629 | p = argstr(p, argbackq, EXP_TILDE, NULL); |
deb63bfb | 630 | STACKSTRNUL(expdest); |
deb63bfb A |
631 | startp = stackblock() + startloc; |
632 | ||
633 | switch (subtype) { | |
634 | case VSASSIGN: | |
635 | setvar(var, startp, 0); | |
636 | amount = startp - expdest; | |
637 | STADJUST(amount, expdest); | |
254f12f7 | 638 | return p; |
deb63bfb A |
639 | |
640 | case VSQUESTION: | |
641 | if (*p != CTLENDVAR) { | |
642 | outfmt(out2, "%s\n", startp); | |
643 | error((char *)NULL); | |
644 | } | |
645 | error("%.*s: parameter %snot set", (int)(p - var - 1), | |
646 | var, (varflags & VSNUL) ? "null or " : ""); | |
71aad674 A |
647 | |
648 | default: | |
649 | abort(); | |
650 | } | |
651 | } | |
652 | ||
653 | ||
654 | /* | |
655 | * Expand a variable, and return a pointer to the next character in the | |
656 | * input string. | |
657 | */ | |
658 | ||
deb63bfb | 659 | static const char * |
254f12f7 A |
660 | evalvar(const char *p, struct nodelist **restrict argbackq, int flag, |
661 | struct worddest *dst) | |
71aad674 A |
662 | { |
663 | int subtype; | |
664 | int varflags; | |
deb63bfb | 665 | const char *var; |
71aad674 A |
666 | const char *val; |
667 | int patloc; | |
668 | int c; | |
669 | int set; | |
670 | int special; | |
671 | int startloc; | |
672 | int varlen; | |
673 | int varlenb; | |
deb63bfb | 674 | char buf[21]; |
71aad674 A |
675 | |
676 | varflags = (unsigned char)*p++; | |
677 | subtype = varflags & VSTYPE; | |
678 | var = p; | |
679 | special = 0; | |
680 | if (! is_name(*p)) | |
681 | special = 1; | |
682 | p = strchr(p, '=') + 1; | |
71aad674 A |
683 | if (varflags & VSLINENO) { |
684 | set = 1; | |
685 | special = 1; | |
686 | val = NULL; | |
687 | } else if (special) { | |
688 | set = varisset(var, varflags & VSNUL); | |
689 | val = NULL; | |
690 | } else { | |
691 | val = bltinlookup(var, 1); | |
692 | if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { | |
693 | val = NULL; | |
694 | set = 0; | |
695 | } else | |
696 | set = 1; | |
697 | } | |
698 | varlen = 0; | |
699 | startloc = expdest - stackblock(); | |
700 | if (!set && uflag && *var != '@' && *var != '*') { | |
701 | switch (subtype) { | |
702 | case VSNORMAL: | |
703 | case VSTRIMLEFT: | |
704 | case VSTRIMLEFTMAX: | |
705 | case VSTRIMRIGHT: | |
706 | case VSTRIMRIGHTMAX: | |
707 | case VSLENGTH: | |
708 | error("%.*s: parameter not set", (int)(p - var - 1), | |
709 | var); | |
710 | } | |
711 | } | |
712 | if (set && subtype != VSPLUS) { | |
713 | /* insert the value of the variable */ | |
714 | if (special) { | |
deb63bfb A |
715 | if (varflags & VSLINENO) { |
716 | if (p - var > (ptrdiff_t)sizeof(buf)) | |
717 | abort(); | |
718 | memcpy(buf, var, p - var - 1); | |
719 | buf[p - var - 1] = '\0'; | |
720 | strtodest(buf, flag, subtype, | |
721 | varflags & VSQUOTE, dst); | |
722 | } else | |
723 | varvalue(var, varflags & VSQUOTE, subtype, flag, | |
724 | dst); | |
71aad674 A |
725 | if (subtype == VSLENGTH) { |
726 | varlenb = expdest - stackblock() - startloc; | |
727 | varlen = varlenb; | |
728 | if (localeisutf8) { | |
729 | val = stackblock() + startloc; | |
730 | for (;val != expdest; val++) | |
731 | if ((*val & 0xC0) == 0x80) | |
732 | varlen--; | |
733 | } | |
734 | STADJUST(-varlenb, expdest); | |
735 | } | |
736 | } else { | |
737 | if (subtype == VSLENGTH) { | |
738 | for (;*val; val++) | |
739 | if (!localeisutf8 || | |
740 | (*val & 0xC0) != 0x80) | |
741 | varlen++; | |
742 | } | |
743 | else | |
744 | strtodest(val, flag, subtype, | |
deb63bfb | 745 | varflags & VSQUOTE, dst); |
71aad674 A |
746 | } |
747 | } | |
748 | ||
749 | if (subtype == VSPLUS) | |
750 | set = ! set; | |
751 | ||
71aad674 A |
752 | switch (subtype) { |
753 | case VSLENGTH: | |
deb63bfb A |
754 | cvtnum(varlen, buf); |
755 | strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst); | |
71aad674 A |
756 | break; |
757 | ||
758 | case VSNORMAL: | |
254f12f7 | 759 | return p; |
71aad674 A |
760 | |
761 | case VSPLUS: | |
762 | case VSMINUS: | |
763 | if (!set) { | |
254f12f7 A |
764 | return argstr(p, argbackq, |
765 | flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) | | |
deb63bfb | 766 | (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst); |
71aad674 | 767 | } |
71aad674 A |
768 | break; |
769 | ||
770 | case VSTRIMLEFT: | |
771 | case VSTRIMLEFTMAX: | |
772 | case VSTRIMRIGHT: | |
773 | case VSTRIMRIGHTMAX: | |
774 | if (!set) | |
775 | break; | |
776 | /* | |
777 | * Terminate the string and start recording the pattern | |
778 | * right after it | |
779 | */ | |
780 | STPUTC('\0', expdest); | |
781 | patloc = expdest - stackblock(); | |
254f12f7 | 782 | p = subevalvar_trim(p, argbackq, patloc, subtype, startloc); |
deb63bfb A |
783 | reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst); |
784 | if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE) | |
785 | dst->state = WORD_QUOTEMARK; | |
254f12f7 | 786 | return p; |
71aad674 A |
787 | |
788 | case VSASSIGN: | |
789 | case VSQUESTION: | |
790 | if (!set) { | |
254f12f7 A |
791 | p = subevalvar_misc(p, argbackq, var, subtype, |
792 | startloc, varflags); | |
793 | /* assert(subtype == VSASSIGN); */ | |
794 | val = lookupvar(var); | |
795 | strtodest(val, flag, subtype, varflags & VSQUOTE, dst); | |
796 | return p; | |
71aad674 | 797 | } |
71aad674 A |
798 | break; |
799 | ||
800 | case VSERROR: | |
801 | c = p - var - 1; | |
802 | error("${%.*s%s}: Bad substitution", c, var, | |
803 | (c > 0 && *p != CTLENDVAR) ? "..." : ""); | |
804 | ||
805 | default: | |
806 | abort(); | |
807 | } | |
808 | ||
254f12f7 | 809 | { /* skip to end of alternative */ |
71aad674 A |
810 | int nesting = 1; |
811 | for (;;) { | |
812 | if ((c = *p++) == CTLESC) | |
813 | p++; | |
254f12f7 A |
814 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) |
815 | *argbackq = (*argbackq)->next; | |
816 | else if (c == CTLVAR) { | |
71aad674 A |
817 | if ((*p++ & VSTYPE) != VSNORMAL) |
818 | nesting++; | |
819 | } else if (c == CTLENDVAR) { | |
820 | if (--nesting == 0) | |
821 | break; | |
822 | } | |
823 | } | |
824 | } | |
825 | return p; | |
826 | } | |
827 | ||
828 | ||
829 | ||
830 | /* | |
254f12f7 | 831 | * Test whether a special or positional parameter is set. |
71aad674 A |
832 | */ |
833 | ||
834 | static int | |
835 | varisset(const char *name, int nulok) | |
836 | { | |
837 | ||
838 | if (*name == '!') | |
839 | return backgndpidset(); | |
840 | else if (*name == '@' || *name == '*') { | |
841 | if (*shellparam.p == NULL) | |
842 | return 0; | |
843 | ||
844 | if (nulok) { | |
845 | char **av; | |
846 | ||
847 | for (av = shellparam.p; *av; av++) | |
848 | if (**av != '\0') | |
849 | return 1; | |
850 | return 0; | |
851 | } | |
852 | } else if (is_digit(*name)) { | |
853 | char *ap; | |
854 | long num; | |
855 | ||
856 | errno = 0; | |
857 | num = strtol(name, NULL, 10); | |
858 | if (errno != 0 || num > shellparam.nparam) | |
859 | return 0; | |
860 | ||
861 | if (num == 0) | |
862 | ap = arg0; | |
863 | else | |
864 | ap = shellparam.p[num - 1]; | |
865 | ||
866 | if (nulok && (ap == NULL || *ap == '\0')) | |
867 | return 0; | |
868 | } | |
869 | return 1; | |
870 | } | |
871 | ||
872 | static void | |
deb63bfb A |
873 | strtodest(const char *p, int flag, int subtype, int quoted, |
874 | struct worddest *dst) | |
71aad674 | 875 | { |
deb63bfb A |
876 | if (subtype == VSLENGTH || subtype == VSTRIMLEFT || |
877 | subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT || | |
878 | subtype == VSTRIMRIGHTMAX) | |
879 | STPUTS(p, expdest); | |
880 | else if (flag & EXP_SPLIT && !quoted && dst != NULL) | |
881 | STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst); | |
882 | else if (flag & (EXP_GLOB | EXP_CASE)) | |
71aad674 A |
883 | STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); |
884 | else | |
885 | STPUTS(p, expdest); | |
886 | } | |
887 | ||
deb63bfb A |
888 | static void |
889 | reprocess(int startloc, int flag, int subtype, int quoted, | |
890 | struct worddest *dst) | |
891 | { | |
892 | static char *buf = NULL; | |
893 | static size_t buflen = 0; | |
894 | char *startp; | |
895 | size_t len, zpos, zlen; | |
896 | ||
897 | startp = stackblock() + startloc; | |
898 | len = expdest - startp; | |
899 | if (len >= SIZE_MAX / 2) | |
900 | abort(); | |
901 | INTOFF; | |
902 | if (len >= buflen) { | |
903 | ckfree(buf); | |
904 | buf = NULL; | |
905 | } | |
906 | if (buflen < 128) | |
907 | buflen = 128; | |
908 | while (len >= buflen) | |
909 | buflen <<= 1; | |
910 | if (buf == NULL) | |
911 | buf = ckmalloc(buflen); | |
912 | INTON; | |
913 | memcpy(buf, startp, len); | |
914 | buf[len] = '\0'; | |
915 | STADJUST(-len, expdest); | |
916 | for (zpos = 0;;) { | |
917 | zlen = strlen(buf + zpos); | |
918 | strtodest(buf + zpos, flag, subtype, quoted, dst); | |
919 | zpos += zlen + 1; | |
920 | if (zpos == len + 1) | |
921 | break; | |
922 | if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len))) | |
923 | NEXTWORD('\0', flag, expdest, dst); | |
924 | } | |
925 | } | |
926 | ||
71aad674 | 927 | /* |
254f12f7 | 928 | * Add the value of a special or positional parameter to the stack string. |
71aad674 A |
929 | */ |
930 | ||
931 | static void | |
deb63bfb A |
932 | varvalue(const char *name, int quoted, int subtype, int flag, |
933 | struct worddest *dst) | |
71aad674 A |
934 | { |
935 | int num; | |
936 | char *p; | |
937 | int i; | |
deb63bfb | 938 | int splitlater; |
71aad674 A |
939 | char sep[2]; |
940 | char **ap; | |
deb63bfb A |
941 | char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1]; |
942 | ||
943 | if (subtype == VSLENGTH) | |
944 | flag &= ~EXP_FULL; | |
945 | splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || | |
946 | subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX; | |
71aad674 A |
947 | |
948 | switch (*name) { | |
949 | case '$': | |
950 | num = rootpid; | |
951 | break; | |
952 | case '?': | |
953 | num = oexitstatus; | |
954 | break; | |
955 | case '#': | |
956 | num = shellparam.nparam; | |
957 | break; | |
958 | case '!': | |
959 | num = backgndpidval(); | |
960 | break; | |
961 | case '-': | |
deb63bfb A |
962 | p = buf; |
963 | for (i = 0 ; i < NSHORTOPTS ; i++) { | |
964 | if (optval[i]) | |
965 | *p++ = optletter[i]; | |
71aad674 | 966 | } |
deb63bfb A |
967 | *p = '\0'; |
968 | strtodest(buf, flag, subtype, quoted, dst); | |
71aad674 A |
969 | return; |
970 | case '@': | |
deb63bfb | 971 | if (flag & EXP_SPLIT && quoted) { |
71aad674 | 972 | for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { |
deb63bfb A |
973 | strtodest(p, flag, subtype, quoted, dst); |
974 | if (*ap) { | |
975 | if (splitlater) | |
976 | STPUTC('\0', expdest); | |
977 | else | |
978 | NEXTWORD('\0', flag, expdest, | |
979 | dst); | |
980 | } | |
71aad674 | 981 | } |
deb63bfb A |
982 | if (shellparam.nparam > 0) |
983 | dst->state = WORD_QUOTEMARK; | |
71aad674 A |
984 | return; |
985 | } | |
986 | /* FALLTHROUGH */ | |
987 | case '*': | |
988 | if (ifsset()) | |
989 | sep[0] = ifsval()[0]; | |
990 | else | |
991 | sep[0] = ' '; | |
992 | sep[1] = '\0'; | |
993 | for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { | |
deb63bfb | 994 | strtodest(p, flag, subtype, quoted, dst); |
71aad674 A |
995 | if (!*ap) |
996 | break; | |
997 | if (sep[0]) | |
deb63bfb A |
998 | strtodest(sep, flag, subtype, quoted, dst); |
999 | else if (flag & EXP_SPLIT && !quoted && **ap != '\0') { | |
1000 | if (splitlater) | |
1001 | STPUTC('\0', expdest); | |
1002 | else | |
1003 | NEXTWORD('\0', flag, expdest, dst); | |
1004 | } | |
71aad674 A |
1005 | } |
1006 | return; | |
1007 | default: | |
1008 | if (is_digit(*name)) { | |
1009 | num = atoi(name); | |
1010 | if (num == 0) | |
1011 | p = arg0; | |
1012 | else if (num > 0 && num <= shellparam.nparam) | |
1013 | p = shellparam.p[num - 1]; | |
1014 | else | |
1015 | return; | |
deb63bfb | 1016 | strtodest(p, flag, subtype, quoted, dst); |
71aad674 A |
1017 | } |
1018 | return; | |
1019 | } | |
deb63bfb A |
1020 | cvtnum(num, buf); |
1021 | strtodest(buf, flag, subtype, quoted, dst); | |
71aad674 A |
1022 | } |
1023 | ||
1024 | ||
1025 | ||
71aad674 A |
1026 | static char expdir[PATH_MAX]; |
1027 | #define expdir_end (expdir + sizeof(expdir)) | |
1028 | ||
1029 | /* | |
1030 | * Perform pathname generation and remove control characters. | |
deb63bfb A |
1031 | * At this point, the only control characters should be CTLESC. |
1032 | * The results are stored in the list dstlist. | |
71aad674 A |
1033 | */ |
1034 | static void | |
deb63bfb | 1035 | expandmeta(char *pattern, struct arglist *dstlist) |
71aad674 A |
1036 | { |
1037 | char *p; | |
deb63bfb | 1038 | int firstmatch; |
71aad674 A |
1039 | char c; |
1040 | ||
deb63bfb A |
1041 | firstmatch = dstlist->count; |
1042 | p = pattern; | |
1043 | for (; (c = *p) != '\0'; p++) { | |
1044 | /* fast check for meta chars */ | |
1045 | if (c == '*' || c == '?' || c == '[') { | |
1046 | INTOFF; | |
1047 | expmeta(expdir, pattern, dstlist); | |
1048 | INTON; | |
1049 | break; | |
71aad674 | 1050 | } |
deb63bfb A |
1051 | } |
1052 | if (dstlist->count == firstmatch) { | |
1053 | /* | |
1054 | * no matches | |
1055 | */ | |
1056 | rmescapes(pattern); | |
1057 | appendarglist(dstlist, pattern); | |
1058 | } else { | |
1059 | qsort(&dstlist->args[firstmatch], | |
1060 | dstlist->count - firstmatch, | |
1061 | sizeof(dstlist->args[0]), expsortcmp); | |
71aad674 A |
1062 | } |
1063 | } | |
1064 | ||
1065 | ||
1066 | /* | |
1067 | * Do metacharacter (i.e. *, ?, [...]) expansion. | |
1068 | */ | |
1069 | ||
1070 | static void | |
deb63bfb | 1071 | expmeta(char *enddir, char *name, struct arglist *arglist) |
71aad674 A |
1072 | { |
1073 | const char *p; | |
1074 | const char *q; | |
1075 | const char *start; | |
1076 | char *endname; | |
1077 | int metaflag; | |
1078 | struct stat statb; | |
1079 | DIR *dirp; | |
1080 | struct dirent *dp; | |
1081 | int atend; | |
1082 | int matchdot; | |
1083 | int esc; | |
1084 | int namlen; | |
1085 | ||
1086 | metaflag = 0; | |
1087 | start = name; | |
1088 | for (p = name; esc = 0, *p; p += esc + 1) { | |
1089 | if (*p == '*' || *p == '?') | |
1090 | metaflag = 1; | |
1091 | else if (*p == '[') { | |
1092 | q = p + 1; | |
1093 | if (*q == '!' || *q == '^') | |
1094 | q++; | |
1095 | for (;;) { | |
71aad674 A |
1096 | if (*q == CTLESC) |
1097 | q++; | |
1098 | if (*q == '/' || *q == '\0') | |
1099 | break; | |
1100 | if (*++q == ']') { | |
1101 | metaflag = 1; | |
1102 | break; | |
1103 | } | |
1104 | } | |
1105 | } else if (*p == '\0') | |
1106 | break; | |
71aad674 A |
1107 | else { |
1108 | if (*p == CTLESC) | |
1109 | esc++; | |
1110 | if (p[esc] == '/') { | |
1111 | if (metaflag) | |
1112 | break; | |
1113 | start = p + esc + 1; | |
1114 | } | |
1115 | } | |
1116 | } | |
1117 | if (metaflag == 0) { /* we've reached the end of the file name */ | |
1118 | if (enddir != expdir) | |
1119 | metaflag++; | |
1120 | for (p = name ; ; p++) { | |
71aad674 A |
1121 | if (*p == CTLESC) |
1122 | p++; | |
1123 | *enddir++ = *p; | |
1124 | if (*p == '\0') | |
1125 | break; | |
1126 | if (enddir == expdir_end) | |
1127 | return; | |
1128 | } | |
1129 | if (metaflag == 0 || lstat(expdir, &statb) >= 0) | |
deb63bfb | 1130 | appendarglist(arglist, stsavestr(expdir)); |
71aad674 A |
1131 | return; |
1132 | } | |
1133 | endname = name + (p - name); | |
1134 | if (start != name) { | |
1135 | p = name; | |
1136 | while (p < start) { | |
71aad674 A |
1137 | if (*p == CTLESC) |
1138 | p++; | |
1139 | *enddir++ = *p++; | |
1140 | if (enddir == expdir_end) | |
1141 | return; | |
1142 | } | |
1143 | } | |
1144 | if (enddir == expdir) { | |
1145 | p = "."; | |
1146 | } else if (enddir == expdir + 1 && *expdir == '/') { | |
1147 | p = "/"; | |
1148 | } else { | |
1149 | p = expdir; | |
1150 | enddir[-1] = '\0'; | |
1151 | } | |
1152 | if ((dirp = opendir(p)) == NULL) | |
1153 | return; | |
1154 | if (enddir != expdir) | |
1155 | enddir[-1] = '/'; | |
1156 | if (*endname == 0) { | |
1157 | atend = 1; | |
1158 | } else { | |
1159 | atend = 0; | |
1160 | *endname = '\0'; | |
1161 | endname += esc + 1; | |
1162 | } | |
1163 | matchdot = 0; | |
1164 | p = start; | |
71aad674 A |
1165 | if (*p == CTLESC) |
1166 | p++; | |
1167 | if (*p == '.') | |
1168 | matchdot++; | |
1169 | while (! int_pending() && (dp = readdir(dirp)) != NULL) { | |
1170 | if (dp->d_name[0] == '.' && ! matchdot) | |
1171 | continue; | |
deb63bfb | 1172 | if (patmatch(start, dp->d_name)) { |
71aad674 A |
1173 | namlen = dp->d_namlen; |
1174 | if (enddir + namlen + 1 > expdir_end) | |
1175 | continue; | |
1176 | memcpy(enddir, dp->d_name, namlen + 1); | |
1177 | if (atend) | |
deb63bfb | 1178 | appendarglist(arglist, stsavestr(expdir)); |
71aad674 A |
1179 | else { |
1180 | if (dp->d_type != DT_UNKNOWN && | |
1181 | dp->d_type != DT_DIR && | |
1182 | dp->d_type != DT_LNK) | |
1183 | continue; | |
1184 | if (enddir + namlen + 2 > expdir_end) | |
1185 | continue; | |
1186 | enddir[namlen] = '/'; | |
1187 | enddir[namlen + 1] = '\0'; | |
deb63bfb | 1188 | expmeta(enddir + namlen + 1, endname, arglist); |
71aad674 A |
1189 | } |
1190 | } | |
1191 | } | |
1192 | closedir(dirp); | |
1193 | if (! atend) | |
1194 | endname[-esc - 1] = esc ? CTLESC : '/'; | |
1195 | } | |
1196 | ||
1197 | ||
deb63bfb A |
1198 | static int |
1199 | expsortcmp(const void *p1, const void *p2) | |
71aad674 | 1200 | { |
deb63bfb A |
1201 | const char *s1 = *(const char * const *)p1; |
1202 | const char *s2 = *(const char * const *)p2; | |
71aad674 | 1203 | |
deb63bfb | 1204 | return (strcoll(s1, s2)); |
71aad674 A |
1205 | } |
1206 | ||
1207 | ||
1208 | ||
1209 | static wchar_t | |
1210 | get_wc(const char **p) | |
1211 | { | |
1212 | wchar_t c; | |
1213 | int chrlen; | |
1214 | ||
1215 | chrlen = mbtowc(&c, *p, 4); | |
1216 | if (chrlen == 0) | |
1217 | return 0; | |
1218 | else if (chrlen == -1) | |
1219 | c = 0; | |
1220 | else | |
1221 | *p += chrlen; | |
1222 | return c; | |
1223 | } | |
1224 | ||
1225 | ||
1226 | /* | |
1227 | * See if a character matches a character class, starting at the first colon | |
1228 | * of "[:class:]". | |
1229 | * If a valid character class is recognized, a pointer to the next character | |
1230 | * after the final closing bracket is stored into *end, otherwise a null | |
1231 | * pointer is stored into *end. | |
1232 | */ | |
1233 | static int | |
1234 | match_charclass(const char *p, wchar_t chr, const char **end) | |
1235 | { | |
1236 | char name[20]; | |
1237 | const char *nameend; | |
1238 | wctype_t cclass; | |
1239 | ||
1240 | *end = NULL; | |
1241 | p++; | |
1242 | nameend = strstr(p, ":]"); | |
1243 | if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || | |
1244 | nameend == p) | |
1245 | return 0; | |
1246 | memcpy(name, p, nameend - p); | |
1247 | name[nameend - p] = '\0'; | |
1248 | *end = nameend + 2; | |
1249 | cclass = wctype(name); | |
1250 | /* An unknown class matches nothing but is valid nevertheless. */ | |
1251 | if (cclass == 0) | |
1252 | return 0; | |
1253 | return iswctype(chr, cclass); | |
1254 | } | |
1255 | ||
1256 | ||
1257 | /* | |
1258 | * Returns true if the pattern matches the string. | |
1259 | */ | |
1260 | ||
1261 | static int | |
deb63bfb | 1262 | patmatch(const char *pattern, const char *string) |
71aad674 A |
1263 | { |
1264 | const char *p, *q, *end; | |
1265 | const char *bt_p, *bt_q; | |
1266 | char c; | |
1267 | wchar_t wc, wc2; | |
1268 | ||
1269 | p = pattern; | |
1270 | q = string; | |
1271 | bt_p = NULL; | |
1272 | bt_q = NULL; | |
1273 | for (;;) { | |
1274 | switch (c = *p++) { | |
1275 | case '\0': | |
1276 | if (*q != '\0') | |
1277 | goto backtrack; | |
1278 | return 1; | |
1279 | case CTLESC: | |
71aad674 A |
1280 | if (*q++ != *p++) |
1281 | goto backtrack; | |
1282 | break; | |
71aad674 | 1283 | case '?': |
71aad674 A |
1284 | if (*q == '\0') |
1285 | return 0; | |
1286 | if (localeisutf8) { | |
1287 | wc = get_wc(&q); | |
1288 | /* | |
1289 | * A '?' does not match invalid UTF-8 but a | |
1290 | * '*' does, so backtrack. | |
1291 | */ | |
1292 | if (wc == 0) | |
1293 | goto backtrack; | |
1294 | } else | |
deb63bfb | 1295 | q++; |
71aad674 A |
1296 | break; |
1297 | case '*': | |
1298 | c = *p; | |
deb63bfb | 1299 | while (c == '*') |
71aad674 A |
1300 | c = *++p; |
1301 | /* | |
1302 | * If the pattern ends here, we know the string | |
1303 | * matches without needing to look at the rest of it. | |
1304 | */ | |
1305 | if (c == '\0') | |
1306 | return 1; | |
1307 | /* | |
1308 | * First try the shortest match for the '*' that | |
1309 | * could work. We can forget any earlier '*' since | |
1310 | * there is no way having it match more characters | |
1311 | * can help us, given that we are already here. | |
1312 | */ | |
1313 | bt_p = p; | |
1314 | bt_q = q; | |
1315 | break; | |
1316 | case '[': { | |
deb63bfb | 1317 | const char *savep, *saveq; |
71aad674 A |
1318 | int invert, found; |
1319 | wchar_t chr; | |
1320 | ||
deb63bfb | 1321 | savep = p, saveq = q; |
71aad674 A |
1322 | invert = 0; |
1323 | if (*p == '!' || *p == '^') { | |
1324 | invert++; | |
1325 | p++; | |
1326 | } | |
1327 | found = 0; | |
71aad674 A |
1328 | if (*q == '\0') |
1329 | return 0; | |
1330 | if (localeisutf8) { | |
1331 | chr = get_wc(&q); | |
1332 | if (chr == 0) | |
1333 | goto backtrack; | |
1334 | } else | |
1335 | chr = (unsigned char)*q++; | |
1336 | c = *p++; | |
1337 | do { | |
deb63bfb A |
1338 | if (c == '\0') { |
1339 | p = savep, q = saveq; | |
1340 | c = '['; | |
1341 | goto dft; | |
1342 | } | |
71aad674 A |
1343 | if (c == '[' && *p == ':') { |
1344 | found |= match_charclass(p, chr, &end); | |
1345 | if (end != NULL) | |
1346 | p = end; | |
1347 | } | |
1348 | if (c == CTLESC) | |
1349 | c = *p++; | |
1350 | if (localeisutf8 && c & 0x80) { | |
1351 | p--; | |
1352 | wc = get_wc(&p); | |
1353 | if (wc == 0) /* bad utf-8 */ | |
1354 | return 0; | |
1355 | } else | |
1356 | wc = (unsigned char)c; | |
1357 | if (*p == '-' && p[1] != ']') { | |
1358 | p++; | |
71aad674 A |
1359 | if (*p == CTLESC) |
1360 | p++; | |
1361 | if (localeisutf8) { | |
1362 | wc2 = get_wc(&p); | |
1363 | if (wc2 == 0) /* bad utf-8 */ | |
1364 | return 0; | |
1365 | } else | |
1366 | wc2 = (unsigned char)*p++; | |
1367 | if ( collate_range_cmp(chr, wc) >= 0 | |
1368 | && collate_range_cmp(chr, wc2) <= 0 | |
1369 | ) | |
1370 | found = 1; | |
1371 | } else { | |
1372 | if (chr == wc) | |
1373 | found = 1; | |
1374 | } | |
1375 | } while ((c = *p++) != ']'); | |
1376 | if (found == invert) | |
1377 | goto backtrack; | |
1378 | break; | |
1379 | } | |
1380 | dft: default: | |
71aad674 A |
1381 | if (*q == '\0') |
1382 | return 0; | |
1383 | if (*q++ == c) | |
1384 | break; | |
1385 | backtrack: | |
1386 | /* | |
1387 | * If we have a mismatch (other than hitting the end | |
1388 | * of the string), go back to the last '*' seen and | |
1389 | * have it match one additional character. | |
1390 | */ | |
1391 | if (bt_p == NULL) | |
1392 | return 0; | |
71aad674 A |
1393 | if (*bt_q == '\0') |
1394 | return 0; | |
1395 | bt_q++; | |
1396 | p = bt_p; | |
1397 | q = bt_q; | |
1398 | break; | |
1399 | } | |
1400 | } | |
1401 | } | |
1402 | ||
1403 | ||
1404 | ||
1405 | /* | |
1406 | * Remove any CTLESC and CTLQUOTEMARK characters from a string. | |
1407 | */ | |
1408 | ||
1409 | void | |
1410 | rmescapes(char *str) | |
1411 | { | |
1412 | char *p, *q; | |
1413 | ||
1414 | p = str; | |
1415 | while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { | |
1416 | if (*p++ == '\0') | |
1417 | return; | |
1418 | } | |
1419 | q = p; | |
1420 | while (*p) { | |
1421 | if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { | |
1422 | p++; | |
1423 | continue; | |
1424 | } | |
1425 | if (*p == CTLESC) | |
1426 | p++; | |
1427 | *q++ = *p++; | |
1428 | } | |
1429 | *q = '\0'; | |
1430 | } | |
1431 | ||
1432 | ||
1433 | ||
1434 | /* | |
1435 | * See if a pattern matches in a case statement. | |
1436 | */ | |
1437 | ||
1438 | int | |
1439 | casematch(union node *pattern, const char *val) | |
1440 | { | |
1441 | struct stackmark smark; | |
254f12f7 | 1442 | struct nodelist *argbackq; |
71aad674 A |
1443 | int result; |
1444 | char *p; | |
1445 | ||
1446 | setstackmark(&smark); | |
1447 | argbackq = pattern->narg.backquote; | |
1448 | STARTSTACKSTR(expdest); | |
254f12f7 | 1449 | argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL); |
71aad674 A |
1450 | STPUTC('\0', expdest); |
1451 | p = grabstackstr(expdest); | |
deb63bfb | 1452 | result = patmatch(p, val); |
71aad674 A |
1453 | popstackmark(&smark); |
1454 | return result; | |
1455 | } | |
1456 | ||
1457 | /* | |
1458 | * Our own itoa(). | |
1459 | */ | |
1460 | ||
deb63bfb | 1461 | static void |
71aad674 A |
1462 | cvtnum(int num, char *buf) |
1463 | { | |
1464 | char temp[32]; | |
1465 | int neg = num < 0; | |
1466 | char *p = temp + 31; | |
1467 | ||
1468 | temp[31] = '\0'; | |
1469 | ||
1470 | do { | |
1471 | *--p = num % 10 + '0'; | |
1472 | } while ((num /= 10) != 0); | |
1473 | ||
1474 | if (neg) | |
1475 | *--p = '-'; | |
1476 | ||
deb63bfb | 1477 | memcpy(buf, p, temp + 32 - p); |
71aad674 A |
1478 | } |
1479 | ||
1480 | /* | |
1481 | * Do most of the work for wordexp(3). | |
1482 | */ | |
1483 | ||
1484 | int | |
1485 | wordexpcmd(int argc, char **argv) | |
1486 | { | |
1487 | size_t len; | |
1488 | int i; | |
1489 | ||
1490 | out1fmt("%08x", argc - 1); | |
1491 | for (i = 1, len = 0; i < argc; i++) | |
1492 | len += strlen(argv[i]); | |
1493 | out1fmt("%08x", (int)len); | |
1494 | for (i = 1; i < argc; i++) | |
1495 | outbin(argv[i], strlen(argv[i]) + 1, out1); | |
1496 | return (0); | |
1497 | } | |
deb63bfb A |
1498 | |
1499 | /* | |
1500 | * Do most of the work for wordexp(3), new version. | |
1501 | */ | |
1502 | ||
1503 | int | |
1504 | freebsd_wordexpcmd(int argc __unused, char **argv __unused) | |
1505 | { | |
1506 | struct arglist arglist; | |
1507 | union node *args, *n; | |
1508 | size_t len; | |
1509 | int ch; | |
1510 | int protected = 0; | |
1511 | int fd = -1; | |
1512 | int i; | |
1513 | ||
1514 | while ((ch = nextopt("f:p")) != '\0') { | |
1515 | switch (ch) { | |
1516 | case 'f': | |
1517 | fd = number(shoptarg); | |
1518 | break; | |
1519 | case 'p': | |
1520 | protected = 1; | |
1521 | break; | |
1522 | } | |
1523 | } | |
1524 | if (*argptr != NULL) | |
1525 | error("wrong number of arguments"); | |
1526 | if (fd < 0) | |
1527 | error("missing fd"); | |
1528 | INTOFF; | |
1529 | setinputfd(fd, 1); | |
1530 | INTON; | |
1531 | args = parsewordexp(); | |
1532 | popfile(); /* will also close fd */ | |
1533 | if (protected) | |
1534 | for (n = args; n != NULL; n = n->narg.next) { | |
1535 | if (n->narg.backquote != NULL) { | |
1536 | outcslow('C', out1); | |
1537 | error("command substitution disabled"); | |
1538 | } | |
1539 | } | |
1540 | outcslow(' ', out1); | |
1541 | emptyarglist(&arglist); | |
1542 | for (n = args; n != NULL; n = n->narg.next) | |
1543 | expandarg(n, &arglist, EXP_FULL | EXP_TILDE); | |
1544 | for (i = 0, len = 0; i < arglist.count; i++) | |
1545 | len += strlen(arglist.args[i]); | |
1546 | out1fmt("%016x %016zx", arglist.count, len); | |
1547 | for (i = 0; i < arglist.count; i++) | |
1548 | outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1); | |
1549 | return (0); | |
1550 | } |