xref: /illumos-gate/usr/src/cmd/csh/sh.set.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved. The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #include "sh.h"
18 #include "sh.tconst.h"
19 extern int	didchdir;
20 
21 /*
22  * C Shell
23  */
24 
25 void	asx(tchar *, int, tchar *);
26 void	putn1(int);
27 void	set(tchar *, tchar *);
28 void	set1(tchar *, tchar **, struct varent *);
29 void	setq(tchar *, tchar **, struct varent *);
30 void	unset1(tchar *[], struct varent *);
31 void	unsetv1(struct varent *);
32 void	exportpath(tchar **);
33 void	balance(struct varent *, int, int);
34 tchar	*operate(tchar, tchar *, tchar *);
35 tchar	*getinx(tchar *, int *);
36 tchar	*xset(tchar *, tchar ***);
37 struct varent	*getvx(tchar *, int);
38 
39 void
40 doset(tchar **v)
41 {
42 	tchar *p;
43 	tchar *vp, op;
44 	tchar **vecp;
45 	bool hadsub;
46 	int subscr;
47 	tchar *retp;
48 
49 #ifdef TRACE
50 	tprintf("TRACE- doset()\n");
51 #endif
52 	v++;
53 	p = *v++;
54 	if (p == 0) {
55 		prvars();
56 		return;
57 	}
58 	do {
59 		hadsub = 0;
60 		/*
61 		 * check for proper variable syntax
62 		 * must be alphanumeric, start with a letter and
63 		 * be at most 20 characters
64 		 */
65 		for (vp = p; alnum(*p); p++)
66 			continue;
67 		if (vp == p || !letter(*vp))
68 			goto setsyn;
69 		if ((p - vp) > MAX_VAR_LEN)
70 			bferr("Variable name too long");
71 		if (*p == '[') {
72 			hadsub++;
73 			p = getinx(p, &subscr);
74 		}
75 		if (op = *p) {
76 			*p++ = 0;
77 			if (*p == 0 && *v && **v == '(')
78 				p = *v++;
79 		} else if (*v && eq(*v, S_EQ /* "=" */)) {
80 			op = '=', v++;
81 			if (*v)
82 				p = *v++;
83 		}
84 		if (op && op != '=')
85 setsyn:
86 			bferr("Syntax error");
87 		if (eq(p, S_LPAR /* "(" */)) {
88 			tchar **e = v;
89 
90 			if (hadsub)
91 				goto setsyn;
92 			for (;;) {
93 				if (!*e)
94 					bferr("Missing )");
95 				if (**e == ')')
96 					break;
97 				e++;
98 			}
99 			p = *e;
100 			*e = 0;
101 			vecp = saveblk(v);
102 			set1(vp, vecp, &shvhed);
103 			*e = p;
104 			v = e + 1;
105 		} else if (hadsub) {
106 			retp = savestr(p);
107 			asx(vp, subscr, retp);
108 			xfree(retp);
109 			retp = 0;
110 		} else
111 			set(vp, savestr(p));
112 		if (eq(vp, S_path /* "path" */)) {
113 			exportpath(adrof(S_path /* "path" */)->vec);
114 			dohash(xhash);
115 		} else if (eq(vp, S_histchars /* "histchars" */)) {
116 			tchar *p = value(S_histchars /* "histchars" */);
117 			HIST = *p++;
118 			HISTSUB = *p;
119 		} else if (eq(vp, S_user /* "user" */))
120 			local_setenv(S_USER /* "USER" */, value(vp));
121 		else if (eq(vp, S_term /* "term" */))
122 			local_setenv(S_TERM /* "TERM" */, value(vp));
123 		else if (eq(vp, S_home /* "home" */))
124 			local_setenv(S_HOME /* "HOME" */, value(vp));
125 #ifdef FILEC
126 		else if (eq(vp, S_filec /* "filec" */))
127 			filec = 1;
128 		else if (eq(vp, S_cdpath /* "cdpath" */))
129 			dohash(xhash2);
130 #endif
131 	} while (p = *v++);
132 }
133 
134 tchar *
135 getinx(tchar *cp, int *ip)
136 {
137 
138 #ifdef TRACE
139 	tprintf("TRACE- getinx()\n");
140 #endif
141 	*ip = 0;
142 	*cp++ = 0;
143 	while (*cp && digit(*cp))
144 		*ip = *ip * 10 + *cp++ - '0';
145 	if (*cp++ != ']')
146 		bferr("Subscript error");
147 	return (cp);
148 }
149 
150 void
151 asx(tchar *vp, int subscr, tchar *p)
152 {
153 	struct varent *v = getvx(vp, subscr);
154 
155 #ifdef TRACE
156 	tprintf("TRACE- asx()\n");
157 #endif
158 	xfree(v->vec[subscr - 1]);
159 	v->vec[subscr - 1] = globone(p);
160 }
161 
162 struct varent *
163 getvx(tchar *vp, int subscr)
164 {
165 	struct varent *v = adrof(vp);
166 
167 #ifdef TRACE
168 	tprintf("TRACE- getvx()\n");
169 #endif
170 	if (v == 0)
171 		udvar(vp);
172 	if (subscr < 1 || subscr > blklen(v->vec))
173 		bferr("Subscript out of range");
174 	return (v);
175 }
176 
177 tchar plusplus[2] = { '1', 0 };
178 
179 void
180 dolet(tchar **v)
181 {
182 	tchar *p;
183 	tchar *vp, c, op;
184 	bool hadsub;
185 	int subscr;
186 
187 	v++;
188 	p = *v++;
189 	if (p == 0) {
190 		prvars();
191 		return;
192 	}
193 	do {
194 		hadsub = 0;
195 		for (vp = p; alnum(*p); p++)
196 			continue;
197 		if (vp == p || !letter(*vp))
198 			goto letsyn;
199 		if (*p == '[') {
200 			hadsub++;
201 			p = getinx(p, &subscr);
202 		}
203 		if (*p == 0 && *v)
204 			p = *v++;
205 		if (op = *p)
206 			*p++ = 0;
207 		else
208 			goto letsyn;
209 		vp = savestr(vp);
210 		if (op == '=') {
211 			c = '=';
212 			p = xset(p, &v);
213 		} else {
214 			c = *p++;
215 			/* if (any(c, "+-")) { */
216 			if (c == '+' || c == '-') {
217 				if (c != op || *p)
218 					goto letsyn;
219 				p = plusplus;
220 			} else {
221 				/* if (any(op, "<>")) { */
222 				if (op == '<' || op == '>') {
223 					if (c != op)
224 						goto letsyn;
225 					c = *p++;
226 letsyn:
227 					bferr("Syntax error");
228 				}
229 				if (c != '=')
230 					goto letsyn;
231 				p = xset(p, &v);
232 			}
233 		}
234 		if (op == '=')
235 			if (hadsub)
236 				asx(vp, subscr, p);
237 			else
238 				set(vp, p);
239 		else
240 			if (hadsub)
241 #ifndef V6
242 				/* avoid bug in vax CC */
243 				{
244 					struct varent *gv = getvx(vp, subscr);
245 
246 					asx(vp, subscr, operate(op, gv->vec[subscr - 1], p));
247 				}
248 #else
249 				asx(vp, subscr, operate(op, getvx(vp, subscr)->vec[subscr - 1], p));
250 #endif
251 			else
252 				set(vp, operate(op, value(vp), p));
253 		if (eq(vp, S_path /* "path" */)) {
254 			exportpath(adrof(S_path /* "path" */)->vec);
255 			dohash(xhash);
256 		}
257 
258 		if (eq(vp, S_cdpath /* "cdpath" */))
259 			dohash(xhash2);
260 
261 		xfree(vp);
262 		if (c != '=')
263 			xfree(p);
264 	} while (p = *v++);
265 }
266 
267 tchar *
268 xset(tchar *cp, tchar ***vp)
269 {
270 	tchar *dp;
271 
272 #ifdef TRACE
273 	tprintf("TRACE- xset()\n");
274 #endif
275 	if (*cp) {
276 		dp = savestr(cp);
277 		--(*vp);
278 		xfree(**vp);
279 		**vp = dp;
280 	}
281 	return (putn(exp(vp)));
282 }
283 
284 tchar *
285 operate(tchar op, tchar *vp, tchar *p)
286 {
287 	tchar opr[2];
288 	tchar *vec[5];
289 	tchar **v = vec;
290 	tchar **vecp = v;
291 	int i;
292 
293 	if (op != '=') {
294 		if (*vp)
295 			*v++ = vp;
296 		opr[0] = op;
297 		opr[1] = 0;
298 		*v++ = opr;
299 		if (op == '<' || op == '>')
300 			*v++ = opr;
301 	}
302 	*v++ = p;
303 	*v++ = 0;
304 	i = exp(&vecp);
305 	if (*vecp)
306 		bferr("Expression syntax");
307 	return (putn(i));
308 }
309 
310 static tchar *putp;
311 
312 tchar *
313 putn(int n)
314 {
315 	static tchar number[15];
316 
317 #ifdef TRACE
318 	tprintf("TRACE- putn()\n");
319 #endif
320 	putp = number;
321 	if (n < 0) {
322 		n = -n;
323 		*putp++ = '-';
324 	}
325 	if (sizeof (int) == 2 && n == -32768) {
326 		*putp++ = '3';
327 		n = 2768;
328 #ifdef pdp11
329 	}
330 #else
331 	} else if (sizeof (int) == 4 && n == 0x80000000) {
332 		*putp++ = '2';
333 		n = 147483648;
334 	}
335 #endif
336 	putn1(n);
337 	*putp = 0;
338 	return (savestr(number));
339 }
340 
341 void
342 putn1(int n)
343 {
344 #ifdef TRACE
345 	tprintf("TRACE- putn1()\n");
346 #endif
347 	if (n > 9)
348 		putn1(n / 10);
349 	*putp++ = n % 10 + '0';
350 }
351 
352 int
353 getn(tchar *cp)
354 {
355 	int n;
356 	int sign;
357 
358 #ifdef TRACE
359 	tprintf("TRACE- getn()\n");
360 #endif
361 	sign = 0;
362 	if (cp[0] == '+' && cp[1])
363 		cp++;
364 	if (*cp == '-') {
365 		sign++;
366 		cp++;
367 		if (!digit(*cp))
368 			goto badnum;
369 	}
370 	n = 0;
371 	while (digit(*cp))
372 		n = n * 10 + *cp++ - '0';
373 	if (*cp)
374 		goto badnum;
375 	return (sign ? -n : n);
376 badnum:
377 	bferr("Badly formed number");
378 	return (0);
379 }
380 
381 tchar *
382 value1(tchar *var, struct varent *head)
383 {
384 	struct varent *vp;
385 
386 #ifdef TRACE
387 	tprintf("TRACE- value1()\n");
388 #endif
389 	vp = adrof1(var, head);
390 	return (vp == 0 || vp->vec[0] == 0 ? S_ /* "" */ : vp->vec[0]);
391 }
392 
393 struct varent *
394 madrof(tchar *pat, struct varent *vp)
395 {
396 	struct varent *vp1;
397 
398 #ifdef TRACE
399 	tprintf("TRACE- madrof()\n");
400 #endif
401 	for (; vp; vp = vp->v_right) {
402 		if (vp->v_left && (vp1 = madrof(pat, vp->v_left)))
403 			return vp1;
404 		if (Gmatch(vp->v_name, pat))
405 			return vp;
406 	}
407 	return vp;
408 }
409 
410 struct varent *
411 adrof1(tchar *name, struct varent *v)
412 {
413 	int cmp;
414 
415 #ifdef TRACE
416 	tprintf("TRACE- adrof1()\n");
417 #endif
418 	v = v->v_left;
419 	while (v && ((cmp = *name - *v->v_name) ||
420 	    (cmp = strcmp_(name, v->v_name))))
421 		if (cmp < 0)
422 			v = v->v_left;
423 		else
424 			v = v->v_right;
425 	return v;
426 }
427 
428 /*
429  * The caller is responsible for putting value in a safe place
430  */
431 void
432 set(tchar *var, tchar *val)
433 {
434 	tchar **vec =  (tchar **)xalloc(2 * sizeof (tchar **));
435 
436 #ifdef TRACE
437 	tprintf("TRACE- set()\n");
438 #endif
439 	vec[0] = onlyread(val) ? savestr(val) : val;
440 	vec[1] = 0;
441 	set1(var, vec, &shvhed);
442 }
443 
444 void
445 set1(tchar *var, tchar **vec, struct varent *head)
446 {
447 	tchar **oldv = vec;
448 
449 #ifdef TRACE
450 	tprintf("TRACE- set1()\n");
451 #endif
452 	gflag = 0;
453 	/*
454 	 * If setting cwd variable via "set cwd=/tmp/something"
455 	 * then do globbing.  But if we are setting the cwd
456 	 * becuz of a cd, chdir, pushd, popd, do not do globbing.
457 	 */
458 	if ((!(eq(var, S_cwd))) || (eq(var, S_cwd) && (didchdir == 0)))
459 		{
460 		tglob(oldv);
461 		}
462 	if (gflag) {
463 		vec = glob(oldv);
464 		if (vec == 0) {
465 			bferr("No match");
466 			blkfree(oldv);
467 			return;
468 		}
469 		blkfree(oldv);
470 		gargv = 0;
471 	}
472 	setq(var, vec, head);
473 }
474 
475 void
476 setq(tchar *name, tchar **vec, struct varent *p)
477 {
478 	struct varent *c;
479 	int f;
480 
481 #ifdef TRACE
482 	tprintf("TRACE- setq()\n");
483 #endif
484 	f = 0;			/* tree hangs off the header's left link */
485 	while (c = p->v_link[f]) {
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 *)xalloc(sizeof (struct varent));
495 	c->v_name = savestr(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 unset(tchar *v[])
506 {
507 
508 #ifdef TRACE
509 	tprintf("TRACE- unset()\n");
510 #endif
511 	unset1(v, &shvhed);
512 	if (adrof(S_histchars /* "histchars" */) == 0) {
513 		HIST = '!';
514 		HISTSUB = '^';
515 	}
516 #ifdef FILEC
517 	if (adrof(S_filec /* "filec" */) == 0)
518 		filec = 0;
519 #endif
520 }
521 
522 void
523 unset1(tchar *v[], struct varent *head)
524 {
525 	struct varent *vp;
526 	int cnt;
527 
528 #ifdef TRACE
529 	tprintf("TRACE- unset1()\n");
530 #endif
531 	while (*++v) {
532 		cnt = 0;
533 		while (vp = madrof(*v, head->v_left))
534 			unsetv1(vp), cnt++;
535 		if (cnt == 0)
536 			setname(*v);
537 	}
538 }
539 
540 void
541 unsetv(tchar *var)
542 {
543 	struct varent *vp;
544 
545 #ifdef TRACE
546 	tprintf("TRACE- unsetv()\n");
547 #endif
548 	if ((vp = adrof1(var, &shvhed)) == 0)
549 		udvar(var);
550 	unsetv1(vp);
551 }
552 
553 void
554 unsetv1(struct varent *p)
555 {
556 	struct varent *c, *pp;
557 	int f;
558 
559 #ifdef TRACE
560 	tprintf("TRACE- unsetv1()\n");
561 #endif
562 	/*
563 	 * Free associated memory first to avoid complications.
564 	 */
565 	blkfree(p->vec);
566 	xfree(p->v_name);
567 	/*
568 	 * If p is missing one child, then we can move the other
569 	 * into where p is.  Otherwise, we find the predecessor
570 	 * of p, which is guaranteed to have no right child, copy
571 	 * it into p, and move it's left child into it.
572 	 */
573 	if (p->v_right == 0)
574 		c = p->v_left;
575 	else if (p->v_left == 0)
576 		c = p->v_right;
577 	else {
578 		for (c = p->v_left; c->v_right; c = c->v_right)
579 			;
580 		p->v_name = c->v_name;
581 		p->vec = c->vec;
582 		p = c;
583 		c = p->v_left;
584 	}
585 	/*
586 	 * Move c into where p is.
587 	 */
588 	pp = p->v_parent;
589 	f = pp->v_right == p;
590 	if (pp->v_link[f] = c)
591 		c->v_parent = pp;
592 	/*
593 	 * Free the deleted node, and rebalance.
594 	 */
595 	xfree(p);
596 	balance(pp, f, 1);
597 }
598 
599 void
600 setNS(tchar *cp)
601 {
602 #ifdef TRACE
603 	tprintf("TRACE- setNS()\n");
604 #endif
605 
606 	set(cp, S_ /* "" */);
607 }
608 
609 void
610 shift(tchar **v)
611 {
612 	struct varent *argv;
613 	tchar *name;
614 
615 #ifdef TRACE
616 	tprintf("TRACE- shift()\n");
617 #endif
618 	v++;
619 	name = *v;
620 	if (name == 0)
621 		name = S_argv /* "argv" */;
622 	else
623 		(void) strip(name);
624 	argv = adrof(name);
625 	if (argv == 0)
626 		udvar(name);
627 	if (argv->vec[0] == 0)
628 		bferr("No more words");
629 	lshift(argv->vec, 1);
630 }
631 
632 void
633 exportpath(tchar **val)
634 {
635 	tchar exppath[PATHSIZ];
636 
637 #ifdef TRACE
638 	tprintf("TRACE- exportpath()\n");
639 #endif
640 	exppath[0] = 0;
641 	if (val)
642 		while (*val) {
643 			if (strlen_(*val) + strlen_(exppath) + 2 > PATHSIZ) {
644 				printf("Warning: ridiculously long PATH truncated\n");
645 				break;
646 			}
647 			(void) strcat_(exppath, *val++);
648 			if (*val == 0 || eq(*val, S_RPAR /* ")" */))
649 				break;
650 			(void) strcat_(exppath, S_COLON /* ":" */);
651 		}
652 	local_setenv(S_PATH /* "PATH" */, exppath);
653 }
654 
655 	/* macros to do single rotations on node p */
656 #define	rright(p) (\
657 	t = (p)->v_left,\
658 	(t)->v_parent = (p)->v_parent,\
659 	((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\
660 	(t->v_right = (p))->v_parent = t,\
661 	(p) = t)
662 #define	rleft(p) (\
663 	t = (p)->v_right,\
664 	(t)->v_parent = (p)->v_parent,\
665 	((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\
666 	(t->v_left = (p))->v_parent = t,\
667 	(p) = t)
668 
669 /*
670  * Rebalance a tree, starting at p and up.
671  * F == 0 means we've come from p's left child.
672  * D == 1 means we've just done a delete, otherwise an insert.
673  */
674 void
675 balance(struct varent *p, int f, int d)
676 {
677 	struct varent *pp;
678 	struct varent *t;		/* used by the rotate macros */
679 	int ff;
680 
681 #ifdef TRACE
682 	tprintf("TRACE- balance()\n");
683 #endif
684 	/*
685 	 * Ok, from here on, p is the node we're operating on;
686 	 * pp is it's parent; f is the branch of p from which we have come;
687 	 * ff is the branch of pp which is p.
688 	 */
689 	for (; pp = p->v_parent; p = pp, f = ff) {
690 		ff = pp->v_right == p;
691 		if (f ^ d) {		/* right heavy */
692 			switch (p->v_bal) {
693 			case -1:		/* was left heavy */
694 				p->v_bal = 0;
695 				break;
696 			case 0:			/* was balanced */
697 				p->v_bal = 1;
698 				break;
699 			case 1:			/* was already right heavy */
700 				switch (p->v_right->v_bal) {
701 				case 1:			/* sigle rotate */
702 					pp->v_link[ff] = rleft(p);
703 					p->v_left->v_bal = 0;
704 					p->v_bal = 0;
705 					break;
706 				case 0:			/* single rotate */
707 					pp->v_link[ff] = rleft(p);
708 					p->v_left->v_bal = 1;
709 					p->v_bal = -1;
710 					break;
711 				case -1:		/* double rotate */
712 					rright(p->v_right);
713 					pp->v_link[ff] = rleft(p);
714 					p->v_left->v_bal =
715 						p->v_bal < 1 ? 0 : -1;
716 					p->v_right->v_bal =
717 						p->v_bal > -1 ? 0 : 1;
718 					p->v_bal = 0;
719 					break;
720 				}
721 				break;
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 					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.
758 		 * If from 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 	int len;
770 
771 #ifdef TRACE
772 	tprintf("TRACE- plist()\n");
773 #endif
774 	if (setintr)
775 		(void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT));
776 	for (;;) {
777 		while (p->v_left)
778 			p = p->v_left;
779 	x:
780 		if (p->v_parent == 0)		/* is it the header? */
781 			return;
782 		len = blklen(p->vec);
783 		printf("%t", p->v_name);
784 		Putchar('\t');
785 		if (len != 1)
786 			Putchar('(');
787 		blkpr(p->vec);
788 		if (len != 1)
789 			Putchar(')');
790 		Putchar('\n');
791 		if (p->v_right) {
792 			p = p->v_right;
793 			continue;
794 		}
795 		do {
796 			c = p;
797 			p = p->v_parent;
798 		} while (p->v_right == c);
799 		goto x;
800 	}
801 }
802