| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: var.c,v 1.59 2017/08/30 17:08:45 jca Exp $ */  | 
    ||
2  | 
    |||
3  | 
    #include <sys/stat.h>  | 
    ||
4  | 
    |||
5  | 
    #include <ctype.h>  | 
    ||
6  | 
    #include <errno.h>  | 
    ||
7  | 
    #include <limits.h>  | 
    ||
8  | 
    #include <stdlib.h>  | 
    ||
9  | 
    #include <string.h>  | 
    ||
10  | 
    #include <time.h>  | 
    ||
11  | 
    #include <unistd.h>  | 
    ||
12  | 
    |||
13  | 
    #include "sh.h"  | 
    ||
14  | 
    |||
15  | 
    /*  | 
    ||
16  | 
    * Variables  | 
    ||
17  | 
    *  | 
    ||
18  | 
    * WARNING: unreadable code, needs a rewrite  | 
    ||
19  | 
    *  | 
    ||
20  | 
    * if (flag&INTEGER), val.i contains integer value, and type contains base.  | 
    ||
21  | 
    * otherwise, (val.s + type) contains string value.  | 
    ||
22  | 
    * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.  | 
    ||
23  | 
    */  | 
    ||
24  | 
    static struct tbl vtemp;  | 
    ||
25  | 
    static struct table specials;  | 
    ||
26  | 
    static char *formatstr(struct tbl *, const char *);  | 
    ||
27  | 
    static void export(struct tbl *, const char *);  | 
    ||
28  | 
    static int special(const char *);  | 
    ||
29  | 
    static void unspecial(const char *);  | 
    ||
30  | 
    static void getspec(struct tbl *);  | 
    ||
31  | 
    static void setspec(struct tbl *);  | 
    ||
32  | 
    static void unsetspec(struct tbl *);  | 
    ||
33  | 
    static struct tbl *arraysearch(struct tbl *, int);  | 
    ||
34  | 
    |||
35  | 
    /*  | 
    ||
36  | 
    * create a new block for function calls and simple commands  | 
    ||
37  | 
    * assume caller has allocated and set up genv->loc  | 
    ||
38  | 
    */  | 
    ||
39  | 
    void  | 
    ||
40  | 
    newblock(void)  | 
    ||
41  | 
    { | 
    ||
42  | 
    struct block *l;  | 
    ||
43  | 
    	static char *const empty[] = {null}; | 
    ||
44  | 
    |||
45  | 
    5284842  | 
    l = alloc(sizeof(struct block), ATEMP);  | 
    |
46  | 
    2642421  | 
    l->flags = 0;  | 
    |
47  | 
    2642421  | 
    ainit(&l->area); /* todo: could use genv->area (l->area => l->areap) */  | 
    |
48  | 
    ✓✓ | 2642421  | 
    	if (!genv->loc) { | 
    
49  | 
    104763  | 
    l->argc = 0;  | 
    |
50  | 
    l->argv = (char **) empty;  | 
    ||
51  | 
    104763  | 
    	} else { | 
    |
52  | 
    2537658  | 
    l->argc = genv->loc->argc;  | 
    |
53  | 
    2537658  | 
    l->argv = genv->loc->argv;  | 
    |
54  | 
    }  | 
    ||
55  | 
    2642421  | 
    l->exit = l->error = NULL;  | 
    |
56  | 
    2642421  | 
    ktinit(&l->vars, &l->area, 0);  | 
    |
57  | 
    2642421  | 
    ktinit(&l->funs, &l->area, 0);  | 
    |
58  | 
    2642421  | 
    l->next = genv->loc;  | 
    |
59  | 
    2642421  | 
    genv->loc = l;  | 
    |
60  | 
    2642421  | 
    }  | 
    |
61  | 
    |||
62  | 
    /*  | 
    ||
63  | 
    * pop a block handling special variables  | 
    ||
64  | 
    */  | 
    ||
65  | 
    void  | 
    ||
66  | 
    popblock(void)  | 
    ||
67  | 
    { | 
    ||
68  | 
    5042982  | 
    struct block *l = genv->loc;  | 
    |
69  | 
    2521491  | 
    struct tbl *vp, **vpp = l->vars.tbls, *vq;  | 
    |
70  | 
    int i;  | 
    ||
71  | 
    |||
72  | 
    2521491  | 
    genv->loc = l->next; /* pop block */  | 
    |
73  | 
    ✓✓ | 5143526  | 
    for (i = l->vars.size; --i >= 0; )  | 
    
74  | 
    ✓✓✓✓ | 
    59612  | 
    		if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) { | 
    
75  | 
    ✓✓ | 4156  | 
    if ((vq = global(vp->name))->flag & ISSET)  | 
    
76  | 
    4144  | 
    setspec(vq);  | 
    |
77  | 
    else  | 
    ||
78  | 
    12  | 
    unsetspec(vq);  | 
    |
79  | 
    }  | 
    ||
80  | 
    ✓✓ | 2521491  | 
    if (l->flags & BF_DOGETOPTS)  | 
    
81  | 
    2440  | 
    user_opt = l->getopts_state;  | 
    |
82  | 
    2521491  | 
    afreeall(&l->area);  | 
    |
83  | 
    2521491  | 
    afree(l, ATEMP);  | 
    |
84  | 
    2521491  | 
    }  | 
    |
85  | 
    |||
86  | 
    /* called by main() to initialize variable data structures */  | 
    ||
87  | 
    void  | 
    ||
88  | 
    initvar(void)  | 
    ||
89  | 
    { | 
    ||
90  | 
    	static const struct { | 
    ||
91  | 
    const char *name;  | 
    ||
92  | 
    int v;  | 
    ||
93  | 
    	} names[] = { | 
    ||
94  | 
    		{ "COLUMNS",		V_COLUMNS }, | 
    ||
95  | 
    		{ "IFS",		V_IFS }, | 
    ||
96  | 
    		{ "OPTIND",		V_OPTIND }, | 
    ||
97  | 
    		{ "PATH",		V_PATH }, | 
    ||
98  | 
    		{ "POSIXLY_CORRECT",	V_POSIXLY_CORRECT }, | 
    ||
99  | 
    		{ "TMPDIR",		V_TMPDIR }, | 
    ||
100  | 
    #ifdef HISTORY  | 
    ||
101  | 
    		{ "HISTCONTROL",	V_HISTCONTROL }, | 
    ||
102  | 
    		{ "HISTFILE",		V_HISTFILE }, | 
    ||
103  | 
    		{ "HISTSIZE",		V_HISTSIZE }, | 
    ||
104  | 
    #endif /* HISTORY */  | 
    ||
105  | 
    #ifdef EDIT  | 
    ||
106  | 
    		{ "EDITOR",		V_EDITOR }, | 
    ||
107  | 
    		{ "VISUAL",		V_VISUAL }, | 
    ||
108  | 
    #endif /* EDIT */  | 
    ||
109  | 
    		{ "MAIL",		V_MAIL }, | 
    ||
110  | 
    		{ "MAILCHECK",		V_MAILCHECK }, | 
    ||
111  | 
    		{ "MAILPATH",		V_MAILPATH }, | 
    ||
112  | 
    		{ "RANDOM",		V_RANDOM }, | 
    ||
113  | 
    		{ "SECONDS",		V_SECONDS }, | 
    ||
114  | 
    		{ "TMOUT",		V_TMOUT }, | 
    ||
115  | 
    		{ "LINENO",		V_LINENO }, | 
    ||
116  | 
    		{ NULL,	0 } | 
    ||
117  | 
    };  | 
    ||
118  | 
    int i;  | 
    ||
119  | 
    struct tbl *tp;  | 
    ||
120  | 
    |||
121  | 
    209526  | 
    ktinit(&specials, APERM, 32); /* must be 2^n (currently 17 specials) */  | 
    |
122  | 
    ✓✓ | 3980994  | 
    	for (i = 0; names[i].name; i++) { | 
    
123  | 
    1885734  | 
    tp = ktenter(&specials, names[i].name, hash(names[i].name));  | 
    |
124  | 
    1885734  | 
    tp->flag = DEFINED|ISSET;  | 
    |
125  | 
    1885734  | 
    tp->type = names[i].v;  | 
    |
126  | 
    }  | 
    ||
127  | 
    104763  | 
    }  | 
    |
128  | 
    |||
129  | 
    /* Used to calculate an array index for global()/local(). Sets *arrayp to  | 
    ||
130  | 
    * non-zero if this is an array, sets *valp to the array index, returns  | 
    ||
131  | 
    * the basename of the array.  | 
    ||
132  | 
    */  | 
    ||
133  | 
    static const char *  | 
    ||
134  | 
    array_index_calc(const char *n, bool *arrayp, int *valp)  | 
    ||
135  | 
    { | 
    ||
136  | 
    const char *p;  | 
    ||
137  | 
    int len;  | 
    ||
138  | 
    |||
139  | 
    25793606  | 
    *arrayp = false;  | 
    |
140  | 
    12896803  | 
    p = skip_varname(n, false);  | 
    |
141  | 
    ✓✓✓✓ ✓✗  | 
    24111885  | 
    	if (p != n && *p == '[' && (len = array_ref_len(p))) { | 
    
142  | 
    char *sub, *tmp;  | 
    ||
143  | 
    408  | 
    long rval;  | 
    |
144  | 
    |||
145  | 
    /* Calculate the value of the subscript */  | 
    ||
146  | 
    408  | 
    *arrayp = true;  | 
    |
147  | 
    408  | 
    tmp = str_nsave(p+1, len-2, ATEMP);  | 
    |
148  | 
    408  | 
    sub = substitute(tmp, 0);  | 
    |
149  | 
    408  | 
    afree(tmp, ATEMP);  | 
    |
150  | 
    408  | 
    n = str_nsave(n, p - n, ATEMP);  | 
    |
151  | 
    408  | 
    evaluate(sub, &rval, KSH_UNWIND_ERROR, true);  | 
    |
152  | 
    ✗✓ | 408  | 
    if (rval < 0 || rval > INT_MAX)  | 
    
153  | 
    			errorf("%s: subscript %ld out of range", n, rval); | 
    ||
154  | 
    408  | 
    *valp = rval;  | 
    |
155  | 
    408  | 
    afree(sub, ATEMP);  | 
    |
156  | 
    408  | 
    }  | 
    |
157  | 
    12896803  | 
    return n;  | 
    |
158  | 
    }  | 
    ||
