sh.set.c revision 316958
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
35RCSID("$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
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);
58static	int		 set_noclobber  (Char **);
59
60/*
61 * C Shell
62 */
63
64static void
65update_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*/
248void
249doset(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
351static Char *
352getinx(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
363static void
364asx(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
377static struct varent *
378getvx(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*/
390void
391dolet(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
487static Char *
488xset(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
501static Char *
502operate(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
527static Char *putp;
528
529Char *
530putn(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
544static void
545putn1(tcsh_number_t n)
546{
547    if (n > 9)
548	putn1(n / 10);
549    *putp++ = (Char)(n % 10 + '0');
550}
551
552tcsh_number_t
553getn(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
589Char   *
590value1(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
602static struct varent *
603madrof(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
616struct varent *
617adrof1(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
631void
632setcopy(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 */
646void
647setv(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
656void
657set1(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
727void
728setq(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*/
758void
759unset(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
829void
830unset1(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
847void
848unsetv(Char *var)
849{
850    struct varent *vp;
851
852    if ((vp = adrof1(var, &shvhed)) == 0)
853	udvar(var);
854    unsetv1(vp);
855}
856
857static void
858unsetv1(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. */
902void
903setNS(const Char *varName)
904{
905    setcopy(varName, STRNULL, VAR_READWRITE);
906}
907
908/*ARGSUSED*/
909void
910shift(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
931static void
932exportpath(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
950static int
951set_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
992static struct varent *
993rleft(struct varent *p)
994{
995    return (p);
996}
997static struct varent *
998rright(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 */
1011static void
1012balance(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
1114void
1115plist(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;
1123x:
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)
1156extern int dspmbyte_ls;
1157
1158void
1159update_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 */
1272void
1273autoset_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)
1331void
1332autoset_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
1354void
1355update_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