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