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