Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /*- |
2 | * Copyright (c) 1991, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
99512ac4 PA |
4 | * Copyright (c) 1997-2005 |
5 | * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. | |
3e3895bf KP |
6 | * Copyright (c) 2010-2015 |
7 | * Jilles Tjoelker <jilles@stack.nl>. All rights reserved. | |
984263bc MD |
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. | |
69763be3 | 20 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
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 | ||
02d0b1ce MD |
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> | |
3e3895bf | 43 | __FBSDID("$FreeBSD: head/bin/sh/expand.c 341164 2018-11-28 20:03:53Z jilles $"); |
02d0b1ce | 44 | |
984263bc MD |
45 | #include <sys/types.h> |
46 | #include <sys/time.h> | |
47 | #include <sys/stat.h> | |
984263bc | 48 | #include <dirent.h> |
99512ac4 PA |
49 | #include <errno.h> |
50 | #include <inttypes.h> | |
984263bc | 51 | #include <limits.h> |
99512ac4 | 52 | #include <pwd.h> |
984263bc | 53 | #include <stdio.h> |
99512ac4 | 54 | #include <stdlib.h> |
d13d0ee2 | 55 | #include <string.h> |
99512ac4 | 56 | #include <unistd.h> |
ead7935b | 57 | #include <wchar.h> |
215809b7 | 58 | #include <wctype.h> |
984263bc MD |
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" | |
c3e3bc7a | 82 | #include "builtins.h" |
984263bc | 83 | |
3e3895bf | 84 | enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK }; |
984263bc | 85 | |
3e3895bf KP |
86 | struct worddest { |
87 | struct arglist *list; | |
88 | enum wordstate state; | |
984263bc MD |
89 | }; |
90 | ||
99512ac4 | 91 | static char *expdest; /* output of current string */ |
3e3895bf KP |
92 | |
93 | static const char *argstr(const char *, struct nodelist **restrict, int, | |
94 | struct worddest *); | |
95 | static const char *exptilde(const char *, int); | |
96 | static const char *expari(const char *, struct nodelist **restrict, int, | |
97 | struct worddest *); | |
98 | static void expbackq(union node *, int, int, struct worddest *); | |
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 *); | |
02d0b1ce | 105 | static int varisset(const char *, int); |
3e3895bf KP |
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 *); | |
ead7935b | 114 | static int collate_range_cmp(wchar_t, wchar_t); |
99512ac4 | 115 | |
3e3895bf KP |
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 | ||
99512ac4 | 145 | static int |
ead7935b | 146 | collate_range_cmp(wchar_t c1, wchar_t c2) |
984263bc | 147 | { |
3e3895bf | 148 | wchar_t s1[2], s2[2]; |
984263bc | 149 | |
984263bc | 150 | s1[0] = c1; |
3e3895bf | 151 | s1[1] = L'\0'; |
984263bc | 152 | s2[0] = c2; |
3e3895bf | 153 | s2[1] = L'\0'; |
ead7935b | 154 | return (wcscoll(s1, s2)); |
984263bc MD |
155 | } |
156 | ||
99512ac4 PA |
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) | |
984263bc | 169 | |
3e3895bf KP |
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 | ||
984263bc | 217 | /* |
99512ac4 PA |
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 | * | |
3e3895bf KP |
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. | |
984263bc | 228 | */ |
984263bc MD |
229 | void |
230 | expandarg(union node *arg, struct arglist *arglist, int flag) | |
231 | { | |
3e3895bf KP |
232 | struct worddest exparg; |
233 | struct nodelist *argbackq; | |
984263bc | 234 | |
3e3895bf KP |
235 | if (fflag) |
236 | flag &= ~EXP_GLOB; | |
984263bc | 237 | argbackq = arg->narg.backquote; |
3e3895bf KP |
238 | exparg.list = arglist; |
239 | exparg.state = WORD_IDLE; | |
984263bc | 240 | STARTSTACKSTR(expdest); |
3e3895bf | 241 | argstr(arg->narg.text, &argbackq, flag, &exparg); |
984263bc | 242 | if (arglist == NULL) { |
97fe2313 | 243 | STACKSTRNUL(expdest); |
984263bc MD |
244 | return; /* here document expanded */ |
245 | } | |
3e3895bf KP |
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 | } | |
984263bc | 255 | } |
3e3895bf KP |
256 | if ((flag & EXP_SPLIT) == 0) |
257 | appendarglist(arglist, grabstackstr(expdest)); | |
984263bc MD |
258 | } |
259 | ||
260 | ||
261 | ||
262 | /* | |
99512ac4 PA |
263 | * Perform parameter expansion, command substitution and arithmetic |
264 | * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. | |
02d0b1ce | 265 | * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. |
99512ac4 | 266 | * This is used to expand word in ${var+word} etc. |
3e3895bf | 267 | * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC |
99512ac4 | 268 | * characters to allow for further processing. |
3e3895bf KP |
269 | * |
270 | * If EXP_SPLIT is set, dst receives any complete words produced. | |
984263bc | 271 | */ |
3e3895bf KP |
272 | static const char * |
273 | argstr(const char *p, struct nodelist **restrict argbackq, int flag, | |
274 | struct worddest *dst) | |
984263bc MD |
275 | { |
276 | char c; | |
3e3895bf | 277 | int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */ |
984263bc | 278 | int firsteq = 1; |
99512ac4 PA |
279 | int split_lit; |
280 | int lit_quoted; | |
984263bc | 281 | |
99512ac4 PA |
282 | split_lit = flag & EXP_SPLIT_LIT; |
283 | lit_quoted = flag & EXP_LIT_QUOTED; | |
284 | flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); | |
984263bc MD |
285 | if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) |
286 | p = exptilde(p, flag); | |
287 | for (;;) { | |
99512ac4 | 288 | CHECKSTRSPACE(2, expdest); |
984263bc MD |
289 | switch (c = *p++) { |
290 | case '\0': | |
02d0b1ce | 291 | return (p - 1); |
99512ac4 | 292 | case CTLENDVAR: |
02d0b1ce MD |
293 | case CTLENDARI: |
294 | return (p); | |
984263bc | 295 | case CTLQUOTEMARK: |
99512ac4 | 296 | lit_quoted = 1; |
984263bc | 297 | /* "$@" syntax adherence hack */ |
3e3895bf KP |
298 | if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && |
299 | p[2] == '@' && p[3] == '=') | |
984263bc | 300 | break; |
3e3895bf KP |
301 | if ((flag & EXP_SPLIT) != 0 && expdest == stackblock()) |
302 | dst->state = WORD_QUOTEMARK; | |
99512ac4 PA |
303 | break; |
304 | case CTLQUOTEEND: | |
305 | lit_quoted = 0; | |
984263bc MD |
306 | break; |
307 | case CTLESC: | |
984263bc | 308 | c = *p++; |
3e3895bf KP |
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); | |
99512ac4 | 316 | USTPUTC(c, expdest); |
984263bc MD |
317 | break; |
318 | case CTLVAR: | |
3e3895bf | 319 | p = evalvar(p, argbackq, flag, dst); |
984263bc MD |
320 | break; |
321 | case CTLBACKQ: | |
322 | case CTLBACKQ|CTLQUOTE: | |
3e3895bf KP |
323 | expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst); |
324 | *argbackq = (*argbackq)->next; | |
984263bc | 325 | break; |
02d0b1ce | 326 | case CTLARI: |
3e3895bf | 327 | p = expari(p, argbackq, flag, dst); |
984263bc MD |
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 | */ | |
3e3895bf KP |
335 | if (split_lit && !lit_quoted && |
336 | strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { | |
337 | NEXTWORD(c, flag, expdest, dst); | |
338 | break; | |
339 | } | |
99512ac4 | 340 | USTPUTC(c, expdest); |
99512ac4 PA |
341 | if (flag & EXP_VARTILDE && *p == '~' && |
342 | (c != '=' || firsteq)) { | |
343 | if (c == '=') | |
344 | firsteq = 0; | |
984263bc MD |
345 | p = exptilde(p, flag); |
346 | } | |
347 | break; | |
348 | default: | |
3e3895bf KP |
349 | if (split_lit && !lit_quoted && |
350 | strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { | |
351 | NEXTWORD(c, flag, expdest, dst); | |
352 | break; | |
353 | } | |
99512ac4 | 354 | USTPUTC(c, expdest); |
984263bc MD |
355 | } |
356 | } | |
984263bc MD |
357 | } |
358 | ||
99512ac4 PA |
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 | */ | |
3e3895bf KP |
363 | static const char * |
364 | exptilde(const char *p, int flag) | |
984263bc | 365 | { |
3e3895bf KP |
366 | char c; |
367 | const char *startp = p; | |
368 | const char *user; | |
984263bc MD |
369 | struct passwd *pw; |
370 | char *home; | |
3e3895bf | 371 | int len; |
984263bc | 372 | |
02d0b1ce MD |
373 | for (;;) { |
374 | c = *p; | |
984263bc | 375 | switch(c) { |
99512ac4 PA |
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: | |
984263bc MD |
382 | case CTLQUOTEMARK: |
383 | return (startp); | |
384 | case ':': | |
02d0b1ce | 385 | if ((flag & EXP_VARTILDE) == 0) |
3e3895bf | 386 | break; |
02d0b1ce MD |
387 | /* FALLTHROUGH */ |
388 | case '\0': | |
984263bc | 389 | case '/': |
99512ac4 | 390 | case CTLENDVAR: |
3e3895bf KP |
391 | len = p - startp - 1; |
392 | STPUTBIN(startp + 1, len, expdest); | |
393 | STACKSTRNUL(expdest); | |
394 | user = expdest - len; | |
395 | if (*user == '\0') { | |
02d0b1ce | 396 | home = lookupvar("HOME"); |
3e3895bf KP |
397 | } else { |
398 | pw = getpwnam(user); | |
02d0b1ce | 399 | home = pw != NULL ? pw->pw_dir : NULL; |
3e3895bf KP |
400 | } |
401 | STADJUST(-len, expdest); | |
02d0b1ce | 402 | if (home == NULL || *home == '\0') |
3e3895bf KP |
403 | return (startp); |
404 | strtodest(home, flag, VSNORMAL, 1, NULL); | |
02d0b1ce MD |
405 | return (p); |
406 | } | |
407 | p++; | |
408 | } | |
984263bc MD |
409 | } |
410 | ||
411 | ||
984263bc | 412 | /* |
02d0b1ce | 413 | * Expand arithmetic expression. |
984263bc | 414 | */ |
3e3895bf KP |
415 | static const char * |
416 | expari(const char *p, struct nodelist **restrict argbackq, int flag, | |
417 | struct worddest *dst) | |
984263bc | 418 | { |
02d0b1ce | 419 | char *q, *start; |
99512ac4 | 420 | arith_t result; |
984263bc | 421 | int begoff; |
984263bc | 422 | int quoted; |
02d0b1ce | 423 | int adj; |
984263bc | 424 | |
02d0b1ce MD |
425 | quoted = *p++ == '"'; |
426 | begoff = expdest - stackblock(); | |
3e3895bf | 427 | p = argstr(p, argbackq, 0, NULL); |
02d0b1ce MD |
428 | STPUTC('\0', expdest); |
429 | start = stackblock() + begoff; | |
430 | ||
99512ac4 | 431 | q = grabstackstr(expdest); |
02d0b1ce | 432 | result = arith(start); |
99512ac4 | 433 | ungrabstackstr(q, expdest); |
02d0b1ce MD |
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); | |
3e3895bf KP |
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); | |
02d0b1ce | 452 | return p; |
984263bc MD |
453 | } |
454 | ||
455 | ||
456 | /* | |
99512ac4 | 457 | * Perform command substitution. |
984263bc | 458 | */ |
99512ac4 | 459 | static void |
3e3895bf | 460 | expbackq(union node *cmd, int quoted, int flag, struct worddest *dst) |
984263bc MD |
461 | { |
462 | struct backcmd in; | |
463 | int i; | |
464 | char buf[128]; | |
465 | char *p; | |
466 | char *dest = expdest; | |
984263bc | 467 | char lastc; |
984263bc | 468 | char const *syntax = quoted? DQSYNTAX : BASESYNTAX; |
3e3895bf | 469 | int quotes = flag & (EXP_GLOB | EXP_CASE); |
b1abb130 | 470 | size_t nnl; |
3e3895bf KP |
471 | const char *ifs; |
472 | int startloc; | |
984263bc MD |
473 | |
474 | INTOFF; | |
984263bc MD |
475 | p = grabstackstr(dest); |
476 | evalbackcmd(cmd, &in); | |
477 | ungrabstackstr(p, dest); | |
984263bc MD |
478 | |
479 | p = in.buf; | |
3e3895bf | 480 | startloc = dest - stackblock(); |
5b762f1a | 481 | nnl = 0; |
3e3895bf KP |
482 | if (!quoted && flag & EXP_SPLIT) |
483 | ifs = ifsset() ? ifsval() : " \t\n"; | |
484 | else | |
485 | ifs = ""; | |
486 | /* Remove trailing newlines */ | |
984263bc MD |
487 | for (;;) { |
488 | if (--in.nleft < 0) { | |
489 | if (in.fd < 0) | |
490 | break; | |
3e3895bf KP |
491 | while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) |
492 | ; | |
984263bc MD |
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++; | |
3e3895bf KP |
500 | if (lastc == '\0') |
501 | continue; | |
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') | |
5b762f1a | 508 | nnl++; |
3e3895bf KP |
509 | else |
510 | NEXTWORD(lastc, flag, dest, dst); | |
511 | } else { | |
512 | CHECKSTRSPACE(2, dest); | |
513 | if (quotes && syntax[(int)lastc] == CCTL) | |
514 | USTPUTC(CTLESC, dest); | |
515 | USTPUTC(lastc, dest); | |
984263bc MD |
516 | } |
517 | } | |
3e3895bf KP |
518 | while (dest > stackblock() + startloc && STTOPC(dest) == '\n') |
519 | STUNPUTC(dest); | |
984263bc | 520 | |
984263bc MD |
521 | if (in.fd >= 0) |
522 | close(in.fd); | |
523 | if (in.buf) | |
524 | ckfree(in.buf); | |
3e3895bf KP |
525 | if (in.jp) { |
526 | p = grabstackstr(dest); | |
02d0b1ce | 527 | exitstatus = waitforjob(in.jp, (int *)NULL); |
3e3895bf KP |
528 | ungrabstackstr(p, dest); |
529 | } | |
530 | TRACE(("expbackq: done\n")); | |
984263bc MD |
531 | expdest = dest; |
532 | INTON; | |
533 | } | |
534 | ||
535 | ||
536 | ||
02d0b1ce MD |
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 | ||
3e3895bf KP |
548 | static const char * |
549 | subevalvar_trim(const char *p, struct nodelist **restrict argbackq, int strloc, | |
550 | int subtype, int startloc) | |
984263bc MD |
551 | { |
552 | char *startp; | |
553 | char *loc = NULL; | |
3e3895bf | 554 | char *str; |
984263bc | 555 | int c = 0; |
984263bc MD |
556 | int amount; |
557 | ||
3e3895bf | 558 | p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL); |
984263bc | 559 | STACKSTRNUL(expdest); |
984263bc | 560 | startp = stackblock() + startloc; |
3e3895bf | 561 | str = stackblock() + strloc; |
984263bc MD |
562 | |
563 | switch (subtype) { | |
984263bc MD |
564 | case VSTRIMLEFT: |
565 | for (loc = startp; loc < str; loc++) { | |
566 | c = *loc; | |
567 | *loc = '\0'; | |
3e3895bf | 568 | if (patmatch(str, startp)) { |
984263bc | 569 | *loc = c; |
02d0b1ce | 570 | recordleft(str, loc, startp); |
3e3895bf | 571 | return p; |
984263bc MD |
572 | } |
573 | *loc = c; | |
984263bc | 574 | } |
3e3895bf | 575 | break; |
984263bc MD |
576 | |
577 | case VSTRIMLEFTMAX: | |
578 | for (loc = str - 1; loc >= startp;) { | |
579 | c = *loc; | |
580 | *loc = '\0'; | |
3e3895bf | 581 | if (patmatch(str, startp)) { |
984263bc | 582 | *loc = c; |
02d0b1ce | 583 | recordleft(str, loc, startp); |
3e3895bf | 584 | return p; |
984263bc MD |
585 | } |
586 | *loc = c; | |
587 | loc--; | |
984263bc | 588 | } |
3e3895bf | 589 | break; |
984263bc MD |
590 | |
591 | case VSTRIMRIGHT: | |
592 | for (loc = str - 1; loc >= startp;) { | |
3e3895bf | 593 | if (patmatch(str, loc)) { |
984263bc MD |
594 | amount = loc - expdest; |
595 | STADJUST(amount, expdest); | |
3e3895bf | 596 | return p; |
984263bc MD |
597 | } |
598 | loc--; | |
984263bc | 599 | } |
3e3895bf | 600 | break; |
984263bc MD |
601 | |
602 | case VSTRIMRIGHTMAX: | |
603 | for (loc = startp; loc < str - 1; loc++) { | |
3e3895bf | 604 | if (patmatch(str, loc)) { |
984263bc MD |
605 | amount = loc - expdest; |
606 | STADJUST(amount, expdest); | |
3e3895bf | 607 | return p; |
984263bc | 608 | } |
984263bc | 609 | } |
3e3895bf | 610 | break; |
984263bc MD |
611 | |
612 | ||
3e3895bf KP |
613 | default: |
614 | abort(); | |
615 | } | |
616 | amount = (expdest - stackblock() - strloc) + 1; | |
617 | STADJUST(-amount, expdest); | |
618 | return p; | |
619 | } | |
620 | ||
621 | ||
622 | static const char * | |
623 | subevalvar_misc(const char *p, struct nodelist **restrict argbackq, | |
624 | const char *var, int subtype, int startloc, int varflags) | |
625 | { | |
626 | const char *end; | |
627 | char *startp; | |
628 | int amount; | |
629 | ||
630 | end = argstr(p, argbackq, EXP_TILDE, NULL); | |
631 | STACKSTRNUL(expdest); | |
632 | startp = stackblock() + startloc; | |
633 | ||
634 | switch (subtype) { | |
635 | case VSASSIGN: | |
636 | setvar(var, startp, 0); | |
637 | amount = startp - expdest; | |
638 | STADJUST(amount, expdest); | |
639 | return end; | |
640 | ||
641 | case VSQUESTION: | |
642 | if (*p != CTLENDVAR) { | |
643 | outfmt(out2, "%s\n", startp); | |
644 | error((char *)NULL); | |
645 | } | |
646 | error("%.*s: parameter %snot set", (int)(p - var - 1), | |
647 | var, (varflags & VSNUL) ? "null or " : ""); | |
648 | ||
984263bc MD |
649 | default: |
650 | abort(); | |
651 | } | |
984263bc MD |
652 | } |
653 | ||
654 | ||
655 | /* | |
656 | * Expand a variable, and return a pointer to the next character in the | |
657 | * input string. | |
658 | */ | |
659 | ||
3e3895bf KP |
660 | static const char * |
661 | evalvar(const char *p, struct nodelist **restrict argbackq, int flag, | |
662 | struct worddest *dst) | |
984263bc MD |
663 | { |
664 | int subtype; | |
665 | int varflags; | |
3e3895bf | 666 | const char *var; |
02d0b1ce | 667 | const char *val; |
984263bc MD |
668 | int patloc; |
669 | int c; | |
670 | int set; | |
671 | int special; | |
672 | int startloc; | |
673 | int varlen; | |
fd296645 | 674 | int varlenb; |
3e3895bf | 675 | char buf[21]; |
984263bc | 676 | |
af260b21 | 677 | varflags = (unsigned char)*p++; |
984263bc MD |
678 | subtype = varflags & VSTYPE; |
679 | var = p; | |
680 | special = 0; | |
681 | if (! is_name(*p)) | |
682 | special = 1; | |
683 | p = strchr(p, '=') + 1; | |
e0752c76 SK |
684 | if (varflags & VSLINENO) { |
685 | set = 1; | |
02d0b1ce MD |
686 | special = 1; |
687 | val = NULL; | |
e0752c76 | 688 | } else if (special) { |
984263bc MD |
689 | set = varisset(var, varflags & VSNUL); |
690 | val = NULL; | |
691 | } else { | |
692 | val = bltinlookup(var, 1); | |
693 | if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { | |
694 | val = NULL; | |
695 | set = 0; | |
696 | } else | |
697 | set = 1; | |
698 | } | |
699 | varlen = 0; | |
700 | startloc = expdest - stackblock(); | |
99512ac4 | 701 | if (!set && uflag && *var != '@' && *var != '*') { |
984263bc MD |
702 | switch (subtype) { |
703 | case VSNORMAL: | |
704 | case VSTRIMLEFT: | |
705 | case VSTRIMLEFTMAX: | |
706 | case VSTRIMRIGHT: | |
707 | case VSTRIMRIGHTMAX: | |
708 | case VSLENGTH: | |
af260b21 PA |
709 | error("%.*s: parameter not set", (int)(p - var - 1), |
710 | var); | |
984263bc MD |
711 | } |
712 | } | |
713 | if (set && subtype != VSPLUS) { | |
714 | /* insert the value of the variable */ | |
715 | if (special) { | |
3e3895bf KP |
716 | if (varflags & VSLINENO) { |
717 | if (p - var > (ptrdiff_t)sizeof(buf)) | |
718 | abort(); | |
719 | memcpy(buf, var, p - var - 1); | |
720 | buf[p - var - 1] = '\0'; | |
721 | strtodest(buf, flag, subtype, | |
722 | varflags & VSQUOTE, dst); | |
723 | } else | |
724 | varvalue(var, varflags & VSQUOTE, subtype, flag, | |
725 | dst); | |
984263bc | 726 | if (subtype == VSLENGTH) { |
fd296645 PA |
727 | varlenb = expdest - stackblock() - startloc; |
728 | varlen = varlenb; | |
729 | if (localeisutf8) { | |
730 | val = stackblock() + startloc; | |
731 | for (;val != expdest; val++) | |
732 | if ((*val & 0xC0) == 0x80) | |
733 | varlen--; | |
734 | } | |
735 | STADJUST(-varlenb, expdest); | |
984263bc MD |
736 | } |
737 | } else { | |
984263bc MD |
738 | if (subtype == VSLENGTH) { |
739 | for (;*val; val++) | |
fd296645 PA |
740 | if (!localeisutf8 || |
741 | (*val & 0xC0) != 0x80) | |
742 | varlen++; | |
984263bc | 743 | } |
3e3895bf | 744 | else |
02d0b1ce | 745 | strtodest(val, flag, subtype, |
3e3895bf | 746 | varflags & VSQUOTE, dst); |
984263bc MD |
747 | } |
748 | } | |
749 | ||
750 | if (subtype == VSPLUS) | |
751 | set = ! set; | |
752 | ||
984263bc MD |
753 | switch (subtype) { |
754 | case VSLENGTH: | |
3e3895bf KP |
755 | cvtnum(varlen, buf); |
756 | strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst); | |
02d0b1ce | 757 | break; |
984263bc MD |
758 | |
759 | case VSNORMAL: | |
3e3895bf | 760 | return p; |
984263bc MD |
761 | |
762 | case VSPLUS: | |
763 | case VSMINUS: | |
764 | if (!set) { | |
3e3895bf KP |
765 | return argstr(p, argbackq, |
766 | flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) | | |
767 | (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst); | |
984263bc | 768 | } |
984263bc MD |
769 | break; |
770 | ||
771 | case VSTRIMLEFT: | |
772 | case VSTRIMLEFTMAX: | |
773 | case VSTRIMRIGHT: | |
774 | case VSTRIMRIGHTMAX: | |
775 | if (!set) | |
776 | break; | |
777 | /* | |
778 | * Terminate the string and start recording the pattern | |
779 | * right after it | |
780 | */ | |
781 | STPUTC('\0', expdest); | |
782 | patloc = expdest - stackblock(); | |
3e3895bf KP |
783 | p = subevalvar_trim(p, argbackq, patloc, subtype, startloc); |
784 | reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst); | |
785 | if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE) | |
786 | dst->state = WORD_QUOTEMARK; | |
787 | return p; | |
984263bc MD |
788 | |
789 | case VSASSIGN: | |
790 | case VSQUESTION: | |
791 | if (!set) { | |
3e3895bf KP |
792 | p = subevalvar_misc(p, argbackq, var, subtype, |
793 | startloc, varflags); | |
794 | /* assert(subtype == VSASSIGN); */ | |
795 | val = lookupvar(var); | |
796 | strtodest(val, flag, subtype, varflags & VSQUOTE, dst); | |
797 | return p; | |
984263bc | 798 | } |
984263bc MD |
799 | break; |
800 | ||
af260b21 PA |
801 | case VSERROR: |
802 | c = p - var - 1; | |
803 | error("${%.*s%s}: Bad substitution", c, var, | |
804 | (c > 0 && *p != CTLENDVAR) ? "..." : ""); | |
805 | ||
984263bc MD |
806 | default: |
807 | abort(); | |
808 | } | |
02d0b1ce | 809 | |
3e3895bf | 810 | { /* skip to end of alternative */ |
984263bc MD |
811 | int nesting = 1; |
812 | for (;;) { | |
813 | if ((c = *p++) == CTLESC) | |
814 | p++; | |
3e3895bf KP |
815 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) |
816 | *argbackq = (*argbackq)->next; | |
817 | else if (c == CTLVAR) { | |
984263bc MD |
818 | if ((*p++ & VSTYPE) != VSNORMAL) |
819 | nesting++; | |
820 | } else if (c == CTLENDVAR) { | |
821 | if (--nesting == 0) | |
822 | break; | |
823 | } | |
824 | } | |
825 | } | |
826 | return p; | |
827 | } | |
828 | ||
829 | ||
830 | ||
831 | /* | |
3e3895bf | 832 | * Test whether a special or positional parameter is set. |
984263bc MD |
833 | */ |
834 | ||
99512ac4 | 835 | static int |
02d0b1ce | 836 | varisset(const char *name, int nulok) |
984263bc MD |
837 | { |
838 | ||
839 | if (*name == '!') | |
99512ac4 | 840 | return backgndpidset(); |
984263bc MD |
841 | else if (*name == '@' || *name == '*') { |
842 | if (*shellparam.p == NULL) | |
843 | return 0; | |
844 | ||
845 | if (nulok) { | |
846 | char **av; | |
847 | ||
848 | for (av = shellparam.p; *av; av++) | |
849 | if (**av != '\0') | |
850 | return 1; | |
851 | return 0; | |
852 | } | |
853 | } else if (is_digit(*name)) { | |
854 | char *ap; | |
02d0b1ce | 855 | long num; |
984263bc | 856 | |
02d0b1ce MD |
857 | errno = 0; |
858 | num = strtol(name, NULL, 10); | |
859 | if (errno != 0 || num > shellparam.nparam) | |
984263bc MD |
860 | return 0; |
861 | ||
862 | if (num == 0) | |
863 | ap = arg0; | |
864 | else | |
865 | ap = shellparam.p[num - 1]; | |
866 | ||
867 | if (nulok && (ap == NULL || *ap == '\0')) | |
868 | return 0; | |
869 | } | |
870 | return 1; | |
871 | } | |
872 | ||
99512ac4 | 873 | static void |
3e3895bf KP |
874 | strtodest(const char *p, int flag, int subtype, int quoted, |
875 | struct worddest *dst) | |
99512ac4 | 876 | { |
3e3895bf KP |
877 | if (subtype == VSLENGTH || subtype == VSTRIMLEFT || |
878 | subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT || | |
879 | subtype == VSTRIMRIGHTMAX) | |
880 | STPUTS(p, expdest); | |
881 | else if (flag & EXP_SPLIT && !quoted && dst != NULL) | |
882 | STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst); | |
883 | else if (flag & (EXP_GLOB | EXP_CASE)) | |
99512ac4 PA |
884 | STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); |
885 | else | |
886 | STPUTS(p, expdest); | |
887 | } | |
984263bc | 888 | |
3e3895bf KP |
889 | static void |
890 | reprocess(int startloc, int flag, int subtype, int quoted, | |
891 | struct worddest *dst) | |
892 | { | |
893 | static char *buf = NULL; | |
894 | static size_t buflen = 0; | |
895 | char *startp; | |
896 | size_t len, zpos, zlen; | |
897 | ||
898 | startp = stackblock() + startloc; | |
899 | len = expdest - startp; | |
900 | if (len >= SIZE_MAX / 2 || len > PTRDIFF_MAX) | |
901 | abort(); | |
902 | INTOFF; | |
903 | if (len >= buflen) { | |
904 | ckfree(buf); | |
905 | buf = NULL; | |
906 | } | |
907 | if (buflen < 128) | |
908 | buflen = 128; | |
909 | while (len >= buflen) | |
910 | buflen <<= 1; | |
911 | if (buf == NULL) | |
912 | buf = ckmalloc(buflen); | |
913 | INTON; | |
914 | memcpy(buf, startp, len); | |
915 | buf[len] = '\0'; | |
916 | STADJUST(-(ptrdiff_t)len, expdest); | |
917 | for (zpos = 0;;) { | |
918 | zlen = strlen(buf + zpos); | |
919 | strtodest(buf + zpos, flag, subtype, quoted, dst); | |
920 | zpos += zlen + 1; | |
921 | if (zpos == len + 1) | |
922 | break; | |
923 | if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len))) | |
924 | NEXTWORD('\0', flag, expdest, dst); | |
925 | } | |
926 | } | |
927 | ||
984263bc | 928 | /* |
3e3895bf | 929 | * Add the value of a special or positional parameter to the stack string. |
984263bc MD |
930 | */ |
931 | ||
99512ac4 | 932 | static void |
3e3895bf KP |
933 | varvalue(const char *name, int quoted, int subtype, int flag, |
934 | struct worddest *dst) | |
984263bc MD |
935 | { |
936 | int num; | |
937 | char *p; | |
938 | int i; | |
3e3895bf | 939 | int splitlater; |
02d0b1ce | 940 | char sep[2]; |
984263bc | 941 | char **ap; |
3e3895bf KP |
942 | char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1]; |
943 | ||
944 | if (subtype == VSLENGTH) | |
945 | flag &= ~EXP_FULL; | |
946 | splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || | |
947 | subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX; | |
984263bc MD |
948 | |
949 | switch (*name) { | |
950 | case '$': | |
951 | num = rootpid; | |
02d0b1ce | 952 | break; |
984263bc MD |
953 | case '?': |
954 | num = oexitstatus; | |
02d0b1ce | 955 | break; |
984263bc MD |
956 | case '#': |
957 | num = shellparam.nparam; | |
02d0b1ce | 958 | break; |
984263bc | 959 | case '!': |
99512ac4 | 960 | num = backgndpidval(); |
984263bc MD |
961 | break; |
962 | case '-': | |
3e3895bf KP |
963 | p = buf; |
964 | for (i = 0 ; i < NSHORTOPTS ; i++) { | |
965 | if (optval[i]) | |
966 | *p++ = optletter[i]; | |
984263bc | 967 | } |
3e3895bf KP |
968 | *p = '\0'; |
969 | strtodest(buf, flag, subtype, quoted, dst); | |
02d0b1ce | 970 | return; |
984263bc | 971 | case '@': |
3e3895bf | 972 | if (flag & EXP_SPLIT && quoted) { |
984263bc | 973 | for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { |
3e3895bf KP |
974 | strtodest(p, flag, subtype, quoted, dst); |
975 | if (*ap) { | |
976 | if (splitlater) | |
977 | STPUTC('\0', expdest); | |
978 | else | |
979 | NEXTWORD('\0', flag, expdest, | |
980 | dst); | |
981 | } | |
984263bc | 982 | } |
3e3895bf KP |
983 | if (shellparam.nparam > 0) |
984 | dst->state = WORD_QUOTEMARK; | |
02d0b1ce | 985 | return; |
984263bc | 986 | } |
af260b21 | 987 | /* FALLTHROUGH */ |
984263bc | 988 | case '*': |
af260b21 | 989 | if (ifsset()) |
02d0b1ce | 990 | sep[0] = ifsval()[0]; |
984263bc | 991 | else |
02d0b1ce MD |
992 | sep[0] = ' '; |
993 | sep[1] = '\0'; | |
984263bc | 994 | for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { |
3e3895bf | 995 | strtodest(p, flag, subtype, quoted, dst); |
5b14c76d PA |
996 | if (!*ap) |
997 | break; | |
02d0b1ce | 998 | if (sep[0]) |
3e3895bf KP |
999 | strtodest(sep, flag, subtype, quoted, dst); |
1000 | else if (flag & EXP_SPLIT && !quoted && **ap != '\0') { | |
1001 | if (splitlater) | |
1002 | STPUTC('\0', expdest); | |
1003 | else | |
1004 | NEXTWORD('\0', flag, expdest, dst); | |
1005 | } | |
984263bc | 1006 | } |
02d0b1ce | 1007 | return; |
984263bc MD |
1008 | default: |
1009 | if (is_digit(*name)) { | |
1010 | num = atoi(name); | |
02d0b1ce MD |
1011 | if (num == 0) |
1012 | p = arg0; | |
1013 | else if (num > 0 && num <= shellparam.nparam) | |
984263bc | 1014 | p = shellparam.p[num - 1]; |
02d0b1ce MD |
1015 | else |
1016 | return; | |
3e3895bf | 1017 | strtodest(p, flag, subtype, quoted, dst); |
984263bc | 1018 | } |
99512ac4 PA |
1019 | return; |
1020 | } | |
3e3895bf KP |
1021 | cvtnum(num, buf); |
1022 | strtodest(buf, flag, subtype, quoted, dst); | |
984263bc MD |
1023 | } |
1024 | ||
1025 | ||
3e3895bf | 1026 | |
99512ac4 PA |
1027 | static char expdir[PATH_MAX]; |
1028 | #define expdir_end (expdir + sizeof(expdir)) | |
984263bc MD |
1029 | |
1030 | /* | |
99512ac4 | 1031 | * Perform pathname generation and remove control characters. |
3e3895bf KP |
1032 | * At this point, the only control characters should be CTLESC. |
1033 | * The results are stored in the list dstlist. | |
984263bc | 1034 | */ |
99512ac4 | 1035 | static void |
3e3895bf | 1036 | expandmeta(char *pattern, struct arglist *dstlist) |
984263bc MD |
1037 | { |
1038 | char *p; | |
3e3895bf | 1039 | int firstmatch; |
984263bc | 1040 | char c; |
984263bc | 1041 | |
3e3895bf KP |
1042 | firstmatch = dstlist->count; |
1043 | p = pattern; | |
1044 | for (; (c = *p) != '\0'; p++) { | |
1045 | /* fast check for meta chars */ | |
1046 | if (c == '*' || c == '?' || c == '[') { | |
1047 | INTOFF; | |
1048 | expmeta(expdir, pattern, dstlist); | |
1049 | INTON; | |
1050 | break; | |
984263bc | 1051 | } |
3e3895bf KP |
1052 | } |
1053 | if (dstlist->count == firstmatch) { | |
1054 | /* | |
1055 | * no matches | |
1056 | */ | |
1057 | rmescapes(pattern); | |
1058 | appendarglist(dstlist, pattern); | |
1059 | } else { | |
1060 | qsort(&dstlist->args[firstmatch], | |
1061 | dstlist->count - firstmatch, | |
1062 | sizeof(dstlist->args[0]), expsortcmp); | |
984263bc MD |
1063 | } |
1064 | } | |
1065 | ||
1066 | ||
1067 | /* | |
1068 | * Do metacharacter (i.e. *, ?, [...]) expansion. | |
1069 | */ | |
1070 | ||
99512ac4 | 1071 | static void |
3e3895bf | 1072 | expmeta(char *enddir, char *name, struct arglist *arglist) |
984263bc | 1073 | { |
b1abb130 PA |
1074 | const char *p; |
1075 | const char *q; | |
1076 | const char *start; | |
984263bc MD |
1077 | char *endname; |
1078 | int metaflag; | |
1079 | struct stat statb; | |
1080 | DIR *dirp; | |
1081 | struct dirent *dp; | |
1082 | int atend; | |
1083 | int matchdot; | |
99512ac4 | 1084 | int esc; |
867f2c40 | 1085 | int namlen; |
984263bc MD |
1086 | |
1087 | metaflag = 0; | |
1088 | start = name; | |
99512ac4 | 1089 | for (p = name; esc = 0, *p; p += esc + 1) { |
984263bc MD |
1090 | if (*p == '*' || *p == '?') |
1091 | metaflag = 1; | |
1092 | else if (*p == '[') { | |
1093 | q = p + 1; | |
1094 | if (*q == '!' || *q == '^') | |
1095 | q++; | |
1096 | for (;;) { | |
984263bc MD |
1097 | if (*q == CTLESC) |
1098 | q++; | |
1099 | if (*q == '/' || *q == '\0') | |
1100 | break; | |
1101 | if (*++q == ']') { | |
1102 | metaflag = 1; | |
1103 | break; | |
1104 | } | |
1105 | } | |
984263bc MD |
1106 | } else if (*p == '\0') |
1107 | break; | |
99512ac4 PA |
1108 | else { |
1109 | if (*p == CTLESC) | |
1110 | esc++; | |
1111 | if (p[esc] == '/') { | |
1112 | if (metaflag) | |
1113 | break; | |
1114 | start = p + esc + 1; | |
1115 | } | |
984263bc MD |
1116 | } |
1117 | } | |
1118 | if (metaflag == 0) { /* we've reached the end of the file name */ | |
1119 | if (enddir != expdir) | |
1120 | metaflag++; | |
1121 | for (p = name ; ; p++) { | |
984263bc MD |
1122 | if (*p == CTLESC) |
1123 | p++; | |
1124 | *enddir++ = *p; | |
1125 | if (*p == '\0') | |
1126 | break; | |
99512ac4 PA |
1127 | if (enddir == expdir_end) |
1128 | return; | |
984263bc | 1129 | } |
85ef70cf | 1130 | if (metaflag == 0 || lstat(expdir, &statb) >= 0) |
3e3895bf | 1131 | appendarglist(arglist, stsavestr(expdir)); |
984263bc MD |
1132 | return; |
1133 | } | |
b1abb130 | 1134 | endname = name + (p - name); |
984263bc MD |
1135 | if (start != name) { |
1136 | p = name; | |
1137 | while (p < start) { | |
984263bc MD |
1138 | if (*p == CTLESC) |
1139 | p++; | |
1140 | *enddir++ = *p++; | |
99512ac4 PA |
1141 | if (enddir == expdir_end) |
1142 | return; | |
984263bc MD |
1143 | } |
1144 | } | |
1145 | if (enddir == expdir) { | |
02d0b1ce | 1146 | p = "."; |
984263bc | 1147 | } else if (enddir == expdir + 1 && *expdir == '/') { |
02d0b1ce | 1148 | p = "/"; |
984263bc | 1149 | } else { |
99512ac4 | 1150 | p = expdir; |
984263bc MD |
1151 | enddir[-1] = '\0'; |
1152 | } | |
99512ac4 | 1153 | if ((dirp = opendir(p)) == NULL) |
984263bc MD |
1154 | return; |
1155 | if (enddir != expdir) | |
1156 | enddir[-1] = '/'; | |
1157 | if (*endname == 0) { | |
1158 | atend = 1; | |
1159 | } else { | |
1160 | atend = 0; | |
99512ac4 PA |
1161 | *endname = '\0'; |
1162 | endname += esc + 1; | |
984263bc MD |
1163 | } |
1164 | matchdot = 0; | |
1165 | p = start; | |
984263bc MD |
1166 | if (*p == CTLESC) |
1167 | p++; | |
1168 | if (*p == '.') | |
1169 | matchdot++; | |
1170 | while (! int_pending() && (dp = readdir(dirp)) != NULL) { | |
1171 | if (dp->d_name[0] == '.' && ! matchdot) | |
1172 | continue; | |
3e3895bf | 1173 | if (patmatch(start, dp->d_name)) { |
81de66bf | 1174 | #ifdef _DIRENT_HAVE_D_NAMLEN |
867f2c40 | 1175 | namlen = dp->d_namlen; |
81de66bf | 1176 | #else |
1177 | namlen = strlen(dp->d_name); | |
1178 | #endif | |
867f2c40 | 1179 | if (enddir + namlen + 1 > expdir_end) |
99512ac4 | 1180 | continue; |
867f2c40 | 1181 | memcpy(enddir, dp->d_name, namlen + 1); |
99512ac4 | 1182 | if (atend) |
3e3895bf | 1183 | appendarglist(arglist, stsavestr(expdir)); |
99512ac4 | 1184 | else { |
c6011742 PA |
1185 | if (dp->d_type != DT_UNKNOWN && |
1186 | dp->d_type != DT_DIR && | |
1187 | dp->d_type != DT_LNK) | |
1188 | continue; | |
867f2c40 | 1189 | if (enddir + namlen + 2 > expdir_end) |
984263bc | 1190 | continue; |
867f2c40 PA |
1191 | enddir[namlen] = '/'; |
1192 | enddir[namlen + 1] = '\0'; | |
3e3895bf | 1193 | expmeta(enddir + namlen + 1, endname, arglist); |
984263bc MD |
1194 | } |
1195 | } | |
1196 | } | |
1197 | closedir(dirp); | |
1198 | if (! atend) | |
99512ac4 | 1199 | endname[-esc - 1] = esc ? CTLESC : '/'; |
984263bc MD |
1200 | } |
1201 | ||
1202 | ||
3e3895bf KP |
1203 | static int |
1204 | expsortcmp(const void *p1, const void *p2) | |
984263bc | 1205 | { |
3e3895bf KP |
1206 | const char *s1 = *(const char * const *)p1; |
1207 | const char *s2 = *(const char * const *)p2; | |
984263bc | 1208 | |
3e3895bf | 1209 | return (strcoll(s1, s2)); |
984263bc MD |
1210 | } |
1211 | ||
1212 | ||
1213 | ||
ead7935b PA |
1214 | static wchar_t |
1215 | get_wc(const char **p) | |
1216 | { | |
1217 | wchar_t c; | |
1218 | int chrlen; | |
1219 | ||
1220 | chrlen = mbtowc(&c, *p, 4); | |
1221 | if (chrlen == 0) | |
1222 | return 0; | |
1223 | else if (chrlen == -1) | |
1224 | c = 0; | |
1225 | else | |
1226 | *p += chrlen; | |
1227 | return c; | |
1228 | } | |
1229 | ||
1230 | ||
215809b7 PA |
1231 | /* |
1232 | * See if a character matches a character class, starting at the first colon | |
1233 | * of "[:class:]". | |
1234 | * If a valid character class is recognized, a pointer to the next character | |
1235 | * after the final closing bracket is stored into *end, otherwise a null | |
1236 | * pointer is stored into *end. | |
1237 | */ | |
1238 | static int | |
1239 | match_charclass(const char *p, wchar_t chr, const char **end) | |
1240 | { | |
1241 | char name[20]; | |
1242 | const char *nameend; | |
1243 | wctype_t cclass; | |
1244 | ||
1245 | *end = NULL; | |
1246 | p++; | |
1247 | nameend = strstr(p, ":]"); | |
b1abb130 PA |
1248 | if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || |
1249 | nameend == p) | |
215809b7 PA |
1250 | return 0; |
1251 | memcpy(name, p, nameend - p); | |
1252 | name[nameend - p] = '\0'; | |
1253 | *end = nameend + 2; | |
1254 | cclass = wctype(name); | |
1255 | /* An unknown class matches nothing but is valid nevertheless. */ | |
1256 | if (cclass == 0) | |
1257 | return 0; | |
1258 | return iswctype(chr, cclass); | |
1259 | } | |
1260 | ||
1261 | ||
984263bc MD |
1262 | /* |
1263 | * Returns true if the pattern matches the string. | |
1264 | */ | |
1265 | ||
b6bd99f6 | 1266 | static int |
3e3895bf | 1267 | patmatch(const char *pattern, const char *string) |
984263bc | 1268 | { |
215809b7 | 1269 | const char *p, *q, *end; |
ea8fa3ef | 1270 | const char *bt_p, *bt_q; |
984263bc | 1271 | char c; |
ead7935b | 1272 | wchar_t wc, wc2; |
984263bc MD |
1273 | |
1274 | p = pattern; | |
1275 | q = string; | |
ea8fa3ef PA |
1276 | bt_p = NULL; |
1277 | bt_q = NULL; | |
984263bc MD |
1278 | for (;;) { |
1279 | switch (c = *p++) { | |
1280 | case '\0': | |
ea8fa3ef PA |
1281 | if (*q != '\0') |
1282 | goto backtrack; | |
1283 | return 1; | |
984263bc | 1284 | case CTLESC: |
984263bc | 1285 | if (*q++ != *p++) |
ea8fa3ef | 1286 | goto backtrack; |
984263bc | 1287 | break; |
984263bc | 1288 | case '?': |
ea8fa3ef PA |
1289 | if (*q == '\0') |
1290 | return 0; | |
1291 | if (localeisutf8) { | |
ead7935b | 1292 | wc = get_wc(&q); |
ea8fa3ef PA |
1293 | /* |
1294 | * A '?' does not match invalid UTF-8 but a | |
1295 | * '*' does, so backtrack. | |
1296 | */ | |
1297 | if (wc == 0) | |
1298 | goto backtrack; | |
1299 | } else | |
3e3895bf | 1300 | q++; |
984263bc MD |
1301 | break; |
1302 | case '*': | |
1303 | c = *p; | |
3e3895bf | 1304 | while (c == '*') |
984263bc | 1305 | c = *++p; |
ea8fa3ef PA |
1306 | /* |
1307 | * If the pattern ends here, we know the string | |
1308 | * matches without needing to look at the rest of it. | |
1309 | */ | |
1310 | if (c == '\0') | |
1311 | return 1; | |
1312 | /* | |
1313 | * First try the shortest match for the '*' that | |
1314 | * could work. We can forget any earlier '*' since | |
1315 | * there is no way having it match more characters | |
1316 | * can help us, given that we are already here. | |
1317 | */ | |
1318 | bt_p = p; | |
1319 | bt_q = q; | |
1320 | break; | |
984263bc | 1321 | case '[': { |
3e3895bf | 1322 | const char *savep, *saveq; |
984263bc | 1323 | int invert, found; |
ead7935b | 1324 | wchar_t chr; |
984263bc | 1325 | |
3e3895bf | 1326 | savep = p, saveq = q; |
984263bc MD |
1327 | invert = 0; |
1328 | if (*p == '!' || *p == '^') { | |
1329 | invert++; | |
1330 | p++; | |
1331 | } | |
1332 | found = 0; | |
ea8fa3ef PA |
1333 | if (*q == '\0') |
1334 | return 0; | |
1335 | if (localeisutf8) { | |
ead7935b | 1336 | chr = get_wc(&q); |
ea8fa3ef PA |
1337 | if (chr == 0) |
1338 | goto backtrack; | |
1339 | } else | |
ead7935b | 1340 | chr = (unsigned char)*q++; |
984263bc MD |
1341 | c = *p++; |
1342 | do { | |
3e3895bf KP |
1343 | if (c == '\0') { |
1344 | p = savep, q = saveq; | |
1345 | c = '['; | |
1346 | goto dft; | |
1347 | } | |
215809b7 PA |
1348 | if (c == '[' && *p == ':') { |
1349 | found |= match_charclass(p, chr, &end); | |
3e3895bf | 1350 | if (end != NULL) { |
215809b7 | 1351 | p = end; |
3e3895bf KP |
1352 | continue; |
1353 | } | |
215809b7 | 1354 | } |
984263bc MD |
1355 | if (c == CTLESC) |
1356 | c = *p++; | |
ead7935b PA |
1357 | if (localeisutf8 && c & 0x80) { |
1358 | p--; | |
1359 | wc = get_wc(&p); | |
1360 | if (wc == 0) /* bad utf-8 */ | |
1361 | return 0; | |
1362 | } else | |
1363 | wc = (unsigned char)c; | |
984263bc MD |
1364 | if (*p == '-' && p[1] != ']') { |
1365 | p++; | |
984263bc MD |
1366 | if (*p == CTLESC) |
1367 | p++; | |
ead7935b PA |
1368 | if (localeisutf8) { |
1369 | wc2 = get_wc(&p); | |
1370 | if (wc2 == 0) /* bad utf-8 */ | |
1371 | return 0; | |
1372 | } else | |
1373 | wc2 = (unsigned char)*p++; | |
1374 | if ( collate_range_cmp(chr, wc) >= 0 | |
1375 | && collate_range_cmp(chr, wc2) <= 0 | |
984263bc MD |
1376 | ) |
1377 | found = 1; | |
984263bc | 1378 | } else { |
ead7935b | 1379 | if (chr == wc) |
984263bc MD |
1380 | found = 1; |
1381 | } | |
1382 | } while ((c = *p++) != ']'); | |
1383 | if (found == invert) | |
ea8fa3ef | 1384 | goto backtrack; |
984263bc MD |
1385 | break; |
1386 | } | |
1387 | dft: default: | |
ea8fa3ef PA |
1388 | if (*q == '\0') |
1389 | return 0; | |
1390 | if (*q++ == c) | |
1391 | break; | |
1392 | backtrack: | |
1393 | /* | |
1394 | * If we have a mismatch (other than hitting the end | |
1395 | * of the string), go back to the last '*' seen and | |
1396 | * have it match one additional character. | |
1397 | */ | |
1398 | if (bt_p == NULL) | |
1399 | return 0; | |
ea8fa3ef | 1400 | if (*bt_q == '\0') |
984263bc | 1401 | return 0; |
ea8fa3ef PA |
1402 | bt_q++; |
1403 | p = bt_p; | |
1404 | q = bt_q; | |
984263bc MD |
1405 | break; |
1406 | } | |
1407 | } | |
984263bc MD |
1408 | } |
1409 | ||
1410 | ||
1411 | ||
1412 | /* | |
99512ac4 | 1413 | * Remove any CTLESC and CTLQUOTEMARK characters from a string. |
984263bc MD |
1414 | */ |
1415 | ||
1416 | void | |
1417 | rmescapes(char *str) | |
1418 | { | |
1419 | char *p, *q; | |
1420 | ||
1421 | p = str; | |
99512ac4 | 1422 | while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { |
984263bc MD |
1423 | if (*p++ == '\0') |
1424 | return; | |
1425 | } | |
1426 | q = p; | |
1427 | while (*p) { | |
99512ac4 | 1428 | if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { |
984263bc MD |
1429 | p++; |
1430 | continue; | |
1431 | } | |
1432 | if (*p == CTLESC) | |
1433 | p++; | |
1434 | *q++ = *p++; | |
1435 | } | |
1436 | *q = '\0'; | |
1437 | } | |
1438 | ||
1439 | ||
1440 | ||
1441 | /* | |
1442 | * See if a pattern matches in a case statement. | |
1443 | */ | |
1444 | ||
1445 | int | |
99512ac4 | 1446 | casematch(union node *pattern, const char *val) |
984263bc MD |
1447 | { |
1448 | struct stackmark smark; | |
3e3895bf | 1449 | struct nodelist *argbackq; |
984263bc MD |
1450 | int result; |
1451 | char *p; | |
1452 | ||
1453 | setstackmark(&smark); | |
1454 | argbackq = pattern->narg.backquote; | |
1455 | STARTSTACKSTR(expdest); | |
3e3895bf | 1456 | argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL); |
984263bc MD |
1457 | STPUTC('\0', expdest); |
1458 | p = grabstackstr(expdest); | |
3e3895bf | 1459 | result = patmatch(p, val); |
984263bc MD |
1460 | popstackmark(&smark); |
1461 | return result; | |
1462 | } | |
1463 | ||
1464 | /* | |
1465 | * Our own itoa(). | |
1466 | */ | |
1467 | ||
3e3895bf | 1468 | static void |
984263bc MD |
1469 | cvtnum(int num, char *buf) |
1470 | { | |
1471 | char temp[32]; | |
1472 | int neg = num < 0; | |
1473 | char *p = temp + 31; | |
1474 | ||
1475 | temp[31] = '\0'; | |
1476 | ||
1477 | do { | |
1478 | *--p = num % 10 + '0'; | |
1479 | } while ((num /= 10) != 0); | |
1480 | ||
1481 | if (neg) | |
1482 | *--p = '-'; | |
1483 | ||
3e3895bf | 1484 | memcpy(buf, p, temp + 32 - p); |
984263bc | 1485 | } |
d13d0ee2 PA |
1486 | |
1487 | /* | |
1488 | * Do most of the work for wordexp(3). | |
1489 | */ | |
1490 | ||
1491 | int | |
1492 | wordexpcmd(int argc, char **argv) | |
1493 | { | |
1494 | size_t len; | |
1495 | int i; | |
1496 | ||
1497 | out1fmt("%08x", argc - 1); | |
1498 | for (i = 1, len = 0; i < argc; i++) | |
1499 | len += strlen(argv[i]); | |
1500 | out1fmt("%08x", (int)len); | |
99512ac4 PA |
1501 | for (i = 1; i < argc; i++) |
1502 | outbin(argv[i], strlen(argv[i]) + 1, out1); | |
d13d0ee2 PA |
1503 | return (0); |
1504 | } | |
3e3895bf | 1505 | |
3e3895bf KP |
1506 | /* |
1507 | * Do most of the work for wordexp(3), new version. | |
1508 | */ | |
1509 | ||
1510 | int | |
a31d3627 | 1511 | wordexp2cmd(int argc __unused, char **argv __unused) |
3e3895bf KP |
1512 | { |
1513 | struct arglist arglist; | |
1514 | union node *args, *n; | |
1515 | size_t len; | |
1516 | int ch; | |
1517 | int protected = 0; | |
1518 | int fd = -1; | |
1519 | int i; | |
1520 | ||
1521 | while ((ch = nextopt("f:p")) != '\0') { | |
1522 | switch (ch) { | |
1523 | case 'f': | |
1524 | fd = number(shoptarg); | |
1525 | break; | |
1526 | case 'p': | |
1527 | protected = 1; | |
1528 | break; | |
1529 | } | |
1530 | } | |
1531 | if (*argptr != NULL) | |
1532 | error("wrong number of arguments"); | |
1533 | if (fd < 0) | |
1534 | error("missing fd"); | |
1535 | INTOFF; | |
1536 | setinputfd(fd, 1); | |
1537 | INTON; | |
1538 | args = parsewordexp(); | |
1539 | popfile(); /* will also close fd */ | |
1540 | if (protected) | |
1541 | for (n = args; n != NULL; n = n->narg.next) { | |
1542 | if (n->narg.backquote != NULL) { | |
1543 | outcslow('C', out1); | |
1544 | error("command substitution disabled"); | |
1545 | } | |
1546 | } | |
1547 | outcslow(' ', out1); | |
1548 | emptyarglist(&arglist); | |
1549 | for (n = args; n != NULL; n = n->narg.next) | |
1550 | expandarg(n, &arglist, EXP_FULL | EXP_TILDE); | |
1551 | for (i = 0, len = 0; i < arglist.count; i++) | |
1552 | len += strlen(arglist.args[i]); | |
1553 | out1fmt("%016x %016zx", arglist.count, len); | |
1554 | for (i = 0; i < arglist.count; i++) | |
1555 | outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1); | |
1556 | return (0); | |
1557 | } |