Subversion Repositories HelenOS

Rev

Rev 2714 | Details | Compare with Previous | Last modification | View Log | RSS feed

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