| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: eval.c,v 1.54 2017/08/27 00:29:04 nayden Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * Expansion - quoting, separation, substitution, globbing  | 
    ||
5  | 
    */  | 
    ||
6  | 
    |||
7  | 
    #include <sys/stat.h>  | 
    ||
8  | 
    |||
9  | 
    #include <ctype.h>  | 
    ||
10  | 
    #include <dirent.h>  | 
    ||
11  | 
    #include <fcntl.h>  | 
    ||
12  | 
    #include <pwd.h>  | 
    ||
13  | 
    #include <stdio.h>  | 
    ||
14  | 
    #include <string.h>  | 
    ||
15  | 
    #include <unistd.h>  | 
    ||
16  | 
    |||
17  | 
    #include "sh.h"  | 
    ||
18  | 
    |||
19  | 
    /*  | 
    ||
20  | 
    * string expansion  | 
    ||
21  | 
    *  | 
    ||
22  | 
     * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. | 
    ||
23  | 
     * second pass: alternation ({,}), filename expansion (*?[]). | 
    ||
24  | 
    */  | 
    ||
25  | 
    |||
26  | 
    /* expansion generator state */  | 
    ||
27  | 
    typedef struct Expand { | 
    ||
28  | 
    /* int type; */ /* see expand() */  | 
    ||
29  | 
    const char *str; /* string */  | 
    ||
30  | 
    	union { | 
    ||
31  | 
    const char **strv;/* string[] */  | 
    ||
32  | 
    struct shf *shf;/* file */  | 
    ||
33  | 
    } u; /* source */  | 
    ||
34  | 
    	struct tbl *var;	/* variable in ${var..} */ | 
    ||
35  | 
    short split; /* split "$@" / call waitlast $() */  | 
    ||
36  | 
    } Expand;  | 
    ||
37  | 
    |||
38  | 
    #define XBASE 0 /* scanning original */  | 
    ||
39  | 
    #define	XSUB		1	/* expanding ${} string */ | 
    ||
40  | 
    #define XARGSEP 2 /* ifs0 between "$*" */  | 
    ||
41  | 
    #define XARG 3 /* expanding $*, $@ */  | 
    ||
42  | 
    #define XCOM 4 /* expanding $() */  | 
    ||
43  | 
    #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */  | 
    ||
44  | 
    #define XSUBMID		6	/* middle of expanding ${} */ | 
    ||
45  | 
    |||
46  | 
    /* States used for field splitting */  | 
    ||
47  | 
    #define IFS_WORD 0 /* word has chars (or quotes) */  | 
    ||
48  | 
    #define IFS_WS 1 /* have seen IFS white-space */  | 
    ||
49  | 
    #define IFS_NWS 2 /* have seen IFS non-white-space */  | 
    ||
50  | 
    |||
51  | 
    static int varsub(Expand *, char *, char *, int *, int *);  | 
    ||
52  | 
    static int comsub(Expand *, char *);  | 
    ||
53  | 
    static char *trimsub(char *, char *, int);  | 
    ||
54  | 
    static void glob(char *, XPtrV *, int);  | 
    ||
55  | 
    static void globit(XString *, char **, char *, XPtrV *, int);  | 
    ||
56  | 
    static char *maybe_expand_tilde(char *, XString *, char **, int);  | 
    ||
57  | 
    static char *tilde(char *);  | 
    ||
58  | 
    static char *homedir(char *);  | 
    ||
59  | 
    #ifdef BRACE_EXPAND  | 
    ||
60  | 
    static void alt_expand(XPtrV *, char *, char *, char *, int);  | 
    ||
61  | 
    #endif  | 
    ||
62  | 
    |||
63  | 
    /* compile and expand word */  | 
    ||
64  | 
    char *  | 
    ||
65  | 
    substitute(const char *cp, int f)  | 
    ||
66  | 
    { | 
    ||
67  | 
    struct source *s, *sold;  | 
    ||
68  | 
    |||
69  | 
    520986  | 
    sold = source;  | 
    |
70  | 
    260493  | 
    s = pushs(SWSTR, ATEMP);  | 
    |
71  | 
    260493  | 
    s->start = s->str = cp;  | 
    |
72  | 
    260493  | 
    source = s;  | 
    |
73  | 
    ✗✓ | 260493  | 
    if (yylex(ONEWORD) != LWORD)  | 
    
74  | 
    internal_errorf(1, "substitute");  | 
    ||
75  | 
    260493  | 
    source = sold;  | 
    |
76  | 
    260493  | 
    afree(s, ATEMP);  | 
    |
77  | 
    260493  | 
    return evalstr(yylval.cp, f);  | 
    |
78  | 
    }  | 
    ||
79  | 
    |||
80  | 
    /*  | 
    ||
81  | 
    * expand arg-list  | 
    ||
82  | 
    */  | 
    ||
83  | 
    char **  | 
    ||
84  | 
    eval(char **ap, int f)  | 
    ||
85  | 
    { | 
    ||
86  | 
    11557742  | 
    XPtrV w;  | 
    |
87  | 
    |||
88  | 
    ✓✓ | 5778871  | 
    if (*ap == NULL)  | 
    
89  | 
    2681125  | 
    return ap;  | 
    |
90  | 
    3097746  | 
    XPinit(w, 32);  | 
    |
91  | 
    ✗✓ | 6195492  | 
    XPput(w, NULL); /* space for shell name */  | 
    
92  | 
    ✓✓ | 29912361  | 
    while (*ap != NULL)  | 
    
93  | 
    11858715  | 
    expand(*ap++, &w, f);  | 
    |
94  | 
    ✓✓ | 6194387  | 
    XPput(w, NULL);  | 
    
95  | 
    3097185  | 
    return (char **) XPclose(w) + 1;  | 
    |
96  | 
    5778310  | 
    }  | 
    |
97  | 
    |||
98  | 
    /*  | 
    ||
99  | 
    * expand string  | 
    ||
100  | 
    */  | 
    ||
101  | 
    char *  | 
    ||
102  | 
    evalstr(char *cp, int f)  | 
    ||
103  | 
    { | 
    ||
104  | 
    6028654  | 
    XPtrV w;  | 
    |
105  | 
    |||
106  | 
    3009039  | 
    XPinit(w, 1);  | 
    |
107  | 
    3009039  | 
    expand(cp, &w, f);  | 
    |
108  | 
    ✓✗ | 9027117  | 
    cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);  | 
    
109  | 
    3009039  | 
    XPfree(w);  | 
    |
110  | 
    3009039  | 
    return cp;  | 
    |
111  | 
    3009039  | 
    }  | 
    |
112  | 
    |||
113  | 
    /*  | 
    ||
114  | 
    * expand string - return only one component  | 
    ||
115  | 
    * used from iosetup to expand redirection files  | 
    ||
116  | 
    */  | 
    ||
117  | 
    char *  | 
    ||
118  | 
    evalonestr(char *cp, int f)  | 
    ||
119  | 
    { | 
    ||
120  | 
    230488  | 
    XPtrV w;  | 
    |
121  | 
    |||
122  | 
    115244  | 
    XPinit(w, 1);  | 
    |
123  | 
    115244  | 
    expand(cp, &w, f);  | 
    |
124  | 
    ✗✓✗ | 115244  | 
    	switch (XPsize(w)) { | 
    
125  | 
    case 0:  | 
    ||
126  | 
    cp = null;  | 
    ||
127  | 
    break;  | 
    ||
128  | 
    case 1:  | 
    ||
129  | 
    115244  | 
    cp = (char*) *XPptrv(w);  | 
    |
130  | 
    115244  | 
    break;  | 
    |
131  | 
    default:  | 
    ||
132  | 
    cp = evalstr(cp, f&~DOGLOB);  | 
    ||
133  | 
    break;  | 
    ||
134  | 
    }  | 
    ||
135  | 
    115244  | 
    XPfree(w);  | 
    |
136  | 
    115244  | 
    return cp;  | 
    |
137  | 
    115244  | 
    }  | 
    |
138  | 
    |||
139  | 
    /* for nested substitution: ${var:=$var2} */ | 
    ||
140  | 
    typedef struct SubType { | 
    ||
141  | 
    short stype; /* [=+-?%#] action after expanded word */  | 
    ||
142  | 
    short base; /* begin position of expanded word */  | 
    ||
143  | 
    short f; /* saved value of f (DOPAT, etc) */  | 
    ||
144  | 
    	struct tbl *var;	/* variable for ${var..} */ | 
    ||
145  | 
    	short	quote;		/* saved value of quote (for ${..[%#]..}) */ | 
    ||
146  | 
    struct SubType *prev; /* old type */  | 
    ||
147  | 
    struct SubType *next; /* poped type (to avoid re-allocating) */  | 
    ||
148  | 
    } SubType;  | 
    ||
149  | 
    |||
150  | 
    void  | 
    ||
151  | 
    expand(char *cp, /* input word */  | 
    ||
152  | 
    XPtrV *wp, /* output words */  | 
    ||
153  | 
    int f) /* DO* flags */  | 
    ||
154  | 
    { | 
    ||
155  | 
    int c = 0;  | 
    ||
156  | 
    int type; /* expansion type */  | 
    ||
157  | 
    int quote = 0; /* quoted */  | 
    ||
158  | 
    29987154  | 
    XString ds; /* destination string */  | 
    |
159  | 
    char *dp, *sp; /* dest., source */  | 
    ||
160  | 
    int fdo, word; /* second pass flags; have word */  | 
    ||
161  | 
    int doblank; /* field splitting of parameter/command subst */  | 
    ||
162  | 
    14993577  | 
    	Expand x = { | 
    |
163  | 
    /* expansion variables */  | 
    ||
164  | 
    		NULL, { NULL }, NULL, 0 | 
    ||
165  | 
    };  | 
    ||
166  | 
    14993577  | 
    SubType st_head, *st;  | 
    |
167  | 
    int newlines = 0; /* For trailing newlines in COMSUB */  | 
    ||
168  | 
    int saw_eq, tilde_ok;  | 
    ||
169  | 
    int make_magic;  | 
    ||
170  | 
    size_t len;  | 
    ||
171  | 
    |||
172  | 
    ✗✓ | 14993577  | 
    if (cp == NULL)  | 
    
173  | 
    internal_errorf(1, "expand(NULL)");  | 
    ||
174  | 
    /* for alias, readonly, set, typeset commands */  | 
    ||
175  | 
    ✓✓✓✓ | 
    15761986  | 
    	if ((f & DOVACHECK) && is_wdvarassign(cp)) { | 
    
176  | 
    377391  | 
    f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);  | 
    |
177  | 
    377391  | 
    f |= DOASNTILDE;  | 
    |
178  | 
    377391  | 
    }  | 
    |
179  | 
    ✓✓ | 14993577  | 
    if (Flag(FNOGLOB))  | 
    
180  | 
    8734  | 
    f &= ~DOGLOB;  | 
    |