159  | 
    |||
160  | 
    /*  | 
    ||
161  | 
    * Search for variable, if not found create globally.  | 
    ||
162  | 
    */  | 
    ||
163  | 
    struct tbl *  | 
    ||
164  | 
    global(const char *n)  | 
    ||
165  | 
    { | 
    ||
166  | 
    23673414  | 
    struct block *l = genv->loc;  | 
    |
167  | 
    struct tbl *vp;  | 
    ||
168  | 
    long num;  | 
    ||
169  | 
    int c;  | 
    ||
170  | 
    unsigned int h;  | 
    ||
171  | 
    11836707  | 
    bool array;  | 
    |
172  | 
    11836707  | 
    int val;  | 
    |
173  | 
    |||
174  | 
    /* Check to see if this is an array */  | 
    ||
175  | 
    11836707  | 
    n = array_index_calc(n, &array, &val);  | 
    |
176  | 
    11836707  | 
    h = hash(n);  | 
    |
177  | 
    11836707  | 
    c = (unsigned char)n[0];  | 
    |
178  | 
    ✓✓ | 11836707  | 
    	if (!letter(c)) { | 
    
179  | 
    ✗✓ | 1682129  | 
    if (array)  | 
    
180  | 
    			errorf("bad substitution"); | 
    ||
181  | 
    vp = &vtemp;  | 
    ||
182  | 
    1682129  | 
    vp->flag = DEFINED;  | 
    |
183  | 
    1682129  | 
    vp->type = 0;  | 
    |
184  | 
    1682129  | 
    vp->areap = ATEMP;  | 
    |
185  | 
    1682129  | 
    *vp->name = c;  | 
    |
186  | 
    ✓✓ | 1682129  | 
    		if (digit(c)) { | 
    
187  | 
    1488147  | 
    errno = 0;  | 
    |
188  | 
    1488147  | 
    num = strtol(n, NULL, 10);  | 
    |
189  | 
    ✓✗✓✓ | 
    2976294  | 
    if (errno == 0 && num <= l->argc)  | 
    
190  | 
    /* setstr can't fail here */  | 
    ||
191  | 
    1402034  | 
    setstr(vp, l->argv[num], KSH_RETURN_ERROR);  | 
    |
192  | 
    1488147  | 
    vp->flag |= RDONLY;  | 
    |
193  | 
    1488147  | 
    return vp;  | 
    |
194  | 
    }  | 
    ||
195  | 
    193982  | 
    vp->flag |= RDONLY;  | 
    |
196  | 
    ✗✓ | 193982  | 
    if (n[1] != '\0')  | 
    
197  | 
    return vp;  | 
    ||
198  | 
    193982  | 
    vp->flag |= ISSET|INTEGER;  | 
    |
199  | 
    ✓✓✓✓ ✓✓  | 
    193982  | 
    		switch (c) { | 
    
200  | 
    case '$':  | 
    ||
201  | 
    7  | 
    vp->val.i = kshpid;  | 
    |
202  | 
    7  | 
    break;  | 
    |
203  | 
    case '!':  | 
    ||
204  | 
    /* If no job, expand to nothing */  | 
    ||
205  | 
    ✗✓ | 397  | 
    if ((vp->val.i = j_async()) == 0)  | 
    
206  | 
    vp->flag &= ~(ISSET|INTEGER);  | 
    ||
207  | 
    break;  | 
    ||
208  | 
    case '?':  | 
    ||
209  | 
    47131  | 
    vp->val.i = exstat;  | 
    |
210  | 
    47131  | 
    break;  | 
    |
211  | 
    case '#':  | 
    ||
212  | 
    146295  | 
    vp->val.i = l->argc;  | 
    |
213  | 
    146295  | 
    break;  | 
    |
214  | 
    case '-':  | 
    ||
215  | 
    14  | 
    vp->flag &= ~INTEGER;  | 
    |
216  | 
    14  | 
    vp->val.s = getoptions();  | 
    |
217  | 
    14  | 
    break;  | 
    |
218  | 
    default:  | 
    ||
219  | 
    138  | 
    vp->flag &= ~(ISSET|INTEGER);  | 
    |
220  | 
    138  | 
    }  | 
    |
221  | 
    193982  | 
    return vp;  | 
    |
222  | 
    }  | 
    ||
223  | 
    19393642  | 
    	for (l = genv->loc; ; l = l->next) { | 
    |
224  | 
    19393642  | 
    vp = ktsearch(&l->vars, n, h);  | 
    |
225  | 
    ✓✓ | 19393642  | 
    		if (vp != NULL) { | 
    
226  | 
    ✓✓ | 6505399  | 
    if (array)  | 
    
227  | 
    302  | 
    return arraysearch(vp, val);  | 
    |
228  | 
    else  | 
    ||
229  | 
    6505097  | 
    return vp;  | 
    |
230  | 
    }  | 
    ||
231  | 
    ✓✓ | 12888243  | 
    if (l->next == NULL)  | 
    
232  | 
    break;  | 
    ||
233  | 
    }  | 
    ||
234  | 
    3649179  | 
    vp = ktenter(&l->vars, n, h);  | 
    |
235  | 
    ✓✓ | 3649179  | 
    if (array)  | 
    
236  | 
    76  | 
    vp = arraysearch(vp, val);  | 
    |
237  | 
    3649179  | 
    vp->flag |= DEFINED;  | 
    |
238  | 
    ✓✓ | 3649179  | 
    if (special(n))  | 
    
239  | 
    633828  | 
    vp->flag |= SPECIAL;  | 
    |
240  | 
    3649179  | 
    return vp;  | 
    |
241  | 
    11836707  | 
    }  | 
    |
242  | 
    |||
243  | 
    /*  | 
    ||
244  | 
    * Search for local variable, if not found create locally.  | 
    ||
245  | 
    */  | 
    ||
246  | 
    struct tbl *  | 
    ||
247  | 
    local(const char *n, bool copy)  | 
    ||
248  | 
    { | 
    ||
249  | 
    1060096  | 
    struct block *l = genv->loc;  | 
    |
250  | 
    struct tbl *vp;  | 
    ||
251  | 
    unsigned int h;  | 
    ||
252  | 
    1060096  | 
    bool array;  | 
    |
253  | 
    1060096  | 
    int val;  | 
    |
254  | 
    |||
255  | 
    /* Check to see if this is an array */  | 
    ||
256  | 
    1060096  | 
    n = array_index_calc(n, &array, &val);  | 
    |
257  | 
    1060096  | 
    h = hash(n);  | 
    |
258  | 
    ✗✓ | 1060096  | 
    	if (!letter(*n)) { | 
    
259  | 
    vp = &vtemp;  | 
    ||
260  | 
    vp->flag = DEFINED|RDONLY;  | 
    ||
261  | 
    vp->type = 0;  | 
    ||
262  | 
    vp->areap = ATEMP;  | 
    ||
263  | 
    return vp;  | 
    ||
264  | 
    }  | 
    ||
265  | 
    1060096  | 
    vp = ktenter(&l->vars, n, h);  | 
    |
266  | 
    ✓✓✓✗ | 
    1068563  | 
    	if (copy && !(vp->flag & DEFINED)) { | 
    
267  | 
    struct block *ll = l;  | 
    ||
268  | 
    struct tbl *vq = NULL;  | 
    ||
269  | 
    |||
270  | 
    ✓✓✓✓ | 
    58431  | 
    while ((ll = ll->next) && !(vq = ktsearch(&ll->vars, n, h)))  | 
    
271  | 
    ;  | 
    ||
272  | 
    ✓✓ | 8467  | 
    		if (vq) { | 
    
273  | 
    4174  | 
    vp->flag |= vq->flag &  | 
    |
274  | 
    (EXPORT | INTEGER | RDONLY | LJUST | RJUST |  | 
    ||
275  | 
    ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);  | 
    ||
276  | 
    ✗✓ | 4174  | 
    if (vq->flag & INTEGER)  | 
    
277  | 
    vp->type = vq->type;  | 
    ||
278  | 
    4174  | 
    vp->u2.field = vq->u2.field;  | 
    |
279  | 
    4174  | 
    }  | 
    |
280  | 
    8467  | 
    }  | 
    |
281  | 
    ✓✓ | 1060096  | 
    if (array)  | 
    
282  | 
    30  | 
    vp = arraysearch(vp, val);  | 
    |
283  | 
    1060096  | 
    vp->flag |= DEFINED;  | 
    |
284  | 
    ✓✓ | 1060096  | 
    if (special(n))  | 
    
285  | 
    633394  | 
    vp->flag |= SPECIAL;  | 
    |
286  | 
    1060096  | 
    return vp;  | 
    |
287  | 
    1060096  | 
    }  | 
    |
288  | 
    |||
289  | 
    /* get variable string value */  | 
    ||
290  | 
    char *  | 
    ||
291  | 
    str_val(struct tbl *vp)  | 
    ||
