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