181  | 
    ✗✓ | 14993577  | 
    if (Flag(FMARKDIRS))  | 
    
182  | 
    f |= DOMARKDIRS;  | 
    ||
183  | 
    #ifdef BRACE_EXPAND  | 
    ||
184  | 
    ✓✓✓✓ | 
    29986875  | 
    if (Flag(FBRACEEXPAND) && (f & DOGLOB))  | 
    
185  | 
    11472740  | 
    f |= DOBRACE_;  | 
    |
186  | 
    #endif /* BRACE_EXPAND */  | 
    ||
187  | 
    |||
188  | 
    14993577  | 
    Xinit(ds, dp, 128, ATEMP); /* init dest. string */  | 
    |
189  | 
    type = XBASE;  | 
    ||
190  | 
    sp = cp;  | 
    ||
191  | 
    fdo = 0;  | 
    ||
192  | 
    saw_eq = 0;  | 
    ||
193  | 
    14993577  | 
    tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */  | 
    |
194  | 
    doblank = 0;  | 
    ||
195  | 
    make_magic = 0;  | 
    ||
196  | 
    14993577  | 
    word = (f&DOBLANK) ? IFS_WS : IFS_WORD;  | 
    |
197  | 
    14993577  | 
    st_head.next = NULL;  | 
    |
198  | 
    st = &st_head;  | 
    ||
199  | 
    |||