292  | 
    { | 
    ||
293  | 
    char *s;  | 
    ||
294  | 
    |||
295  | 
    ✓✓ | 13264282  | 
    if ((vp->flag&SPECIAL))  | 
    
296  | 
    748141  | 
    getspec(vp);  | 
    |
297  | 
    ✓✓ | 6632141  | 
    if (!(vp->flag&ISSET))  | 
    
298  | 
    749984  | 
    s = null; /* special to dollar() */  | 
    |
299  | 
    ✓✓ | 5882157  | 
    else if (!(vp->flag&INTEGER)) /* string source */  | 
    
300  | 
    5450908  | 
    s = vp->val.s + vp->type;  | 
    |
301  | 
    	else {				/* integer source */ | 
    ||
302  | 
    /* worst case number length is when base=2, so use BITS(long) */  | 
    ||
303  | 
    /* minus base # number null */  | 
    ||
304  | 
    431249  | 
    char strbuf[1 + 2 + 1 + BITS(long) + 1];  | 
    |
305  | 
    431249  | 
    const char *digits = (vp->flag & UCASEV_AL) ?  | 
    |
306  | 
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" :  | 
    ||
307  | 
    "0123456789abcdefghijklmnopqrstuvwxyz";  | 
    ||
308  | 
    unsigned long n;  | 
    ||
309  | 
    int base;  | 
    ||
310  | 
    |||
311  | 
    431249  | 
    s = strbuf + sizeof(strbuf);  | 
    |
312  | 
    ✗✓ | 431249  | 
    if (vp->flag & INT_U)  | 
    
313  | 
    n = (unsigned long) vp->val.i;  | 
    ||
314  | 
    else  | 
    ||
315  | 
    431249  | 
    n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;  | 
    |
316  | 
    ✓✓ | 993194  | 
    base = (vp->type == 0) ? 10 : vp->type;  | 
    
317  | 
    ✓✗✗✓ | 
    862498  | 
    if (base < 2 || base > strlen(digits))  | 
    
318  | 
    base = 10;  | 
    ||
319  | 
    |||
320  | 
    431249  | 
    *--s = '\0';  | 
    |
321  | 
    431249  | 
    		do { | 
    |
322  | 
    920963  | 
    *--s = digits[n % base];  | 
    |
323  | 
    920963  | 
    n /= base;  | 
    |
324  | 
    ✓✓ | 920963  | 
    } while (n != 0);  | 
    
325  | 
    ✓✓ | 431249  | 
    		if (base != 10) { | 
    
326  | 
    78  | 
    *--s = '#';  | 
    |
327  | 
    78  | 
    *--s = digits[base % 10];  | 
    |
328  | 
    ✗✓ | 78  | 
    if (base >= 10)  | 
    
329  | 
    *--s = digits[base / 10];  | 
    ||
330  | 
    }  | 
    ||
331  | 
    ✓✗✓✓ | 
    862498  | 
    if (!(vp->flag & INT_U) && vp->val.i < 0)  | 
    
332  | 
    6  | 
    *--s = '-';  | 
    |
333  | 
    ✗✓ | 431249  | 
    if (vp->flag & (RJUST|LJUST)) /* case already dealt with */  | 
    
334  | 
    s = formatstr(vp, s);  | 
    ||
335  | 
    else  | 
    ||
336  | 
    431249  | 
    s = str_save(s, ATEMP);  | 
    |
337  | 
    431249  | 
    }  | 
    |
338  | 
    6632141  | 
    return s;  | 
    |
339  | 
    }  | 
    ||
340  | 
    |||
341  | 
    /* get variable integer value, with error checking */  | 
    ||
342  | 
    long  | 
    ||
343  | 
    intval(struct tbl *vp)  | 
    ||
344  | 
    { | 
    ||
345  | 
    1261264  | 
    long num;  | 
    |
346  | 
    int base;  | 
    ||
347  | 
    |||
348  | 
    630632  | 
    base = getint(vp, &num, false);  | 
    |
349  | 
    ✗✓ | 630632  | 
    if (base == -1)  | 
    
350  | 
    /* XXX check calls - is error here ok by POSIX? */  | 
    ||
351  | 
    		errorf("%s: bad number", str_val(vp)); | 
    ||
352  | 
    1261264  | 
    return num;  | 
    |
353  | 
    630632  | 
    }  | 
    |
354  | 
    |||
355  | 
    /* set variable to string value */  | 
    ||
356  | 
    int  | 
    ||
357  | 
    setstr(struct tbl *vq, const char *s, int error_ok)  | 
    ||
358  | 
    { | 
    ||
359  | 
    const char *fs = NULL;  | 
    ||
360  | 
    16625276  | 
    int no_ro_check = error_ok & KSH_IGNORE_RDONLY;  | 
    |
361  | 
    8312638  | 
    error_ok &= ~KSH_IGNORE_RDONLY;  | 
    |
362  | 
    ✗✓ | 8312638  | 
    	if ((vq->flag & RDONLY) && !no_ro_check) { | 
    
363  | 
    warningf(true, "%s: is read only", vq->name);  | 
    ||
364  | 
    if (!error_ok)  | 
    ||
365  | 
    errorf(NULL);  | 
    ||
366  | 
    return 0;  | 
    ||
367  | 
    }  | 
    ||
368  | 
    ✓✓ | 8312638  | 
    	if (!(vq->flag&INTEGER)) { /* string dest */ | 
    
369  | 
    ✓✓ | 7787535  | 
    		if ((vq->flag&ALLOC)) { | 
    
370  | 
    /* debugging */  | 
    ||
371  | 
    ✓✓✗✓ | 
    4648523  | 
    if (s >= vq->val.s &&  | 
    
372  | 
    1579275  | 
    s <= vq->val.s + strlen(vq->val.s))  | 
    |
373  | 
    internal_errorf(true,  | 
    ||
374  | 
    "setstr: %s=%s: assigning to self",  | 
    ||
375  | 
    vq->name, s);  | 
    ||
376  | 
    3069248  | 
    afree(vq->val.s, vq->areap);  | 
    |
377  | 
    3069248  | 
    }  | 
    |
378  | 
    7787535  | 
    vq->flag &= ~(ISSET|ALLOC);  | 
    |
379  | 
    7787535  | 
    vq->type = 0;  | 
    |
380  | 
    ✓✗✓✓ | 
    15575070  | 
    if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))  | 
    
381  | 
    2904  | 
    fs = s = formatstr(vq, s);  | 
    |
382  | 
    ✓✓ | 7787535  | 
    if ((vq->flag&EXPORT))  | 
    
383  | 
    2660628  | 
    export(vq, s);  | 
    |
384  | 
    		else { | 
    ||
385  | 
    5126907  | 
    vq->val.s = str_save(s, vq->areap);  | 
    |
386  | 
    5126907  | 
    vq->flag |= ALLOC;  | 
    |
387  | 
    }  | 
    ||
388  | 
    	} else {		/* integer dest */ | 
    ||
389  | 
    ✗✓ | 525073  | 
    if (!v_evaluate(vq, s, error_ok, true))  | 
    
390  | 
    return 0;  | 
    ||
391  | 
    }  | 
    ||
392  | 
    8312608  | 
    vq->flag |= ISSET;  | 
    |
393  | 
    ✓✓ | 8312608  | 
    if ((vq->flag&SPECIAL))  | 
    
394  | 
    849961  | 
    setspec(vq);  | 
    |
395  | 
    8312608  | 
    afree((void *)fs, ATEMP);  | 
    |
396  | 
    8312608  | 
    return 1;  | 
    |
397  | 
    8312608  | 
    }  | 
    |
398  | 
    |||
399  | 
    /* set variable to integer */  | 
    ||
400  | 
    void  | 
    ||
401  | 
    setint(struct tbl *vq, long int n)  | 
    ||
402  | 
    { | 
    ||
403  | 
    ✓✓ | 217890  | 
    	if (!(vq->flag&INTEGER)) { | 
    
404  | 
    struct tbl *vp = &vtemp;  | 
    ||
405  | 
    106723  | 
    vp->flag = (ISSET|INTEGER);  | 
    |
406  | 
    106723  | 
    vp->type = 0;  | 
    |
407  | 
    106723  | 
    vp->areap = ATEMP;  | 
    |
408  | 
    106723  | 
    vp->val.i = n;  | 
    |
409  | 
    /* setstr can't fail here */  | 
    ||
410  | 
    106723  | 
    setstr(vq, str_val(vp), KSH_RETURN_ERROR);  | 
    |
411  | 
    106723  | 
    } else  | 
    |
412  | 
    2222  | 
    vq->val.i = n;  | 
    |
413  | 
    108945  | 
    vq->flag |= ISSET;  | 
    |
414  | 
    ✓✓ | 108945  | 
    if ((vq->flag&SPECIAL))  | 
    
415  | 
    917  | 
    setspec(vq);  | 
    |
416  | 
    108945  | 
    }  | 
    |
417  | 
    |||
418  | 
    int  | 
    ||
419  | 
    getint(struct tbl *vp, long int *nump, bool arith)  | 
    ||
420  | 
    { | 
    ||
421  | 
    char *s;  | 
    ||
422  | 
    int c;  | 
    ||
423  | 
    int base, neg;  | 
    ||
424  | 
    int have_base = 0;  | 
    ||
425  | 
    long num;  | 
    ||
426  | 
    |||
427  | 
    ✓✓ | 3013668  | 
    if (vp->flag&SPECIAL)  | 
    
428  | 
    3870  | 
    getspec(vp);  | 
    |
429  | 
    /* XXX is it possible for ISSET to be set and val.s to be 0? */  | 
    ||
430  | 
    ✓✗✓✓ ✗✓  | 
    7283409  | 
    if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))  | 
    
431  | 
    return -1;  | 
    ||
432  | 
    ✓✓ | 3013668  | 
    	if (vp->flag&INTEGER) { | 
    
433  | 
    1757595  | 
    *nump = vp->val.i;  | 
    |
434  | 
    1757595  | 
    return vp->type;  | 
    |
435  | 
    }  | 
    ||
436  | 
    1256073  | 
    s = vp->val.s + vp->type;  | 
    |
437  | 
    1256073  | 
    if (s == NULL) /* redundant given initial test */  | 
    |
438  | 
    s = null;  | 
    ||
439  | 
    base = 10;  | 
    ||
440  | 
    num = 0;  | 
    ||
441  | 
    neg = 0;  | 
    ||
442  | 
    ✓✓✓✓ ✓✓  | 
    2250795  | 
    	if (arith && *s == '0' && *(s+1)) { | 
    
443  | 
    6  | 
    s++;  | 
    |
444  | 
    ✓✗✗✓ | 
    12  | 
    		if (*s == 'x' || *s == 'X') { | 
    
445  | 
    s++;  | 
    ||
446  | 
    base = 16;  | 
    ||
447  | 
    ✗✓ | 6  | 
    		} else if (vp->flag & ZEROFIL) { | 
    
448  | 
    while (*s == '0')  | 
    ||
449  | 
    s++;  | 
    ||
450  | 
    } else  | 
    ||
451  | 
    base = 8;  | 
    ||
452  | 
    have_base++;  | 
    ||
453  | 
    6  | 
    }  | 
    |
