Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/expand.c
Parent Directory | Revision Log
Revision 815 -
(show annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 33679 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 33679 byte(s)
-updated to klibc-1.5.15
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. |
6 | * |
7 | * This code is derived from software contributed to Berkeley by |
8 | * Kenneth Almquist. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <sys/types.h> |
36 | #include <sys/time.h> |
37 | #include <sys/stat.h> |
38 | #include <dirent.h> |
39 | #include <unistd.h> |
40 | #ifdef HAVE_GETPWNAM |
41 | #include <pwd.h> |
42 | #endif |
43 | #include <stdlib.h> |
44 | #include <stdio.h> |
45 | #include <limits.h> |
46 | #include <string.h> |
47 | #if defined(__GLIBC__) |
48 | #if !defined(FNMATCH_BROKEN) |
49 | #include <fnmatch.h> |
50 | #if !defined(GLOB_BROKEN) |
51 | #include <glob.h> |
52 | #endif |
53 | #else |
54 | #include <ctype.h> |
55 | #endif |
56 | #endif |
57 | |
58 | /* |
59 | * Routines to expand arguments to commands. We have to deal with |
60 | * backquotes, shell variables, and file metacharacters. |
61 | */ |
62 | |
63 | #include "shell.h" |
64 | #include "main.h" |
65 | #include "nodes.h" |
66 | #include "eval.h" |
67 | #include "expand.h" |
68 | #include "syntax.h" |
69 | #include "parser.h" |
70 | #include "jobs.h" |
71 | #include "options.h" |
72 | #include "var.h" |
73 | #include "output.h" |
74 | #include "memalloc.h" |
75 | #include "error.h" |
76 | #include "mystring.h" |
77 | #include "show.h" |
78 | #include "system.h" |
79 | |
80 | /* |
81 | * _rmescape() flags |
82 | */ |
83 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
84 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
85 | #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ |
86 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ |
87 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ |
88 | |
89 | /* Add CTLESC when necessary. */ |
90 | #define QUOTES_ESC (EXP_FULL | EXP_CASE) |
91 | /* Do not skip NUL characters. */ |
92 | #define QUOTES_KEEPNUL EXP_TILDE |
93 | |
94 | /* |
95 | * Structure specifying which parts of the string should be searched |
96 | * for IFS characters. |
97 | */ |
98 | |
99 | struct ifsregion { |
100 | struct ifsregion *next; /* next region in list */ |
101 | int begoff; /* offset of start of region */ |
102 | int endoff; /* offset of end of region */ |
103 | int nulonly; /* search for nul bytes only */ |
104 | }; |
105 | |
106 | /* output of current string */ |
107 | static char *expdest; |
108 | /* list of back quote expressions */ |
109 | static struct nodelist *argbackq; |
110 | /* first struct in list of ifs regions */ |
111 | static struct ifsregion ifsfirst; |
112 | /* last struct in list */ |
113 | static struct ifsregion *ifslastp; |
114 | /* holds expanded arg list */ |
115 | static struct arglist exparg; |
116 | |
117 | STATIC void argstr(char *, int); |
118 | STATIC char *exptilde(char *, char *, int); |
119 | STATIC void expbackq(union node *, int, int); |
120 | STATIC const char *subevalvar(char *, char *, int, int, int, int, int); |
121 | STATIC char *evalvar(char *, int); |
122 | STATIC size_t strtodest(const char *, const char *, int); |
123 | STATIC void memtodest(const char *, size_t, const char *, int); |
124 | STATIC ssize_t varvalue(char *, int, int); |
125 | STATIC void recordregion(int, int, int); |
126 | STATIC void removerecordregions(int); |
127 | STATIC void ifsbreakup(char *, struct arglist *); |
128 | STATIC void ifsfree(void); |
129 | STATIC void expandmeta(struct strlist *, int); |
130 | #if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) |
131 | STATIC void addglob(const glob_t *); |
132 | #else |
133 | STATIC void expmeta(char *, char *); |
134 | #endif |
135 | STATIC void addfname(char *); |
136 | #if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) |
137 | STATIC struct strlist *expsort(struct strlist *); |
138 | STATIC struct strlist *msort(struct strlist *, int); |
139 | #endif |
140 | STATIC int patmatch(char *, const char *); |
141 | #if !defined(__GLIBC__) || defined(FNMATCH_BROKEN) |
142 | STATIC int pmatch(const char *, const char *); |
143 | #else |
144 | #define pmatch(a, b) !fnmatch((a), (b), 0) |
145 | #endif |
146 | STATIC int cvtnum(long); |
147 | STATIC size_t esclen(const char *, const char *); |
148 | STATIC char *scanleft(char *, char *, char *, char *, int, int); |
149 | STATIC char *scanright(char *, char *, char *, char *, int, int); |
150 | STATIC void varunset(const char *, const char *, const char *, int) |
151 | __attribute__((__noreturn__)); |
152 | |
153 | |
154 | /* |
155 | * Prepare a pattern for a glob(3) call. |
156 | * |
157 | * Returns an stalloced string. |
158 | */ |
159 | |
160 | STATIC inline char * |
161 | preglob(const char *pattern, int quoted, int flag) { |
162 | flag |= RMESCAPE_GLOB; |
163 | if (quoted) { |
164 | flag |= RMESCAPE_QUOTED; |
165 | } |
166 | return _rmescapes((char *)pattern, flag); |
167 | } |
168 | |
169 | |
170 | STATIC size_t |
171 | esclen(const char *start, const char *p) { |
172 | size_t esc = 0; |
173 | |
174 | while (p > start && *--p == (char)CTLESC) { |
175 | esc++; |
176 | } |
177 | return esc; |
178 | } |
179 | |
180 | |
181 | static inline const char *getpwhome(const char *name) |
182 | { |
183 | #ifdef HAVE_GETPWNAM |
184 | struct passwd *pw = getpwnam(name); |
185 | return pw ? pw->pw_dir : 0; |
186 | #else |
187 | return 0; |
188 | #endif |
189 | } |
190 | |
191 | |
192 | /* |
193 | * Expand shell variables and backquotes inside a here document. |
194 | */ |
195 | |
196 | void |
197 | expandhere(union node *arg, int fd) |
198 | { |
199 | herefd = fd; |
200 | expandarg(arg, (struct arglist *)NULL, 0); |
201 | xwrite(fd, stackblock(), expdest - (char *)stackblock()); |
202 | } |
203 | |
204 | |
205 | /* |
206 | * Perform variable substitution and command substitution on an argument, |
207 | * placing the resulting list of arguments in arglist. If EXP_FULL is true, |
208 | * perform splitting and file name expansion. When arglist is NULL, perform |
209 | * here document expansion. |
210 | */ |
211 | |
212 | void |
213 | expandarg(union node *arg, struct arglist *arglist, int flag) |
214 | { |
215 | struct strlist *sp; |
216 | char *p; |
217 | |
218 | argbackq = arg->narg.backquote; |
219 | STARTSTACKSTR(expdest); |
220 | ifsfirst.next = NULL; |
221 | ifslastp = NULL; |
222 | argstr(arg->narg.text, flag); |
223 | p = _STPUTC('\0', expdest); |
224 | expdest = p - 1; |
225 | if (arglist == NULL) { |
226 | return; /* here document expanded */ |
227 | } |
228 | p = grabstackstr(p); |
229 | exparg.lastp = &exparg.list; |
230 | /* |
231 | * TODO - EXP_REDIR |
232 | */ |
233 | if (flag & EXP_FULL) { |
234 | ifsbreakup(p, &exparg); |
235 | *exparg.lastp = NULL; |
236 | exparg.lastp = &exparg.list; |
237 | expandmeta(exparg.list, flag); |
238 | } else { |
239 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ |
240 | rmescapes(p); |
241 | sp = (struct strlist *)stalloc(sizeof (struct strlist)); |
242 | sp->text = p; |
243 | *exparg.lastp = sp; |
244 | exparg.lastp = &sp->next; |
245 | } |
246 | if (ifsfirst.next) |
247 | ifsfree(); |
248 | *exparg.lastp = NULL; |
249 | if (exparg.list) { |
250 | *arglist->lastp = exparg.list; |
251 | arglist->lastp = exparg.lastp; |
252 | } |
253 | } |
254 | |
255 | |
256 | |
257 | /* |
258 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
259 | * characters to allow for further processing. Otherwise treat |
260 | * $@ like $* since no splitting will be performed. |
261 | */ |
262 | |
263 | STATIC void |
264 | argstr(char *p, int flag) |
265 | { |
266 | static const char spclchars[] = { |
267 | '=', |
268 | ':', |
269 | CTLQUOTEMARK, |
270 | CTLENDVAR, |
271 | CTLESC, |
272 | CTLVAR, |
273 | CTLBACKQ, |
274 | CTLBACKQ | CTLQUOTE, |
275 | CTLENDARI, |
276 | 0 |
277 | }; |
278 | const char *reject = spclchars; |
279 | int c; |
280 | int quotes = flag & QUOTES_ESC; |
281 | int breakall = flag & EXP_WORD; |
282 | int inquotes; |
283 | size_t length; |
284 | int startloc; |
285 | |
286 | if (!(flag & EXP_VARTILDE)) { |
287 | reject += 2; |
288 | } else if (flag & EXP_VARTILDE2) { |
289 | reject++; |
290 | } |
291 | inquotes = 0; |
292 | length = 0; |
293 | if (flag & EXP_TILDE) { |
294 | char *q; |
295 | |
296 | flag &= ~EXP_TILDE; |
297 | tilde: |
298 | q = p; |
299 | if (*q == (char)CTLESC && (flag & EXP_QWORD)) |
300 | q++; |
301 | if (*q == '~') |
302 | p = exptilde(p, q, flag); |
303 | } |
304 | start: |
305 | startloc = expdest - (char *)stackblock(); |
306 | for (;;) { |
307 | length += strcspn(p + length, reject); |
308 | c = (signed char)p[length]; |
309 | if (c && (!(c & 0x80) || c == CTLENDARI)) { |
310 | /* c == '=' || c == ':' || c == CTLENDARI */ |
311 | length++; |
312 | } |
313 | if (length > 0) { |
314 | int newloc; |
315 | expdest = stnputs(p, length, expdest); |
316 | newloc = expdest - (char *)stackblock(); |
317 | if (breakall && !inquotes && newloc > startloc) { |
318 | recordregion(startloc, newloc, 0); |
319 | } |
320 | startloc = newloc; |
321 | } |
322 | p += length + 1; |
323 | length = 0; |
324 | |
325 | switch (c) { |
326 | case '\0': |
327 | goto breakloop; |
328 | case '=': |
329 | if (flag & EXP_VARTILDE2) { |
330 | p--; |
331 | continue; |
332 | } |
333 | flag |= EXP_VARTILDE2; |
334 | reject++; |
335 | /* fall through */ |
336 | case ':': |
337 | /* |
338 | * sort of a hack - expand tildes in variable |
339 | * assignments (after the first '=' and after ':'s). |
340 | */ |
341 | if (*--p == '~') { |
342 | goto tilde; |
343 | } |
344 | continue; |
345 | } |
346 | |
347 | switch (c) { |
348 | case CTLENDVAR: /* ??? */ |
349 | goto breakloop; |
350 | case CTLQUOTEMARK: |
351 | /* "$@" syntax adherence hack */ |
352 | if ( |
353 | !inquotes && |
354 | !memcmp(p, dolatstr, DOLATSTRLEN) && |
355 | (p[4] == (char)CTLQUOTEMARK || ( |
356 | p[4] == (char)CTLENDVAR && |
357 | p[5] == (char)CTLQUOTEMARK |
358 | )) |
359 | ) { |
360 | p = evalvar(p + 1, flag) + 1; |
361 | goto start; |
362 | } |
363 | inquotes = !inquotes; |
364 | addquote: |
365 | if (quotes) { |
366 | p--; |
367 | length++; |
368 | startloc++; |
369 | } |
370 | break; |
371 | case CTLESC: |
372 | startloc++; |
373 | length++; |
374 | goto addquote; |
375 | case CTLVAR: |
376 | p = evalvar(p, flag); |
377 | goto start; |
378 | case CTLBACKQ: |
379 | c = 0; |
380 | case CTLBACKQ|CTLQUOTE: |
381 | expbackq(argbackq->n, c, quotes); |
382 | argbackq = argbackq->next; |
383 | goto start; |
384 | case CTLENDARI: |
385 | p--; |
386 | expari(quotes); |
387 | goto start; |
388 | } |
389 | } |
390 | breakloop: |
391 | ; |
392 | } |
393 | |
394 | STATIC char * |
395 | exptilde(char *startp, char *p, int flag) |
396 | { |
397 | signed char c; |
398 | char *name; |
399 | const char *home; |
400 | int quotes = flag & QUOTES_ESC; |
401 | int startloc; |
402 | |
403 | name = p + 1; |
404 | |
405 | while ((c = *++p) != '\0') { |
406 | switch(c) { |
407 | case CTLESC: |
408 | return (startp); |
409 | case CTLQUOTEMARK: |
410 | return (startp); |
411 | case ':': |
412 | if (flag & EXP_VARTILDE) |
413 | goto done; |
414 | break; |
415 | case '/': |
416 | case CTLENDVAR: |
417 | goto done; |
418 | } |
419 | } |
420 | done: |
421 | *p = '\0'; |
422 | if (*name == '\0') { |
423 | home = lookupvar(homestr); |
424 | } else { |
425 | home = getpwhome(name); |
426 | } |
427 | if (!home || !*home) |
428 | goto lose; |
429 | *p = c; |
430 | startloc = expdest - (char *)stackblock(); |
431 | strtodest(home, SQSYNTAX, quotes); |
432 | recordregion(startloc, expdest - (char *)stackblock(), 0); |
433 | return (p); |
434 | lose: |
435 | *p = c; |
436 | return (startp); |
437 | } |
438 | |
439 | |
440 | STATIC void |
441 | removerecordregions(int endoff) |
442 | { |
443 | if (ifslastp == NULL) |
444 | return; |
445 | |
446 | if (ifsfirst.endoff > endoff) { |
447 | while (ifsfirst.next != NULL) { |
448 | struct ifsregion *ifsp; |
449 | INTOFF; |
450 | ifsp = ifsfirst.next->next; |
451 | ckfree(ifsfirst.next); |
452 | ifsfirst.next = ifsp; |
453 | INTON; |
454 | } |
455 | if (ifsfirst.begoff > endoff) |
456 | ifslastp = NULL; |
457 | else { |
458 | ifslastp = &ifsfirst; |
459 | ifsfirst.endoff = endoff; |
460 | } |
461 | return; |
462 | } |
463 | |
464 | ifslastp = &ifsfirst; |
465 | while (ifslastp->next && ifslastp->next->begoff < endoff) |
466 | ifslastp=ifslastp->next; |
467 | while (ifslastp->next != NULL) { |
468 | struct ifsregion *ifsp; |
469 | INTOFF; |
470 | ifsp = ifslastp->next->next; |
471 | ckfree(ifslastp->next); |
472 | ifslastp->next = ifsp; |
473 | INTON; |
474 | } |
475 | if (ifslastp->endoff > endoff) |
476 | ifslastp->endoff = endoff; |
477 | } |
478 | |
479 | |
480 | /* |
481 | * Expand arithmetic expression. Backup to start of expression, |
482 | * evaluate, place result in (backed up) result, adjust string position. |
483 | */ |
484 | void |
485 | expari(int quotes) |
486 | { |
487 | char *p, *start; |
488 | int begoff; |
489 | int flag; |
490 | int len; |
491 | |
492 | /* ifsfree(); */ |
493 | |
494 | /* |
495 | * This routine is slightly over-complicated for |
496 | * efficiency. Next we scan backwards looking for the |
497 | * start of arithmetic. |
498 | */ |
499 | start = stackblock(); |
500 | p = expdest - 1; |
501 | *p = '\0'; |
502 | p--; |
503 | do { |
504 | int esc; |
505 | |
506 | while (*p != (char)CTLARI) { |
507 | p--; |
508 | #ifdef DEBUG |
509 | if (p < start) { |
510 | sh_error("missing CTLARI (shouldn't happen)"); |
511 | } |
512 | #endif |
513 | } |
514 | |
515 | esc = esclen(start, p); |
516 | if (!(esc % 2)) { |
517 | break; |
518 | } |
519 | |
520 | p -= esc + 1; |
521 | } while (1); |
522 | |
523 | begoff = p - start; |
524 | |
525 | removerecordregions(begoff); |
526 | |
527 | flag = p[1]; |
528 | |
529 | expdest = p; |
530 | |
531 | if (quotes) |
532 | rmescapes(p + 2); |
533 | |
534 | len = cvtnum(arith(p + 2)); |
535 | |
536 | if (flag != '"') |
537 | recordregion(begoff, begoff + len, 0); |
538 | } |
539 | |
540 | |
541 | /* |
542 | * Expand stuff in backwards quotes. |
543 | */ |
544 | |
545 | STATIC void |
546 | expbackq(union node *cmd, int quoted, int quotes) |
547 | { |
548 | struct backcmd in; |
549 | int i; |
550 | char buf[128]; |
551 | char *p; |
552 | char *dest; |
553 | int startloc; |
554 | char const *syntax = quoted? DQSYNTAX : BASESYNTAX; |
555 | struct stackmark smark; |
556 | |
557 | INTOFF; |
558 | setstackmark(&smark); |
559 | dest = expdest; |
560 | startloc = dest - (char *)stackblock(); |
561 | grabstackstr(dest); |
562 | evalbackcmd(cmd, (struct backcmd *) &in); |
563 | popstackmark(&smark); |
564 | |
565 | p = in.buf; |
566 | i = in.nleft; |
567 | if (i == 0) |
568 | goto read; |
569 | for (;;) { |
570 | memtodest(p, i, syntax, quotes); |
571 | read: |
572 | if (in.fd < 0) |
573 | break; |
574 | do { |
575 | i = read(in.fd, buf, sizeof buf); |
576 | } while (i < 0 && errno == EINTR); |
577 | TRACE(("expbackq: read returns %d\n", i)); |
578 | if (i <= 0) |
579 | break; |
580 | p = buf; |
581 | } |
582 | |
583 | if (in.buf) |
584 | ckfree(in.buf); |
585 | if (in.fd >= 0) { |
586 | close(in.fd); |
587 | back_exitstatus = waitforjob(in.jp); |
588 | } |
589 | INTON; |
590 | |
591 | /* Eat all trailing newlines */ |
592 | dest = expdest; |
593 | for (; dest > (char *)stackblock() && dest[-1] == '\n';) |
594 | STUNPUTC(dest); |
595 | expdest = dest; |
596 | |
597 | if (quoted == 0) |
598 | recordregion(startloc, dest - (char *)stackblock(), 0); |
599 | TRACE(("evalbackq: size=%d: \"%.*s\"\n", |
600 | (dest - (char *)stackblock()) - startloc, |
601 | (dest - (char *)stackblock()) - startloc, |
602 | stackblock() + startloc)); |
603 | } |
604 | |
605 | |
606 | STATIC char * |
607 | scanleft( |
608 | char *startp, char *rmesc, char *rmescend, char *str, int quotes, |
609 | int zero |
610 | ) { |
611 | char *loc; |
612 | char *loc2; |
613 | char c; |
614 | |
615 | loc = startp; |
616 | loc2 = rmesc; |
617 | do { |
618 | int match; |
619 | const char *s = loc2; |
620 | c = *loc2; |
621 | if (zero) { |
622 | *loc2 = '\0'; |
623 | s = rmesc; |
624 | } |
625 | match = pmatch(str, s); |
626 | *loc2 = c; |
627 | if (match) |
628 | return loc; |
629 | if (quotes && *loc == (char)CTLESC) |
630 | loc++; |
631 | loc++; |
632 | loc2++; |
633 | } while (c); |
634 | return 0; |
635 | } |
636 | |
637 | |
638 | STATIC char * |
639 | scanright( |
640 | char *startp, char *rmesc, char *rmescend, char *str, int quotes, |
641 | int zero |
642 | ) { |
643 | int esc = 0; |
644 | char *loc; |
645 | char *loc2; |
646 | |
647 | for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { |
648 | int match; |
649 | char c = *loc2; |
650 | const char *s = loc2; |
651 | if (zero) { |
652 | *loc2 = '\0'; |
653 | s = rmesc; |
654 | } |
655 | match = pmatch(str, s); |
656 | *loc2 = c; |
657 | if (match) |
658 | return loc; |
659 | loc--; |
660 | if (quotes) { |
661 | if (--esc < 0) { |
662 | esc = esclen(startp, loc); |
663 | } |
664 | if (esc % 2) { |
665 | esc--; |
666 | loc--; |
667 | } |
668 | } |
669 | } |
670 | return 0; |
671 | } |
672 | |
673 | STATIC const char * |
674 | subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) |
675 | { |
676 | char *startp; |
677 | char *loc; |
678 | int saveherefd = herefd; |
679 | struct nodelist *saveargbackq = argbackq; |
680 | int amount; |
681 | char *rmesc, *rmescend; |
682 | int zero; |
683 | char *(*scan)(char *, char *, char *, char *, int , int); |
684 | |
685 | herefd = -1; |
686 | argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); |
687 | STPUTC('\0', expdest); |
688 | herefd = saveherefd; |
689 | argbackq = saveargbackq; |
690 | startp = stackblock() + startloc; |
691 | |
692 | switch (subtype) { |
693 | case VSASSIGN: |
694 | setvar(str, startp, 0); |
695 | amount = startp - expdest; |
696 | STADJUST(amount, expdest); |
697 | return startp; |
698 | |
699 | case VSQUESTION: |
700 | varunset(p, str, startp, varflags); |
701 | /* NOTREACHED */ |
702 | } |
703 | |
704 | subtype -= VSTRIMRIGHT; |
705 | #ifdef DEBUG |
706 | if (subtype < 0 || subtype > 3) |
707 | abort(); |
708 | #endif |
709 | |
710 | rmesc = startp; |
711 | rmescend = stackblock() + strloc; |
712 | if (quotes) { |
713 | rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); |
714 | if (rmesc != startp) { |
715 | rmescend = expdest; |
716 | startp = stackblock() + startloc; |
717 | } |
718 | } |
719 | rmescend--; |
720 | str = stackblock() + strloc; |
721 | preglob(str, varflags & VSQUOTE, 0); |
722 | |
723 | /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ |
724 | zero = subtype >> 1; |
725 | /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ |
726 | scan = (subtype & 1) ^ zero ? scanleft : scanright; |
727 | |
728 | loc = scan(startp, rmesc, rmescend, str, quotes, zero); |
729 | if (loc) { |
730 | if (zero) { |
731 | memmove(startp, loc, str - loc); |
732 | loc = startp + (str - loc) - 1; |
733 | } |
734 | *loc = '\0'; |
735 | amount = loc - expdest; |
736 | STADJUST(amount, expdest); |
737 | } |
738 | return loc; |
739 | } |
740 | |
741 | |
742 | /* |
743 | * Expand a variable, and return a pointer to the next character in the |
744 | * input string. |
745 | */ |
746 | STATIC char * |
747 | evalvar(char *p, int flag) |
748 | { |
749 | int subtype; |
750 | int varflags; |
751 | char *var; |
752 | int patloc; |
753 | int c; |
754 | int startloc; |
755 | ssize_t varlen; |
756 | int easy; |
757 | int quotes; |
758 | int quoted; |
759 | |
760 | quotes = flag & QUOTES_ESC; |
761 | varflags = *p++; |
762 | subtype = varflags & VSTYPE; |
763 | quoted = varflags & VSQUOTE; |
764 | var = p; |
765 | easy = (!quoted || (*var == '@' && shellparam.nparam)); |
766 | startloc = expdest - (char *)stackblock(); |
767 | p = strchr(p, '=') + 1; |
768 | |
769 | again: |
770 | varlen = varvalue(var, varflags, flag); |
771 | if (varflags & VSNUL) |
772 | varlen--; |
773 | |
774 | if (subtype == VSPLUS) { |
775 | varlen = -1 - varlen; |
776 | goto vsplus; |
777 | } |
778 | |
779 | if (subtype == VSMINUS) { |
780 | vsplus: |
781 | if (varlen < 0) { |
782 | argstr( |
783 | p, flag | EXP_TILDE | |
784 | (quoted ? EXP_QWORD : EXP_WORD) |
785 | ); |
786 | goto end; |
787 | } |
788 | if (easy) |
789 | goto record; |
790 | goto end; |
791 | } |
792 | |
793 | if (subtype == VSASSIGN || subtype == VSQUESTION) { |
794 | if (varlen < 0) { |
795 | if (subevalvar(p, var, 0, subtype, startloc, |
796 | varflags, 0)) { |
797 | varflags &= ~VSNUL; |
798 | /* |
799 | * Remove any recorded regions beyond |
800 | * start of variable |
801 | */ |
802 | removerecordregions(startloc); |
803 | goto again; |
804 | } |
805 | goto end; |
806 | } |
807 | if (easy) |
808 | goto record; |
809 | goto end; |
810 | } |
811 | |
812 | if (varlen < 0 && uflag) |
813 | varunset(p, var, 0, 0); |
814 | |
815 | if (subtype == VSLENGTH) { |
816 | cvtnum(varlen > 0 ? varlen : 0); |
817 | goto record; |
818 | } |
819 | |
820 | if (subtype == VSNORMAL) { |
821 | if (!easy) |
822 | goto end; |
823 | record: |
824 | recordregion(startloc, expdest - (char *)stackblock(), quoted); |
825 | goto end; |
826 | } |
827 | |
828 | #ifdef DEBUG |
829 | switch (subtype) { |
830 | case VSTRIMLEFT: |
831 | case VSTRIMLEFTMAX: |
832 | case VSTRIMRIGHT: |
833 | case VSTRIMRIGHTMAX: |
834 | break; |
835 | default: |
836 | abort(); |
837 | } |
838 | #endif |
839 | |
840 | if (varlen >= 0) { |
841 | /* |
842 | * Terminate the string and start recording the pattern |
843 | * right after it |
844 | */ |
845 | STPUTC('\0', expdest); |
846 | patloc = expdest - (char *)stackblock(); |
847 | if (subevalvar(p, NULL, patloc, subtype, |
848 | startloc, varflags, quotes) == 0) { |
849 | int amount = expdest - ( |
850 | (char *)stackblock() + patloc - 1 |
851 | ); |
852 | STADJUST(-amount, expdest); |
853 | } |
854 | /* Remove any recorded regions beyond start of variable */ |
855 | removerecordregions(startloc); |
856 | goto record; |
857 | } |
858 | |
859 | end: |
860 | if (subtype != VSNORMAL) { /* skip to end of alternative */ |
861 | int nesting = 1; |
862 | for (;;) { |
863 | if ((c = (signed char)*p++) == CTLESC) |
864 | p++; |
865 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { |
866 | if (varlen >= 0) |
867 | argbackq = argbackq->next; |
868 | } else if (c == CTLVAR) { |
869 | if ((*p++ & VSTYPE) != VSNORMAL) |
870 | nesting++; |
871 | } else if (c == CTLENDVAR) { |
872 | if (--nesting == 0) |
873 | break; |
874 | } |
875 | } |
876 | } |
877 | return p; |
878 | } |
879 | |
880 | |
881 | /* |
882 | * Put a string on the stack. |
883 | */ |
884 | |
885 | STATIC void |
886 | memtodest(const char *p, size_t len, const char *syntax, int quotes) { |
887 | char *q; |
888 | |
889 | if (unlikely(!len)) |
890 | return; |
891 | |
892 | q = makestrspace(len * 2, expdest); |
893 | |
894 | do { |
895 | int c = (signed char)*p++; |
896 | if (c) { |
897 | if ((quotes & QUOTES_ESC) && |
898 | (syntax[c] == CCTL || syntax[c] == CBACK)) |
899 | USTPUTC(CTLESC, q); |
900 | } else if (!(quotes & QUOTES_KEEPNUL)) |
901 | continue; |
902 | USTPUTC(c, q); |
903 | } while (--len); |
904 | |
905 | expdest = q; |
906 | } |
907 | |
908 | |
909 | STATIC size_t |
910 | strtodest(p, syntax, quotes) |
911 | const char *p; |
912 | const char *syntax; |
913 | int quotes; |
914 | { |
915 | size_t len = strlen(p); |
916 | memtodest(p, len, syntax, quotes); |
917 | return len; |
918 | } |
919 | |
920 | |
921 | |
922 | /* |
923 | * Add the value of a specialized variable to the stack string. |
924 | */ |
925 | |
926 | STATIC ssize_t |
927 | varvalue(char *name, int varflags, int flags) |
928 | { |
929 | int num; |
930 | char *p; |
931 | int i; |
932 | int sep; |
933 | char sepc; |
934 | char **ap; |
935 | char const *syntax; |
936 | int quoted = varflags & VSQUOTE; |
937 | int subtype = varflags & VSTYPE; |
938 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
939 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
940 | ssize_t len = 0; |
941 | |
942 | sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; |
943 | syntax = quoted ? DQSYNTAX : BASESYNTAX; |
944 | |
945 | switch (*name) { |
946 | case '$': |
947 | num = rootpid; |
948 | goto numvar; |
949 | case '?': |
950 | num = exitstatus; |
951 | goto numvar; |
952 | case '#': |
953 | num = shellparam.nparam; |
954 | goto numvar; |
955 | case '!': |
956 | num = backgndpid; |
957 | if (num == 0) |
958 | return -1; |
959 | numvar: |
960 | len = cvtnum(num); |
961 | break; |
962 | case '-': |
963 | p = makestrspace(NOPTS, expdest); |
964 | for (i = NOPTS - 1; i >= 0; i--) { |
965 | if (optlist[i]) { |
966 | USTPUTC(optletters[i], p); |
967 | len++; |
968 | } |
969 | } |
970 | expdest = p; |
971 | break; |
972 | case '@': |
973 | if (sep) |
974 | goto param; |
975 | /* fall through */ |
976 | case '*': |
977 | sep = ifsset() ? ifsval()[0] : ' '; |
978 | param: |
979 | if (!(ap = shellparam.p)) |
980 | return -1; |
981 | sepc = sep; |
982 | while ((p = *ap++)) { |
983 | len += strtodest(p, syntax, quotes); |
984 | |
985 | if (*ap && sep) { |
986 | len++; |
987 | memtodest(&sepc, 1, syntax, quotes); |
988 | } |
989 | } |
990 | break; |
991 | case '0': |
992 | case '1': |
993 | case '2': |
994 | case '3': |
995 | case '4': |
996 | case '5': |
997 | case '6': |
998 | case '7': |
999 | case '8': |
1000 | case '9': |
1001 | num = atoi(name); |
1002 | if (num < 0 || num > shellparam.nparam) |
1003 | return -1; |
1004 | p = num ? shellparam.p[num - 1] : arg0; |
1005 | goto value; |
1006 | default: |
1007 | p = lookupvar(name); |
1008 | value: |
1009 | if (!p) |
1010 | return -1; |
1011 | |
1012 | len = strtodest(p, syntax, quotes); |
1013 | break; |
1014 | } |
1015 | |
1016 | if (discard) |
1017 | STADJUST(-len, expdest); |
1018 | return len; |
1019 | } |
1020 | |
1021 | |
1022 | |
1023 | /* |
1024 | * Record the fact that we have to scan this region of the |
1025 | * string for IFS characters. |
1026 | */ |
1027 | |
1028 | STATIC void |
1029 | recordregion(int start, int end, int nulonly) |
1030 | { |
1031 | struct ifsregion *ifsp; |
1032 | |
1033 | if (ifslastp == NULL) { |
1034 | ifsp = &ifsfirst; |
1035 | } else { |
1036 | INTOFF; |
1037 | ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); |
1038 | ifsp->next = NULL; |
1039 | ifslastp->next = ifsp; |
1040 | INTON; |
1041 | } |
1042 | ifslastp = ifsp; |
1043 | ifslastp->begoff = start; |
1044 | ifslastp->endoff = end; |
1045 | ifslastp->nulonly = nulonly; |
1046 | } |
1047 | |
1048 | |
1049 | |
1050 | /* |
1051 | * Break the argument string into pieces based upon IFS and add the |
1052 | * strings to the argument list. The regions of the string to be |
1053 | * searched for IFS characters have been stored by recordregion. |
1054 | */ |
1055 | STATIC void |
1056 | ifsbreakup(char *string, struct arglist *arglist) |
1057 | { |
1058 | struct ifsregion *ifsp; |
1059 | struct strlist *sp; |
1060 | char *start; |
1061 | char *p; |
1062 | char *q; |
1063 | const char *ifs, *realifs; |
1064 | int ifsspc; |
1065 | int nulonly; |
1066 | |
1067 | |
1068 | start = string; |
1069 | if (ifslastp != NULL) { |
1070 | ifsspc = 0; |
1071 | nulonly = 0; |
1072 | realifs = ifsset() ? ifsval() : defifs; |
1073 | ifsp = &ifsfirst; |
1074 | do { |
1075 | p = string + ifsp->begoff; |
1076 | nulonly = ifsp->nulonly; |
1077 | ifs = nulonly ? nullstr : realifs; |
1078 | ifsspc = 0; |
1079 | while (p < string + ifsp->endoff) { |
1080 | q = p; |
1081 | if (*p == (char)CTLESC) |
1082 | p++; |
1083 | if (strchr(ifs, *p)) { |
1084 | if (!nulonly) |
1085 | ifsspc = (strchr(defifs, *p) != NULL); |
1086 | /* Ignore IFS whitespace at start */ |
1087 | if (q == start && ifsspc) { |
1088 | p++; |
1089 | start = p; |
1090 | continue; |
1091 | } |
1092 | *q = '\0'; |
1093 | sp = (struct strlist *)stalloc(sizeof *sp); |
1094 | sp->text = start; |
1095 | *arglist->lastp = sp; |
1096 | arglist->lastp = &sp->next; |
1097 | p++; |
1098 | if (!nulonly) { |
1099 | for (;;) { |
1100 | if (p >= string + ifsp->endoff) { |
1101 | break; |
1102 | } |
1103 | q = p; |
1104 | if (*p == (char)CTLESC) |
1105 | p++; |
1106 | if (strchr(ifs, *p) == NULL ) { |
1107 | p = q; |
1108 | break; |
1109 | } else if (strchr(defifs, *p) == NULL) { |
1110 | if (ifsspc) { |
1111 | p++; |
1112 | ifsspc = 0; |
1113 | } else { |
1114 | p = q; |
1115 | break; |
1116 | } |
1117 | } else |
1118 | p++; |
1119 | } |
1120 | } |
1121 | start = p; |
1122 | } else |
1123 | p++; |
1124 | } |
1125 | } while ((ifsp = ifsp->next) != NULL); |
1126 | if (nulonly) |
1127 | goto add; |
1128 | } |
1129 | |
1130 | if (!*start) |
1131 | return; |
1132 | |
1133 | add: |
1134 | sp = (struct strlist *)stalloc(sizeof *sp); |
1135 | sp->text = start; |
1136 | *arglist->lastp = sp; |
1137 | arglist->lastp = &sp->next; |
1138 | } |
1139 | |
1140 | STATIC void |
1141 | ifsfree(void) |
1142 | { |
1143 | struct ifsregion *p; |
1144 | |
1145 | INTOFF; |
1146 | p = ifsfirst.next; |
1147 | do { |
1148 | struct ifsregion *ifsp; |
1149 | ifsp = p->next; |
1150 | ckfree(p); |
1151 | p = ifsp; |
1152 | } while (p); |
1153 | ifslastp = NULL; |
1154 | ifsfirst.next = NULL; |
1155 | INTON; |
1156 | } |
1157 | |
1158 | |
1159 | |
1160 | /* |
1161 | * Expand shell metacharacters. At this point, the only control characters |
1162 | * should be escapes. The results are stored in the list exparg. |
1163 | */ |
1164 | |
1165 | #if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) |
1166 | STATIC void |
1167 | expandmeta(str, flag) |
1168 | struct strlist *str; |
1169 | int flag; |
1170 | { |
1171 | /* TODO - EXP_REDIR */ |
1172 | |
1173 | while (str) { |
1174 | const char *p; |
1175 | glob_t pglob; |
1176 | int i; |
1177 | |
1178 | if (fflag) |
1179 | goto nometa; |
1180 | INTOFF; |
1181 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
1182 | i = glob(p, GLOB_NOMAGIC, 0, &pglob); |
1183 | if (p != str->text) |
1184 | ckfree(p); |
1185 | switch (i) { |
1186 | case 0: |
1187 | if (!(pglob.gl_flags & GLOB_MAGCHAR)) |
1188 | goto nometa2; |
1189 | addglob(&pglob); |
1190 | globfree(&pglob); |
1191 | INTON; |
1192 | break; |
1193 | case GLOB_NOMATCH: |
1194 | nometa2: |
1195 | globfree(&pglob); |
1196 | INTON; |
1197 | nometa: |
1198 | *exparg.lastp = str; |
1199 | rmescapes(str->text); |
1200 | exparg.lastp = &str->next; |
1201 | break; |
1202 | default: /* GLOB_NOSPACE */ |
1203 | sh_error("Out of space"); |
1204 | } |
1205 | str = str->next; |
1206 | } |
1207 | } |
1208 | |
1209 | |
1210 | /* |
1211 | * Add the result of glob(3) to the list. |
1212 | */ |
1213 | |
1214 | STATIC void |
1215 | addglob(pglob) |
1216 | const glob_t *pglob; |
1217 | { |
1218 | char **p = pglob->gl_pathv; |
1219 | |
1220 | do { |
1221 | addfname(*p); |
1222 | } while (*++p); |
1223 | } |
1224 | |
1225 | |
1226 | #else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ |
1227 | STATIC char *expdir; |
1228 | |
1229 | |
1230 | STATIC void |
1231 | expandmeta(struct strlist *str, int flag) |
1232 | { |
1233 | static const char metachars[] = { |
1234 | '*', '?', '[', 0 |
1235 | }; |
1236 | /* TODO - EXP_REDIR */ |
1237 | |
1238 | while (str) { |
1239 | struct strlist **savelastp; |
1240 | struct strlist *sp; |
1241 | char *p; |
1242 | |
1243 | if (fflag) |
1244 | goto nometa; |
1245 | if (!strpbrk(str->text, metachars)) |
1246 | goto nometa; |
1247 | savelastp = exparg.lastp; |
1248 | |
1249 | INTOFF; |
1250 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
1251 | { |
1252 | int i = strlen(str->text); |
1253 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ |
1254 | } |
1255 | |
1256 | expmeta(expdir, p); |
1257 | ckfree(expdir); |
1258 | if (p != str->text) |
1259 | ckfree(p); |
1260 | INTON; |
1261 | if (exparg.lastp == savelastp) { |
1262 | /* |
1263 | * no matches |
1264 | */ |
1265 | nometa: |
1266 | *exparg.lastp = str; |
1267 | rmescapes(str->text); |
1268 | exparg.lastp = &str->next; |
1269 | } else { |
1270 | *exparg.lastp = NULL; |
1271 | *savelastp = sp = expsort(*savelastp); |
1272 | while (sp->next != NULL) |
1273 | sp = sp->next; |
1274 | exparg.lastp = &sp->next; |
1275 | } |
1276 | str = str->next; |
1277 | } |
1278 | } |
1279 | |
1280 | |
1281 | /* |
1282 | * Do metacharacter (i.e. *, ?, [...]) expansion. |
1283 | */ |
1284 | |
1285 | STATIC void |
1286 | expmeta(char *enddir, char *name) |
1287 | { |
1288 | char *p; |
1289 | const char *cp; |
1290 | char *start; |
1291 | char *endname; |
1292 | int metaflag; |
1293 | struct stat64 statb; |
1294 | DIR *dirp; |
1295 | struct dirent *dp; |
1296 | int atend; |
1297 | int matchdot; |
1298 | |
1299 | metaflag = 0; |
1300 | start = name; |
1301 | for (p = name; *p; p++) { |
1302 | if (*p == '*' || *p == '?') |
1303 | metaflag = 1; |
1304 | else if (*p == '[') { |
1305 | char *q = p + 1; |
1306 | if (*q == '!') |
1307 | q++; |
1308 | for (;;) { |
1309 | if (*q == '\\') |
1310 | q++; |
1311 | if (*q == '/' || *q == '\0') |
1312 | break; |
1313 | if (*++q == ']') { |
1314 | metaflag = 1; |
1315 | break; |
1316 | } |
1317 | } |
1318 | } else if (*p == '\\') |
1319 | p++; |
1320 | else if (*p == '/') { |
1321 | if (metaflag) |
1322 | goto out; |
1323 | start = p + 1; |
1324 | } |
1325 | } |
1326 | out: |
1327 | if (metaflag == 0) { /* we've reached the end of the file name */ |
1328 | if (enddir != expdir) |
1329 | metaflag++; |
1330 | p = name; |
1331 | do { |
1332 | if (*p == '\\') |
1333 | p++; |
1334 | *enddir++ = *p; |
1335 | } while (*p++); |
1336 | if (metaflag == 0 || lstat64(expdir, &statb) >= 0) |
1337 | addfname(expdir); |
1338 | return; |
1339 | } |
1340 | endname = p; |
1341 | if (name < start) { |
1342 | p = name; |
1343 | do { |
1344 | if (*p == '\\') |
1345 | p++; |
1346 | *enddir++ = *p++; |
1347 | } while (p < start); |
1348 | } |
1349 | if (enddir == expdir) { |
1350 | cp = "."; |
1351 | } else if (enddir == expdir + 1 && *expdir == '/') { |
1352 | cp = "/"; |
1353 | } else { |
1354 | cp = expdir; |
1355 | enddir[-1] = '\0'; |
1356 | } |
1357 | if ((dirp = opendir(cp)) == NULL) |
1358 | return; |
1359 | if (enddir != expdir) |
1360 | enddir[-1] = '/'; |
1361 | if (*endname == 0) { |
1362 | atend = 1; |
1363 | } else { |
1364 | atend = 0; |
1365 | *endname++ = '\0'; |
1366 | } |
1367 | matchdot = 0; |
1368 | p = start; |
1369 | if (*p == '\\') |
1370 | p++; |
1371 | if (*p == '.') |
1372 | matchdot++; |
1373 | while (! int_pending() && (dp = readdir(dirp)) != NULL) { |
1374 | if (dp->d_name[0] == '.' && ! matchdot) |
1375 | continue; |
1376 | if (pmatch(start, dp->d_name)) { |
1377 | if (atend) { |
1378 | scopy(dp->d_name, enddir); |
1379 | addfname(expdir); |
1380 | } else { |
1381 | for (p = enddir, cp = dp->d_name; |
1382 | (*p++ = *cp++) != '\0';) |
1383 | continue; |
1384 | p[-1] = '/'; |
1385 | expmeta(p, endname); |
1386 | } |
1387 | } |
1388 | } |
1389 | closedir(dirp); |
1390 | if (! atend) |
1391 | endname[-1] = '/'; |
1392 | } |
1393 | #endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ |
1394 | |
1395 | |
1396 | /* |
1397 | * Add a file name to the list. |
1398 | */ |
1399 | |
1400 | STATIC void |
1401 | addfname(char *name) |
1402 | { |
1403 | struct strlist *sp; |
1404 | |
1405 | sp = (struct strlist *)stalloc(sizeof *sp); |
1406 | sp->text = sstrdup(name); |
1407 | *exparg.lastp = sp; |
1408 | exparg.lastp = &sp->next; |
1409 | } |
1410 | |
1411 | |
1412 | #if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) |
1413 | /* |
1414 | * Sort the results of file name expansion. It calculates the number of |
1415 | * strings to sort and then calls msort (short for merge sort) to do the |
1416 | * work. |
1417 | */ |
1418 | |
1419 | STATIC struct strlist * |
1420 | expsort(struct strlist *str) |
1421 | { |
1422 | int len; |
1423 | struct strlist *sp; |
1424 | |
1425 | len = 0; |
1426 | for (sp = str ; sp ; sp = sp->next) |
1427 | len++; |
1428 | return msort(str, len); |
1429 | } |
1430 | |
1431 | |
1432 | STATIC struct strlist * |
1433 | msort(struct strlist *list, int len) |
1434 | { |
1435 | struct strlist *p, *q = NULL; |
1436 | struct strlist **lpp; |
1437 | int half; |
1438 | int n; |
1439 | |
1440 | if (len <= 1) |
1441 | return list; |
1442 | half = len >> 1; |
1443 | p = list; |
1444 | for (n = half ; --n >= 0 ; ) { |
1445 | q = p; |
1446 | p = p->next; |
1447 | } |
1448 | q->next = NULL; /* terminate first half of list */ |
1449 | q = msort(list, half); /* sort first half of list */ |
1450 | p = msort(p, len - half); /* sort second half */ |
1451 | lpp = &list; |
1452 | for (;;) { |
1453 | if (strcmp(p->text, q->text) < 0) { |
1454 | *lpp = p; |
1455 | lpp = &p->next; |
1456 | if ((p = *lpp) == NULL) { |
1457 | *lpp = q; |
1458 | break; |
1459 | } |
1460 | } else { |
1461 | *lpp = q; |
1462 | lpp = &q->next; |
1463 | if ((q = *lpp) == NULL) { |
1464 | *lpp = p; |
1465 | break; |
1466 | } |
1467 | } |
1468 | } |
1469 | return list; |
1470 | } |
1471 | #endif |
1472 | |
1473 | |
1474 | /* |
1475 | * Returns true if the pattern matches the string. |
1476 | */ |
1477 | |
1478 | STATIC inline int |
1479 | patmatch(char *pattern, const char *string) |
1480 | { |
1481 | return pmatch(preglob(pattern, 0, 0), string); |
1482 | } |
1483 | |
1484 | |
1485 | #if !defined(__GLIBC__) || defined(FNMATCH_BROKEN) |
1486 | STATIC int ccmatch(const char *p, int chr, const char **r) |
1487 | { |
1488 | static const struct class { |
1489 | char name[10]; |
1490 | int (*fn)(int); |
1491 | } classes[] = { |
1492 | { .name = ":alnum:]", .fn = isalnum }, |
1493 | { .name = ":cntrl:]", .fn = iscntrl }, |
1494 | { .name = ":lower:]", .fn = islower }, |
1495 | { .name = ":space:]", .fn = isspace }, |
1496 | { .name = ":alpha:]", .fn = isalpha }, |
1497 | { .name = ":digit:]", .fn = isdigit }, |
1498 | { .name = ":print:]", .fn = isprint }, |
1499 | { .name = ":upper:]", .fn = isupper }, |
1500 | { .name = ":blank:]", .fn = isblank }, |
1501 | { .name = ":graph:]", .fn = isgraph }, |
1502 | { .name = ":punct:]", .fn = ispunct }, |
1503 | { .name = ":xdigit:]", .fn = isxdigit }, |
1504 | }; |
1505 | const struct class *class, *end; |
1506 | |
1507 | end = classes + sizeof(classes) / sizeof(classes[0]); |
1508 | for (class = classes; class < end; class++) { |
1509 | const char *q; |
1510 | |
1511 | q = prefix(p, class->name); |
1512 | if (!q) |
1513 | continue; |
1514 | *r = q; |
1515 | return class->fn(chr); |
1516 | } |
1517 | |
1518 | *r = 0; |
1519 | return 0; |
1520 | } |
1521 | |
1522 | STATIC int |
1523 | pmatch(const char *pattern, const char *string) |
1524 | { |
1525 | const char *p, *q; |
1526 | char c; |
1527 | |
1528 | p = pattern; |
1529 | q = string; |
1530 | for (;;) { |
1531 | switch (c = *p++) { |
1532 | case '\0': |
1533 | goto breakloop; |
1534 | case '\\': |
1535 | if (*p) { |
1536 | c = *p++; |
1537 | } |
1538 | goto dft; |
1539 | case '?': |
1540 | if (*q++ == '\0') |
1541 | return 0; |
1542 | break; |
1543 | case '*': |
1544 | c = *p; |
1545 | while (c == '*') |
1546 | c = *++p; |
1547 | if (c != '\\' && c != '?' && c != '*' && c != '[') { |
1548 | while (*q != c) { |
1549 | if (*q == '\0') |
1550 | return 0; |
1551 | q++; |
1552 | } |
1553 | } |
1554 | do { |
1555 | if (pmatch(p, q)) |
1556 | return 1; |
1557 | } while (*q++ != '\0'); |
1558 | return 0; |
1559 | case '[': { |
1560 | const char *startp; |
1561 | int invert, found; |
1562 | char chr; |
1563 | |
1564 | startp = p; |
1565 | invert = 0; |
1566 | if (*p == '!') { |
1567 | invert++; |
1568 | p++; |
1569 | } |
1570 | found = 0; |
1571 | chr = *q++; |
1572 | if (chr == '\0') |
1573 | return 0; |
1574 | c = *p++; |
1575 | do { |
1576 | if (!c) { |
1577 | p = startp; |
1578 | c = *p; |
1579 | goto dft; |
1580 | } |
1581 | if (c == '[') { |
1582 | const char *r; |
1583 | |
1584 | found |= !!ccmatch(p, chr, &r); |
1585 | if (r) { |
1586 | p = r; |
1587 | continue; |
1588 | } |
1589 | } else if (c == '\\') |
1590 | c = *p++; |
1591 | if (*p == '-' && p[1] != ']') { |
1592 | p++; |
1593 | if (*p == '\\') |
1594 | p++; |
1595 | if (chr >= c && chr <= *p) |
1596 | found = 1; |
1597 | p++; |
1598 | } else { |
1599 | if (chr == c) |
1600 | found = 1; |
1601 | } |
1602 | } while ((c = *p++) != ']'); |
1603 | if (found == invert) |
1604 | return 0; |
1605 | break; |
1606 | } |
1607 | dft: default: |
1608 | if (*q++ != c) |
1609 | return 0; |
1610 | break; |
1611 | } |
1612 | } |
1613 | breakloop: |
1614 | if (*q != '\0') |
1615 | return 0; |
1616 | return 1; |
1617 | } |
1618 | #endif |
1619 | |
1620 | |
1621 | |
1622 | /* |
1623 | * Remove any CTLESC characters from a string. |
1624 | */ |
1625 | |
1626 | char * |
1627 | _rmescapes(char *str, int flag) |
1628 | { |
1629 | char *p, *q, *r; |
1630 | static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; |
1631 | unsigned inquotes; |
1632 | int notescaped; |
1633 | int globbing; |
1634 | |
1635 | p = strpbrk(str, qchars); |
1636 | if (!p) { |
1637 | return str; |
1638 | } |
1639 | q = p; |
1640 | r = str; |
1641 | if (flag & RMESCAPE_ALLOC) { |
1642 | size_t len = p - str; |
1643 | size_t fulllen = len + strlen(p) + 1; |
1644 | |
1645 | if (flag & RMESCAPE_GROW) { |
1646 | r = makestrspace(fulllen, expdest); |
1647 | } else if (flag & RMESCAPE_HEAP) { |
1648 | r = ckmalloc(fulllen); |
1649 | } else { |
1650 | r = stalloc(fulllen); |
1651 | } |
1652 | q = r; |
1653 | if (len > 0) { |
1654 | q = mempcpy(q, str, len); |
1655 | } |
1656 | } |
1657 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; |
1658 | globbing = flag & RMESCAPE_GLOB; |
1659 | notescaped = globbing; |
1660 | while (*p) { |
1661 | if (*p == (char)CTLQUOTEMARK) { |
1662 | inquotes = ~inquotes; |
1663 | p++; |
1664 | notescaped = globbing; |
1665 | continue; |
1666 | } |
1667 | if (*p == '\\') { |
1668 | /* naked back slash */ |
1669 | notescaped = 0; |
1670 | goto copy; |
1671 | } |
1672 | if (*p == (char)CTLESC) { |
1673 | p++; |
1674 | if (notescaped && inquotes && *p != '/') { |
1675 | *q++ = '\\'; |
1676 | } |
1677 | } |
1678 | notescaped = globbing; |
1679 | copy: |
1680 | *q++ = *p++; |
1681 | } |
1682 | *q = '\0'; |
1683 | if (flag & RMESCAPE_GROW) { |
1684 | expdest = r; |
1685 | STADJUST(q - r + 1, expdest); |
1686 | } |
1687 | return r; |
1688 | } |
1689 | |
1690 | |
1691 | |
1692 | /* |
1693 | * See if a pattern matches in a case statement. |
1694 | */ |
1695 | |
1696 | int |
1697 | casematch(union node *pattern, char *val) |
1698 | { |
1699 | struct stackmark smark; |
1700 | int result; |
1701 | |
1702 | setstackmark(&smark); |
1703 | argbackq = pattern->narg.backquote; |
1704 | STARTSTACKSTR(expdest); |
1705 | ifslastp = NULL; |
1706 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); |
1707 | STACKSTRNUL(expdest); |
1708 | result = patmatch(stackblock(), val); |
1709 | popstackmark(&smark); |
1710 | return result; |
1711 | } |
1712 | |
1713 | /* |
1714 | * Our own itoa(). |
1715 | */ |
1716 | |
1717 | STATIC int |
1718 | cvtnum(long num) |
1719 | { |
1720 | int len; |
1721 | |
1722 | expdest = makestrspace(32, expdest); |
1723 | len = fmtstr(expdest, 32, "%ld", num); |
1724 | STADJUST(len, expdest); |
1725 | return len; |
1726 | } |
1727 | |
1728 | STATIC void |
1729 | varunset(const char *end, const char *var, const char *umsg, int varflags) |
1730 | { |
1731 | const char *msg; |
1732 | const char *tail; |
1733 | |
1734 | tail = nullstr; |
1735 | msg = "parameter not set"; |
1736 | if (umsg) { |
1737 | if (*end == (char)CTLENDVAR) { |
1738 | if (varflags & VSNUL) |
1739 | tail = " or null"; |
1740 | } else |
1741 | msg = umsg; |
1742 | } |
1743 | sh_error("%.*s: %s%s", end - var - 1, var, msg, tail); |
1744 | } |