200  | 
    221406844  | 
    	while (1) { | 
    |
201  | 
    ✓✓ | 243331821  | 
    Xcheck(ds, dp);  | 
    
202  | 
    |||
203  | 
    ✓✓✗✓ ✓✓✓✓  | 
    390194941  | 
    		switch (type) { | 
    
204  | 
    case XBASE: /* original prefixed string */  | 
    ||
205  | 
    164542664  | 
    c = *sp++;  | 
    |
206  | 
    ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓  | 
    164542664  | 
    			switch (c) { | 
    
207  | 
    case EOS:  | 
    ||
208  | 
    c = 0;  | 
    ||
209  | 
    14982440  | 
    break;  | 
    |
210  | 
    case CHAR:  | 
    ||
211  | 
    58168971  | 
    c = *sp++;  | 
    |
212  | 
    58168971  | 
    break;  | 
    |
213  | 
    case QCHAR:  | 
    ||
214  | 
    1320448  | 
    quote |= 2; /* temporary quote */  | 
    |
215  | 
    1320448  | 
    c = *sp++;  | 
    |
216  | 
    1320448  | 
    break;  | 
    |
217  | 
    case OQUOTE:  | 
    ||
218  | 
    word = IFS_WORD;  | 
    ||
219  | 
    tilde_ok = 0;  | 
    ||
220  | 
    quote = 1;  | 
    ||
221  | 
    3994399  | 
    continue;  | 
    |
222  | 
    case CQUOTE:  | 
    ||
223  | 
    quote = 0;  | 
    ||
224  | 
    3993909  | 
    continue;  | 
    |
225  | 
    case COMSUB:  | 
    ||
226  | 
    tilde_ok = 0;  | 
    ||
227  | 
    ✗✓ | 1369030  | 
    				if (f & DONTRUNCOMMAND) { | 
    
228  | 
    word = IFS_WORD;  | 
    ||
229  | 
    					*dp++ = '$'; *dp++ = '('; | 
    ||
230  | 
    					while (*sp != '\0') { | 
    ||
231  | 
    Xcheck(ds, dp);  | 
    ||
232  | 
    *dp++ = *sp++;  | 
    ||
233  | 
    }  | 
    ||
234  | 
    *dp++ = ')';  | 
    ||
235  | 
    				} else { | 
    ||
236  | 
    1357987  | 
    type = comsub(&x, sp);  | 
    |
237  | 
    ✓✗✓✓ | 
    2715974  | 
    if (type == XCOM && (f&DOBLANK))  | 
    
238  | 
    5677  | 
    doblank++;  | 
    |
239  | 
    1357987  | 
    sp = strchr(sp, 0) + 1;  | 
    |
240  | 
    newlines = 0;  | 
    ||
241  | 
    }  | 
    ||
242  | 
    1357987  | 
    continue;  | 
    |
243  | 
    case EXPRSUB:  | 
    ||
244  | 
    word = IFS_WORD;  | 
    ||
245  | 
    tilde_ok = 0;  | 
    ||
246  | 
    ✗✓ | 130202  | 
    				if (f & DONTRUNCOMMAND) { | 
    
247  | 
    					*dp++ = '$'; *dp++ = '('; *dp++ = '('; | 
    ||
248  | 
    					while (*sp != '\0') { | 
    ||
249  | 
    Xcheck(ds, dp);  | 
    ||
250  | 
    *dp++ = *sp++;  | 
    ||
251  | 
    }  | 
    ||
252  | 
    *dp++ = ')'; *dp++ = ')';  | 
    ||
253  | 
    				} else { | 
    ||
254  | 
    130202  | 
    struct tbl v;  | 
    |
255  | 
    char *p;  | 
    ||
256  | 
    |||
257  | 
    130202  | 
    v.flag = DEFINED|ISSET|INTEGER;  | 
    |
258  | 
    130202  | 
    v.type = 10; /* not default */  | 
    |
259  | 
    130202  | 
    v.name[0] = '\0';  | 
    |
260  | 
    130202  | 
    v_evaluate(&v, substitute(sp, 0),  | 
    |
261  | 
    KSH_UNWIND_ERROR, true);  | 
    ||
262  | 
    130202  | 
    sp = strchr(sp, 0) + 1;  | 
    |
263  | 
    ✓✓ | 671958  | 
    					for (p = str_val(&v); *p; ) { | 
    
264  | 
    ✗✓ | 205782  | 
    Xcheck(ds, dp);  | 
    
265  | 
    205782  | 
    *dp++ = *p++;  | 
    |
266  | 
    }  | 
    ||
267  | 
    130192  | 
    }  | 
    |
268  | 
    130192  | 
    continue;  | 
    |
269  | 
    			case OSUBST: /* ${{#}var{:}[=+-?#%]word} */ | 
    ||
270  | 
    /* format is:  | 
    ||
271  | 
    			   *   OSUBST [{x] plain-variable-part \0 | 
    ||
272  | 
    * compiled-word-part CSUBST [}x]  | 
    ||
273  | 
    * This is where all syntax checking gets done...  | 
    ||
274  | 
    */  | 
    ||
275  | 
    			    { | 
    ||
276  | 
    5365548  | 
    				char *varname = ++sp; /* skip the { or x (}) */ | 
    |
277  | 
    5365548  | 
    int stype;  | 
    |
278  | 
    5365548  | 
    int slen = 0;  | 
    |
279  | 
    |||
280  | 
    5365548  | 
    sp = strchr(sp, '\0') + 1; /* skip variable */  | 
    |
281  | 
    5365548  | 
    type = varsub(&x, varname, sp, &stype, &slen);  | 
    |
282  | 
    ✓✓ | 5365548  | 
    				if (type < 0) { | 
    
283  | 
    char endc;  | 
    ||
284  | 
    char *str, *end;  | 
    ||
285  | 
    |||
286  | 
    sp = varname - 2; /* restore sp */  | 
    ||
287  | 
    end = (char *) wdscan(sp, CSUBST);  | 
    ||
288  | 
    					/* ({) the } or x is already skipped */ | 
    ||
289  | 
    endc = *end;  | 
    ||
290  | 
    *end = EOS;  | 
    ||
291  | 
    str = snptreef(NULL, 64, "%S", sp);  | 
    ||
292  | 
    *end = endc;  | 
    ||
293  | 
    					errorf("%s: bad substitution", str); | 
    ||
294  | 
    }  | 
    ||
295  | 
    ✓✓ | 5365476  | 
    if (f&DOBLANK)  | 
    
296  | 
    3392643  | 
    doblank++;  | 
    |
297  | 
    tilde_ok = 0;  | 
    ||
298  | 
    ✓✓ | 5365476  | 
    				if (type == XBASE) {	/* expand? */ | 
    
299  | 
    ✓✓ | 651685  | 
    					if (!st->next) { | 
    
300  | 
    SubType *newst;  | 
    ||
301  | 
    |||
302  | 
    442159  | 
    newst = alloc(  | 
    |
303  | 
    442159  | 
    sizeof(SubType), ATEMP);  | 
    |
304  | 
    442159  | 
    newst->next = NULL;  | 
    |
305  | 
    442159  | 
    newst->prev = st;  | 
    |
306  | 
    442159  | 
    st->next = newst;  | 
    |
307  | 
    442159  | 
    }  | 
    |
308  | 
    651685  | 
    st = st->next;  | 
    |
309  | 
    651685  | 
    st->stype = stype;  | 
    |
310  | 
    651685  | 
    st->base = Xsavepos(ds, dp);  | 
    |
311  | 
    651685  | 
    st->f = f;  | 
    |
312  | 
    651685  | 
    st->var = x.var;  | 
    |
313  | 
    651685  | 
    st->quote = quote;  | 
    |
314  | 
    /* skip qualifier(s) */  | 
    ||
315  | 
    ✓✗ | 651685  | 
    if (stype)  | 
    
316  | 
    651685  | 
    sp += slen;  | 
    |
317  | 
    ✗✓✓✓ ✓  | 
    651691  | 
    					switch (stype & 0x7f) { | 
    
318  | 
    case '#':  | 
    ||
319  | 
    case '%':  | 
    ||
320  | 
    /* ! DOBLANK,DOBRACE_,DOTILDE */  | 
    ||
321  | 
    20716  | 
    f = DOPAT | (f&DONTRUNCOMMAND) |  | 
    |
322  | 
    DOTEMP_;  | 
    ||
323  | 
    quote = 0;  | 
    ||
324  | 
    /* Prepend open pattern (so |  | 
    ||
325  | 
    * in a trim will work as  | 
    ||
326  | 
    * expected)  | 
    ||
327  | 
    */  | 
    ||
328  | 
    20716  | 
    *dp++ = MAGIC;  | 
    |
329  | 
    20716  | 
    *dp++ = '@' + 0x80U;  | 
    |
330  | 
    20716  | 
    break;  | 
    |
331  | 
    case '=':  | 
    ||
332  | 
    /* Enabling tilde expansion  | 
    ||
333  | 
    * after :'s here is  | 
    ||
334  | 
    * non-standard ksh, but is  | 
    ||
335  | 
    * consistent with rules for  | 
    ||
336  | 
    * other assignments. Not  | 
    ||
337  | 
    * sure what POSIX thinks of  | 
    ||
338  | 
    * this.  | 
    ||
339  | 
    * Not doing tilde expansion  | 
    ||
340  | 
    * for integer variables is a  | 
    ||
341  | 
    * non-POSIX thing - makes  | 
    ||
342  | 
    * sense though, since ~ is  | 
    ||
343  | 
    * a arithmetic operator.  | 
    ||
344  | 
    */  | 
    ||
345  | 
    ✓✗ | 314305  | 
    if (!(x.var->flag & INTEGER))  | 
    
346  | 
    314305  | 
    f |= DOASNTILDE|DOTILDE;  | 
    |
347  | 
    314305  | 
    f |= DOTEMP_;  | 
    |
348  | 
    /* These will be done after the  | 
    ||
349  | 
    * value has been assigned.  | 
    ||
350  | 
    */  | 
    ||
351  | 
    314305  | 
    f &= ~(DOBLANK|DOGLOB|DOBRACE_);  | 
    |
352  | 
    tilde_ok = 1;  | 
    ||
353  | 
    314305  | 
    break;  | 
    |
354  | 
    case '?':  | 
    ||
355  | 
    6  | 
    f &= ~DOBLANK;  | 
    |
356  | 
    6  | 
    f |= DOTEMP_;  | 
    |
357  | 
    /* FALLTHROUGH */  | 
    ||
358  | 
    default:  | 
    ||
359  | 
    /* Enable tilde expansion */  | 
    ||
360  | 
    tilde_ok = 1;  | 
    ||
361  | 
    316664  | 
    f |= DOTILDE;  | 
    |
362  | 
    316664  | 
    }  | 
    |
363  | 
    } else  | 
    ||
364  | 
    /* skip word */  | 
    ||
365  | 
    4713791  | 
    sp = (char *) wdscan(sp, CSUBST);  | 
    |
366  | 
    continue;  | 
    ||
367  | 
    5365476  | 
    }  | 
    |
368  | 
    case CSUBST: /* only get here if expanding word */  | 
    ||
369  | 
    651661  | 
    				sp++; /* ({) skip the } or x */ | 
    |
370  | 
    				tilde_ok = 0;	/* in case of ${unset:-} */ | 
    ||
371  | 
    651661  | 
    *dp = '\0';  | 
    |
372  | 
    651661  | 
    quote = st->quote;  | 
    |
373  | 
    651661  | 
    f = st->f;  | 
    |
374  | 
    ✓✓ | 651661  | 
    if (f&DOBLANK)  | 
    
375  | 
    764  | 
    doblank--;  | 
    |
376  | 
    ✗✓✓✓ ✓  | 
    651661  | 
    				switch (st->stype&0x7f) { | 
    
377  | 
    case '#':  | 
    ||
378  | 
    case '%':  | 
    ||
379  | 
    /* Append end-pattern */  | 
    ||
380  | 
    20716  | 
    *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';  | 
    |
381  | 
    20716  | 
    dp = Xrestpos(ds, dp, st->base);  | 
    |
382  | 
    /* Must use st->var since calling  | 
    ||
383  | 
    * global would break things  | 
    ||
384  | 
    * like x[i+=1].  | 
    ||
385  | 
    */  | 
    ||
386  | 
    41432  | 
    x.str = trimsub(str_val(st->var),  | 
    |
387  | 
    20716  | 
    dp, st->stype);  | 
    |
388  | 
    ✓✓✓✓ | 
    22030  | 
    if (x.str[0] != '\0' || st->quote)  | 
    
389  | 
    19502  | 
    type = XSUB;  | 
    |
390  | 
    else  | 
    ||
391  | 
    type = XNULLSUB;  | 
    ||
392  | 
    ✓✓ | 20716  | 
    if (f&DOBLANK)  | 
    
393  | 
    588  | 
    doblank++;  | 
    |
394  | 
    20716  | 
    st = st->prev;  | 
    |
395  | 
    20716  | 
    continue;  | 
    |
396  | 
    case '=':  | 
    ||
397  | 
    /* Restore our position and substitute  | 
    ||
398  | 
    * the value of st->var (may not be  | 
    ||
399  | 
    * the assigned value in the presence  | 
    ||
400  | 
    * of integer/right-adj/etc attributes).  | 
    ||
401  | 
    */  | 
    ||
402  | 
    314305  | 
    dp = Xrestpos(ds, dp, st->base);  | 
    |
403  | 
    /* Must use st->var since calling  | 
    ||
404  | 
    * global would cause with things  | 
    ||
405  | 
    * like x[i+=1] to be evaluated twice.  | 
    ||
406  | 
    */  | 
    ||
407  | 
    /* Note: not exported by FEXPORT  | 
    ||
408  | 
    * in at&t ksh.  | 
    ||
409  | 
    */  | 
    ||
410  | 
    /* XXX POSIX says readonly is only  | 
    ||
411  | 
    * fatal for special builtins (setstr  | 
    ||
412  | 
    * does readonly check).  | 
    ||
413  | 
    */  | 
    ||
414  | 
    314305  | 
    len = strlen(dp) + 1;  | 
    |
415  | 
    628610  | 
    setstr(st->var,  | 
    |
416  | 
    314305  | 
    debunk(alloc(len, ATEMP),  | 
    |
417  | 
    dp, len), KSH_UNWIND_ERROR);  | 
    ||
418  | 
    314305  | 
    x.str = str_val(st->var);  | 
    |
419  | 
    type = XSUB;  | 
    ||
420  | 
    ✓✓ | 314305  | 
    if (f&DOBLANK)  | 
    
421  | 
    16  | 
    doblank++;  | 
    |
422  | 
    314305  | 
    st = st->prev;  | 
    |
423  | 
    314305  | 
    continue;  | 
    |
424  | 
    case '?':  | 
    ||
425  | 
    				    { | 
    ||
426  | 
    6  | 
    char *s = Xrestpos(ds, dp, st->base);  | 
    |
427  | 
    |||
428  | 
    6  | 
    					errorf("%s: %s", st->var->name, | 
    |
429  | 
    ✗✓ | 6  | 
    dp == s ?  | 
    
430  | 
    "parameter null or not set" :  | 
    ||
431  | 
    (debunk(s, s, strlen(s) + 1), s));  | 
    ||
432  | 
    }  | 
    ||
433  | 
    }  | 
    ||
434  | 
    316634  | 
    st = st->prev;  | 
    |
435  | 
    type = XBASE;  | 
    ||
436  | 
    316634  | 
    continue;  | 
    |
437  | 
    |||
438  | 
    case OPAT: /* open pattern: *(foo|bar) */  | 
    ||
439  | 
    /* Next char is the type of pattern */  | 
    ||
440  | 
    make_magic = 1;  | 
    ||
441  | 
    17658  | 
    c = *sp++ + 0x80;  | 
    |
442  | 
    17658  | 
    break;  | 
    |
443  | 
    |||
444  | 
    case SPAT: /* pattern separator (|) */  | 
    ||
445  | 
    make_magic = 1;  | 
    ||
446  | 
    c = '|';  | 
    ||
447  | 
    11772  | 
    break;  | 
    |
448  | 
    |||
449  | 
    case CPAT: /* close pattern */  | 
    ||
450  | 
    make_magic = 1;  | 
    ||
451  | 
    c = /*(*/ ')';  | 
    ||
452  | 
    17658  | 
    break;  | 
    |
453  | 
    }  | 
    ||
454  | 
    break;  | 
    ||
455  | 
    |||
456  | 
    case XNULLSUB:  | 
    ||
457  | 
    			/* Special case for "$@" (and "${foo[@]}") - no | 
    ||
458  | 
    * word is generated if $# is 0 (unless there is  | 
    ||
459  | 
    * other stuff inside the quotes).  | 
    ||
460  | 
    */  | 
    ||
461  | 
    type = XBASE;  | 
    ||
462  | 
    ✓✓ | 1324  | 
    			if (f&DOBLANK) { | 
    
463  | 
    134  | 
    doblank--;  | 
    |
464  | 
    /* not really correct: x=; "$x$@" should  | 
    ||
465  | 
    * generate a null argument and  | 
    ||
466  | 
    				 * set A; "${@:+}" shouldn't. | 
    ||
467  | 
    */  | 
    ||
468  | 
    ✓✓ | 134  | 
    if (dp == Xstring(ds, dp))  | 
    
469  | 
    128  | 
    word = IFS_WS;  | 
    |
470  | 
    }  | 
    ||
471  | 
    1324  | 
    continue;  | 
    |
472  | 
    |||
473  | 
    case XSUB:  | 
    ||
474  | 
    case XSUBMID:  | 
    ||
475  | 
    ✓✓ | 88175875  | 
    			if ((c = *x.str++) == 0) { | 
    
476  | 
    type = XBASE;  | 
    ||
477  | 
    ✓✓ | 5045194  | 
    if (f&DOBLANK)  | 
    
478  | 
    3390423  | 
    doblank--;  | 
    |
479  | 
    5045194  | 
    continue;  | 
    |
480  | 
    }  | 
    ||
481  | 
    break;  | 
    ||
482  | 
    |||
483  | 
    case XARGSEP:  | 
    ||
484  | 
    type = XARG;  | 
    ||
485  | 
    10923  | 
    quote = 1;  | 
    |
486  | 
    case XARG:  | 
    ||
487  | 
    ✓✓ | 181352  | 
    			if ((c = *x.str++) == '\0') { | 
    
488  | 
    /* force null words to be created so  | 
    ||
489  | 
    * set -- '' 2 ''; foo "$@" will do  | 
    ||
490  | 
    * the right thing  | 
    ||
491  | 
    */  | 
    ||
492  | 
    ✓✓✓✓ | 
    34626  | 
    if (quote && x.split)  | 
    
493  | 
    12675  | 
    word = IFS_WORD;  | 
    |
494  | 
    ✓✓ | 17433  | 
    				if ((x.str = *x.u.strv++) == NULL) { | 
    
495  | 
    type = XBASE;  | 
    ||
496  | 
    ✓✓ | 2294  | 
    if (f&DOBLANK)  | 
    
497  | 
    1902  | 
    doblank--;  | 
    |
498  | 
    2294  | 
    continue;  | 
    |
499  | 
    }  | 
    ||
500  | 
    15139  | 
    c = ifs0;  | 
    |
501  | 
    ✓✓ | 15139  | 
    				if (c == 0) { | 
    
502  | 
    ✓✓✓✓ | 
    72  | 
    if (quote && !x.split)  | 
    
503  | 
    12  | 
    continue;  | 
    |
504  | 
    c = ' ';  | 
    ||
505  | 
    36  | 
    }  | 
    |
506  | 
    ✓✓✓✓ | 
    30146  | 
    				if (quote && x.split) { | 
    
507  | 
    /* terminate word for "$@" */  | 
    ||
508  | 
    type = XARGSEP;  | 
    ||
509  | 
    quote = 0;  | 
    ||
510  | 
    10923  | 
    }  | 
    |
511  | 
    }  | 
    ||
512  | 
    break;  | 
    ||
513  | 
    |||
514  | 
    case XCOM:  | 
    ||
515  | 
    ✓✓ | 64925005  | 
    if (x.u.shf == NULL) /* $(< ...) failed, fake EOF */  | 
    
516  | 
    12  | 
    c = EOF;  | 
    |
517  | 
    ✓✓ | 64924993  | 
    			else if (newlines) {		/* Spit out saved nl's */ | 
    
518  | 
    c = '\n';  | 
    ||
519  | 
    13845  | 
    --newlines;  | 
    |
520  | 
    13845  | 
    			} else { | 
    |
521  | 
    ✓✓✓✓ | 
    269013304  | 
    while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')  | 
    
522  | 
    ✓✓ | 2342178  | 
    if (c == '\n')  | 
    
523  | 
    2341353  | 
    newlines++; /* Save newlines */  | 
    |
524  | 
    ✓✓ | 64911148  | 
    				if (newlines && c != EOF) { | 
    
525  | 
    1332158  | 
    shf_ungetc(c, x.u.shf);  | 
    |
526  | 
    c = '\n';  | 
    ||
527  | 
    1332158  | 
    --newlines;  | 
    |
528  | 
    1332158  | 
    }  | 
    |
529  | 
    }  | 
    ||
530  | 
    ✓✓ | 64925005  | 
    			if (c == EOF) { | 
    
531  | 
    newlines = 0;  | 
    ||
532  | 
    ✓✓ | 1357987  | 
    if (x.u.shf != NULL)  | 
    
533  | 
    1357975  | 
    shf_close(x.u.shf);  | 
    |
534  | 
    ✓✓ | 1357987  | 
    if (x.split)  | 
    
535  | 
    1357975  | 
    subst_exstat = waitlast();  | 
    |
536  | 
    else  | 
    ||
537  | 
    12  | 
    subst_exstat = (x.u.shf == NULL);  | 
    |
538  | 
    type = XBASE;  | 
    ||
539  | 
    ✓✓ | 1357987  | 
    if (f&DOBLANK)  | 
    
540  | 
    5677  | 
    doblank--;  | 
    |
541  | 
    1357987  | 
    continue;  | 
    |
542  | 
    }  | 
    ||
543  | 
    break;  | 
    ||
544  | 
    }  | 
    ||
545  | 
    |||
546  | 
    /* check for end of word or IFS separation */  | 
    ||
547  | 
    ✓✓✓✓ ✓✓✓✓  | 
    670459405  | 
    if (c == 0 || (!quote && (f & DOBLANK) && doblank &&  | 
    
548  | 
    124205082  | 
    		    !make_magic && ctype(c, C_IFS))) { | 
    |
549  | 
    /* How words are broken up:  | 
    ||
550  | 
    * | value of c  | 
    ||
551  | 
    * word | ws nws 0  | 
    ||
552  | 
    * -----------------------------------  | 
    ||
553  | 
    * IFS_WORD w/WS w/NWS w  | 
    ||
554  | 
    * IFS_WS -/WS w/NWS -  | 
    ||
555  | 
    * IFS_NWS -/NWS w/NWS w  | 
    ||
556  | 
    * (w means generate a word)  | 
    ||
557  | 
    * Note that IFS_NWS/0 generates a word (at&t ksh  | 
    ||
558  | 
    * doesn't do this, but POSIX does).  | 
    ||
559  | 
    */  | 
    ||
560  | 
    ✓✓ | 15120650  | 
    if (word == IFS_WORD ||  | 
    
561  | 
    ✓✓ | 11425  | 
    			    (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) { | 
    
562  | 
    char *p;  | 
    ||
563  | 
    |||
564  | 
    15109333  | 
    *dp++ = '\0';  | 
    |
565  | 
    15109333  | 
    p = Xclose(ds, dp);  | 
    |
566  | 
    #ifdef BRACE_EXPAND  | 
    ||
567  | 
    ✓✓ | 15109333  | 
    if (fdo & DOBRACE_)  | 
    
568  | 
    /* also does globbing */  | 
    ||
569  | 
    1018  | 
    alt_expand(wp, p, p,  | 
    |
570  | 
    1018  | 
    p + Xlength(ds, (dp - 1)),  | 
    |
571  | 
    1018  | 
    fdo | (f & DOMARKDIRS));  | 
    |
572  | 
    else  | 
    ||
573  | 
    #endif /* BRACE_EXPAND */  | 
    ||
574  | 
    ✓✓ | 15108315  | 
    if (fdo & DOGLOB)  | 
    
575  | 
    1339205  | 
    glob(p, wp, f & DOMARKDIRS);  | 
    |
576  | 
    ✓✓✓✓ | 
    27499960  | 
    else if ((f & DOPAT) || !(fdo & DOMAGIC_))  | 
    
577  | 
    ✓✓ | 21035110  | 
    XPput(*wp, p);  | 
    
578  | 
    else  | 
    ||
579  | 
    ✓✓ | 6508047  | 
    XPput(*wp, debunk(p, p, strlen(p) + 1));  | 
    
580  | 
    fdo = 0;  | 
    ||
581  | 
    saw_eq = 0;  | 
    ||
582  | 
    15109333  | 
    tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;  | 
    |
583  | 
    ✓✓ | 15109333  | 
    if (c != 0)  | 
    
584  | 
    133834  | 
    Xinit(ds, dp, 128, ATEMP);  | 
    |
585  | 
    15109333  | 
    }  | 
    |
586  | 
    ✓✓ | 15120650  | 
    if (c == 0)  | 
    
587  | 
    return;  | 
    ||
588  | 
    ✓✓ | 138210  | 
    if (word != IFS_NWS)  | 
    
589  | 
    138030  | 
    word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;  | 
    |
590  | 
    		} else { | 
    ||
591  | 
    ✓✓ | 206275057  | 
    			if (type == XSUB) { | 
    
592  | 
    ✓✓✓✗ | 
    4744444  | 
    if (word == IFS_NWS &&  | 
    
593  | 
    48  | 
    				    Xlength(ds, dp) == 0) { | 
    |
594  | 
    char *p;  | 
    ||
595  | 
    |||
596  | 
    ✗✓ | 48  | 
    					if ((p = strdup("")) == NULL) | 
    
597  | 
    internal_errorf(1, "unable "  | 
    ||
598  | 
    "to allocate memory");  | 
    ||
599  | 
    ✗✓ | 96  | 
    XPput(*wp, p);  | 
    
600  | 
    48  | 
    }  | 
    |
601  | 
    type = XSUBMID;  | 
    ||
602  | 
    4744396  | 
    }  | 
    |
603  | 
    |||
604  | 
    /* age tilde_ok info - ~ code tests second bit */  | 
    ||
605  | 
    206275057  | 
    tilde_ok <<= 1;  | 
    |
606  | 
    /* mark any special second pass chars */  | 
    ||
607  | 
    ✓✓ | 206275057  | 
    if (!quote)  | 
    
608  | 
    ✗✗✗✓ ✗✓✗✗ ✓✓✓✓ ✓  | 
    127428185  | 
    				switch (c) { | 
    
609  | 
    case '[':  | 
    ||
610  | 
    case '!':  | 
    ||
611  | 
    case '-':  | 
    ||
612  | 
    case ']':  | 
    ||
613  | 
    /* For character classes - doesn't hurt  | 
    ||
614  | 
    * to have magic !,-,]'s outside of  | 
    ||
615  | 
    * [...] expressions.  | 
    ||
616  | 
    */  | 
    ||
617  | 
    ✓✓ | 4848599  | 
    					if (f & (DOPAT | DOGLOB)) { | 
    
618  | 
    4682720  | 
    fdo |= DOMAGIC_;  | 
    |
619  | 
    ✓✓ | 4682720  | 
    if (c == '[')  | 
    
620  | 
    1354813  | 
    fdo |= f & DOGLOB;  | 
    |
621  | 
    4682720  | 
    *dp++ = MAGIC;  | 
    |
622  | 
    4682720  | 
    }  | 
    |
623  | 
    break;  | 
    ||
624  | 
    case '*':  | 
    ||
625  | 
    case '?':  | 
    ||
626  | 
    ✓✓ | 273210  | 
    					if (f & (DOPAT | DOGLOB)) { | 
    
627  | 
    41436  | 
    fdo |= DOMAGIC_ | (f & DOGLOB);  | 
    |
628  | 
    41436  | 
    *dp++ = MAGIC;  | 
    |
629  | 
    41436  | 
    }  | 
    |
630  | 
    break;  | 
    ||
631  | 
    #ifdef BRACE_EXPAND  | 
    ||
632  | 
    case OBRACE:  | 
    ||
633  | 
    case ',':  | 
    ||
634  | 
    case CBRACE:  | 
    ||
635  | 
    ✓✓✓✓ ✓✓  | 
    21334  | 
    if ((f & DOBRACE_) && (c == OBRACE ||  | 
    
636  | 
    2577  | 
    					    (fdo & DOBRACE_))) { | 
    |
637  | 
    2886  | 
    fdo |= DOBRACE_|DOMAGIC_;  | 
    |
638  | 
    2886  | 
    *dp++ = MAGIC;  | 
    |
639  | 
    2886  | 
    }  | 
    |
640  | 
    break;  | 
    ||
641  | 
    #endif /* BRACE_EXPAND */  | 
    ||
642  | 
    case '=':  | 
    ||
643  | 
    /* Note first unquoted = for ~ */  | 
    ||
644  | 
    ✓✓ | 3919804  | 
    					if (!(f & DOTEMP_) && !saw_eq) { | 
    
645  | 
    saw_eq = 1;  | 
    ||
646  | 
    tilde_ok = 1;  | 
    ||
647  | 
    3895795  | 
    }  | 
    |
648  | 
    break;  | 
    ||
649  | 
    case ':': /* : */  | 
    ||
650  | 
    /* Note unquoted : for ~ */  | 
    ||
651  | 
    ✓✓✓✓ | 
    107634  | 
    if (!(f & DOTEMP_) && (f & DOASNTILDE))  | 
    
652  | 
    34392  | 
    tilde_ok = 1;  | 
    |
653  | 
    break;  | 
    ||
654  | 
    case '~':  | 
    ||
655  | 
    /* tilde_ok is reset whenever  | 
    ||
656  | 
    					 * any of ' " $( $(( ${ } are seen. | 
    ||
657  | 
    * Note that tilde_ok must be preserved  | 
    ||
658  | 
    					 * through the sequence ${A=a=}~ | 
    ||
659  | 
    */  | 
    ||
660  | 
    ✓✓✓✓ | 
    1755  | 
    if (type == XBASE &&  | 
    
661  | 
    ✓✓ | 457  | 
    (f & (DOTILDE|DOASNTILDE)) &&  | 
    
662  | 
    8  | 
    					    (tilde_ok & 2)) { | 
    |
663  | 
    6  | 
    char *p, *dp_x;  | 
    |
664  | 
    |||
665  | 
    6  | 
    dp_x = dp;  | 
    |
666  | 
    6  | 
    p = maybe_expand_tilde(sp,  | 
    |
667  | 
    &ds, &dp_x,  | 
    ||
668  | 
    6  | 
    f & DOASNTILDE);  | 
    |
669  | 
    ✗✓ | 6  | 
    						if (p) { | 
    
670  | 
    if (dp != dp_x)  | 
    ||
671  | 
    word = IFS_WORD;  | 
    ||
672  | 
    dp = dp_x;  | 
    ||
673  | 
    sp = p;  | 
    ||
674  | 
    continue;  | 
    ||
675  | 
    }  | 
    ||
676  | 
    ✗✓ | 12  | 
    }  | 
    
677  | 
    break;  | 
    ||
678  | 
    }  | 
    ||
679  | 
    else  | 
    ||
680  | 
    87967918  | 
    quote &= ~2; /* undo temporary */  | 
    |
681  | 
    |||
682  | 
    ✓✓ | 206275057  | 
    			if (make_magic) { | 
    
683  | 
    make_magic = 0;  | 
    ||
684  | 
    47088  | 
    fdo |= DOMAGIC_ | (f & DOGLOB);  | 
    |
685  | 
    47088  | 
    *dp++ = MAGIC;  | 
    |
686  | 
    ✓✓ | 206275057  | 
    			} else if (ISMAGIC(c)) { | 
    
687  | 
    10671  | 
    fdo |= DOMAGIC_;  | 
    |
688  | 
    10671  | 
    *dp++ = MAGIC;  | 
    |
689  | 
    10671  | 
    }  | 
    |
690  | 
    206332816  | 
    *dp++ = c; /* save output char */  | 
    |
691  | 
    word = IFS_WORD;  | 
    ||
692  | 
    }  | 
    ||
693  | 
    }  | 
    ||
694  | 
    14982440  | 
    }  | 
    |
