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