454  | 
    ✓✓ | 6755090  | 
    	for (c = (unsigned char)*s++; c ; c = (unsigned char)*s++) { | 
    
455  | 
    ✗✓ | 2121490  | 
    		if (c == '-') { | 
    
456  | 
    neg++;  | 
    ||
457  | 
    ✓✓ | 2121490  | 
    		} else if (c == '#') { | 
    
458  | 
    72  | 
    base = (int) num;  | 
    |
459  | 
    ✓✓ | 72  | 
    if (have_base || base < 2 || base > 36)  | 
    
460  | 
    12  | 
    return -1;  | 
    |
461  | 
    num = 0;  | 
    ||
462  | 
    have_base = 1;  | 
    ||
463  | 
    ✓✗✓✗ | 
    4242896  | 
    		} else if (letnum(c)) { | 
    
464  | 
    ✓✗ | 2121418  | 
    if (isdigit(c))  | 
    
465  | 
    2121418  | 
    c -= '0';  | 
    |
466  | 
    else if (islower(c))  | 
    ||
467  | 
    c -= 'a' - 10; /* todo: assumes ascii */  | 
    ||
468  | 
    else if (isupper(c))  | 
    ||
469  | 
    c -= 'A' - 10; /* todo: assumes ascii */  | 
    ||
470  | 
    else  | 
    ||
471  | 
    c = -1; /* _: force error */  | 
    ||
472  | 
    ✓✗✓✓ | 
    4242836  | 
    if (c < 0 || c >= base)  | 
    
473  | 
    6  | 
    return -1;  | 
    |
474  | 
    2121412  | 
    num = num * base + c;  | 
    |
475  | 
    } else  | 
    ||
476  | 
    return -1;  | 
    ||
477  | 
    }  | 
    ||
478  | 
    ✗✓ | 1256055  | 
    if (neg)  | 
    
479  | 
    num = -num;  | 
    ||
480  | 
    1256055  | 
    *nump = num;  | 
    |
481  | 
    1256055  | 
    return base;  | 
    |
482  | 
    3013668  | 
    }  | 
    |
483  | 
    |||
484  | 
    /* convert variable vq to integer variable, setting its value from vp  | 
    ||
485  | 
    * (vq and vp may be the same)  | 
    ||
486  | 
    */  | 
    ||
487  | 
    struct tbl *  | 
    ||
488  | 
    setint_v(struct tbl *vq, struct tbl *vp, bool arith)  | 
    ||
489  | 
    { | 
    ||
490  | 
    int base;  | 
    ||
491  | 
    2381202  | 
    long num;  | 
    |
492  | 
    |||
493  | 
    ✓✓ | 2381202  | 
    if ((base = getint(vp, &num, arith)) == -1)  | 
    
494  | 
    18  | 
    return NULL;  | 
    |
495  | 
    ✓✓✗✓ | 
    3507237  | 
    	if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) { | 
    
496  | 
    vq->flag &= ~ALLOC;  | 
    ||
497  | 
    afree(vq->val.s, vq->areap);  | 
    ||
498  | 
    }  | 
    ||
499  | 
    2381184  | 
    vq->val.i = num;  | 
    |
500  | 
    ✓✓ | 2381184  | 
    if (vq->type == 0) /* default base */  | 
    
501  | 
    2250896  | 
    vq->type = base;  | 
    |
502  | 
    2381184  | 
    vq->flag |= ISSET|INTEGER;  | 
    |
503  | 
    ✓✓ | 2381184  | 
    if (vq->flag&SPECIAL)  | 
    
504  | 
    420064  | 
    setspec(vq);  | 
    |
505  | 
    2381184  | 
    return vq;  | 
    |
506  | 
    2381202  | 
    }  | 
    |
507  | 
    |||
508  | 
    static char *  | 
    ||
509  | 
    formatstr(struct tbl *vp, const char *s)  | 
    ||
510  | 
    { | 
    ||
511  | 
    int olen, nlen;  | 
    ||
512  | 
    char *p, *q;  | 
    ||
513  | 
    |||
514  | 
    5808  | 
    olen = strlen(s);  | 
    |
515  | 
    |||
516  | 
    ✗✓ | 2904  | 
    	if (vp->flag & (RJUST|LJUST)) { | 
    
517  | 
    if (!vp->u2.field) /* default field width */  | 
    ||
518  | 
    vp->u2.field = olen;  | 
    ||
519  | 
    nlen = vp->u2.field;  | 
    ||
520  | 
    } else  | 
    ||
521  | 
    nlen = olen;  | 
    ||
522  | 
    |||
523  | 
    2904  | 
    p = alloc(nlen + 1, ATEMP);  | 
    |
524  | 
    ✗✓ | 2904  | 
    	if (vp->flag & (RJUST|LJUST)) { | 
    
525  | 
    int slen;  | 
    ||
526  | 
    |||
527  | 
    		if (vp->flag & RJUST) { | 
    ||
528  | 
    const char *q = s + olen;  | 
    ||
529  | 
    /* strip trailing spaces (at&t ksh uses q[-1] == ' ') */  | 
    ||
530  | 
    while (q > s && isspace((unsigned char)q[-1]))  | 
    ||
531  | 
    --q;  | 
    ||
532  | 
    slen = q - s;  | 
    ||
533  | 
    			if (slen > vp->u2.field) { | 
    ||
534  | 
    s += slen - vp->u2.field;  | 
    ||
535  | 
    slen = vp->u2.field;  | 
    ||
536  | 
    }  | 
    ||
537  | 
    shf_snprintf(p, nlen + 1,  | 
    ||
538  | 
    ((vp->flag & ZEROFIL) && digit(*s)) ?  | 
    ||
539  | 
    "%0*s%.*s" : "%*s%.*s",  | 
    ||
540  | 
    vp->u2.field - slen, null, slen, s);  | 
    ||
541  | 
    		} else { | 
    ||
542  | 
    /* strip leading spaces/zeros */  | 
    ||
543  | 
    while (isspace((unsigned char)*s))  | 
    ||
544  | 
    s++;  | 
    ||
545  | 
    if (vp->flag & ZEROFIL)  | 
    ||
546  | 
    while (*s == '0')  | 
    ||
547  | 
    s++;  | 
    ||
548  | 
    shf_snprintf(p, nlen + 1, "%-*.*s",  | 
    ||
549  | 
    vp->u2.field, vp->u2.field, s);  | 
    ||
550  | 
    }  | 
    ||
551  | 
    } else  | 
    ||
552  | 
    2904  | 
    memcpy(p, s, olen + 1);  | 
    |
553  | 
    |||
554  | 
    ✗✓ | 2904  | 
    	if (vp->flag & UCASEV_AL) { | 
    
555  | 
    for (q = p; *q; q++)  | 
    ||
556  | 
    if (islower((unsigned char)*q))  | 
    ||
557  | 
    *q = toupper((unsigned char)*q);  | 
    ||
558  | 
    ✓✗ | 2904  | 
    	} else if (vp->flag & LCASEV) { | 
    
559  | 
    ✓✓ | 73080  | 
    for (q = p; *q; q++)  | 
    
560  | 
    ✗✓ | 33636  | 
    if (isupper((unsigned char)*q))  | 
    
561  | 
    *q = tolower((unsigned char)*q);  | 
    ||
562  | 
    }  | 
    ||
563  | 
    |||
564  | 
    2904  | 
    return p;  | 
    |
565  | 
    }  | 
    ||
566  | 
    |||
567  | 
    /*  | 
    ||
568  | 
    * make vp->val.s be "name=value" for quick exporting.  | 
    ||
569  | 
    */  | 
    ||
570  | 
    static void  | 
    ||
571  | 
    export(struct tbl *vp, const char *val)  | 
    ||
572  | 
    { | 
    ||
573  | 
    char *xp;  | 
    ||
574  | 
    ✓✓ | 8017107  | 
    char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;  | 
    
575  | 
    2669562  | 
    int namelen = strlen(vp->name);  | 
    |
576  | 
    2669562  | 
    int vallen = strlen(val) + 1;  | 
    |
577  | 
    |||
578  | 
    2669562  | 
    vp->flag |= ALLOC;  | 
    |
579  | 
    2669562  | 
    xp = alloc(namelen + 1 + vallen, vp->areap);  | 
    |
580  | 
    2669562  | 
    memcpy(vp->val.s = xp, vp->name, namelen);  | 
    |
581  | 
    2669562  | 
    xp += namelen;  | 
    |
582  | 
    2669562  | 
    *xp++ = '=';  | 
    |
583  | 
    2669562  | 
    vp->type = xp - vp->val.s; /* offset to value */  | 
    |
584  | 
    2669562  | 
    memcpy(xp, val, vallen);  | 
    |
585  | 
    2669562  | 
    afree(op, vp->areap);  | 
    |
586  | 
    2669562  | 
    }  | 
    |
587  | 
    |||
588  | 
    /*  | 
    ||
589  | 
    * lookup variable (according to (set&LOCAL)),  | 
    ||
590  | 
    * set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL,  | 
    ||
591  | 
    * LCASEV, UCASEV_AL), and optionally set its value if an assignment.  | 
    ||
592  | 
    */  | 
    ||
593  | 
    struct tbl *  | 
    ||
594  | 
    typeset(const char *var, int set, int clr, int field, int base)  | 
    ||