695  | 
    |||
696  | 
    /*  | 
    ||
697  | 
     * Prepare to generate the string returned by ${} substitution. | 
    ||
698  | 
    */  | 
    ||
699  | 
    static int  | 
    ||
700  | 
    varsub(Expand *xp, char *sp, char *word,  | 
    ||
701  | 
    int *stypep, /* becomes qualifier type */  | 
    ||
702  | 
    int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */  | 
    ||
703  | 
    { | 
    ||
704  | 
    int c;  | 
    ||
705  | 
    int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */  | 
    ||
706  | 
    int stype; /* substitution type */  | 
    ||
707  | 
    int slen;  | 
    ||
708  | 
    char *p;  | 
    ||
709  | 
    struct tbl *vp;  | 
    ||
710  | 
    int zero_ok = 0;  | 
    ||
711  | 
    |||
712  | 
    ✓✓ | 10731108  | 
    if (sp[0] == '\0') /* Bad variable name */  | 
    
713  | 
    6  | 
    return -1;  | 
    |
714  | 
    |||
715  | 
    5365548  | 
    xp->var = NULL;  | 
    |
716  | 
    |||
717  | 
    	/* ${#var}, string length or array size */ | 
    ||
718  | 
    ✓✓✓✓ | 
    5511939  | 
    	if (sp[0] == '#' && (c = sp[1]) != '\0') { | 
    
719  | 
    		/* Can't have any modifiers for ${#...} */ | 
    ||
720  | 
    ✗✓ | 96  | 
    if (*word != CSUBST)  | 
    
721  | 
    return -1;  | 
    ||
722  | 
    96  | 
    sp++;  | 
    |
723  | 
    /* Check for size of array */  | 
    ||
724  | 
    ✓✗✓✓ ✓✗✓✗  | 
    378  | 
    		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { | 
    
725  | 
    int n = 0;  | 
    ||
726  | 
    |||
727  | 
    96  | 
    vp = global(arrayname(sp));  | 
    |
728  | 
    ✓✓ | 96  | 
    if (vp->flag & (ISSET|ARRAY))  | 
    
729  | 
    51  | 
    zero_ok = 1;  | 
    |
730  | 
    ✓✓ | 408  | 
    for (; vp; vp = vp->u.array)  | 
    
731  | 
    ✓✓ | 108  | 
    if (vp->flag & ISSET)  | 
    
732  | 
    57  | 
    n++;  | 
    |
733  | 
    c = n; /* ksh88/ksh93 go for number, not max index */  | 
    ||
734  | 
    ✗✗ | 96  | 
    } else if (c == '*' || c == '@')  | 
    
735  | 
    c = genv->loc->argc;  | 
    ||
736  | 
    		else { | 
    ||
737  | 
    p = str_val(global(sp));  | 
    ||
738  | 
    zero_ok = p != null;  | 
    ||
739  | 
    c = strlen(p);  | 
    ||
740  | 
    }  | 
    ||
741  | 
    ✗✓ | 96  | 
    if (Flag(FNOUNSET) && c == 0 && !zero_ok)  | 
    
742  | 
    			errorf("%s: parameter not set", sp); | 
    ||
743  | 
    96  | 
    *stypep = 0; /* unqualified variable/string substitution */  | 
    |
744  | 
    96  | 
    xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);  | 
    |
745  | 
    96  | 
    return XSUB;  | 
    |
746  | 
    }  | 
    ||
747  | 
    |||
748  | 
    /* Check for qualifiers in word part */  | 
    ||
749  | 
    stype = 0;  | 
    ||
750  | 
    ✓✓ | 11386441  | 
    c = word[slen = 0] == CHAR ? word[1] : 0;  | 
    
751  | 
    ✓✓ | 5365452  | 
    	if (c == ':') { | 
    
752  | 
    slen += 2;  | 
    ||
753  | 
    stype = 0x80;  | 
    ||
754  | 
    ✓✗ | 10515  | 
    c = word[slen + 0] == CHAR ? word[slen + 1] : 0;  | 
    
755  | 
    3505  | 
    }  | 
    |
756  | 
    ✓✓ | 5365452  | 
    	if (ctype(c, C_SUBOP1)) { | 
    
757  | 
    634755  | 
    slen += 2;  | 
    |
758  | 
    634755  | 
    stype |= c;  | 
    |
759  | 
    ✓✓ | 5365452  | 
    	} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ | 
    
760  | 
    20716  | 
    slen += 2;  | 
    |
761  | 
    stype = c;  | 
    ||
762  | 
    ✓✓✓✓ | 
    32934  | 
    		if (word[slen + 0] == CHAR && c == word[slen + 1]) { | 
    
763  | 
    11676  | 
    stype |= 0x80;  | 
    |
764  | 
    11676  | 
    slen += 2;  | 
    |
765  | 
    11676  | 
    }  | 
    |
766  | 
    ✗✓ | 4709981  | 
    } else if (stype) /* : is not ok */  | 
    
