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