xref: /openbsd-src/bin/csh/set.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1 /*	$OpenBSD: set.c,v 1.25 2023/03/08 04:43:04 guenther Exp $	*/
2 /*	$NetBSD: set.c,v 1.8 1995/03/21 18:35:52 mycroft Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 
37 #include "csh.h"
38 #include "extern.h"
39 
40 static Char	*getinx(Char *, int *);
41 static void	 asx(Char *, int, Char *);
42 static struct varent
43 		*getvx(Char *, int);
44 static Char	*xset(Char *, Char ***);
45 static Char	*operate(int, Char *, Char *);
46 static struct varent
47 		*madrof(Char *, struct varent *);
48 static void	 unsetv1(struct varent *);
49 static void	 exportpath(Char **);
50 static void	 balance(struct varent *, int, int);
51 
52 
53 /*
54  * C Shell
55  */
56 
57 void
doset(Char ** v,struct command * t)58 doset(Char **v, struct command *t)
59 {
60     Char *p;
61     Char   *vp, op;
62     Char  **vecp;
63     bool    hadsub;
64     int     subscr;
65 
66     v++;
67     p = *v++;
68     if (p == 0) {
69 	prvars();
70 	return;
71     }
72     do {
73 	hadsub = 0;
74 	vp = p;
75 	if (letter(*p))
76 	    for (; alnum(*p); p++)
77 		continue;
78 	if (vp == p || !letter(*vp))
79 	    stderror(ERR_NAME | ERR_VARBEGIN);
80 	if ((p - vp) > MAXVARLEN) {
81 	    stderror(ERR_NAME | ERR_VARTOOLONG);
82 	    return;
83 	}
84 	if (*p == '[') {
85 	    hadsub++;
86 	    p = getinx(p, &subscr);
87 	}
88 	if ((op = *p) != '\0') {
89 	    *p++ = 0;
90 	    if (*p == 0 && *v && **v == '(')
91 		p = *v++;
92 	}
93 	else if (*v && eq(*v, STRequal)) {
94 	    op = '=', v++;
95 	    if (*v)
96 		p = *v++;
97 	}
98 	if (op && op != '=')
99 	    stderror(ERR_NAME | ERR_SYNTAX);
100 	if (eq(p, STRLparen)) {
101 	    Char **e = v;
102 
103 	    if (hadsub)
104 		stderror(ERR_NAME | ERR_SYNTAX);
105 	    for (;;) {
106 		if (!*e)
107 		    stderror(ERR_NAME | ERR_MISSING, ')');
108 		if (**e == ')')
109 		    break;
110 		e++;
111 	    }
112 	    p = *e;
113 	    *e = 0;
114 	    vecp = saveblk(v);
115 	    set1(vp, vecp, &shvhed);
116 	    *e = p;
117 	    v = e + 1;
118 	}
119 	else if (hadsub)
120 	    asx(vp, subscr, Strsave(p));
121 	else
122 	    set(vp, Strsave(p));
123 	if (eq(vp, STRpath)) {
124 	    exportpath(adrof(STRpath)->vec);
125 	    dohash(NULL, NULL);
126 	}
127 	else if (eq(vp, STRhistchars)) {
128 	    Char *pn = value(STRhistchars);
129 
130 	    HIST = *pn++;
131 	    HISTSUB = *pn;
132 	}
133 	else if (eq(vp, STRuser)) {
134 	    Setenv(STRUSER, value(vp));
135 	    Setenv(STRLOGNAME, value(vp));
136 	}
137 	else if (eq(vp, STRwordchars)) {
138 	    word_chars = value(vp);
139 	}
140 	else if (eq(vp, STRterm))
141 	    Setenv(STRTERM, value(vp));
142 	else if (eq(vp, STRhome)) {
143 	    Char *cp;
144 
145 	    cp = Strsave(value(vp));	/* get the old value back */
146 
147 	    /*
148 	     * convert to canonical pathname (possibly resolving symlinks)
149 	     */
150 	    cp = dcanon(cp, cp);
151 
152 	    set(vp, Strsave(cp));	/* have to save the new val */
153 
154 	    /* and now mirror home with HOME */
155 	    Setenv(STRHOME, cp);
156 	    /* fix directory stack for new tilde home */
157 	    dtilde();
158 	    free(cp);
159 	}
160 	else if (eq(vp, STRfilec))
161 	    filec = 1;
162     } while ((p = *v++) != NULL);
163 }
164 
165 static Char *
getinx(Char * cp,int * ip)166 getinx(Char *cp, int *ip)
167 {
168 
169     *ip = 0;
170     *cp++ = 0;
171     while (*cp && Isdigit(*cp))
172 	*ip = *ip * 10 + *cp++ - '0';
173     if (*cp++ != ']')
174 	stderror(ERR_NAME | ERR_SUBSCRIPT);
175     return (cp);
176 }
177 
178 static void
asx(Char * vp,int subscr,Char * p)179 asx(Char *vp, int subscr, Char *p)
180 {
181     struct varent *v = getvx(vp, subscr);
182 
183     free(v->vec[subscr - 1]);
184     v->vec[subscr - 1] = globone(p, G_APPEND);
185 }
186 
187 static struct varent *
getvx(Char * vp,int subscr)188 getvx(Char *vp, int subscr)
189 {
190     struct varent *v = adrof(vp);
191 
192     if (v == 0)
193 	udvar(vp);
194     if (subscr < 1 || subscr > blklen(v->vec))
195 	stderror(ERR_NAME | ERR_RANGE);
196     return (v);
197 }
198 
199 void
dolet(Char ** v,struct command * t)200 dolet(Char **v, struct command *t)
201 {
202     Char *p;
203     Char   *vp, c, op;
204     bool    hadsub;
205     int     subscr;
206 
207     v++;
208     p = *v++;
209     if (p == 0) {
210 	prvars();
211 	return;
212     }
213     do {
214 	hadsub = 0;
215 	vp = p;
216 	if (letter(*p))
217 	    for (; alnum(*p); p++)
218 		continue;
219 	if (vp == p || !letter(*vp))
220 	    stderror(ERR_NAME | ERR_VARBEGIN);
221 	if ((p - vp) > MAXVARLEN)
222 	    stderror(ERR_NAME | ERR_VARTOOLONG);
223 	if (*p == '[') {
224 	    hadsub++;
225 	    p = getinx(p, &subscr);
226 	}
227 	if (*p == 0 && *v)
228 	    p = *v++;
229 	if ((op = *p) != '\0')
230 	    *p++ = 0;
231 	else
232 	    stderror(ERR_NAME | ERR_ASSIGN);
233 
234 	if (*p == '\0' && *v == NULL)
235 	    stderror(ERR_NAME | ERR_ASSIGN);
236 
237 	vp = Strsave(vp);
238 	if (op == '=') {
239 	    c = '=';
240 	    p = xset(p, &v);
241 	}
242 	else {
243 	    c = *p++;
244 	    if (any("+-", c)) {
245 		if (c != op || *p)
246 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
247 		p = Strsave(STR1);
248 	    }
249 	    else {
250 		if (any("<>", op)) {
251 		    if (c != op)
252 			stderror(ERR_NAME | ERR_UNKNOWNOP);
253 		    c = *p++;
254 		    stderror(ERR_NAME | ERR_SYNTAX);
255 		}
256 		if (c != '=')
257 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
258 		p = xset(p, &v);
259 	    }
260 	}
261 	if (op == '=')
262 	    if (hadsub)
263 		asx(vp, subscr, p);
264 	    else
265 		set(vp, p);
266 	else if (hadsub) {
267 	    struct varent *gv = getvx(vp, subscr);
268 
269 	    asx(vp, subscr, operate(op, gv->vec[subscr - 1], p));
270 	}
271 	else
272 	    set(vp, operate(op, value(vp), p));
273 	if (eq(vp, STRpath)) {
274 	    exportpath(adrof(STRpath)->vec);
275 	    dohash(NULL, NULL);
276 	}
277 	free(vp);
278 	if (c != '=')
279 	    free(p);
280     } while ((p = *v++) != NULL);
281 }
282 
283 static Char *
xset(Char * cp,Char *** vp)284 xset(Char *cp, Char ***vp)
285 {
286     Char *dp;
287 
288     if (*cp) {
289 	dp = Strsave(cp);
290 	--(*vp);
291 	free(** vp);
292 	**vp = dp;
293     }
294     return (putn(expr(vp)));
295 }
296 
297 static Char *
operate(int op,Char * vp,Char * p)298 operate(int op, Char *vp, Char *p)
299 {
300     Char    opr[2];
301     Char   *vec[5];
302     Char **v = vec;
303     Char  **vecp = v;
304     int i;
305 
306     if (op != '=') {
307 	if (*vp)
308 	    *v++ = vp;
309 	opr[0] = op;
310 	opr[1] = 0;
311 	*v++ = opr;
312 	if (op == '<' || op == '>')
313 	    *v++ = opr;
314     }
315     *v++ = p;
316     *v++ = 0;
317     i = expr(&vecp);
318     if (*vecp)
319 	stderror(ERR_NAME | ERR_EXPRESSION);
320     return (putn(i));
321 }
322 
323 Char   *
putn(int n)324 putn(int n)
325 {
326     char number[15];
327     int i;
328 
329     i = snprintf(number, sizeof(number), "%d", n);
330     if (i < 0 || i >= sizeof(number))
331 	return (STRNULL);
332     return (SAVE(number));
333 }
334 
335 int
getn(Char * cp)336 getn(Char *cp)
337 {
338     int n;
339     int     sign;
340 
341     sign = 0;
342     if (cp[0] == '+' && cp[1])
343 	cp++;
344     if (*cp == '-') {
345 	sign++;
346 	cp++;
347 	if (!Isdigit(*cp))
348 	    stderror(ERR_NAME | ERR_BADNUM);
349     }
350     n = 0;
351     while (Isdigit(*cp))
352 	n = n * 10 + *cp++ - '0';
353     if (*cp)
354 	stderror(ERR_NAME | ERR_BADNUM);
355     return (sign ? -n : n);
356 }
357 
358 Char   *
value1(Char * var,struct varent * head)359 value1(Char *var, struct varent *head)
360 {
361     struct varent *vp;
362 
363     vp = adrof1(var, head);
364     return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]);
365 }
366 
367 static struct varent *
madrof(Char * pat,struct varent * vp)368 madrof(Char *pat, struct varent *vp)
369 {
370     struct varent *vp1;
371 
372     for (; vp; vp = vp->v_right) {
373 	if (vp->v_left && (vp1 = madrof(pat, vp->v_left)))
374 	    return vp1;
375 	if (Gmatch(vp->v_name, pat))
376 	    return vp;
377     }
378     return vp;
379 }
380 
381 struct varent *
adrof1(Char * name,struct varent * v)382 adrof1(Char *name, struct varent *v)
383 {
384     int cmp;
385 
386     v = v->v_left;
387     while (v && ((cmp = *name - *v->v_name) ||
388 		 (cmp = Strcmp(name, v->v_name))))
389 	if (cmp < 0)
390 	    v = v->v_left;
391 	else
392 	    v = v->v_right;
393     return v;
394 }
395 
396 /*
397  * The caller is responsible for putting value in a safe place
398  */
399 void
set(Char * var,Char * val)400 set(Char *var, Char *val)
401 {
402     Char **vec = xreallocarray(NULL, 2, sizeof(*vec));
403 
404     vec[0] = val;
405     vec[1] = 0;
406     set1(var, vec, &shvhed);
407 }
408 
409 void
set1(Char * var,Char ** vec,struct varent * head)410 set1(Char *var, Char **vec, struct varent *head)
411 {
412     Char **oldv = vec;
413 
414     gflag = 0;
415     tglob(oldv);
416     if (gflag) {
417 	vec = globall(oldv);
418 	if (vec == 0) {
419 	    blkfree(oldv);
420 	    stderror(ERR_NAME | ERR_NOMATCH);
421 	    return;
422 	}
423 	blkfree(oldv);
424 	gargv = 0;
425     }
426     setq(var, vec, head);
427 }
428 
429 
430 void
setq(Char * name,Char ** vec,struct varent * p)431 setq(Char *name, Char **vec, struct varent *p)
432 {
433     struct varent *c;
434     int f;
435 
436     f = 0;			/* tree hangs off the header's left link */
437     while ((c = p->v_link[f]) != NULL) {
438 	if ((f = *name - *c->v_name) == 0 &&
439 	    (f = Strcmp(name, c->v_name)) == 0) {
440 	    blkfree(c->vec);
441 	    goto found;
442 	}
443 	p = c;
444 	f = f > 0;
445     }
446     p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent));
447     c->v_name = Strsave(name);
448     c->v_bal = 0;
449     c->v_left = c->v_right = 0;
450     c->v_parent = p;
451     balance(p, f, 0);
452 found:
453     trim(c->vec = vec);
454 }
455 
456 void
unset(Char ** v,struct command * t)457 unset(Char **v, struct command *t)
458 {
459     unset1(v, &shvhed);
460     if (adrof(STRfilec) == 0)
461 	filec = 0;
462     if (adrof(STRhistchars) == 0) {
463 	HIST = '!';
464 	HISTSUB = '^';
465     }
466     if (adrof(STRwordchars) == 0)
467 	word_chars = STR_WORD_CHARS;
468 }
469 
470 void
unset1(Char * v[],struct varent * head)471 unset1(Char *v[], struct varent *head)
472 {
473     struct varent *vp;
474     int cnt;
475 
476     while (*++v) {
477 	cnt = 0;
478 	while ((vp = madrof(*v, head->v_left)) != NULL)
479 	    unsetv1(vp), cnt++;
480 	if (cnt == 0)
481 	    setname(vis_str(*v));
482     }
483 }
484 
485 void
unsetv(Char * var)486 unsetv(Char *var)
487 {
488     struct varent *vp;
489 
490     if ((vp = adrof1(var, &shvhed)) == 0)
491 	udvar(var);
492     unsetv1(vp);
493 }
494 
495 static void
unsetv1(struct varent * p)496 unsetv1(struct varent *p)
497 {
498     struct varent *c, *pp;
499     int f;
500 
501     /*
502      * Free associated memory first to avoid complications.
503      */
504     blkfree(p->vec);
505     free(p->v_name);
506     /*
507      * If p is missing one child, then we can move the other into where p is.
508      * Otherwise, we find the predecessor of p, which is guaranteed to have no
509      * right child, copy it into p, and move its left child into it.
510      */
511     if (p->v_right == 0)
512 	c = p->v_left;
513     else if (p->v_left == 0)
514 	c = p->v_right;
515     else {
516 	for (c = p->v_left; c->v_right; c = c->v_right)
517 	    continue;
518 	p->v_name = c->v_name;
519 	p->vec = c->vec;
520 	p = c;
521 	c = p->v_left;
522     }
523     /*
524      * Move c into where p is.
525      */
526     pp = p->v_parent;
527     f = pp->v_right == p;
528     if ((pp->v_link[f] = c) != NULL)
529 	c->v_parent = pp;
530     /*
531      * Free the deleted node, and rebalance.
532      */
533     free(p);
534     balance(pp, f, 1);
535 }
536 
537 void
setNS(Char * cp)538 setNS(Char *cp)
539 {
540     set(cp, Strsave(STRNULL));
541 }
542 
543 void
shift(Char ** v,struct command * t)544 shift(Char **v, struct command *t)
545 {
546     struct varent *argv;
547     Char *name;
548 
549     v++;
550     name = *v;
551     if (name == 0)
552 	name = STRargv;
553     else
554 	(void) strip(name);
555     argv = adrof(name);
556     if (argv == 0)
557 	udvar(name);
558     if (argv->vec[0] == 0)
559 	stderror(ERR_NAME | ERR_NOMORE);
560     lshift(argv->vec, 1);
561 }
562 
563 static void
exportpath(Char ** val)564 exportpath(Char **val)
565 {
566     Char    exppath[BUFSIZ];
567 
568     exppath[0] = 0;
569     if (val)
570 	while (*val) {
571 	    if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZ) {
572 		(void) fprintf(csherr,
573 			       "Warning: ridiculously long PATH truncated\n");
574 		break;
575 	    }
576 	    (void) Strlcat(exppath, *val++, sizeof exppath/sizeof(Char));
577 	    if (*val == 0 || eq(*val, STRRparen))
578 		break;
579 	    (void) Strlcat(exppath, STRcolon, sizeof exppath/sizeof(Char));
580 	}
581     Setenv(STRPATH, exppath);
582 }
583 
584 /* macros to do single rotations on node p */
585 #define rright(p) (\
586 	t = (p)->v_left,\
587 	(t)->v_parent = (p)->v_parent,\
588 	((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\
589 	(t->v_right = (p))->v_parent = t,\
590 	(p) = t)
591 #define rleft(p) (\
592 	t = (p)->v_right,\
593 	(t)->v_parent = (p)->v_parent,\
594 	((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\
595 	(t->v_left = (p))->v_parent = t,\
596 	(p) = t)
597 
598 /*
599  * Rebalance a tree, starting at p and up.
600  * F == 0 means we've come from p's left child.
601  * D == 1 means we've just done a delete, otherwise an insert.
602  */
603 static void
balance(struct varent * p,int f,int d)604 balance(struct varent *p, int f, int d)
605 {
606     struct varent *pp;
607     struct varent *t;	/* used by the rotate macros */
608     int ff;
609 
610     /*
611      * Ok, from here on, p is the node we're operating on; pp is its parent; f
612      * is the branch of p from which we have come; ff is the branch of pp which
613      * is p.
614      */
615     for (; (pp = p->v_parent) != NULL; p = pp, f = ff) {
616 	ff = pp->v_right == p;
617 	if (f ^ d) {		/* right heavy */
618 	    switch (p->v_bal) {
619 	    case -1:		/* was left heavy */
620 		p->v_bal = 0;
621 		break;
622 	    case 0:		/* was balanced */
623 		p->v_bal = 1;
624 		break;
625 	    case 1:		/* was already right heavy */
626 		switch (p->v_right->v_bal) {
627 		case 1:	/* single rotate */
628 		    pp->v_link[ff] = rleft(p);
629 		    p->v_left->v_bal = 0;
630 		    p->v_bal = 0;
631 		    break;
632 		case 0:	/* single rotate */
633 		    pp->v_link[ff] = rleft(p);
634 		    p->v_left->v_bal = 1;
635 		    p->v_bal = -1;
636 		    break;
637 		case -1:	/* double rotate */
638 		    (void) rright(p->v_right);
639 		    pp->v_link[ff] = rleft(p);
640 		    p->v_left->v_bal =
641 			p->v_bal < 1 ? 0 : -1;
642 		    p->v_right->v_bal =
643 			p->v_bal > -1 ? 0 : 1;
644 		    p->v_bal = 0;
645 		    break;
646 		}
647 		break;
648 	    }
649 	}
650 	else {			/* left heavy */
651 	    switch (p->v_bal) {
652 	    case 1:		/* was right heavy */
653 		p->v_bal = 0;
654 		break;
655 	    case 0:		/* was balanced */
656 		p->v_bal = -1;
657 		break;
658 	    case -1:		/* was already left heavy */
659 		switch (p->v_left->v_bal) {
660 		case -1:	/* single rotate */
661 		    pp->v_link[ff] = rright(p);
662 		    p->v_right->v_bal = 0;
663 		    p->v_bal = 0;
664 		    break;
665 		case 0:	/* single rotate */
666 		    pp->v_link[ff] = rright(p);
667 		    p->v_right->v_bal = -1;
668 		    p->v_bal = 1;
669 		    break;
670 		case 1:	/* double rotate */
671 		    (void) rleft(p->v_left);
672 		    pp->v_link[ff] = rright(p);
673 		    p->v_left->v_bal =
674 			p->v_bal < 1 ? 0 : -1;
675 		    p->v_right->v_bal =
676 			p->v_bal > -1 ? 0 : 1;
677 		    p->v_bal = 0;
678 		    break;
679 		}
680 		break;
681 	    }
682 	}
683 	/*
684 	 * If from insert, then we terminate when p is balanced. If from
685 	 * delete, then we terminate when p is unbalanced.
686 	 */
687 	if ((p->v_bal == 0) ^ d)
688 	    break;
689     }
690 }
691 
692 void
plist(struct varent * p)693 plist(struct varent *p)
694 {
695     struct varent *c;
696     int len;
697     sigset_t sigset;
698 
699     if (setintr) {
700 	sigemptyset(&sigset);
701 	sigaddset(&sigset, SIGINT);
702 	sigprocmask(SIG_UNBLOCK, &sigset, NULL);
703     }
704 
705     for (;;) {
706 	while (p->v_left)
707 	    p = p->v_left;
708 x:
709 	if (p->v_parent == 0)	/* is it the header? */
710 	    return;
711 	len = blklen(p->vec);
712 	(void) fprintf(cshout, "%s\t", short2str(p->v_name));
713 	if (len != 1)
714 	    (void) fputc('(', cshout);
715 	blkpr(cshout, p->vec);
716 	if (len != 1)
717 	    (void) fputc(')', cshout);
718 	(void) fputc('\n', cshout);
719 	if (p->v_right) {
720 	    p = p->v_right;
721 	    continue;
722 	}
723 	do {
724 	    c = p;
725 	    p = p->v_parent;
726 	} while (p->v_right == c);
727 	goto x;
728     }
729 }
730