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