767  | 
    return -1;  | 
    ||
768  | 
    ✓✓✓✓ | 
    10075433  | 
    if (!stype && *word != CSUBST)  | 
    
769  | 
    66  | 
    return -1;  | 
    |
770  | 
    5365386  | 
    *stypep = stype;  | 
    |
771  | 
    5365386  | 
    *slenp = slen;  | 
    |
772  | 
    |||
773  | 
    5365386  | 
    c = sp[0];  | 
    |
774  | 
    ✓✓ | 5365386  | 
    	if (c == '*' || c == '@') { | 
    
775  | 
    ✗✗✗✓ | 
    1968  | 
    		switch (stype & 0x7f) { | 
    
776  | 
    case '=': /* can't assign to a vector */  | 
    ||
777  | 
    case '%': /* can't trim a vector (yet) */  | 
    ||
778  | 
    case '#':  | 
    ||
779  | 
    return -1;  | 
    ||
780  | 
    }  | 
    ||
781  | 
    ✓✓ | 1968  | 
    		if (genv->loc->argc == 0) { | 
    
782  | 
    138  | 
    xp->str = null;  | 
    |
783  | 
    138  | 
    xp->var = global(sp);  | 
    |
784  | 
    138  | 
    state = c == '@' ? XNULLSUB : XSUB;  | 
    |
785  | 
    138  | 
    		} else { | 
    |
786  | 
    1830  | 
    xp->u.strv = (const char **) genv->loc->argv + 1;  | 
    |
787  | 
    1830  | 
    xp->str = *xp->u.strv++;  | 
    |
788  | 
    1830  | 
    xp->split = c == '@'; /* $@ */  | 
    |
789  | 
    state = XARG;  | 
    ||
790  | 
    }  | 
    ||
791  | 
    zero_ok = 1; /* exempt "$@" and "$*" from 'set -u' */  | 
    ||
792  | 
    1968  | 
    	} else { | 
    |
793  | 
    ✓✓✓✓ ✓✓✓✗  | 
    5364684  | 
    		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { | 
    
794  | 
    XPtrV wv;  | 
    ||
795  | 
    |||
796  | 
    ✗✗✗✗ ✓  | 
    464  | 
    			switch (stype & 0x7f) { | 
    
797  | 
    case '=': /* can't assign to a vector */  | 
    ||
798  | 
    case '%': /* can't trim a vector (yet) */  | 
    ||
799  | 
    case '#':  | 
    ||
800  | 
    case '?':  | 
    ||
801  | 
    return -1;  | 
    ||
802  | 
    }  | 
    ||
803  | 
    464  | 
    XPinit(wv, 32);  | 
    |
804  | 
    464  | 
    vp = global(arrayname(sp));  | 
    |
805  | 
    ✓✓ | 10078  | 
    			for (; vp; vp = vp->u.array) { | 
    
806  | 
    ✓✗ | 4575  | 
    if (!(vp->flag&ISSET))  | 
    
807  | 
    continue;  | 
    ||
808  | 
    ✗✓ | 9150  | 
    XPput(wv, str_val(vp));  | 
    
809  | 
    4575  | 
    }  | 
    |
810  | 
    ✗✓ | 464  | 
    			if (XPsize(wv) == 0) { | 
    
811  | 
    xp->str = null;  | 
    ||
812  | 
    state = p[1] == '@' ? XNULLSUB : XSUB;  | 
    ||
813  | 
    XPfree(wv);  | 
    ||
814  | 
    			} else { | 
    ||
815  | 
    ✗✓ | 928  | 
    XPput(wv, 0);  | 
    
816  | 
    464  | 
    xp->u.strv = (const char **) XPptrv(wv);  | 
    |
817  | 
    464  | 
    xp->str = *xp->u.strv++;  | 
    |
818  | 
    464  | 
    				xp->split = p[1] == '@'; /* ${foo[@]} */ | 
    |
819  | 
    state = XARG;  | 
    ||
820  | 
    }  | 
    ||
821  | 
    ✓✗ | 464  | 
    		} else { | 
    
822  | 
    /* Can't assign things like $! or $1 */  | 
    ||
823  | 
    ✓✓✗✓ | 
    5677627  | 
    if ((stype & 0x7f) == '=' &&  | 
    
824  | 
    ✓✗ | 629346  | 
    (ctype(*sp, C_VAR1) || digit(*sp)))  | 
    
825  | 
    return -1;  | 
    ||
826  | 
    5362954  | 
    xp->var = global(sp);  | 
    |
827  | 
    5362954  | 
    xp->str = str_val(xp->var);  | 
    |
828  | 
    state = XSUB;  | 
    ||
829  | 
    }  | 
    ||
830  | 
    }  | 
    ||