595  | 
    { | 
    ||
596  | 
    struct tbl *vp;  | 
    ||
597  | 
    struct tbl *vpbase, *t;  | 
    ||
598  | 
    char *tvar;  | 
    ||
599  | 
    const char *val;  | 
    ||
600  | 
    |||
601  | 
    /* check for valid variable name, search for value */  | 
    ||
602  | 
    12734454  | 
    val = skip_varname(var, false);  | 
    |
603  | 
    ✗✓ | 6367227  | 
    if (val == var)  | 
    
604  | 
    return NULL;  | 
    ||
605  | 
    ✓✓ | 6367227  | 
    	if (*val == '[') { | 
    
606  | 
    int len;  | 
    ||
607  | 
    |||
608  | 
    284  | 
    len = array_ref_len(val);  | 
    |
609  | 
    ✗✓ | 284  | 
    if (len == 0)  | 
    
610  | 
    return NULL;  | 
    ||
611  | 
    /* IMPORT is only used when the shell starts up and is  | 
    ||
612  | 
    * setting up its environment. Allow only simple array  | 
    ||
613  | 
    * references at this time since parameter/command substitution  | 
    ||
614  | 
    * is preformed on the [expression], which would be a major  | 
    ||
615  | 
    * security hole.  | 
    ||
616  | 
    */  | 
    ||
617  | 
    ✗✓ | 284  | 
    		if (set & IMPORT) { | 
    
618  | 
    int i;  | 
    ||
619  | 
    for (i = 1; i < len - 1; i++)  | 
    ||
620  | 
    if (!digit(val[i]))  | 
    ||
621  | 
    return NULL;  | 
    ||
622  | 
    }  | 
    ||
623  | 
    284  | 
    val += len;  | 
    |
624  | 
    ✓✗ | 284  | 
    }  | 
    
625  | 
    ✓✓ | 6367227  | 
    if (*val == '=')  | 
    
626  | 
    5722551  | 
    tvar = str_nsave(var, val++ - var, ATEMP);  | 
    |
627  | 
    	else { | 
    ||
628  | 
    /* Importing from original environment: must have an = */  | 
    ||
629  | 
    ✗✓ | 644676  | 
    if (set & IMPORT)  | 
    
630  | 
    return NULL;  | 
    ||
631  | 
    tvar = (char *) var;  | 
    ||
632  | 
    val = NULL;  | 
    ||
633  | 
    }  | 
    ||
634  | 
    |||
635  | 
    /* Prevent typeset from creating a local PATH/ENV/SHELL */  | 
    ||
636  | 
    ✓✓✓✗ ✗✓  | 
    6369987  | 
    if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||  | 
    
637  | 
    ✓✗ | 2760  | 
    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))  | 
    
638  | 
    		errorf("%s: restricted", tvar); | 
    ||
639  | 
    |||
640  | 
    ✓✓ | 19101681  | 
    vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :  | 
    
641  | 
    5307137  | 
    global(tvar);  | 
    |
642  | 
    6367227  | 
    set &= ~(LOCAL|LOCAL_COPY);  | 
    |
643  | 
    |||
644  | 
    ✓✓ | 19101681  | 
    vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;  | 
    
645  | 
    |||
646  | 
    /* only allow export flag to be set. at&t ksh allows any attribute to  | 
    ||
647  | 
    * be changed, which means it can be truncated or modified  | 
    ||
648  | 
    * (-L/-R/-Z/-i).  | 
    ||
649  | 
    */  | 
    ||
650  | 
    ✓✓✗✗ | 
    6367227  | 
    if ((vpbase->flag&RDONLY) &&  | 
    
651  | 
    ✗✓ | 6  | 
    (val || clr || (set & ~EXPORT)))  | 
    
652  | 
    /* XXX check calls - is error here ok by POSIX? */  | 
    ||
653  | 
    		errorf("%s: is read only", tvar); | 
    ||
654  | 
    ✓✓ | 6367221  | 
    if (val)  | 
    
655  | 
    5722545  | 
    afree(tvar, ATEMP);  | 
    |
656  | 
    |||
657  | 
    /* most calls are with set/clr == 0 */  | 
    ||
658  | 
    ✓✓ | 6367221  | 
    	if (set | clr) { | 
    
659  | 
    int ok = 1;  | 
    ||
660  | 
    /* XXX if x[0] isn't set, there will be problems: need to have  | 
    ||
661  | 
    * one copy of attributes for arrays...  | 
    ||
662  | 
    */  | 
    ||
663  | 
    ✓✓ | 14370748  | 
    		for (t = vpbase; t; t = t->u.array) { | 
    
664  | 
    int fake_assign;  | 
    ||
665  | 
    int error_ok = KSH_RETURN_ERROR;  | 
    ||
666  | 
    char *s = NULL;  | 
    ||
667  | 
    char *free_me = NULL;  | 
    ||
668  | 
    |||
669  | 
    ✓✓✓✓ ✗✓  | 
    8048646  | 
    fake_assign = (t->flag & ISSET) && (!val || t != vp) &&  | 
    
670  | 
    ✓✗ | 536206  | 
    ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||  | 
    
671  | 
    ✓✓✓✗ | 
    536218  | 
    ((t->flag & INTEGER) && (clr & INTEGER)) ||  | 
    
672  | 
    ✓✓ | 1072400  | 
    (!(t->flag & INTEGER) && (set & INTEGER)));  | 
    
673  | 
    ✓✓ | 3592702  | 
    			if (fake_assign) { | 
    
674  | 
    ✗✓ | 104775  | 
    				if (t->flag & INTEGER) { | 
    
675  | 
    s = str_val(t);  | 
    ||
676  | 
    free_me = NULL;  | 
    ||
677  | 
    				} else { | 
    ||
678  | 
    104775  | 
    s = t->val.s + t->type;  | 
    |
679  | 
    ✓✗ | 314325  | 
    free_me = (t->flag & ALLOC) ? t->val.s :  | 
    
680  | 
    NULL;  | 
    ||
681  | 
    }  | 
    ||
682  | 
    104775  | 
    t->flag &= ~ALLOC;  | 
    |
683  | 
    104775  | 
    }  | 
    |
684  | 
    ✓✓✓✓ | 
    7185362  | 
    			if (!(t->flag & INTEGER) && (set & INTEGER)) { | 
    
685  | 
    628848  | 
    t->type = 0;  | 
    |
686  | 
    628848  | 
    t->flag &= ~ALLOC;  | 
    |
687  | 
    628848  | 
    }  | 
    |
688  | 
    ✓✗✓✓ | 
    7185404  | 
    			if (!(t->flag & RDONLY) && (set & RDONLY)) { | 
    
689  | 
    /* allow var to be initialized read-only */  | 
    ||
690  | 
    error_ok |= KSH_IGNORE_RDONLY;  | 
    ||
691  | 
    211572  | 
    }  | 
    |
692  | 
    3592702  | 
    t->flag = (t->flag | set) & ~clr;  | 
    |
693  | 
    /* Don't change base if assignment is to be done,  | 
    ||
694  | 
    * in case assignment fails.  | 
    ||
695  | 
    */  | 
    ||
696  | 
    ✓✓✓✓ ✗✓  | 
    3592816  | 
    if ((set & INTEGER) && base > 0 && (!val || t != vp))  | 
    
697  | 
    18  | 
    t->type = base;  | 
    |
698  | 
    ✗✓ | 3592702  | 
    if (set & (LJUST|RJUST|ZEROFIL))  | 
    
699  | 
    t->u2.field = field;  | 
    ||
700  | 
    ✓✓ | 3592702  | 
    			if (fake_assign) { | 
    
701  | 
    ✗✓ | 104775  | 
    				if (!setstr(t, s, error_ok)) { | 
    
702  | 
    /* Somewhat arbitrary action here:  | 
    ||
703  | 
    * zap contents of variable, but keep  | 
    ||
704  | 
    * the flag settings.  | 
    ||
705  | 
    */  | 
    ||
706  | 
    ok = 0;  | 
    ||
707  | 
    if (t->flag & INTEGER)  | 
    ||
708  | 
    t->flag &= ~ISSET;  | 
    ||
709  | 
    					else { | 
    ||
710  | 
    if (t->flag & ALLOC)  | 
    ||
711  | 
    afree(t->val.s, t->areap);  | 
    ||
712  | 
    t->flag &= ~(ISSET|ALLOC);  | 
    ||
713  | 
    t->type = 0;  | 
    ||
714  | 
    }  | 
    ||
715  | 
    }  | 
    ||
716  | 
    104775  | 
    afree(free_me, t->areap);  | 
    |
717  | 
    104775  | 
    }  | 
    |
718  | 
    }  | 
    ||
719  | 
    ✗✓ | 3592672  | 
    if (!ok)  | 
    
720  | 
    errorf(NULL);  | 
    ||
721  | 
    3592672  | 
    }  | 
    |
722  | 
    |||
723  | 
    ✓✓ | 6367221  | 
    	if (val != NULL) { | 
    
724  | 
    ✓✓ | 5722545  | 
    		if (vp->flag&INTEGER) { | 
    
725  | 
    /* do not zero base before assignment */  | 
    ||
726  | 
    420280  | 
    setstr(vp, val, KSH_UNWIND_ERROR | KSH_IGNORE_RDONLY);  | 
    |
727  | 
    /* Done after assignment to override default */  | 
    ||
728  | 
    ✓✓ | 420280  | 
    if (base > 0)  | 
    
729  | 
    42  | 
    vp->type = base;  | 
    |
730  | 
    } else  | 
    ||
731  | 
    /* setstr can't fail (readonly check already done) */  | 
    ||
732  | 
    5302235  | 
    setstr(vp, val, KSH_RETURN_ERROR | KSH_IGNORE_RDONLY);  | 
    |
733  | 
    }  | 
    ||
734  | 
    |||
735  | 
    /* only x[0] is ever exported, so use vpbase */  | 
    ||
736  | 
    ✓✓✓✓ ✓✓  | 
    12089685  | 
    if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&  | 
    
737  | 
    2861244  | 
    vpbase->type == 0)  | 
    |
738  | 
    ✓✓ | 26289  | 
    export(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);  | 
    
739  | 
    |||
740  | 
    6367191  | 
    return vp;  | 
    |
741  | 
    6367191  | 
    }  | 
    |
742  | 
    |||
743  | 
    /* Unset a variable. array_ref is set if there was an array reference in  | 
    ||
744  | 
    * the name lookup (eg, x[2]).  | 
    ||
745  | 
    */  | 
    ||
746  | 
    void  | 
    ||
747  | 
    unset(struct tbl *vp, int array_ref)  | 
    ||
