xref: /dragonfly/contrib/tcsh-6/sh.set.c (revision d8082429)
1 /*
2  * sh.set.c: Setting and Clearing of variables
3  */
4 /*-
5  * Copyright (c) 1980, 1991 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 #include "sh.h"
33 #include "ed.h"
34 #include "tw.h"
35 
36 #ifdef HAVE_NL_LANGINFO
37 #include <langinfo.h>
38 #endif
39 
40 extern int GotTermCaps;
41 int numeof = 0;
42 
43 static	void		 update_vars	(Char *);
44 static	Char		*getinx		(Char *, int *);
45 static	void		 asx		(Char *, int, Char *);
46 static	struct varent 	*getvx		(Char *, int);
47 static	Char		*xset		(Char *, Char ***);
48 static	Char		*operate	(int, Char *, Char *);
49 static	void	 	 putn1		(tcsh_number_t);
50 static	struct varent	*madrof		(Char *, struct varent *);
51 static	void		 unsetv1	(struct varent *);
52 static	void		 balance	(struct varent *, int, int);
53 static	int		 set_noclobber  (Char **);
54 
55 /*
56  * C Shell
57  */
58 
59 static void
60 update_vars(Char *vp)
61 {
62     if (eq(vp, STRpath)) {
63 	struct varent *p = adrof(STRpath);
64 	if (p == NULL)
65 	    stderror(ERR_NAME | ERR_UNDVAR);
66 	else {
67 	    exportpath(p->vec);
68 	    dohash(NULL, NULL);
69 	}
70     }
71     else if (eq(vp, STRnoclobber)) {
72 	struct varent *p = adrof(STRnoclobber);
73 	if (p == NULL)
74 	    stderror(ERR_NAME | ERR_UNDVAR);
75 	else
76 	    no_clobber = set_noclobber(p->vec);
77     }
78     else if (eq(vp, STRhistchars)) {
79 	Char *pn = varval(vp);
80 
81 	HIST = *pn++;
82 	if (HIST)
83 	    HISTSUB = *pn;
84 	else
85 	    HISTSUB = HIST;
86     }
87     else if (eq(vp, STRpromptchars)) {
88 	Char *pn = varval(vp);
89 
90 	PRCH = *pn++;
91 	if (PRCH)
92 	    PRCHROOT = *pn;
93 	else
94 	    PRCHROOT = PRCH;
95     }
96     else if (eq(vp, STRhistlit)) {
97 	HistLit = 1;
98     }
99     else if (eq(vp, STRuser)) {
100 	tsetenv(STRKUSER, varval(vp));
101 	tsetenv(STRLOGNAME, varval(vp));
102     }
103     else if (eq(vp, STRgroup)) {
104 	tsetenv(STRKGROUP, varval(vp));
105     }
106     else if (eq(vp, STRwordchars)) {
107 	word_chars = varval(vp);
108     }
109     else if (eq(vp, STRloginsh)) {
110 	loginsh = 1;
111     }
112     else if (eq(vp, STRanyerror)) {
113 	anyerror = 1;
114     }
115     else if (eq(vp, STRsymlinks)) {
116 	Char *pn = varval(vp);
117 
118 	if (eq(pn, STRignore))
119 	    symlinks = SYM_IGNORE;
120 	else if (eq(pn, STRexpand))
121 	    symlinks = SYM_EXPAND;
122 	else if (eq(pn, STRchase))
123 	    symlinks = SYM_CHASE;
124 	else
125 	    symlinks = 0;
126     }
127     else if (eq(vp, STRterm)) {
128 	Char *cp = varval(vp);
129 	tsetenv(STRKTERM, cp);
130 #ifdef DOESNT_WORK_RIGHT
131 	cp = getenv("TERMCAP");
132 	if (cp && (*cp != '/'))	/* if TERMCAP and not a path */
133 	    Unsetenv(STRTERMCAP);
134 #endif /* DOESNT_WORK_RIGHT */
135 	GotTermCaps = 0;
136 	if (noediting && Strcmp(cp, STRnetwork) != 0 &&
137 	    Strcmp(cp, STRunknown) != 0 && Strcmp(cp, STRdumb) != 0) {
138 	    editing = 1;
139 	    noediting = 0;
140 	    setNS(STRedit);
141 	}
142 	ed_Init();		/* reset the editor */
143     }
144     else if (eq(vp, STRhome)) {
145 	Char *cp, *canon;
146 
147 	cp = Strsave(varval(vp));	/* get the old value back */
148 	cleanup_push(cp, xfree);
149 
150 	/*
151 	 * convert to cononical pathname (possibly resolving symlinks)
152 	 */
153 	canon = dcanon(cp, cp);
154 	cleanup_ignore(cp);
155 	cleanup_until(cp);
156 	cleanup_push(canon, xfree);
157 
158 	setcopy(vp, canon, VAR_READWRITE);	/* have to save the new val */
159 
160 	/* and now mirror home with HOME */
161 	tsetenv(STRKHOME, canon);
162 	/* fix directory stack for new tilde home */
163 	dtilde();
164 	cleanup_until(canon);
165     }
166     else if (eq(vp, STRedit)) {
167 	editing = 1;
168 	noediting = 0;
169 	/* PWP: add more stuff in here later */
170     }
171     else if (eq(vp, STRvimode)) {
172 	VImode = 1;
173 	update_wordchars();
174     }
175     else if (eq(vp, STRshlvl)) {
176 	tsetenv(STRKSHLVL, varval(vp));
177     }
178     else if (eq(vp, STRignoreeof)) {
179 	Char *cp;
180 	numeof = 0;
181     	for ((cp = varval(STRignoreeof)); cp && *cp; cp++) {
182 	    if (!Isdigit(*cp)) {
183 		numeof = 0;
184 		break;
185 	    }
186 	    numeof = numeof * 10 + *cp - '0';
187 	}
188 	if (numeof <= 0) numeof = 26;	/* Sanity check */
189     }
190     else if (eq(vp, STRbackslash_quote)) {
191 	bslash_quote = 1;
192     }
193     else if (eq(vp, STRcompat_expr)) {
194 	compat_expr = 1;
195     }
196     else if (eq(vp, STRdirstack)) {
197 	dsetstack();
198     }
199     else if (eq(vp, STRrecognize_only_executables)) {
200 	tw_cmd_free();
201     }
202     else if (eq(vp, STRkillring)) {
203 	SetKillRing((int)getn(varval(vp)));
204     }
205     else if (eq(vp, STRhistory)) {
206 	sethistory((int)getn(varval(vp)));
207     }
208 #ifndef HAVENOUTMP
209     else if (eq(vp, STRwatch)) {
210 	resetwatch();
211     }
212 #endif /* HAVENOUTMP */
213     else if (eq(vp, STRimplicitcd)) {
214 	implicit_cd = ((eq(varval(vp), STRverbose)) ? 2 : 1);
215     }
216     else if (eq(vp, STRcdtohome)) {
217 	cdtohome = 1;
218     }
219 #ifdef COLOR_LS_F
220     else if (eq(vp, STRcolor)) {
221 	set_color_context();
222     }
223 #endif /* COLOR_LS_F */
224 #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE)
225     else if(eq(vp, CHECK_MBYTEVAR) || eq(vp, STRnokanji)) {
226 	update_dspmbyte_vars();
227     }
228 #endif
229 #ifdef NLS_CATALOGS
230     else if (eq(vp, STRcatalog)) {
231 	nlsclose();
232 	nlsinit();
233     }
234 #if defined(FILEC) && defined(TIOCSTI)
235     else if (eq(vp, STRfilec))
236 	filec = 1;
237 #endif
238 #endif /* NLS_CATALOGS */
239 }
240 
241 
242 /*ARGSUSED*/
243 void
244 doset(Char **v, struct command *c)
245 {
246     Char *p;
247     Char   *vp;
248     Char  **vecp;
249     int    hadsub;
250     int     subscr;
251     int	    flags = VAR_READWRITE;
252     int    first_match = 0;
253     int    last_match = 0;
254     int    changed = 0;
255 
256     USE(c);
257     v++;
258     do {
259 	changed = 0;
260 	/*
261 	 * Readonly addition From: Tim P. Starrin <noid@cyborg.larc.nasa.gov>
262 	 */
263 	if (*v && eq(*v, STRmr)) {
264 	    flags = VAR_READONLY;
265 	    v++;
266 	    changed = 1;
267 	}
268 	if (*v && eq(*v, STRmf) && !last_match) {
269 	    first_match = 1;
270 	    v++;
271 	    changed = 1;
272 	}
273 	if (*v && eq(*v, STRml) && !first_match) {
274 	    last_match = 1;
275 	    v++;
276 	    changed = 1;
277 	}
278     } while(changed);
279     p = *v++;
280     if (p == 0) {
281 	plist(&shvhed, flags);
282 	return;
283     }
284     do {
285 	hadsub = 0;
286 	vp = p;
287 	if (!letter(*p))
288 	    stderror(ERR_NAME | ERR_VARBEGIN);
289 	do {
290 	    p++;
291 	} while (alnum(*p));
292 	if (*p == '[') {
293 	    hadsub++;
294 	    p = getinx(p, &subscr);
295 	}
296 	if (*p != '\0' && *p != '=')
297 	    stderror(ERR_NAME | ERR_VARALNUM);
298 	if (*p == '=') {
299 	    *p++ = '\0';
300 	    if (*p == '\0' && *v != NULL && **v == '(')
301 		p = *v++;
302 	}
303 	else if (*v && eq(*v, STRequal)) {
304 	    if (*++v != NULL)
305 		p = *v++;
306 	}
307 	if (eq(p, STRLparen)) {
308 	    Char **e = v;
309 
310 	    if (hadsub)
311 		stderror(ERR_NAME | ERR_SYNTAX);
312 	    for (;;) {
313 		if (!*e)
314 		    stderror(ERR_NAME | ERR_MISSING, ')');
315 		if (**e == ')')
316 		    break;
317 		e++;
318 	    }
319 	    p = *e;
320 	    *e = 0;
321 	    vecp = saveblk(v);
322 	    if (first_match)
323 	       flags |= VAR_FIRST;
324 	    else if (last_match)
325 	       flags |= VAR_LAST;
326 
327 	    set1(vp, vecp, &shvhed, flags);
328 	    *e = p;
329 	    v = e + 1;
330 	}
331 	else if (hadsub) {
332 	    Char *copy;
333 
334 	    copy = Strsave(p);
335 	    cleanup_push(copy, xfree);
336 	    asx(vp, subscr, copy);
337 	    cleanup_ignore(copy);
338 	    cleanup_until(copy);
339 	}
340 	else
341 	    setv(vp, Strsave(p), flags);
342 	update_vars(vp);
343     } while ((p = *v++) != NULL);
344 }
345 
346 static Char *
347 getinx(Char *cp, int *ip)
348 {
349     *ip = 0;
350     *cp++ = 0;
351     while (*cp && Isdigit(*cp))
352 	*ip = *ip * 10 + *cp++ - '0';
353     if (*cp++ != ']')
354 	stderror(ERR_NAME | ERR_SUBSCRIPT);
355     return (cp);
356 }
357 
358 static void
359 asx(Char *vp, int subscr, Char *p)
360 {
361     struct varent *v = getvx(vp, subscr);
362     Char *prev;
363 
364     if (v->v_flags & VAR_READONLY)
365 	stderror(ERR_READONLY|ERR_NAME, v->v_name);
366     prev = v->vec[subscr - 1];
367     cleanup_push(prev, xfree);
368     v->vec[subscr - 1] = globone(p, G_APPEND);
369     cleanup_until(prev);
370 }
371 
372 static struct varent *
373 getvx(Char *vp, int subscr)
374 {
375     struct varent *v = adrof(vp);
376 
377     if (v == 0)
378 	udvar(vp);
379     if (subscr < 1 || subscr > blklen(v->vec))
380 	stderror(ERR_NAME | ERR_RANGE);
381     return (v);
382 }
383 
384 /*ARGSUSED*/
385 void
386 dolet(Char **v, struct command *dummy)
387 {
388     Char *p;
389     Char   *vp, c, op;
390     int    hadsub;
391     int     subscr;
392 
393     USE(dummy);
394     v++;
395     p = *v++;
396     if (p == 0) {
397 	prvars();
398 	return;
399     }
400     do {
401 	hadsub = 0;
402 	vp = p;
403 	if (letter(*p))
404 	    for (; alnum(*p); p++)
405 		continue;
406 	if (vp == p || !letter(*vp))
407 	    stderror(ERR_NAME | ERR_VARBEGIN);
408 	if (*p == '[') {
409 	    hadsub++;
410 	    p = getinx(p, &subscr);
411 	}
412 	if (*p == 0 && *v)
413 	    p = *v++;
414 	if ((op = *p) != 0)
415 	    *p++ = 0;
416 	else
417 	    stderror(ERR_NAME | ERR_ASSIGN);
418 
419 	/*
420 	 * if there is no expression after the '=' then print a "Syntax Error"
421 	 * message - strike
422 	 */
423 	if (*p == '\0' && *v == NULL)
424 	    stderror(ERR_NAME | ERR_ASSIGN);
425 
426 	vp = Strsave(vp);
427 	cleanup_push(vp, xfree);
428 	if (op == '=') {
429 	    c = '=';
430 	    p = xset(p, &v);
431 	}
432 	else {
433 	    c = *p++;
434 	    if (any("+-", c)) {
435 		if (c != op || *p)
436 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
437 		p = Strsave(STR1);
438 	    }
439 	    else {
440 		if (any("<>", op)) {
441 		    if (c != op)
442 			stderror(ERR_NAME | ERR_UNKNOWNOP);
443 		    stderror(ERR_NAME | ERR_SYNTAX);
444 		}
445 		if (c != '=')
446 		    stderror(ERR_NAME | ERR_UNKNOWNOP);
447 		p = xset(p, &v);
448 	    }
449 	}
450 	cleanup_push(p, xfree);
451 	if (op == '=') {
452 	    if (hadsub)
453 		asx(vp, subscr, p);
454 	    else
455 		setv(vp, p, VAR_READWRITE);
456 	    cleanup_ignore(p);
457 	}
458 	else if (hadsub) {
459 	    struct varent *gv = getvx(vp, subscr);
460 	    Char *val;
461 
462 	    val = operate(op, gv->vec[subscr - 1], p);
463 	    cleanup_push(val, xfree);
464 	    asx(vp, subscr, val);
465 	    cleanup_ignore(val);
466 	    cleanup_until(val);
467 	}
468 	else {
469 	    Char *val;
470 
471 	    val = operate(op, varval(vp), p);
472 	    cleanup_push(val, xfree);
473 	    setv(vp, val, VAR_READWRITE);
474 	    cleanup_ignore(val);
475 	    cleanup_until(val);
476 	}
477 	update_vars(vp);
478 	cleanup_until(vp);
479     } while ((p = *v++) != NULL);
480 }
481 
482 static Char *
483 xset(Char *cp, Char ***vp)
484 {
485     Char *dp;
486 
487     if (*cp) {
488 	dp = Strsave(cp);
489 	--(*vp);
490 	xfree(** vp);
491 	**vp = dp;
492     }
493     return (putn(expr(vp)));
494 }
495 
496 static Char *
497 operate(int op, Char *vp, Char *p)
498 {
499     Char    opr[2];
500     Char   *vec[5];
501     Char **v = vec;
502     Char  **vecp = v;
503     tcsh_number_t i;
504 
505     if (op != '=') {
506 	if (*vp)
507 	    *v++ = vp;
508 	opr[0] = op;
509 	opr[1] = 0;
510 	*v++ = opr;
511 	if (op == '<' || op == '>')
512 	    *v++ = opr;
513     }
514     *v++ = p;
515     *v++ = 0;
516     i = expr(&vecp);
517     if (*vecp)
518 	stderror(ERR_NAME | ERR_EXPRESSION);
519     return (putn(i));
520 }
521 
522 static Char *putp;
523 
524 Char *
525 putn(tcsh_number_t n)
526 {
527     Char nbuf[1024]; /* Enough even for octal */
528 
529     putp = nbuf;
530     if (n < 0) {
531 	n = -n;
532 	*putp++ = '-';
533     }
534     putn1(n);
535     *putp = 0;
536     return (Strsave(nbuf));
537 }
538 
539 static void
540 putn1(tcsh_number_t n)
541 {
542     if (n > 9)
543 	putn1(n / 10);
544     *putp++ = (Char)(n % 10 + '0');
545 }
546 
547 tcsh_number_t
548 getn(const Char *cp)
549 {
550     tcsh_number_t n;
551     int     sign;
552     int base;
553 
554     if (!cp)			/* PWP: extra error checking */
555 	stderror(ERR_NAME | ERR_BADNUM);
556 
557     sign = 0;
558     if (cp[0] == '+' && cp[1])
559 	cp++;
560     if (*cp == '-') {
561 	sign++;
562 	cp++;
563 	if (!Isdigit(*cp))
564 	    stderror(ERR_NAME | ERR_BADNUM);
565     }
566 
567     if (cp[0] == '0' && cp[1] && is_set(STRparseoctal))
568 	base = 8;
569     else
570 	base = 10;
571 
572     n = 0;
573     while (Isdigit(*cp))
574     {
575 	if (base == 8 && *cp >= '8')
576 	    stderror(ERR_NAME | ERR_BADNUM);
577 	n = n * base + *cp++ - '0';
578     }
579     if (*cp)
580 	stderror(ERR_NAME | ERR_BADNUM);
581     return (sign ? -n : n);
582 }
583 
584 Char   *
585 value1(Char *var, struct varent *head)
586 {
587     struct varent *vp;
588 
589     if (!var || !head)		/* PWP: extra error checking */
590 	return (STRNULL);
591 
592     vp = adrof1(var, head);
593     return ((vp == NULL || vp->vec == NULL || vp->vec[0] == NULL) ?
594 	STRNULL : vp->vec[0]);
595 }
596 
597 static struct varent *
598 madrof(Char *pat, struct varent *vp)
599 {
600     struct varent *vp1;
601 
602     for (vp = vp->v_left; vp; vp = vp->v_right) {
603 	if (vp->v_left && (vp1 = madrof(pat, vp)) != NULL)
604 	    return vp1;
605 	if (Gmatch(vp->v_name, pat))
606 	    return vp;
607     }
608     return vp;
609 }
610 
611 struct varent *
612 adrof1(const Char *name, struct varent *v)
613 {
614     int cmp;
615 
616     v = v->v_left;
617     while (v && ((cmp = *name - *v->v_name) != 0 ||
618 		 (cmp = Strcmp(name, v->v_name)) != 0))
619 	if (cmp < 0)
620 	    v = v->v_left;
621 	else
622 	    v = v->v_right;
623     return v;
624 }
625 
626 void
627 setcopy(const Char *var, const Char *val, int flags)
628 {
629     Char *copy;
630 
631     copy = Strsave(val);
632     cleanup_push(copy, xfree);
633     setv(var, copy, flags);
634     cleanup_ignore(copy);
635     cleanup_until(copy);
636 }
637 
638 /*
639  * The caller is responsible for putting value in a safe place
640  */
641 void
642 setv(const Char *var, Char *val, int flags)
643 {
644     Char **vec = xmalloc(2 * sizeof(Char **));
645 
646     vec[0] = val;
647     vec[1] = 0;
648     set1(var, vec, &shvhed, flags);
649 }
650 
651 void
652 set1(const Char *var, Char **vec, struct varent *head, int flags)
653 {
654     Char **oldv = vec;
655 
656     if ((flags & VAR_NOGLOB) == 0) {
657 	int gflag;
658 
659 	gflag = tglob(oldv);
660 	if (gflag) {
661 	    vec = globall(oldv, gflag);
662 	    if (vec == 0) {
663 		blkfree(oldv);
664 		stderror(ERR_NAME | ERR_NOMATCH);
665 	    }
666 	    blkfree(oldv);
667 	}
668     }
669     /*
670      * Uniqueness addition from: Michael Veksler <mveksler@vnet.ibm.com>
671      */
672     if ( flags & (VAR_FIRST | VAR_LAST) ) {
673 	/*
674 	 * Code for -f (VAR_FIRST) and -l (VAR_LAST) options.
675 	 * Method:
676 	 *  Delete all duplicate words leaving "holes" in the word array (vec).
677 	 *  Then remove the "holes", keeping the order of the words unchanged.
678 	 */
679 	if (vec && vec[0] && vec[1]) { /* more than one word ? */
680 	    int i, j;
681 	    int num_items;
682 
683 	    for (num_items = 0; vec[num_items]; num_items++)
684 	        continue;
685 	    if (flags & VAR_FIRST) {
686 		/* delete duplications, keeping first occurance */
687 		for (i = 1; i < num_items; i++)
688 		    for (j = 0; j < i; j++)
689 			/* If have earlier identical item, remove i'th item */
690 			if (vec[i] && vec[j] && Strcmp(vec[j], vec[i]) == 0) {
691 			    xfree(vec[i]);
692 			    vec[i] = NULL;
693 			    break;
694 			}
695 	    } else if (flags & VAR_LAST) {
696 	      /* delete duplications, keeping last occurance */
697 		for (i = 0; i < num_items - 1; i++)
698 		    for (j = i + 1; j < num_items; j++)
699 			/* If have later identical item, remove i'th item */
700 			if (vec[i] && vec[j] && Strcmp(vec[j], vec[i]) == 0) {
701 			    /* remove identical item (the first) */
702 			    xfree(vec[i]);
703 			    vec[i] = NULL;
704 			}
705 	    }
706 	    /* Compress items - remove empty items */
707 	    for (j = i = 0; i < num_items; i++)
708 	       if (vec[i])
709 		  vec[j++] = vec[i];
710 
711 	    /* NULL-fy remaining items */
712 	    for (; j < num_items; j++)
713 		 vec[j] = NULL;
714 	}
715 	/* don't let the attribute propagate */
716 	flags &= ~(VAR_FIRST|VAR_LAST);
717     }
718     setq(var, vec, head, flags);
719 }
720 
721 
722 void
723 setq(const Char *name, Char **vec, struct varent *p, int flags)
724 {
725     struct varent *c;
726     int f;
727 
728     f = 0;			/* tree hangs off the header's left link */
729     while ((c = p->v_link[f]) != 0) {
730 	if ((f = *name - *c->v_name) == 0 &&
731 	    (f = Strcmp(name, c->v_name)) == 0) {
732 	    if (c->v_flags & VAR_READONLY)
733 		stderror(ERR_READONLY|ERR_NAME, c->v_name);
734 	    blkfree(c->vec);
735 	    c->v_flags = flags;
736 	    trim(c->vec = vec);
737 	    return;
738 	}
739 	p = c;
740 	f = f > 0;
741     }
742     p->v_link[f] = c = xmalloc(sizeof(struct varent));
743     c->v_name = Strsave(name);
744     c->v_flags = flags;
745     c->v_bal = 0;
746     c->v_left = c->v_right = 0;
747     c->v_parent = p;
748     balance(p, f, 0);
749     trim(c->vec = vec);
750 }
751 
752 /*ARGSUSED*/
753 void
754 unset(Char **v, struct command *c)
755 {
756     int did_roe, did_edit;
757 
758     USE(c);
759     did_roe = adrof(STRrecognize_only_executables) != NULL;
760     did_edit = adrof(STRedit) != NULL;
761     unset1(v, &shvhed);
762 
763 #if defined(FILEC) && defined(TIOCSTI)
764     if (adrof(STRfilec) == 0)
765 	filec = 0;
766 #endif /* FILEC && TIOCSTI */
767 
768     if (adrof(STRhistchars) == 0) {
769 	HIST = '!';
770 	HISTSUB = '^';
771     }
772     if (adrof(STRignoreeof) == 0)
773 	numeof = 0;
774     if (adrof(STRpromptchars) == 0) {
775 	PRCH = tcsh ? '>' : '%';
776 	PRCHROOT = '#';
777     }
778     if (adrof(STRnoclobber) == 0)
779 	no_clobber = 0;
780     if (adrof(STRhistlit) == 0)
781 	HistLit = 0;
782     if (adrof(STRloginsh) == 0)
783 	loginsh = 0;
784     if (adrof(STRanyerror) == 0)
785 	anyerror = 0;
786     if (adrof(STRwordchars) == 0)
787 	word_chars = STR_WORD_CHARS;
788     if (adrof(STRedit) == 0)
789 	editing = 0;
790     if (adrof(STRbackslash_quote) == 0)
791 	bslash_quote = 0;
792     if (adrof(STRcompat_expr) == 0)
793 	compat_expr = 0;
794     if (adrof(STRsymlinks) == 0)
795 	symlinks = 0;
796     if (adrof(STRimplicitcd) == 0)
797 	implicit_cd = 0;
798     if (adrof(STRcdtohome) == 0)
799 	cdtohome = 0;
800     if (adrof(STRkillring) == 0)
801 	SetKillRing(0);
802     if (did_edit && noediting && adrof(STRedit) == 0)
803 	noediting = 0;
804     if (adrof(STRvimode) == 0)
805 	VImode = 0;
806     if (did_roe && adrof(STRrecognize_only_executables) == 0)
807 	tw_cmd_free();
808     if (adrof(STRhistory) == 0)
809 	sethistory(0);
810 #ifdef COLOR_LS_F
811     if (adrof(STRcolor) == 0)
812 	set_color_context();
813 #endif /* COLOR_LS_F */
814 #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE)
815     update_dspmbyte_vars();
816 #endif
817     update_wordchars();
818 #ifdef NLS_CATALOGS
819     nlsclose();
820     nlsinit();
821 #endif /* NLS_CATALOGS */
822 }
823 
824 void
825 unset1(Char *v[], struct varent *head)
826 {
827     struct varent *vp;
828     int cnt;
829 
830     while (*++v) {
831 	cnt = 0;
832 	while ((vp = madrof(*v, head)) != NULL)
833 	    if (vp->v_flags & VAR_READONLY)
834 		stderror(ERR_READONLY|ERR_NAME, vp->v_name);
835 	    else
836 		unsetv1(vp), cnt++;
837 	if (cnt == 0)
838 	    setname(short2str(*v));
839     }
840 }
841 
842 void
843 unsetv(Char *var)
844 {
845     struct varent *vp;
846 
847     if ((vp = adrof1(var, &shvhed)) == 0)
848 	udvar(var);
849     unsetv1(vp);
850 }
851 
852 static void
853 unsetv1(struct varent *p)
854 {
855     struct varent *c, *pp;
856     int f;
857 
858     /*
859      * Free associated memory first to avoid complications.
860      */
861     blkfree(p->vec);
862     xfree(p->v_name);
863     /*
864      * If p is missing one child, then we can move the other into where p is.
865      * Otherwise, we find the predecessor of p, which is guaranteed to have no
866      * right child, copy it into p, and move it's left child into it.
867      */
868     if (p->v_right == 0)
869 	c = p->v_left;
870     else if (p->v_left == 0)
871 	c = p->v_right;
872     else {
873 	for (c = p->v_left; c->v_right; c = c->v_right)
874 	    continue;
875 	p->v_name = c->v_name;
876 	p->v_flags = c->v_flags;
877 	p->vec = c->vec;
878 	p = c;
879 	c = p->v_left;
880     }
881 
882     /*
883      * Move c into where p is.
884      */
885     pp = p->v_parent;
886     f = pp->v_right == p;
887     if ((pp->v_link[f] = c) != 0)
888 	c->v_parent = pp;
889     /*
890      * Free the deleted node, and rebalance.
891      */
892     xfree(p);
893     balance(pp, f, 1);
894 }
895 
896 /* Set variable name to NULL. */
897 void
898 setNS(const Char *varName)
899 {
900     setcopy(varName, STRNULL, VAR_READWRITE);
901 }
902 
903 /*ARGSUSED*/
904 void
905 shift(Char **v, struct command *c)
906 {
907     struct varent *argv;
908     Char *name;
909 
910     USE(c);
911     v++;
912     name = *v;
913     if (name == 0)
914 	name = STRargv;
915     else
916 	(void) strip(name);
917     argv = adrof(name);
918     if (argv == NULL || argv->vec == NULL)
919 	udvar(name);
920     if (argv->vec[0] == 0)
921 	stderror(ERR_NAME | ERR_NOMORE);
922     lshift(argv->vec, 1);
923     update_vars(name);
924 }
925 
926 void
927 exportpath(Char **val)
928 {
929     struct Strbuf buf = Strbuf_INIT;
930     Char    	*exppath;
931 
932     if (val)
933 	while (*val) {
934 	    Strbuf_append(&buf, *val++);
935 	    if (*val == 0 || eq(*val, STRRparen))
936 		break;
937 	    Strbuf_append1(&buf, PATHSEP);
938 	}
939     exppath = Strbuf_finish(&buf);
940     cleanup_push(exppath, xfree);
941     tsetenv(STRKPATH, exppath);
942     cleanup_until(exppath);
943 }
944 
945 static int
946 set_noclobber(Char **val)
947 {
948     Char *option;
949     int nc = NOCLOBBER_DEFAULT;
950 
951     if (val == NULL)
952 	return nc;
953     while (*val) {
954 	if (*val == 0 || eq(*val, STRRparen))
955 	    return nc;
956 
957 	option = *val++;
958 
959 	if (eq(option, STRnotempty))
960 	    nc |= NOCLOBBER_NOTEMPTY;
961 	else if (eq(option, STRask))
962 	    nc |= NOCLOBBER_ASK;
963     }
964     return nc;
965 }
966 
967 #ifndef lint
968  /*
969   * Lint thinks these have null effect
970   */
971  /* macros to do single rotations on node p */
972 # define rright(p) (\
973 	t = (p)->v_left,\
974 	(t)->v_parent = (p)->v_parent,\
975 	(((p)->v_left = t->v_right) != NULL) ?\
976 	    (t->v_right->v_parent = (p)) : 0,\
977 	(t->v_right = (p))->v_parent = t,\
978 	(p) = t)
979 # define rleft(p) (\
980 	t = (p)->v_right,\
981 	((t)->v_parent = (p)->v_parent,\
982 	((p)->v_right = t->v_left) != NULL) ? \
983 		(t->v_left->v_parent = (p)) : 0,\
984 	(t->v_left = (p))->v_parent = t,\
985 	(p) = t)
986 #else
987 static struct varent *
988 rleft(struct varent *p)
989 {
990     return (p);
991 }
992 static struct varent *
993 rright(struct varent *p)
994 {
995     return (p);
996 }
997 
998 #endif /* ! lint */
999 
1000 
1001 /*
1002  * Rebalance a tree, starting at p and up.
1003  * F == 0 means we've come from p's left child.
1004  * D == 1 means we've just done a delete, otherwise an insert.
1005  */
1006 static void
1007 balance(struct varent *p, int f, int d)
1008 {
1009     struct varent *pp;
1010 
1011 #ifndef lint
1012     struct varent *t;	/* used by the rotate macros */
1013 #endif /* !lint */
1014     int ff;
1015 #ifdef lint
1016     ff = 0;	/* Sun's lint is dumb! */
1017 #endif
1018 
1019     /*
1020      * Ok, from here on, p is the node we're operating on; pp is it's parent; f
1021      * is the branch of p from which we have come; ff is the branch of pp which
1022      * is p.
1023      */
1024     for (; (pp = p->v_parent) != 0; p = pp, f = ff) {
1025 	ff = pp->v_right == p;
1026 	if (f ^ d) {		/* right heavy */
1027 	    switch (p->v_bal) {
1028 	    case -1:		/* was left heavy */
1029 		p->v_bal = 0;
1030 		break;
1031 	    case 0:		/* was balanced */
1032 		p->v_bal = 1;
1033 		break;
1034 	    case 1:		/* was already right heavy */
1035 		switch (p->v_right->v_bal) {
1036 		case 1:	/* single rotate */
1037 		    pp->v_link[ff] = rleft(p);
1038 		    p->v_left->v_bal = 0;
1039 		    p->v_bal = 0;
1040 		    break;
1041 		case 0:	/* single rotate */
1042 		    pp->v_link[ff] = rleft(p);
1043 		    p->v_left->v_bal = 1;
1044 		    p->v_bal = -1;
1045 		    break;
1046 		case -1:	/* double rotate */
1047 		    (void) rright(p->v_right);
1048 		    pp->v_link[ff] = rleft(p);
1049 		    p->v_left->v_bal =
1050 			p->v_bal < 1 ? 0 : -1;
1051 		    p->v_right->v_bal =
1052 			p->v_bal > -1 ? 0 : 1;
1053 		    p->v_bal = 0;
1054 		    break;
1055 		default:
1056 		    break;
1057 		}
1058 		break;
1059 	    default:
1060 		break;
1061 	    }
1062 	}
1063 	else {			/* left heavy */
1064 	    switch (p->v_bal) {
1065 	    case 1:		/* was right heavy */
1066 		p->v_bal = 0;
1067 		break;
1068 	    case 0:		/* was balanced */
1069 		p->v_bal = -1;
1070 		break;
1071 	    case -1:		/* was already left heavy */
1072 		switch (p->v_left->v_bal) {
1073 		case -1:	/* single rotate */
1074 		    pp->v_link[ff] = rright(p);
1075 		    p->v_right->v_bal = 0;
1076 		    p->v_bal = 0;
1077 		    break;
1078 		case 0:	/* single rotate */
1079 		    pp->v_link[ff] = rright(p);
1080 		    p->v_right->v_bal = -1;
1081 		    p->v_bal = 1;
1082 		    break;
1083 		case 1:	/* double rotate */
1084 		    (void) rleft(p->v_left);
1085 		    pp->v_link[ff] = rright(p);
1086 		    p->v_left->v_bal =
1087 			p->v_bal < 1 ? 0 : -1;
1088 		    p->v_right->v_bal =
1089 			p->v_bal > -1 ? 0 : 1;
1090 		    p->v_bal = 0;
1091 		    break;
1092 		default:
1093 		    break;
1094 		}
1095 		break;
1096 	    default:
1097 		break;
1098 	    }
1099 	}
1100 	/*
1101 	 * If from insert, then we terminate when p is balanced. If from
1102 	 * delete, then we terminate when p is unbalanced.
1103 	 */
1104 	if ((p->v_bal == 0) ^ d)
1105 	    break;
1106     }
1107 }
1108 
1109 void
1110 plist(struct varent *p, int what)
1111 {
1112     struct varent *c;
1113     int len;
1114 
1115     for (;;) {
1116 	while (p->v_left)
1117 	    p = p->v_left;
1118 x:
1119 	if (p->v_parent == 0)	/* is it the header? */
1120 	    break;
1121 	if ((p->v_flags & what) != 0) {
1122 	    if (setintr) {
1123 		int old_pintr_disabled;
1124 
1125 		pintr_push_enable(&old_pintr_disabled);
1126 		cleanup_until(&old_pintr_disabled);
1127 	    }
1128 	    len = blklen(p->vec);
1129 	    xprintf("%S\t", p->v_name);
1130 	    if (len != 1)
1131 		xputchar('(');
1132 	    blkpr(p->vec);
1133 	    if (len != 1)
1134 		xputchar(')');
1135 	    xputchar('\n');
1136 	}
1137 	if (p->v_right) {
1138 	    p = p->v_right;
1139 	    continue;
1140 	}
1141 	do {
1142 	    c = p;
1143 	    p = p->v_parent;
1144 	} while (p->v_right == c);
1145 	goto x;
1146     }
1147 }
1148 
1149 #if defined(KANJI)
1150 # if defined(SHORT_STRINGS) && defined(DSPMBYTE)
1151 extern int dspmbyte_ls;
1152 
1153 void
1154 update_dspmbyte_vars(void)
1155 {
1156     int lp, iskcode;
1157     Char *dstr1;
1158     struct varent *vp;
1159 
1160     /* if variable "nokanji" is set, multi-byte display is disabled */
1161     if ((vp = adrof(CHECK_MBYTEVAR)) && !adrof(STRnokanji)) {
1162 	_enable_mbdisp = 1;
1163 	dstr1 = vp->vec[0];
1164 	if(eq (dstr1, STRsjis))
1165 	    iskcode = 1;
1166 	else if (eq(dstr1, STReuc))
1167 	    iskcode = 2;
1168 	else if (eq(dstr1, STRbig5))
1169 	    iskcode = 3;
1170 	else if (eq(dstr1, STRutf8))
1171 	    iskcode = 4;
1172 	else if ((dstr1[0] - '0') >= 0 && (dstr1[0] - '0') <= 3) {
1173 	    iskcode = 0;
1174 	}
1175 	else {
1176 	    xprintf(CGETS(18, 2,
1177 	       "Warning: unknown multibyte display; using default(euc(JP))\n"));
1178 	    iskcode = 2;
1179 	}
1180 	if (dstr1 && vp->vec[1] && eq(vp->vec[1], STRls))
1181 	  dspmbyte_ls = 1;
1182 	else
1183 	  dspmbyte_ls = 0;
1184 	for (lp = 0; lp < 256 && iskcode > 0; lp++) {
1185 	    switch (iskcode) {
1186 	    case 1:
1187 		/* Shift-JIS */
1188 		_cmap[lp] = _cmap_mbyte[lp];
1189 		_mbmap[lp] = _mbmap_sjis[lp];
1190 		break;
1191 	    case 2:
1192 		/* 2 ... euc */
1193 		_cmap[lp] = _cmap_mbyte[lp];
1194 		_mbmap[lp] = _mbmap_euc[lp];
1195 		break;
1196 	    case 3:
1197 		/* 3 ... big5 */
1198 		_cmap[lp] = _cmap_mbyte[lp];
1199 		_mbmap[lp] = _mbmap_big5[lp];
1200 		break;
1201 	    case 4:
1202 		/* 4 ... utf8 */
1203 		_cmap[lp] = _cmap_mbyte[lp];
1204 		_mbmap[lp] = _mbmap_utf8[lp];
1205 		break;
1206 	    default:
1207 		xprintf(CGETS(18, 3,
1208 		    "Warning: unknown multibyte code %d; multibyte disabled\n"),
1209 		    iskcode);
1210 		_cmap[lp] = _cmap_c[lp];
1211 		_mbmap[lp] = 0;	/* Default map all 0 */
1212 		_enable_mbdisp = 0;
1213 		break;
1214 	    }
1215 	}
1216 	if (iskcode == 0) {
1217 	    /* check original table */
1218 	    if (Strlen(dstr1) != 256) {
1219 		xprintf(CGETS(18, 4,
1220        "Warning: Invalid multibyte table length (%d); multibyte disabled\n"),
1221 		    Strlen(dstr1));
1222 		_enable_mbdisp = 0;
1223 	    }
1224 	    for (lp = 0; lp < 256 && _enable_mbdisp == 1; lp++) {
1225 		if (!((dstr1[lp] - '0') >= 0 && (dstr1[lp] - '0') <= 3)) {
1226 		    xprintf(CGETS(18, 4,
1227 	   "Warning: bad multibyte code at offset +%d; multibyte diabled\n"),
1228 			lp);
1229 		    _enable_mbdisp = 0;
1230 		    break;
1231 		}
1232 	    }
1233 	    /* set original table */
1234 	    for (lp = 0; lp < 256; lp++) {
1235 		if (_enable_mbdisp == 1) {
1236 		    _cmap[lp] = _cmap_mbyte[lp];
1237 		    _mbmap[lp] = (unsigned short) ((dstr1[lp] - '0') & 0x0f);
1238 		}
1239 		else {
1240 		    _cmap[lp] = _cmap_c[lp];
1241 		    _mbmap[lp] = 0;	/* Default map all 0 */
1242 		}
1243 	    }
1244 	}
1245     }
1246     else {
1247 	for (lp = 0; lp < 256; lp++) {
1248 	    _cmap[lp] = _cmap_c[lp];
1249 	    _mbmap[lp] = 0;	/* Default map all 0 */
1250 	}
1251 	_enable_mbdisp = 0;
1252 	dspmbyte_ls = 0;
1253     }
1254 #ifdef MBYTEDEBUG	/* Sorry, use for beta testing */
1255     {
1256 	Char mbmapstr[300];
1257 	for (lp = 0; lp < 256; lp++)
1258 	    mbmapstr[lp] = _mbmap[lp] + '0';
1259 	mbmapstr[lp] = 0;
1260 	setcopy(STRmbytemap, mbmapstr, VAR_READWRITE);
1261     }
1262 #endif /* MBYTEMAP */
1263 }
1264 
1265 /* dspkanji/dspmbyte autosetting */
1266 /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
1267 void
1268 autoset_dspmbyte(const Char *pcp)
1269 {
1270     int i;
1271     static const struct dspm_autoset_Table {
1272 	Char *n;
1273 	Char *v;
1274     } dspmt[] = {
1275 	{ STRLANGEUCJP, STReuc },
1276 	{ STRLANGEUCKR, STReuc },
1277 	{ STRLANGEUCZH, STReuc },
1278 	{ STRLANGEUCJPB, STReuc },
1279 	{ STRLANGEUCKRB, STReuc },
1280 	{ STRLANGEUCZHB, STReuc },
1281 #ifdef __linux__
1282 	{ STRLANGEUCJPC, STReuc },
1283 #endif
1284 	{ STRLANGSJIS, STRsjis },
1285 	{ STRLANGSJISB, STRsjis },
1286 	{ STRLANGBIG5, STRbig5 },
1287 	{ STRstarutfstar8, STRutf8 },
1288 	{ NULL, NULL }
1289     };
1290 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
1291     static const struct dspm_autoset_Table dspmc[] = {
1292 	{ STRstarutfstar8, STRutf8 },
1293 	{ STReuc, STReuc },
1294 	{ STRGB2312, STReuc },
1295 	{ STRLANGBIG5, STRbig5 },
1296 	{ NULL, NULL }
1297     };
1298     Char *codeset;
1299 
1300     codeset = str2short(nl_langinfo(CODESET));
1301     if (*codeset != '\0') {
1302 	for (i = 0; dspmc[i].n; i++) {
1303 	    const Char *estr;
1304 	    if (dspmc[i].n[0] && t_pmatch(pcp, dspmc[i].n, &estr, 0) > 0) {
1305 		setcopy(CHECK_MBYTEVAR, dspmc[i].v, VAR_READWRITE);
1306 		update_dspmbyte_vars();
1307 		return;
1308 	    }
1309 	}
1310     }
1311 #endif
1312 
1313     if (*pcp == '\0')
1314 	return;
1315 
1316     for (i = 0; dspmt[i].n; i++) {
1317 	const Char *estr;
1318 	if (dspmt[i].n[0] && t_pmatch(pcp, dspmt[i].n, &estr, 0) > 0) {
1319 	    setcopy(CHECK_MBYTEVAR, dspmt[i].v, VAR_READWRITE);
1320 	    update_dspmbyte_vars();
1321 	    break;
1322 	}
1323     }
1324 }
1325 # elif defined(AUTOSET_KANJI)
1326 void
1327 autoset_kanji(void)
1328 {
1329     char *codeset = nl_langinfo(CODESET);
1330 
1331     if (*codeset == '\0') {
1332 	if (adrof(STRnokanji) == NULL)
1333 	    setNS(STRnokanji);
1334 	return;
1335     }
1336 
1337     if (strcasestr(codeset, "SHIFT_JIS") == (char*)0) {
1338 	if (adrof(STRnokanji) == NULL)
1339 	    setNS(STRnokanji);
1340 	return;
1341     }
1342 
1343     if (adrof(STRnokanji) != NULL)
1344 	unsetv(STRnokanji);
1345 }
1346 #endif
1347 #endif
1348 
1349 void
1350 update_wordchars(void)
1351 {
1352     if ((word_chars == STR_WORD_CHARS) || (word_chars == STR_WORD_CHARS_VI)) {
1353 	word_chars = (VImode ? STR_WORD_CHARS_VI : STR_WORD_CHARS);
1354     }
1355 }
1356