831  | 
    |||
832  | 
    5365386  | 
    c = stype&0x7f;  | 
    |
833  | 
    /* test the compiler's code generator */  | 
    ||
834  | 
    ✓✓✓✓ ✓✓✓✓  | 
    20671162  | 
    if (ctype(c, C_SUBOP2) ||  | 
    
835  | 
    ✓✓ | 10689340  | 
    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */  | 
    
836  | 
    ✓✓ | 5344670  | 
    c == '=' || c == '-' || c == '?' : c == '+'))  | 
    
837  | 
    651685  | 
    state = XBASE; /* expand word instead of variable value */  | 
    |
838  | 
    ✓✓✓✓ | 
    5365404  | 
    if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&  | 
    
839  | 
    ✓✗✓✗ | 
    12  | 
    (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))  | 
    
840  | 
    		errorf("%s: parameter not set", sp); | 
    ||
841  | 
    5365380  | 
    return state;  | 
    |
842  | 
    5365548  | 
    }  | 
    |
843  | 
    |||
844  | 
    /*  | 
    ||
845  | 
    * Run the command in $(...) and read its output.  | 
    ||
846  | 
    */  | 
    ||
847  | 
    static int  | 
    ||
848  | 
    comsub(Expand *xp, char *cp)  | 
    ||
849  | 
    { | 
    ||
850  | 
    Source *s, *sold;  | 
    ||
851  | 
    struct op *t;  | 
    ||
852  | 
    struct shf *shf;  | 
    ||
853  | 
    |||
854  | 
    2738060  | 
    s = pushs(SSTRING, ATEMP);  | 
    |
855  | 
    1369030  | 
    s->start = s->str = cp;  | 
    |
856  | 
    1369030  | 
    sold = source;  | 
    |
857  | 
    1369030  | 
    t = compile(s);  | 
    |
858  | 
    1369030  | 
    afree(s, ATEMP);  | 
    |
859  | 
    1369030  | 
    source = sold;  | 
    |
860  | 
    |||
861  | 
    ✗✓ | 1369030  | 
    if (t == NULL)  | 
    
862  | 
    return XBASE;  | 
    ||
863  | 
    |||
864  | 
    ✓✗✓✓ ✓✗  | 
    2738072  | 
    if (t != NULL && t->type == TCOM && /* $(<file) */  | 
    
865  | 
    ✓✓✓✗ | 
    193120  | 
    	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { | 
    
866  | 
    12  | 
    struct ioword *io = *t->ioact;  | 
    |
867  | 
    char *name;  | 
    ||
868  | 
    |||
869  | 
    ✗✓ | 12  | 
    if ((io->flag&IOTYPE) != IOREAD)  | 
    
870  | 
    			errorf("funny $() command: %s", | 
    ||
871  | 
    snptreef(NULL, 32, "%R", io));  | 
    ||
872  | 
    12  | 
    shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,  | 
    |
873  | 
    SHF_MAPHI|SHF_CLEXEC);  | 
    ||
874  | 
    ✓✗ | 12  | 
    if (shf == NULL)  | 
    
875  | 
    12  | 
    warningf(!Flag(FTALKING),  | 
    |
876  | 
    "%s: cannot open $(<) input", name);  | 
    ||
877  | 
    12  | 
    xp->split = 0; /* no waitlast() */  | 
    |
878  | 
    12  | 
    	} else { | 
    |
879  | 
    1369018  | 
    int ofd1, pv[2];  | 
    |
880  | 
    1369018  | 
    openpipe(pv);  | 
    |
881  | 
    1369018  | 
    shf = shf_fdopen(pv[0], SHF_RD, NULL);  | 
    |
882  | 
    1369018  | 
    ofd1 = savefd(1);  | 
    |
883  | 
    ✓✗ | 1369018  | 
    		if (pv[1] != 1) { | 
    
884  | 
    1369018  | 
    ksh_dup2(pv[1], 1, false);  | 
    |
885  | 
    1369018  | 
    close(pv[1]);  | 
    |
886  | 
    1369018  | 
    }  | 
    |
887  | 
    1369018  | 
    execute(t, XFORK|XXCOM|XPIPEO, NULL);  | 
    |
888  | 
    1369018  | 
    restfd(1, ofd1);  | 
    |
889  | 
    1369018  | 
    startlast();  | 
    |
890  | 
    1369018  | 
    xp->split = 1; /* waitlast() */  | 
    |
891  | 
    1369018  | 
    }  | 
    |
892  | 
    |||
893  | 
    1357987  | 
    xp->u.shf = shf;  | 
    |
894  | 
    1357987  | 
    return XCOM;  | 
    |
895  | 
    1357987  | 
    }  | 
    |
896  | 
    |||
897  | 
    /*  | 
    ||
898  | 
     * perform #pattern and %pattern substitution in ${} | 
    ||
899  | 
    */  | 
    ||
900  | 
    |||
901  | 
    static char *  | 
    ||
902  | 
    trimsub(char *str, char *pat, int how)  | 
    ||
903  | 
    { | 
    ||
904  | 
    52001  | 
    char *end = strchr(str, 0);  | 
    |
905  | 
    char *p, c;  | 
    ||
906  | 
    |||
907  | 
    ✓✓✓✓ ✓  | 
    31285  | 
    	switch (how&0xff) {	/* UCHAR_MAX maybe? */ | 
    
908  | 
    case '#': /* shortest at beginning */  | 
    ||
909  | 
    ✓✓ | 203518  | 
    		for (p = str; p <= end; p++) { | 
    
910  | 
    93435  | 
    c = *p; *p = '\0';  | 
    |
911  | 
    ✓✓ | 93435  | 
    			if (gmatch(str, pat, false)) { | 
    
912  | 
    *p = c;  | 
    ||
913  | 
    272  | 
    return p;  | 
    |
914  | 
    }  | 
    ||
915  | 
    *p = c;  | 
    ||
916  | 
    }  | 
    ||
917  | 
    break;  | 
    ||
918  | 
    case '#'|0x80: /* longest match at beginning */  | 
    ||
919  | 
    ✓✓ | 86980  | 
    		for (p = end; p >= str; p--) { | 
    
920  | 
    43478  | 
    c = *p; *p = '\0';  | 
    |
921  | 
    ✓✓ | 43478  | 
    			if (gmatch(str, pat, false)) { | 
    
922  | 
    *p = c;  | 
    ||
923  | 
    2924  | 
    return p;  | 
    |
924  | 
    }  | 
    ||
925  | 
    *p = c;  | 
    ||
926  | 
    }  | 
    ||
927  | 
    break;  | 
    ||
928  | 
    case '%': /* shortest match at end */  | 
    ||
929  | 
    ✓✓ | 5036  | 
    		for (p = end; p >= str; p--) { | 
    
930  | 
    ✓✓ | 2297  | 
    if (gmatch(p, pat, false))  | 
    
931  | 
    223  | 
    return str_nsave(str, p - str, ATEMP);  | 
    |
932  | 
    }  | 
    ||
933  | 
    break;  | 
    ||
934  | 
    case '%'|0x80: /* longest match at end */  | 
    ||
935  | 
    ✓✓ | 113652  | 
    		for (p = str; p <= end; p++) { | 
    
936  | 
    ✓✓ | 54814  | 
    if (gmatch(p, pat, false))  | 
    
937  | 
    6728  | 
    return str_nsave(str, p - str, ATEMP);  | 
    |
938  | 
    }  | 
    ||
939  | 
    break;  | 
    ||
940  | 
    }  | 
    ||
941  | 
    |||
942  | 
    10569  | 
    return str; /* no match, return string */  | 
    |
943  | 
    20716  | 
    }  | 
    |
944  | 
    |||
945  | 
    /*  | 
    ||
946  | 
    * glob  | 
    ||
947  | 
    * Name derived from V6's /etc/glob, the program that expanded filenames.  | 
    ||
948  | 
    */  | 
    ||
949  | 
    |||
950  | 
    /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */  | 
    ||
951  | 
    static void  | 
    ||
952  | 
    glob(char *cp, XPtrV *wp, int markdirs)  | 
    ||
953  | 
    { | 
    ||
954  | 
    2678410  | 
    int oldsize = XPsize(*wp);  | 
    |
955  | 
    |||
956  | 
    ✓✓ | 1339205  | 
    if (glob_str(cp, wp, markdirs) == 0)  | 
    
957  | 
    ✓✓ | 8639  | 
    XPput(*wp, debunk(cp, cp, strlen(cp) + 1));  | 
    
958  | 
    else  | 
    ||
959  | 
    1334888  | 
    qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),  | 
    |
960  | 
    xstrcmp);  | 
    ||
961  | 
    1339205  | 
    }  | 
    |
962  | 
    |||
963  | 
    #define GF_NONE 0  | 
    ||
964  | 
    #define GF_EXCHECK BIT(0) /* do existence check on file */  | 
    ||
965  | 
    #define GF_GLOBBED BIT(1) /* some globbing has been done */  | 
    ||
966  | 
    #define GF_MARKDIR BIT(2) /* add trailing / to directories */  | 
    ||
967  | 
    |||
968  | 
    /* Apply file globbing to cp and store the matching files in wp. Returns  | 
    ||
969  | 
    * the number of matches found.  | 
    ||
970  | 
    */  | 
    ||
971  | 
    int  | 
    ||
972  | 
    glob_str(char *cp, XPtrV *wp, int markdirs)  | 
    ||
973  | 
    { | 
    ||
974  | 
    2678452  | 
    int oldsize = XPsize(*wp);  | 
    |
975  | 
    1339226  | 
    XString xs;  | 
    |
976  | 
    1339226  | 
    char *xp;  | 
    |
977  | 
    |||
978  | 
    1339226  | 
    Xinit(xs, xp, 256, ATEMP);  | 
    |
979  | 
    1339226  | 
    globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);  | 
    |
980  | 
    1339226  | 
    Xfree(xs, xp);  | 
    |
981  | 
    |||
982  | 
    2678452  | 
    return XPsize(*wp) - oldsize;  | 
    |
983  | 
    1339226  | 
    }  | 
    |
984  | 
    |||
985  | 
    static void  | 
    ||
986  | 
    globit(XString *xs, /* dest string */  | 
    ||
987  | 
    char **xpp, /* ptr to dest end */  | 
    ||
988  | 
    char *sp, /* source path */  | 
    ||
989  | 
    XPtrV *wp, /* output list */  | 
    ||
990  | 
    int check) /* GF_* flags */  | 
    ||
991  | 
    { | 
    ||
992  | 
    char *np; /* next source component */  | 
    ||
993  | 
    11190884  | 
    char *xp = *xpp;  | 
    |
994  | 
    char *se;  | 
    ||
995  | 
    char odirsep;  | 
    ||
996  | 
    |||
997  | 
    /* This to allow long expansions to be interrupted */  | 
    ||
998  | 
    5595442  | 
    intrcheck();  | 
    |
999  | 
    |||
1000  | 
    ✓✓ | 5595442  | 
    	if (sp == NULL) {	/* end of source path */ | 
    
1001  | 
    /* We only need to check if the file exists if a pattern  | 
    ||
1002  | 
    * is followed by a non-pattern (eg, foo*x/bar; no check  | 
    ||
1003  | 
    * is needed for foo* since the match must exist) or if  | 
    ||
1004  | 
    * any patterns were expanded and the markdirs option is set.  | 
    ||
1005  | 
    * Symlinks make things a bit tricky...  | 
    ||
1006  | 
    */  | 
    ||
1007  | 
    ✓✗✓✗ | 
    4237173  | 
    if ((check & GF_EXCHECK) ||  | 
    
1008  | 
    ✓✓ | 4237173  | 
    		    ((check & GF_MARKDIR) && (check & GF_GLOBBED))) { | 
    
1009  | 
    #define stat_check() (stat_done ? stat_done : \  | 
    ||
1010  | 
    (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \  | 
    ||
1011  | 
    ? -1 : 1))  | 
    ||
1012  | 
    7  | 
    struct stat lstatb, statb;  | 
    |
1013  | 
    int stat_done = 0; /* -1: failed, 1 ok */  | 
    ||
1014  | 
    |||
1015  | 
    ✗✓ | 7  | 
    if (lstat(Xstring(*xs, xp), &lstatb) < 0)  | 
    
1016  | 
    return;  | 
    ||
1017  | 
    /* special case for systems which strip trailing  | 
    ||
1018  | 
    * slashes from regular files (eg, /etc/passwd/).  | 
    ||
1019  | 
    * SunOS 4.1.3 does this...  | 
    ||
1020  | 
    */  | 
    ||
1021  | 
    ✗✓✗✗ ✗✗  | 
    7  | 
    if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&  | 
    
1022  | 
    xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&  | 
    ||
1023  | 
    (!S_ISLNK(lstatb.st_mode) ||  | 
    ||
1024  | 
    stat_check() < 0 || !S_ISDIR(statb.st_mode)))  | 
    ||
1025  | 
    return;  | 
    ||
1026  | 
    /* Possibly tack on a trailing / if there isn't already  | 
    ||
1027  | 
    * one and if the file is a directory or a symlink to a  | 
    ||
1028  | 
    * directory  | 
    ||
1029  | 
    */  | 
    ||
1030  | 
    ✓✗✓✗ ✗✗  | 
    14  | 
    if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&  | 
    
1031  | 
    ✓✗✓✗ | 
    14  | 
    xp > Xstring(*xs, xp) && xp[-1] != '/' &&  | 
    
1032  | 
    ✓✓ | 7  | 
    (S_ISDIR(lstatb.st_mode) ||  | 
    
1033  | 
    ✗✓✗✗ ✗✗  | 
    3  | 
    (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&  | 
    
1034  | 
    			    S_ISDIR(statb.st_mode)))) { | 
    ||
1035  | 
    4  | 
    *xp++ = '/';  | 
    |
1036  | 
    4  | 
    *xp = '\0';  | 
    |
1037  | 
    4  | 
    }  | 
    |
1038  | 
    ✓✗ | 14  | 
    }  | 
    
1039  | 
    ✓✓ | 8496083  | 
    XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp), ATEMP));  | 
    
1040  | 
    4237166  | 
    return;  | 
    |
1041  | 
    }  | 
    ||
1042  | 
    |||
1043  | 
    ✓✓ | 1358276  | 
    if (xp > Xstring(*xs, xp))  | 
    
1044  | 
    19050  | 
    *xp++ = '/';  | 
    |
1045  | 
    ✓✓ | 2723986  | 
    	while (*sp == '/') { | 
    
1046  | 
    ✗✓ | 3717  | 
    Xcheck(*xs, xp);  | 
    
1047  | 
    3717  | 
    *xp++ = *sp++;  | 
    |
1048  | 
    }  | 
    ||
1049  | 
    1358276  | 
    np = strchr(sp, '/');  | 
    |
1050  | 
    ✓✓ | 1358276  | 
    	if (np != NULL) { | 
    
1051  | 
    se = np;  | 
    ||
1052  | 
    18770  | 
    odirsep = *np; /* don't assume '/', can be multiple kinds */  | 
    |
1053  | 
    18770  | 
    *np++ = '\0';  | 
    |
1054  | 
    18770  | 
    	} else { | 
    |
1055  | 
    odirsep = '\0'; /* keep gcc quiet */  | 
    ||
1056  | 
    1339506  | 
    se = sp + strlen(sp);  | 
    |
1057  | 
    }  | 
    ||
1058  | 
    |||
1059  | 
    |||
1060  | 
    /* Check if sp needs globbing - done to avoid pattern checks for strings  | 
    ||
1061  | 
    * containing MAGIC characters, open ['s without the matching close ],  | 
    ||
1062  | 
    * etc. (otherwise opendir() will be called which may fail because the  | 
    ||
1063  | 
    * directory isn't readable - if no globbing is needed, only execute  | 
    ||
1064  | 
    * permission should be required (as per POSIX)).  | 
    ||
1065  | 
    */  | 
    ||
1066  | 
    ✓✓ | 1358276  | 
    	if (!has_globbing(sp, se)) { | 
    
1067  | 
    ✗✓ | 1348221  | 
    XcheckN(*xs, xp, se - sp + 1);  | 
    
1068  | 
    1348221  | 
    debunk(xp, sp, Xnleft(*xs, xp));  | 
    |
1069  | 
    1348221  | 
    xp += strlen(xp);  | 
    |
1070  | 
    1348221  | 
    *xpp = xp;  | 
    |
1071  | 
    1348221  | 
    globit(xs, xpp, np, wp, check);  | 
    |
1072  | 
    1348221  | 
    	} else { | 
    |
1073  | 
    DIR *dirp;  | 
    ||
1074  | 
    struct dirent *d;  | 
    ||
1075  | 
    char *name;  | 
    ||
1076  | 
    int len;  | 
    ||
1077  | 
    int prefix_len;  | 
    ||
1078  | 
    |||
1079  | 
    10055  | 
    *xp = '\0';  | 
    |
1080  | 
    10055  | 
    prefix_len = Xlength(*xs, xp);  | 
    |
1081  | 
    ✓✓ | 24309  | 
    dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");  | 
    
1082  | 
    ✓✓ | 10055  | 
    if (dirp == NULL)  | 
    
1083  | 
    goto Nodir;  | 
    ||
1084  | 
    ✓✓ | 6182878  | 
    		while ((d = readdir(dirp)) != NULL) { | 
    
1085  | 
    3255347  | 
    name = d->d_name;  | 
    |
1086  | 
    ✓✓✓✗ | 
    3265115  | 
    if (name[0] == '.' &&  | 
    
1087  | 
    ✓✓✓✓ | 
    39216  | 
    (name[1] == 0 || (name[1] == '.' && name[2] == 0)))  | 
    
1088  | 
    19536  | 
    continue; /* always ignore . and .. */  | 
    |
1089  | 
    ✓✓✗✓ ✓✓  | 
    6471622  | 
    if ((*name == '.' && *sp != '.') ||  | 
    
1090  | 
    3235739  | 
    !gmatch(name, sp, true))  | 
    |
1091  | 
    327816  | 
    continue;  | 
    |
1092  | 
    |||
1093  | 
    2907995  | 
    len = strlen(d->d_name) + 1;  | 
    |
1094  | 
    ✗✓ | 2907995  | 
    XcheckN(*xs, xp, len);  | 
    
1095  | 
    2907995  | 
    memcpy(xp, name, len);  | 
    |
1096  | 
    2907995  | 
    *xpp = xp + len - 1;  | 
    |
1097  | 
    2907995  | 
    globit(xs, xpp, np, wp,  | 
    |
1098  | 
    2907995  | 
    (check & GF_MARKDIR) | GF_GLOBBED  | 
    |
1099  | 
    2907995  | 
    | (np ? GF_EXCHECK : GF_NONE));  | 
    |
1100  | 
    2907995  | 
    xp = Xstring(*xs, xp) + prefix_len;  | 
    |
1101  | 
    }  | 
    ||
1102  | 
    9768  | 
    closedir(dirp);  | 
    |
1103  | 
    Nodir:;  | 
    ||
1104  | 
    }  | 
    ||
1105  | 
    |||
1106  | 
    ✓✓ | 1358276  | 
    if (np != NULL)  | 
    
1107  | 
    18770  | 
    *--np = odirsep;  | 
    |
1108  | 
    6953718  | 
    }  | 
    |
1109  | 
    |||
1110  | 
    /* remove MAGIC from string */  | 
    ||
1111  | 
    char *  | 
    ||
1112  | 
    debunk(char *dp, const char *sp, size_t dlen)  | 
    ||
1113  | 
    { | 
    ||
1114  | 
    char *d, *s;  | 
    ||
1115  | 
    |||
1116  | 
    ✓✓ | 9883824  | 
    	if ((s = strchr(sp, MAGIC))) { | 
    
1117  | 
    ✗✓ | 4576689  | 
    if (s - sp >= dlen)  | 
    
1118  | 
    return dp;  | 
    ||
1119  | 
    4576689  | 
    memcpy(dp, sp, s - sp);  | 
    |
1120  | 
    ✓✓✓✗ | 
    37451133  | 
    for (d = dp + (s - sp); *s && (d - dp < dlen); s++)  | 
    
1121  | 
    ✓✓✓✓ ✗✓  | 
    14070246  | 
    if (!ISMAGIC(*s) || !(*++s & 0x80) ||  | 
    
1122  | 
    96  | 
    			    !strchr("*+?@! ", *s & 0x7f)) | 
    |
1123  | 
    9432489  | 
    *d++ = *s;  | 
    |
1124  | 
    			else { | 
    ||
1125  | 
    /* extended pattern operators: *+?@! */  | 
    ||
1126  | 
    ✓✓ | 96  | 
    if ((*s & 0x7f) != ' ')  | 
    
1127  | 
    78  | 
    *d++ = *s & 0x7f;  | 
    |
1128  | 
    ✓✗ | 96  | 
    if (d - dp < dlen)  | 
    
1129  | 
    96  | 
    					*d++ = '('; | 
    |
1130  | 
    }  | 
    ||
1131  | 
    4576689  | 
    *d = '\0';  | 
    |
1132  | 
    ✓✓ | 4941912  | 
    } else if (dp != sp)  | 
    
1133  | 
    351277  | 
    strlcpy(dp, sp, dlen);  | 
    |
1134  | 
    4941912  | 
    return dp;  | 
    |
1135  | 
    4941912  | 
    }  | 
    |