748  | 
    { | 
    ||
749  | 
    ✓✓ | 7712  | 
    if (vp->flag & ALLOC)  | 
    
750  | 
    183  | 
    afree(vp->val.s, vp->areap);  | 
    |
751  | 
    ✓✓ | 3856  | 
    	if ((vp->flag & ARRAY) && !array_ref) { | 
    
752  | 
    struct tbl *a, *tmp;  | 
    ||
753  | 
    |||
754  | 
    /* Free up entire array */  | 
    ||
755  | 
    ✓✓ | 480  | 
    		for (a = vp->u.array; a; ) { | 
    
756  | 
    tmp = a;  | 
    ||
757  | 
    220  | 
    a = a->u.array;  | 
    |
758  | 
    ✓✗ | 220  | 
    if (tmp->flag & ALLOC)  | 
    
759  | 
    220  | 
    afree(tmp->val.s, tmp->areap);  | 
    |
760  | 
    220  | 
    afree(tmp, tmp->areap);  | 
    |
761  | 
    }  | 
    ||
762  | 
    20  | 
    vp->u.array = NULL;  | 
    |
763  | 
    20  | 
    }  | 
    |
764  | 
    /* If foo[0] is being unset, the remainder of the array is kept... */  | 
    ||
765  | 
    3856  | 
    vp->flag &= SPECIAL | (array_ref ? ARRAY|DEFINED : 0);  | 
    |
766  | 
    ✓✓ | 3856  | 
    if (vp->flag & SPECIAL)  | 
    
767  | 
    15  | 
    unsetspec(vp); /* responsible for `unspecial'ing var */  | 
    |
768  | 
    3856  | 
    }  | 
    |
769  | 
    |||
770  | 
    /* return a pointer to the first char past a legal variable name (returns the  | 
    ||
771  | 
    * argument if there is no legal name, returns * a pointer to the terminating  | 
    ||
772  | 
    * null if whole string is legal).  | 
    ||
773  | 
    */  | 
    ||
774  | 
    char *  | 
    ||
775  | 
    skip_varname(const char *s, int aok)  | 
    ||
776  | 
    { | 
    ||
777  | 
    int alen;  | 
    ||
778  | 
    |||
779  | 
    ✓✗✓✓ | 
    57793194  | 
    	if (s && letter(*s)) { | 
    
780  | 
    ✓✓✓✓ ✓✓  | 
    299597979  | 
    while (*++s && letnum(*s))  | 
    
781  | 
    ;  | 
    ||
782  | 
    ✓✓✗✓ ✗✗  | 
    17582479  | 
    if (aok && *s == '[' && (alen = array_ref_len(s)))  | 
    
783  | 
    s += alen;  | 
    ||
784  | 
    }  | 
    ||
785  | 
    19264398  | 
    return (char *) s;  | 
    |
786  | 
    }  | 
    ||
787  | 
    |||
788  | 
    /* Return a pointer to the first character past any legal variable name. */  | 
    ||
789  | 
    char *  | 
    ||
790  | 
    skip_wdvarname(const char *s,  | 
    ||
791  | 
    int aok) /* skip array de-reference? */  | 
    ||
792  | 
    { | 
    ||
793  | 
    ✓✓✓✓ | 
    15855583  | 
    	if (s[0] == CHAR && letter(s[1])) { | 
    
794  | 
    4899796  | 
    		do { | 
    |
795  | 
    24623714  | 
    s += 2;  | 
    |
796  | 
    ✓✓✓✓ ✓✓  | 
    45524765  | 
    } while (s[0] == CHAR && letnum(s[1]));  | 
    
797  | 
    ✓✓✓✓ ✓✓  | 
    10383422  | 
    		if (aok && s[0] == CHAR && s[1] == '[') { | 
    
798  | 
    /* skip possible array de-reference */  | 
    ||
799  | 
    const char *p = s;  | 
    ||
800  | 
    char c;  | 
    ||
801  | 
    int depth = 0;  | 
    ||
802  | 
    |||
803  | 
    4707  | 
    			while (1) { | 
    |
804  | 
    ✓✗ | 4707  | 
    if (p[0] != CHAR)  | 
    
805  | 
    break;  | 
    ||
806  | 
    4707  | 
    c = p[1];  | 
    |
807  | 
    4707  | 
    p += 2;  | 
    |
808  | 
    ✓✓ | 4707  | 
    if (c == '[')  | 
    
809  | 
    824  | 
    depth++;  | 
    |
810  | 
    ✓✓✓✓ | 
    4707  | 
    				else if (c == ']' && --depth == 0) { | 
    
811  | 
    s = p;  | 
    ||
812  | 
    509  | 
    break;  | 
    |
813  | 
    }  | 
    ||
814  | 
    }  | 
    ||
815  | 
    509  | 
    }  | 
    |
816  | 
    }  | 
    ||
817  | 
    5370217  | 
    return (char *) s;  | 
    |
818  | 
    }  | 
    ||
819  | 
    |||
820  | 
    /* Check if coded string s is a variable name */  | 
    ||
821  | 
    int  | 
    ||
822  | 
    is_wdvarname(const char *s, int aok)  | 
    ||
823  | 
    { | 
    ||
824  | 
    100616  | 
    char *p = skip_wdvarname(s, aok);  | 
    |
825  | 
    |||
826  | 
    ✓✓ | 106968  | 
    return p != s && p[0] == EOS;  | 
    
827  | 
    }  | 
    ||
828  | 
    |||
829  | 
    /* Check if coded string s is a variable assignment */  | 
    ||
830  | 
    int  | 
    ||
831  | 
    is_wdvarassign(const char *s)  | 
    ||
832  | 
    { | 
    ||
833  | 
    10639818  | 
    char *p = skip_wdvarname(s, true);  | 
    |
834  | 
    |||
835  | 
    ✓✓✓✓ | 
    16117596  | 
    return p != s && p[0] == CHAR && p[1] == '=';  | 
    
836  | 
    }  | 
    ||
837  | 
    |||
838  | 
    /*  | 
    ||
839  | 
    * Make the exported environment from the exported names in the dictionary.  | 
    ||
840  | 
    */  | 
    ||
841  | 
    char **  | 
    ||
842  | 
    makenv(void)  | 
    ||
843  | 
    { | 
    ||
844  | 
    struct block *l;  | 
    ||
845  | 
    XPtrV env;  | 
    ||
846  | 
    struct tbl *vp, **vpp;  | 
    ||
847  | 
    int i;  | 
    ||
848  | 
    |||
849  | 
    XPinit(env, 64);  | 
    ||
850  | 
    for (l = genv->loc; l != NULL; l = l->next)  | 
    ||
851  | 
    for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; )  | 
    ||
852  | 
    if ((vp = *vpp++) != NULL &&  | 
    ||
853  | 
    			    (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) { | 
    ||
854  | 
    struct block *l2;  | 
    ||
855  | 
    struct tbl *vp2;  | 
    ||
856  | 
    unsigned int h = hash(vp->name);  | 
    ||
857  | 
    |||
858  | 
    /* unexport any redefined instances */  | 
    ||
859  | 
    				for (l2 = l->next; l2 != NULL; l2 = l2->next) { | 
    ||
860  | 
    vp2 = ktsearch(&l2->vars, vp->name, h);  | 
    ||
861  | 
    if (vp2 != NULL)  | 
    ||
862  | 
    vp2->flag &= ~EXPORT;  | 
    ||
863  | 
    }  | 
    ||
864  | 
    				if ((vp->flag&INTEGER)) { | 
    ||
865  | 
    /* integer to string */  | 
    ||
866  | 
    char *val;  | 
    ||
867  | 
    val = str_val(vp);  | 
    ||
868  | 
    vp->flag &= ~(INTEGER|RDONLY);  | 
    ||
869  | 
    /* setstr can't fail here */  | 
    ||
870  | 
    setstr(vp, val, KSH_RETURN_ERROR);  | 
    ||
871  | 
    }  | 
    ||
872  | 
    XPput(env, vp->val.s);  | 
    ||
873  | 
    }  | 
    ||
874  | 
    XPput(env, NULL);  | 
    ||
875  | 
    return (char **) XPclose(env);  | 
    ||
876  | 
    }  | 
    ||
877  | 
    |||
878  | 
    /*  | 
    ||
879  | 
    * Called after a fork in parent to bump the random number generator.  | 
    ||
880  | 
    * Done to ensure children will not get the same random number sequence  | 
    ||
881  | 
    * if the parent doesn't use $RANDOM.  | 
    ||
882  | 
    */  | 
    ||
883  | 
    void  | 
    ||
884  | 
    change_random(void)  | 
    ||
885  | 
    { | 
    ||
886  | 
    5948724  | 
    rand();  | 
    |
887  | 
    2974362  | 
    }  | 
    |
888  | 
    |||
889  | 
    /*  | 
    ||
890  | 
    * handle special variables with side effects - PATH, SECONDS.  | 
    ||
891  | 
    */  | 
    ||
892  | 
    |||
893  | 
    /* Test if name is a special parameter */  | 
    ||
894  | 
    static int  | 
    ||
895  | 
    special(const char *name)  | 
    ||
896  | 
    { | 
    ||
897  | 
    struct tbl *tp;  | 
    ||
898  | 
    |||
899  | 
    13472798  | 
    tp = ktsearch(&specials, name, hash(name));  | 
    |
900  | 
    ✓✓✓✗ | 
    20061310  | 
    return tp && (tp->flag & ISSET) ? tp->type : V_NONE;  | 
    
901  | 
    }  | 
    ||
902  | 
    |||
903  | 
    /* Make a variable non-special */  | 
    ||
904  | 
    static void  | 
    ||
905  | 
    unspecial(const char *name)  | 
    ||
906  | 
    { | 
    ||
907  | 
    struct tbl *tp;  | 
    ||
908  | 
    |||
909  | 
    24  | 
    tp = ktsearch(&specials, name, hash(name));  | 
    |
910  | 
    ✓✗ | 12  | 
    if (tp)  | 
    
911  | 
    12  | 
    ktdelete(tp);  | 
    |
912  | 
    12  | 
    }  | 
    |
913  | 
    |||
914  | 
    static time_t seconds; /* time SECONDS last set */  | 
    ||
915  | 
    static int user_lineno; /* what user set $LINENO to */  | 
    ||
