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