1136  | 
    |||
1137  | 
    /* Check if p is an unquoted name, possibly followed by a / or :. If so  | 
    ||
1138  | 
    * puts the expanded version in *dcp,dp and returns a pointer in p just  | 
    ||
1139  | 
    * past the name, otherwise returns 0.  | 
    ||
1140  | 
    */  | 
    ||
1141  | 
    static char *  | 
    ||
1142  | 
    maybe_expand_tilde(char *p, XString *dsp, char **dpp, int isassign)  | 
    ||
1143  | 
    { | 
    ||
1144  | 
    12  | 
    XString ts;  | 
    |
1145  | 
    6  | 
    char *dp = *dpp;  | 
    |
1146  | 
    char *tp, *r;  | 
    ||
1147  | 
    |||
1148  | 
    6  | 
    Xinit(ts, tp, 16, ATEMP);  | 
    |
1149  | 
    /* : only for DOASNTILDE form */  | 
    ||
1150  | 
    ✓✓✓✗ ✓✗✓✗  | 
    42  | 
    while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))  | 
    
1151  | 
    	{ | 
    ||
1152  | 
    ✗✓ | 6  | 
    Xcheck(ts, tp);  | 
    
1153  | 
    6  | 
    *tp++ = p[1];  | 
    |
1154  | 
    6  | 
    p += 2;  | 
    |
1155  | 
    }  | 
    ||
1156  | 
    6  | 
    *tp = '\0';  | 
    |
1157  | 
    ✗✓✗✗ ✗✗  | 
    18  | 
    r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?  | 
    
1158  | 
    6  | 
    tilde(Xstring(ts, tp)) : NULL;  | 
    |
1159  | 
    6  | 
    Xfree(ts, tp);  | 
    |
1160  | 
    ✗✓ | 6  | 
    	if (r) { | 
    
1161  | 
    		while (*r) { | 
    ||
1162  | 
    Xcheck(*dsp, dp);  | 
    ||
1163  | 
    if (ISMAGIC(*r))  | 
    ||
1164  | 
    *dp++ = MAGIC;  | 
    ||
1165  | 
    *dp++ = *r++;  | 
    ||
1166  | 
    }  | 
    ||
1167  | 
    *dpp = dp;  | 
    ||
1168  | 
    r = p;  | 
    ||
1169  | 
    }  | 
    ||
1170  | 
    6  | 
    return r;  | 
    |
1171  | 
    6  | 
    }  | 
    |
1172  | 
    |||
1173  | 
    /*  | 
    ||
1174  | 
    * tilde expansion  | 
    ||
1175  | 
    *  | 
    ||
1176  | 
    * based on a version by Arnold Robbins  | 
    ||
1177  | 
    */  | 
    ||
1178  | 
    |||
1179  | 
    static char *  | 
    ||
1180  | 
    tilde(char *cp)  | 
    ||
1181  | 
    { | 
    ||
1182  | 
    char *dp;  | 
    ||
1183  | 
    |||
1184  | 
    ✗✓ | 12  | 
    if (cp[0] == '\0')  | 
    
1185  | 
    		dp = str_val(global("HOME")); | 
    ||
1186  | 
    ✗✓✗✗ | 
    6  | 
    else if (cp[0] == '+' && cp[1] == '\0')  | 
    
1187  | 
    		dp = str_val(global("PWD")); | 
    ||
1188  | 
    ✗✓✗✗ | 
    6  | 
    else if (cp[0] == '-' && cp[1] == '\0')  | 
    
1189  | 
    		dp = str_val(global("OLDPWD")); | 
    ||
1190  | 
    else  | 
    ||
1191  | 
    6  | 
    dp = homedir(cp);  | 
    |
1192  | 
    /* If HOME, PWD or OLDPWD are not set, don't expand ~ */  | 
    ||
1193  | 
    ✗✓ | 6  | 
    if (dp == null)  | 
    
1194  | 
    dp = NULL;  | 
    ||
1195  | 
    6  | 
    return dp;  | 
    |
1196  | 
    }  | 
    ||
1197  | 
    |||
1198  | 
    /*  | 
    ||
1199  | 
    * map userid to user's home directory.  | 
    ||
1200  | 
    * note that 4.3's getpw adds more than 6K to the shell,  | 
    ||
1201  | 
    * and the YP version probably adds much more.  | 
    ||
1202  | 
    * we might consider our own version of getpwnam() to keep the size down.  | 
    ||
1203  | 
    */  | 
    ||
1204  | 
    |||
1205  | 
    static char *  | 
    ||
1206  | 
    homedir(char *name)  | 
    ||
1207  | 
    { | 
    ||
1208  | 
    struct tbl *ap;  | 
    ||
1209  | 
    |||
1210  | 
    12  | 
    ap = ktenter(&homedirs, name, hash(name));  | 
    |
1211  | 
    ✓✗ | 6  | 
    	if (!(ap->flag & ISSET)) { | 
    
1212  | 
    struct passwd *pw;  | 
    ||
1213  | 
    |||
1214  | 
    6  | 
    pw = getpwnam(name);  | 
    |
1215  | 
    ✓✗ | 6  | 
    if (pw == NULL)  | 
    
1216  | 
    6  | 
    return NULL;  | 
    |
1217  | 
    ap->val.s = str_save(pw->pw_dir, APERM);  | 
    ||
1218  | 
    ap->flag |= DEFINED|ISSET|ALLOC;  | 
    ||
1219  | 
    }  | 
    ||
1220  | 
    return ap->val.s;  | 
    ||
1221  | 
    6  | 
    }  | 
    |
1222  | 
    |||
1223  | 
    #ifdef BRACE_EXPAND  | 
    ||
1224  | 
    static void  | 
    ||
1225  | 
    alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)  | 
    ||
1226  | 
    { | 
    ||
1227  | 
    int count = 0;  | 
    ||
1228  | 
    char *brace_start, *brace_end, *comma = NULL;  | 
    ||
1229  | 
    char *field_start;  | 
    ||
1230  | 
    char *p;  | 
    ||
1231  | 
    |||
1232  | 
    /* search for open brace */  | 
    ||
1233  | 
    ✓✓✗✓ | 
    9676  | 
    for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)  | 
    
1234  | 
    ;  | 
    ||
1235  | 
    brace_start = p;  | 
    ||
1236  | 
    |||
1237  | 
    /* find matching close brace, if any */  | 
    ||
1238  | 
    ✓✓ | 2886  | 
    	if (p) { | 
    
1239  | 
    comma = NULL;  | 
    ||
1240  | 
    count = 1;  | 
    ||
1241  | 
    ✓✓ | 13356  | 
    		for (p += 2; *p && count; p++) { | 
    
1242  | 
    ✓✓ | 5660  | 
    			if (ISMAGIC(*p)) { | 
    
1243  | 
    ✗✓ | 1868  | 
    if (*++p == OBRACE)  | 
    
1244  | 
    count++;  | 
    ||
1245  | 
    ✓✓ | 1868  | 
    else if (*p == CBRACE)  | 
    
1246  | 
    1018  | 
    --count;  | 
    |
1247  | 
    ✓✗ | 850  | 
    else if (*p == ',' && count == 1)  | 
    
1248  | 
    850  | 
    comma = p;  | 
    |
1249  | 
    }  | 
    ||
1250  | 
    }  | 
    ||
1251  | 
    }  | 
    ||
1252  | 
    /* no valid expansions... */  | 
    ||
1253  | 
    ✓✓ | 2886  | 
    	if (!p || count != 0) { | 
    
1254  | 
    		/* Note that given a{{b,c} we do not expand anything (this is | 
    ||
1255  | 
    		 * what at&t ksh does.  This may be changed to do the {b,c} | 
    ||
1256  | 
    * expansion. }  | 
    ||
1257  | 
    */  | 
    ||
1258  | 
    ✗✓ | 1868  | 
    if (fdo & DOGLOB)  | 
    
1259  | 
    glob(start, wp, fdo & DOMARKDIRS);  | 
    ||
1260  | 
    else  | 
    ||
1261  | 
    ✗✓ | 3736  | 
    XPput(*wp, debunk(start, start, end - start));  | 
    
1262  | 
    1868  | 
    return;  | 
    |
1263  | 
    }  | 
    ||
1264  | 
    brace_end = p;  | 
    ||
1265  | 
    ✓✓ | 1018  | 
    	if (!comma) { | 
    
1266  | 
    168  | 
    alt_expand(wp, start, brace_end, end, fdo);  | 
    |
1267  | 
    168  | 
    return;  | 
    |
1268  | 
    }  | 
    ||
1269  | 
    |||
1270  | 
    /* expand expression */  | 
    ||
1271  | 
    850  | 
    field_start = brace_start + 2;  | 
    |
1272  | 
    count = 1;  | 
    ||
1273  | 
    ✓✓ | 8500  | 
    	for (p = brace_start + 2; p != brace_end; p++) { | 
    
1274  | 
    ✓✓ | 3400  | 
    		if (ISMAGIC(*p)) { | 
    
1275  | 
    ✗✓ | 1700  | 
    if (*++p == OBRACE)  | 
    
1276  | 
    count++;  | 
    ||
1277  | 
    ✓✓✗✓ | 
    2550  | 
    else if ((*p == CBRACE && --count == 0) ||  | 
    
1278  | 
    ✓✗ | 850  | 
    			    (*p == ',' && count == 1)) { | 
    
1279  | 
    char *new;  | 
    ||
1280  | 
    int l1, l2, l3;  | 
    ||
1281  | 
    |||
1282  | 
    1700  | 
    l1 = brace_start - start;  | 
    |
1283  | 
    1700  | 
    l2 = (p - 1) - field_start;  | 
    |
1284  | 
    1700  | 
    l3 = end - brace_end;  | 
    |
1285  | 
    1700  | 
    new = alloc(l1 + l2 + l3 + 1, ATEMP);  | 
    |
1286  | 
    1700  | 
    memcpy(new, start, l1);  | 
    |
1287  | 
    1700  | 
    memcpy(new + l1, field_start, l2);  | 
    |
1288  | 
    1700  | 
    memcpy(new + l1 + l2, brace_end, l3);  | 
    |
1289  | 
    1700  | 
    new[l1 + l2 + l3] = '\0';  | 
    |
1290  | 
    1700  | 
    alt_expand(wp, new, new + l1,  | 
    |
1291  | 
    1700  | 
    new + l1 + l2 + l3, fdo);  | 
    |
1292  | 
    1700  | 
    field_start = p + 1;  | 
    |
1293  | 
    1700  | 
    }  | 
    |
1294  | 
    }  | 
    ||
1295  | 
    }  | 
    ||
1296  | 
    850  | 
    return;  | 
    |
1297  | 
    2886  | 
    }  | 
    |
1298  | 
    #endif /* BRACE_EXPAND */  | 
    
| Generated by: GCOVR (Version 3.3) |