916  | 
    |||
917  | 
    static void  | 
    ||
918  | 
    getspec(struct tbl *vp)  | 
    ||
919  | 
    { | 
    ||
920  | 
    ✓✓✗✓ ✓✓  | 
    1611085  | 
    	switch (special(vp->name)) { | 
    
921  | 
    case V_SECONDS:  | 
    ||
922  | 
    104799  | 
    vp->flag &= ~SPECIAL;  | 
    |
923  | 
    /* On start up the value of SECONDS is used before seconds  | 
    ||
924  | 
    * has been set - don't do anything in this case  | 
    ||
925  | 
    * (see initcoms[] in main.c).  | 
    ||
926  | 
    */  | 
    ||
927  | 
    ✓✓ | 104799  | 
    if (vp->flag & ISSET)  | 
    
928  | 
    36  | 
    setint(vp, (long)(time(NULL) - seconds)); /* XXX 2038 */  | 
    |
929  | 
    vp->flag |= SPECIAL;  | 
    ||
930  | 
    104799  | 
    break;  | 
    |
931  | 
    case V_RANDOM:  | 
    ||
932  | 
    2000  | 
    vp->flag &= ~SPECIAL;  | 
    |
933  | 
    2000  | 
    setint(vp, (long) (rand() & 0x7fff));  | 
    |
934  | 
    vp->flag |= SPECIAL;  | 
    ||
935  | 
    2000  | 
    break;  | 
    |
936  | 
    #ifdef HISTORY  | 
    ||
937  | 
    case V_HISTSIZE:  | 
    ||
938  | 
    vp->flag &= ~SPECIAL;  | 
    ||
939  | 
    setint(vp, (long) histsize);  | 
    ||
940  | 
    vp->flag |= SPECIAL;  | 
    ||
941  | 
    break;  | 
    ||
942  | 
    #endif /* HISTORY */  | 
    ||
943  | 
    case V_OPTIND:  | 
    ||
944  | 
    186  | 
    vp->flag &= ~SPECIAL;  | 
    |
945  | 
    186  | 
    setint(vp, (long) user_opt.uoptind);  | 
    |
946  | 
    vp->flag |= SPECIAL;  | 
    ||
947  | 
    186  | 
    break;  | 
    |
948  | 
    case V_LINENO:  | 
    ||
949  | 
    78  | 
    vp->flag &= ~SPECIAL;  | 
    |
950  | 
    78  | 
    setint(vp, (long) current_lineno + user_lineno);  | 
    |
951  | 
    vp->flag |= SPECIAL;  | 
    ||
952  | 
    78  | 
    break;  | 
    |
953  | 
    }  | 
    ||
954  | 
    859074  | 
    }  | 
    |
955  | 
    |||
956  | 
    static void  | 
    ||
957  | 
    setspec(struct tbl *vp)  | 
    ||
958  | 
    { | 
    ||
959  | 
    char *s;  | 
    ||
960  | 
    |||
961  | 
    ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✓  | 
    3825252  | 
    	switch (special(vp->name)) { | 
    
962  | 
    case V_PATH:  | 
    ||
963  | 
    209618  | 
    afree(path, APERM);  | 
    |
964  | 
    209618  | 
    path = str_save(str_val(vp), APERM);  | 
    |
965  | 
    209618  | 
    flushcom(1); /* clear tracked aliases */  | 
    |
966  | 
    209618  | 
    break;  | 
    |
967  | 
    case V_IFS:  | 
    ||
968  | 
    113117  | 
    setctypes(s = str_val(vp), C_IFS);  | 
    |
969  | 
    113117  | 
    ifs0 = *s;  | 
    |
970  | 
    113117  | 
    break;  | 
    |
971  | 
    case V_OPTIND:  | 
    ||
972  | 
    209538  | 
    vp->flag &= ~SPECIAL;  | 
    |
973  | 
    209538  | 
    getopts_reset((int) intval(vp));  | 
    |
974  | 
    209538  | 
    vp->flag |= SPECIAL;  | 
    |
975  | 
    209538  | 
    break;  | 
    |
976  | 
    case V_POSIXLY_CORRECT:  | 
    ||
977  | 
    12  | 
    change_flag(FPOSIX, OF_SPECIAL, 1);  | 
    |
978  | 
    12  | 
    break;  | 
    |
979  | 
    case V_TMPDIR:  | 
    ||
980  | 
    127  | 
    afree(tmpdir, APERM);  | 
    |
981  | 
    127  | 
    tmpdir = NULL;  | 
    |
982  | 
    /* Use tmpdir iff it is an absolute path, is writable and  | 
    ||
983  | 
    * searchable and is a directory...  | 
    ||
984  | 
    */  | 
    ||
985  | 
    		{ | 
    ||
986  | 
    127  | 
    struct stat statb;  | 
    |
987  | 
    |||
988  | 
    127  | 
    s = str_val(vp);  | 
    |
989  | 
    ✓✗✓✗ ✓✗  | 
    381  | 
    if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&  | 
    
990  | 
    ✓✗ | 254  | 
    stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))  | 
    
991  | 
    127  | 
    tmpdir = str_save(s, APERM);  | 
    |
992  | 
    127  | 
    }  | 
    |
993  | 
    127  | 
    break;  | 
    |
994  | 
    #ifdef HISTORY  | 
    ||
995  | 
    case V_HISTCONTROL:  | 
    ||
996  | 
    18  | 
    sethistcontrol(str_val(vp));  | 
    |
997  | 
    18  | 
    break;  | 
    |
998  | 
    case V_HISTSIZE:  | 
    ||
999  | 
    12  | 
    vp->flag &= ~SPECIAL;  | 
    |
1000  | 
    12  | 
    sethistsize((int) intval(vp));  | 
    |
1001  | 
    12  | 
    vp->flag |= SPECIAL;  | 
    |
1002  | 
    12  | 
    break;  | 
    |
1003  | 
    case V_HISTFILE:  | 
    ||
1004  | 
    2142  | 
    sethistfile(str_val(vp));  | 
    |
1005  | 
    2142  | 
    break;  | 
    |
1006  | 
    #endif /* HISTORY */  | 
    ||
1007  | 
    #ifdef EDIT  | 
    ||
1008  | 
    case V_VISUAL:  | 
    ||
1009  | 
    1998  | 
    set_editmode(str_val(vp));  | 
    |
1010  | 
    1998  | 
    break;  | 
    |
1011  | 
    case V_EDITOR:  | 
    ||
1012  | 
    ✓✓ | 1974  | 
    		if (!(global("VISUAL")->flag & ISSET)) | 
    
1013  | 
    1320  | 
    set_editmode(str_val(vp));  | 
    |
1014  | 
    break;  | 
    ||
1015  | 
    case V_COLUMNS:  | 
    ||
1016  | 
    		{ | 
    ||
1017  | 
    1834  | 
    long l;  | 
    |
1018  | 
    |||
1019  | 
    ✗✓ | 1834  | 
    			if (getint(vp, &l, false) == -1) { | 
    
1020  | 
    x_cols = MIN_COLS;  | 
    ||
1021  | 
    break;  | 
    ||
1022  | 
    }  | 
    ||
1023  | 
    3668  | 
    if (l <= MIN_COLS || l > INT_MAX)  | 
    |
1024  | 
    x_cols = MIN_COLS;  | 
    ||
1025  | 
    else  | 
    ||
1026  | 
    1834  | 
    x_cols = l;  | 
    |
1027  | 
    3668  | 
    }  | 
    |
1028  | 
    break;  | 
    ||
1029  | 
    #endif /* EDIT */  | 
    ||
1030  | 
    case V_MAIL:  | 
    ||
1031  | 
    104082  | 
    mbset(str_val(vp));  | 
    |
1032  | 
    104082  | 
    break;  | 
    |
1033  | 
    case V_MAILPATH:  | 
    ||
1034  | 
    mpset(str_val(vp));  | 
    ||
1035  | 
    break;  | 
    ||
1036  | 
    case V_MAILCHECK:  | 
    ||
1037  | 
    209526  | 
    vp->flag &= ~SPECIAL;  | 
    |
1038  | 
    209526  | 
    mcset(intval(vp));  | 
    |
1039  | 
    209526  | 
    vp->flag |= SPECIAL;  | 
    |
1040  | 
    209526  | 
    break;  | 
    |
1041  | 
    case V_RANDOM:  | 
    ||
1042  | 
    2018  | 
    vp->flag &= ~SPECIAL;  | 
    |
1043  | 
    2018  | 
    srand_deterministic((unsigned int)intval(vp));  | 
    |
1044  | 
    2018  | 
    vp->flag |= SPECIAL;  | 
    |
1045  | 
    2018  | 
    break;  | 
    |
1046  | 
    case V_SECONDS:  | 
    ||
1047  | 
    209526  | 
    vp->flag &= ~SPECIAL;  | 
    |
1048  | 
    209526  | 
    seconds = time(NULL) - intval(vp); /* XXX 2038 */  | 
    |
1049  | 
    209526  | 
    vp->flag |= SPECIAL;  | 
    |
1050  | 
    209526  | 
    break;  | 
    |
1051  | 
    case V_TMOUT:  | 
    ||
1052  | 
    /* at&t ksh seems to do this (only listen if integer) */  | 
    ||
1053  | 
    ✓✗ | 209526  | 
    if (vp->flag & INTEGER)  | 
    
1054  | 
    ✓✗ | 628578  | 
    ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0;  | 
    
1055  | 
    break;  | 
    ||
1056  | 
    case V_LINENO:  | 
    ||
1057  | 
    12  | 
    vp->flag &= ~SPECIAL;  | 
    |
1058  | 
    /* The -1 is because line numbering starts at 1. */  | 
    ||
1059  | 
    12  | 
    user_lineno = (unsigned int) intval(vp) - current_lineno - 1;  | 
    |
1060  | 
    12  | 
    vp->flag |= SPECIAL;  | 
    |
1061  | 
    12  | 
    break;  | 
    |
1062  | 
    }  | 
    ||
1063  | 
    1275086  | 
    }  | 
    |
1064  | 
    |||
1065  | 
    static void  | 
    ||
