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