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