1066  | 
    unsetspec(struct tbl *vp)  | 
    ||
1067  | 
    { | 
    ||
1068  | 
    ✗✗✓✗ ✗✗✗✗ ✗✗✓✓  | 
    78  | 
    	switch (special(vp->name)) { | 
    
1069  | 
    case V_PATH:  | 
    ||
1070  | 
    afree(path, APERM);  | 
    ||
1071  | 
    path = str_save(def_path, APERM);  | 
    ||
1072  | 
    flushcom(1); /* clear tracked aliases */  | 
    ||
1073  | 
    break;  | 
    ||
1074  | 
    case V_IFS:  | 
    ||
1075  | 
    		setctypes(" \t\n", C_IFS); | 
    ||
1076  | 
    ifs0 = ' ';  | 
    ||
1077  | 
    break;  | 
    ||
1078  | 
    case V_TMPDIR:  | 
    ||
1079  | 
    /* should not become unspecial */  | 
    ||
1080  | 
    12  | 
    afree(tmpdir, APERM);  | 
    |
1081  | 
    12  | 
    tmpdir = NULL;  | 
    |
1082  | 
    12  | 
    break;  | 
    |
1083  | 
    case V_MAIL:  | 
    ||
1084  | 
    mbset(NULL);  | 
    ||
1085  | 
    break;  | 
    ||
1086  | 
    case V_MAILPATH:  | 
    ||
1087  | 
    mpset(NULL);  | 
    ||
1088  | 
    break;  | 
    ||
1089  | 
    #ifdef HISTORY  | 
    ||
1090  | 
    case V_HISTCONTROL:  | 
    ||
1091  | 
    sethistcontrol(NULL);  | 
    ||
1092  | 
    break;  | 
    ||
1093  | 
    #endif  | 
    ||
1094  | 
    case V_LINENO:  | 
    ||
1095  | 
    case V_MAILCHECK: /* at&t ksh leaves previous value in place */  | 
    ||
1096  | 
    case V_RANDOM:  | 
    ||
1097  | 
    case V_SECONDS:  | 
    ||
1098  | 
    case V_TMOUT: /* at&t ksh leaves previous value in place */  | 
    ||
1099  | 
    12  | 
    unspecial(vp->name);  | 
    |
1100  | 
    12  | 
    break;  | 
    |
1101  | 
    |||
1102  | 
    /* at&t ksh man page says OPTIND, OPTARG and _ lose special meaning,  | 
    ||
1103  | 
    * but OPTARG does not (still set by getopts) and _ is also still  | 
    ||
1104  | 
    * set in various places.  | 
    ||
1105  | 
    * Don't know what at&t does for:  | 
    ||
1106  | 
    * MAIL, MAILPATH, HISTSIZE, HISTFILE,  | 
    ||
1107  | 
    * Unsetting these in at&t ksh does not loose the `specialness':  | 
    ||
1108  | 
    * no effect: IFS, COLUMNS, PATH, TMPDIR,  | 
    ||
1109  | 
    * VISUAL, EDITOR,  | 
    ||
1110  | 
    * pdkshisms: no effect:  | 
    ||
1111  | 
    * POSIXLY_CORRECT (use set +o posix instead)  | 
    ||
1112  | 
    */  | 
    ||
1113  | 
    }  | 
    ||
1114  | 
    27  | 
    }  | 
    |
1115  | 
    |||
1116  | 
    /*  | 
    ||
1117  | 
    * Search for (and possibly create) a table entry starting with  | 
    ||
1118  | 
    * vp, indexed by val.  | 
    ||
1119  | 
    */  | 
    ||
1120  | 
    static struct tbl *  | 
    ||
1121  | 
    arraysearch(struct tbl *vp, int val)  | 
    ||
1122  | 
    { | 
    ||
1123  | 
    struct tbl *prev, *curr, *new;  | 
    ||
1124  | 
    2370  | 
    size_t namelen = strlen(vp->name) + 1;  | 
    |
1125  | 
    |||
1126  | 
    1185  | 
    vp->flag |= ARRAY|DEFINED;  | 
    |
1127  | 
    1185  | 
    vp->index = 0;  | 
    |
1128  | 
    /* The table entry is always [0] */  | 
    ||
1129  | 
    ✓✓ | 1185  | 
    if (val == 0)  | 
    
1130  | 
    222  | 
    return vp;  | 
    |
1131  | 
    prev = vp;  | 
    ||
1132  | 
    963  | 
    curr = vp->u.array;  | 
    |
1133  | 
    ✓✓✓✓ | 
    15765  | 
    	while (curr && curr->index < val) { | 
    
1134  | 
    prev = curr;  | 
    ||
1135  | 
    3182  | 
    curr = curr->u.array;  | 
    |
1136  | 
    }  | 
    ||
1137  | 
    ✓✓✓✓ | 
    1111  | 
    	if (curr && curr->index == val) { | 
    
1138  | 
    ✓✗ | 124  | 
    if (curr->flag&ISSET)  | 
    
1139  | 
    124  | 
    return curr;  | 
    |
1140  | 
    else  | 
    ||
1141  | 
    new = curr;  | 
    ||
1142  | 
    } else  | 
    ||
1143  | 
    1678  | 
    new = alloc(sizeof(struct tbl) + namelen,  | 
    |
1144  | 
    839  | 
    vp->areap);  | 
    |
1145  | 
    839  | 
    strlcpy(new->name, vp->name, namelen);  | 
    |
1146  | 
    839  | 
    new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL);  | 
    |
1147  | 
    839  | 
    new->type = vp->type;  | 
    |
1148  | 
    839  | 
    new->areap = vp->areap;  | 
    |
1149  | 
    839  | 
    new->u2.field = vp->u2.field;  | 
    |
1150  | 
    839  | 
    new->index = val;  | 
    |
1151  | 
    ✓✗ | 839  | 
    	if (curr != new) {		/* not reusing old array entry */ | 
    
1152  | 
    839  | 
    prev->u.array = new;  | 
    |
1153  | 
    839  | 
    new->u.array = curr;  | 
    |
1154  | 
    839  | 
    }  | 
    |
1155  | 
    839  | 
    return new;  | 
    |
1156  | 
    1185  | 
    }  | 
    |
1157  | 
    |||
1158  | 
    /* Return the length of an array reference (eg, [1+2]) - cp is assumed  | 
    ||
1159  | 
    * to point to the open bracket. Returns 0 if there is no matching closing  | 
    ||
1160  | 
    * bracket.  | 
    ||
1161  | 
    */  | 
    ||
1162  | 
    int  | 
    ||
1163  | 
    array_ref_len(const char *cp)  | 
    ||
1164  | 
    { | 
    ||
1165  | 
    const char *s = cp;  | 
    ||
1166  | 
    int c;  | 
    ||
1167  | 
    int depth = 0;  | 
    ||
1168  | 
    |||
1169  | 
    ✓✗✓✓ ✓✓  | 
    20556  | 
    while ((c = *s++) && (c != ']' || --depth))  | 
    
1170  | 
    ✓✓ | 4056  | 
    if (c == '[')  | 
    
1171  | 
    872  | 
    depth++;  | 
    |
1172  | 
    ✗✓ | 692  | 
    if (!c)  | 
    
1173  | 
    return 0;  | 
    ||
1174  | 
    692  | 
    return s - cp;  | 
    |
1175  | 
    692  | 
    }  | 
    |
1176  | 
    |||
1177  | 
    /*  | 
    ||
1178  | 
    * Make a copy of the base of an array name  | 
    ||
1179  | 
    */  | 
    ||
1180  | 
    char *  | 
    ||
1181  | 
    arrayname(const char *str)  | 
    ||
1182  | 
    { | 
    ||
1183  | 
    const char *p;  | 
    ||
1184  | 
    |||
1185  | 
    ✗✓ | 1688  | 
    if ((p = strchr(str, '[')) == 0)  | 
    
1186  | 
    /* Shouldn't happen, but why worry? */  | 
    ||
1187  | 
    return (char *) str;  | 
    ||
1188  | 
    |||
1189  | 
    844  | 
    return str_nsave(str, p - str, ATEMP);  | 
    |
1190  | 
    844  | 
    }  | 
    |
1191  | 
    |||
1192  | 
    /* Set (or overwrite, if !reset) the array variable var to the values in vals.  | 
    ||
1193  | 
    */  | 
    ||
1194  | 
    void  | 
    ||
1195  | 
    set_array(const char *var, int reset, char **vals)  | 
    ||
1196  | 
    { | 
    ||
1197  | 
    struct tbl *vp, *vq;  | 
    ||
1198  | 
    int i;  | 
    ||
1199  | 
    |||
1200  | 
    /* to get local array, use "typeset foo; set -A foo" */  | 
    ||
1201  | 
    316  | 
    vp = global(var);  | 
    |
1202  | 
    |||
1203  | 
    /* Note: at&t ksh allows set -A but not set +A of a read-only var */  | 
    ||
1204  | 
    ✗✓ | 158  | 
    if ((vp->flag&RDONLY))  | 
    
1205  | 
    		errorf("%s: is read only", var); | 
    ||
1206  | 
    /* This code is quite non-optimal */  | 
    ||
1207  | 
    ✓✗ | 158  | 
    if (reset > 0)  | 
    
1208  | 
    /* trash existing values and attributes */  | 
    ||
1209  | 
    158  | 
    unset(vp, 0);  | 
    |
1210  | 
    /* todo: would be nice for assignment to completely succeed or  | 
    ||
1211  | 
    * completely fail. Only really effects integer arrays:  | 
    ||
1212  | 
    * evaluation of some of vals[] may fail...  | 
    ||
1213  | 
    */  | 
    ||
1214  | 
    ✓✓ | 1870  | 
    	for (i = 0; vals[i]; i++) { | 
    
1215  | 
    777  | 
    vq = arraysearch(vp, i);  | 
    |
1216  | 
    /* would be nice to deal with errors here... (see above) */  | 
    ||
1217  | 
    777  | 
    setstr(vq, vals[i], KSH_RETURN_ERROR);  | 
    |
1218  | 
    }  | 
    ||
1219  | 
    158  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |