1/*
2 * sh.func.c: csh builtin functions
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include "sh.h"
33#include "ed.h"
34#include "tw.h"
35#include "tc.h"
36#ifdef WINNT_NATIVE
37#include "nt.const.h"
38#endif /* WINNT_NATIVE */
39
40#if defined (NLS_CATALOGS) && defined(HAVE_ICONV)
41static iconv_t catgets_iconv; /* Or (iconv_t)-1 */
42#endif
43
44/*
45 * C shell
46 */
47
48extern int MapsAreInited;
49extern int NLSMapsAreInited;
50extern int GotTermCaps;
51
52static int zlast = -1;
53
54static	void	islogin		(void);
55static	void	preread		(void);
56static	void	doagain		(void);
57static  const char *isrchx	(int);
58static	void	search		(int, int, Char *);
59static	int	getword		(struct Strbuf *);
60static	struct wordent	*histgetword	(struct wordent *);
61static	void	toend		(void);
62static	void	xecho		(int, Char **);
63static	int	islocale_var	(Char *);
64static	void	wpfree		(struct whyle *);
65
66const struct biltins *
67isbfunc(struct command *t)
68{
69    Char *cp = t->t_dcom[0];
70    const struct biltins *bp, *bp1, *bp2;
71    static struct biltins label = {"", dozip, 0, 0};
72    static struct biltins foregnd = {"%job", dofg1, 0, 0};
73    static struct biltins backgnd = {"%job &", dobg1, 0, 0};
74
75    /*
76     * We never match a builtin that has quoted the first
77     * character; this has been the traditional way to escape
78     * builtin commands.
79     */
80    if (*cp & QUOTE)
81	return NULL;
82
83    if (*cp != ':' && lastchr(cp) == ':') {
84	label.bname = short2str(cp);
85	return (&label);
86    }
87    if (*cp == '%') {
88	if (t->t_dflg & F_AMPERSAND) {
89	    t->t_dflg &= ~F_AMPERSAND;
90	    backgnd.bname = short2str(cp);
91	    return (&backgnd);
92	}
93	foregnd.bname = short2str(cp);
94	return (&foregnd);
95    }
96#ifdef WARP
97    /*
98     * This is a perhaps kludgy way to determine if the warp builtin is to be
99     * acknowledged or not.  If checkwarp() fails, then we are to assume that
100     * the warp command is invalid, and carry on as we would handle any other
101     * non-builtin command.         -- JDK 2/4/88
102     */
103    if (eq(STRwarp, cp) && !checkwarp()) {
104	return (0);		/* this builtin disabled */
105    }
106#endif /* WARP */
107    /*
108     * Binary search Bp1 is the beginning of the current search range. Bp2 is
109     * one past the end.
110     */
111    for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
112	int i;
113
114	bp = bp1 + ((bp2 - bp1) >> 1);
115	if ((i = ((char) *cp) - *bp->bname) == 0 &&
116	    (i = StrQcmp(cp, str2short(bp->bname))) == 0)
117	    return bp;
118	if (i < 0)
119	    bp2 = bp;
120	else
121	    bp1 = bp + 1;
122    }
123#ifdef WINNT_NATIVE
124    return nt_check_additional_builtins(cp);
125#endif /*WINNT_NATIVE*/
126    return (0);
127}
128
129void
130func(struct command *t, const struct biltins *bp)
131{
132    int     i;
133
134    xechoit(t->t_dcom);
135    setname(bp->bname);
136    i = blklen(t->t_dcom) - 1;
137    if (i < bp->minargs)
138	stderror(ERR_NAME | ERR_TOOFEW);
139    if (i > bp->maxargs)
140	stderror(ERR_NAME | ERR_TOOMANY);
141    (*bp->bfunct) (t->t_dcom, t);
142}
143
144/*ARGSUSED*/
145void
146doonintr(Char **v, struct command *c)
147{
148    Char *cp;
149    Char *vv = v[1];
150
151    USE(c);
152    if (parintr.sa_handler == SIG_IGN)
153	return;
154    if (setintr && intty)
155	stderror(ERR_NAME | ERR_TERMINAL);
156    cp = gointr;
157    gointr = 0;
158    xfree(cp);
159    if (vv == 0) {
160	if (setintr)
161	    sigset_interrupting(SIGINT, queue_pintr);
162	else
163	    (void) signal(SIGINT, SIG_DFL);
164	gointr = 0;
165    }
166    else if (eq((vv = strip(vv)), STRminus)) {
167	(void) signal(SIGINT, SIG_IGN);
168	gointr = Strsave(STRminus);
169    }
170    else {
171	gointr = Strsave(vv);
172	sigset_interrupting(SIGINT, queue_pintr);
173    }
174}
175
176/*ARGSUSED*/
177void
178donohup(Char **v, struct command *c)
179{
180    USE(c);
181    USE(v);
182    if (intty)
183	stderror(ERR_NAME | ERR_TERMINAL);
184    if (setintr == 0) {
185	(void) signal(SIGHUP, SIG_IGN);
186	phup_disabled = 1;
187#ifdef CC
188	submit(getpid());
189#endif /* CC */
190    }
191}
192
193/*ARGSUSED*/
194void
195dohup(Char **v, struct command *c)
196{
197    USE(c);
198    USE(v);
199    if (intty)
200	stderror(ERR_NAME | ERR_TERMINAL);
201    if (setintr == 0)
202    	sigset_interrupting(SIGHUP, SIG_DFL);
203}
204
205
206/*ARGSUSED*/
207void
208dozip(Char **v, struct command *c)
209{
210    USE(c);
211    USE(v);
212}
213
214/*ARGSUSED*/
215void
216dofiletest(Char **v, struct command *c)
217{
218    Char **globbed, **fileptr, *ftest, *res;
219
220    USE(c);
221    if (*(ftest = *++v) != '-')
222	stderror(ERR_NAME | ERR_FILEINQ);
223    ++v;
224
225    v = glob_all_or_error(v);
226    globbed = v;
227    cleanup_push(globbed, blk_cleanup);
228
229    while (*(fileptr = v++) != NULL) {
230	res = filetest(ftest, &fileptr, 0);
231	cleanup_push(res, xfree);
232	xprintf("%S", res);
233	cleanup_until(res);
234	if (*v)
235	    xprintf(" ");
236    }
237    xprintf("\n");
238
239    cleanup_until(globbed);
240}
241
242void
243prvars(void)
244{
245    plist(&shvhed, VAR_ALL);
246}
247
248/*ARGSUSED*/
249void
250doalias(Char **v, struct command *c)
251{
252    struct varent *vp;
253    Char *p;
254
255    USE(c);
256    v++;
257    p = *v++;
258    if (p == 0)
259	plist(&aliases, VAR_ALL);
260    else if (*v == 0) {
261	vp = adrof1(strip(p), &aliases);
262	if (vp && vp->vec)
263	    blkpr(vp->vec), xputchar('\n');
264    }
265    else {
266	if (eq(p, STRalias) || eq(p, STRunalias)) {
267	    setname(short2str(p));
268	    stderror(ERR_NAME | ERR_DANGER);
269	}
270	set1(strip(p), saveblk(v), &aliases, VAR_READWRITE);
271	tw_cmd_free();
272    }
273}
274
275/*ARGSUSED*/
276void
277unalias(Char **v, struct command *c)
278{
279    USE(c);
280    unset1(v, &aliases);
281    tw_cmd_free();
282}
283
284/*ARGSUSED*/
285void
286dologout(Char **v, struct command *c)
287{
288    USE(c);
289    USE(v);
290    islogin();
291    goodbye(NULL, NULL);
292}
293
294/*ARGSUSED*/
295void
296dologin(Char **v, struct command *c)
297{
298#ifdef WINNT_NATIVE
299    USE(c);
300    USE(v);
301#else /* !WINNT_NATIVE */
302    char **p = short2blk(v);
303
304    USE(c);
305    cleanup_push((Char **)p, blk_cleanup);
306    islogin();
307    rechist(NULL, adrof(STRsavehist) != NULL);
308    sigaction(SIGTERM, &parterm, NULL);
309    (void) execv(_PATH_BIN_LOGIN, p);
310    (void) execv(_PATH_USRBIN_LOGIN, p);
311    cleanup_until((Char **)p);
312    untty();
313    xexit(1);
314#endif /* !WINNT_NATIVE */
315}
316
317
318#ifdef NEWGRP
319/*ARGSUSED*/
320void
321donewgrp(Char **v, struct command *c)
322{
323    char **p;
324    if (chkstop == 0 && setintr)
325	panystop(0);
326    sigaction(SIGTERM, &parterm, NULL);
327    p = short2blk(v);
328    /*
329     * From Beto Appleton (beto@aixwiz.austin.ibm.com)
330     * Newgrp can take 2 arguments...
331     */
332    (void) execv(_PATH_BIN_NEWGRP, p);
333    (void) execv(_PATH_USRBIN_NEWGRP, p);
334    blkfree((Char **) p);
335    untty();
336    xexit(1);
337}
338#endif /* NEWGRP */
339
340static void
341islogin(void)
342{
343    if (chkstop == 0 && setintr)
344	panystop(0);
345    if (loginsh)
346	return;
347    stderror(ERR_NOTLOGIN);
348}
349
350void
351doif(Char **v, struct command *kp)
352{
353    int i;
354    Char **vv;
355
356    v++;
357    i = noexec ? 1 : expr(&v);
358    vv = v;
359    if (*vv == NULL)
360	stderror(ERR_NAME | ERR_EMPTYIF);
361    if (eq(*vv, STRthen)) {
362	if (*++vv)
363	    stderror(ERR_NAME | ERR_IMPRTHEN);
364	setname(short2str(STRthen));
365	/*
366	 * If expression was zero, then scan to else , otherwise just fall into
367	 * following code.
368	 */
369	if (!i)
370	    search(TC_IF, 0, NULL);
371	return;
372    }
373    /*
374     * Simple command attached to this if. Left shift the node in this tree,
375     * munging it so we can reexecute it.
376     */
377    if (i) {
378	lshift(kp->t_dcom, vv - kp->t_dcom);
379	reexecute(kp);
380	donefds();
381    }
382}
383
384/*
385 * Reexecute a command, being careful not
386 * to redo i/o redirection, which is already set up.
387 */
388void
389reexecute(struct command *kp)
390{
391    kp->t_dflg &= F_SAVE;
392    kp->t_dflg |= F_REPEAT;
393    /*
394     * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
395     * pgrp's as the jobs would then have no way to get the tty (we can't give
396     * it to them, and our parent wouldn't know their pgrp, etc.
397     */
398    execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
399}
400
401/*ARGSUSED*/
402void
403doelse (Char **v, struct command *c)
404{
405    USE(c);
406    USE(v);
407    if (!noexec)
408	search(TC_ELSE, 0, NULL);
409}
410
411/*ARGSUSED*/
412void
413dogoto(Char **v, struct command *c)
414{
415    Char   *lp;
416
417    USE(c);
418    lp = globone(v[1], G_ERROR);
419    cleanup_push(lp, xfree);
420    if (!noexec)
421	gotolab(lp);
422    cleanup_until(lp);
423}
424
425void
426gotolab(Char *lab)
427{
428    struct whyle *wp;
429    /*
430     * While we still can, locate any unknown ends of existing loops. This
431     * obscure code is the WORST result of the fact that we don't really parse.
432     */
433    zlast = TC_GOTO;
434    for (wp = whyles; wp; wp = wp->w_next)
435	if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) {
436	    search(TC_BREAK, 0, NULL);
437	    btell(&wp->w_end);
438	}
439	else {
440	    bseek(&wp->w_end);
441	}
442    search(TC_GOTO, 0, lab);
443    /*
444     * Eliminate loops which were exited.
445     */
446    wfree();
447}
448
449/*ARGSUSED*/
450void
451doswitch(Char **v, struct command *c)
452{
453    Char *cp, *lp;
454
455    USE(c);
456    v++;
457    if (!*v || *(*v++) != '(')
458	stderror(ERR_SYNTAX);
459    cp = **v == ')' ? STRNULL : *v++;
460    if (*(*v++) != ')')
461	v--;
462    if (*v)
463	stderror(ERR_SYNTAX);
464    lp = globone(cp, G_ERROR);
465    cleanup_push(lp, xfree);
466    if (!noexec)
467	search(TC_SWITCH, 0, lp);
468    cleanup_until(lp);
469}
470
471/*ARGSUSED*/
472void
473dobreak(Char **v, struct command *c)
474{
475    USE(v);
476    USE(c);
477    if (whyles == NULL)
478	stderror(ERR_NAME | ERR_NOTWHILE);
479    if (!noexec)
480	toend();
481}
482
483/*ARGSUSED*/
484void
485doexit(Char **v, struct command *c)
486{
487    USE(c);
488
489    if (chkstop == 0 && (intty || intact) && evalvec == 0)
490	panystop(0);
491    /*
492     * Don't DEMAND parentheses here either.
493     */
494    v++;
495    if (*v) {
496	setv(STRstatus, putn(expr(&v)), VAR_READWRITE);
497	if (*v)
498	    stderror(ERR_NAME | ERR_EXPRESSION);
499    }
500    btoeof();
501#if 0
502    if (intty)
503#endif
504    /* Always close, why only on ttys? */
505	xclose(SHIN);
506}
507
508/*ARGSUSED*/
509void
510doforeach(Char **v, struct command *c)
511{
512    Char *cp, *sp;
513    struct whyle *nwp;
514    int gflag;
515
516    USE(c);
517    v++;
518    cp = sp = strip(*v);
519    if (!letter(*cp))
520	stderror(ERR_NAME | ERR_VARBEGIN);
521    do {
522	cp++;
523    } while (alnum(*cp));
524    if (*cp != '\0')
525	stderror(ERR_NAME | ERR_VARALNUM);
526    cp = *v++;
527    if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
528	stderror(ERR_NAME | ERR_NOPAREN);
529    v++;
530    gflag = tglob(v);
531    if (gflag) {
532	v = globall(v, gflag);
533	if (v == 0 && !noexec)
534	    stderror(ERR_NAME | ERR_NOMATCH);
535    }
536    else {
537	v = saveblk(v);
538	trim(v);
539    }
540    nwp = xcalloc(1, sizeof *nwp);
541    nwp->w_fe = nwp->w_fe0 = v;
542    btell(&nwp->w_start);
543    nwp->w_fename = Strsave(cp);
544    nwp->w_next = whyles;
545    nwp->w_end.type = TCSH_F_SEEK;
546    whyles = nwp;
547    /*
548     * Pre-read the loop so as to be more comprehensible to a terminal user.
549     */
550    zlast = TC_FOREACH;
551    if (intty)
552	preread();
553    if (!noexec)
554	doagain();
555}
556
557/*ARGSUSED*/
558void
559dowhile(Char **v, struct command *c)
560{
561    int status;
562    int again = whyles != 0 &&
563			  SEEKEQ(&whyles->w_start, &lineloc) &&
564			  whyles->w_fename == 0;
565
566    USE(c);
567    v++;
568    /*
569     * Implement prereading here also, taking care not to evaluate the
570     * expression before the loop has been read up from a terminal.
571     */
572    if (noexec)
573	status = 0;
574    else if (intty && !again)
575	status = !exp0(&v, 1);
576    else
577	status = !expr(&v);
578    if (*v && !noexec)
579	stderror(ERR_NAME | ERR_EXPRESSION);
580    if (!again) {
581	struct whyle *nwp = xcalloc(1, sizeof(*nwp));
582
583	nwp->w_start = lineloc;
584	nwp->w_end.type = TCSH_F_SEEK;
585	nwp->w_end.f_seek = 0;
586	nwp->w_end.a_seek = 0;
587	nwp->w_next = whyles;
588	whyles = nwp;
589	zlast = TC_WHILE;
590	if (intty) {
591	    /*
592	     * The tty preread
593	     */
594	    preread();
595	    doagain();
596	    return;
597	}
598    }
599    if (status)
600	/* We ain't gonna loop no more, no more! */
601	toend();
602}
603
604static void
605preread(void)
606{
607    int old_pintr_disabled;
608
609    whyles->w_end.type = TCSH_I_SEEK;
610    if (setintr)
611	pintr_push_enable(&old_pintr_disabled);
612    search(TC_BREAK, 0, NULL);		/* read the expression in */
613    if (setintr)
614	cleanup_until(&old_pintr_disabled);
615    btell(&whyles->w_end);
616}
617
618/*ARGSUSED*/
619void
620doend(Char **v, struct command *c)
621{
622    USE(v);
623    USE(c);
624    if (!whyles)
625	stderror(ERR_NAME | ERR_NOTWHILE);
626    btell(&whyles->w_end);
627    if (!noexec)
628	doagain();
629}
630
631/*ARGSUSED*/
632void
633docontin(Char **v, struct command *c)
634{
635    USE(v);
636    USE(c);
637    if (!whyles)
638	stderror(ERR_NAME | ERR_NOTWHILE);
639    if (!noexec)
640	doagain();
641}
642
643static void
644doagain(void)
645{
646    /* Repeating a while is simple */
647    if (whyles->w_fename == 0) {
648	bseek(&whyles->w_start);
649	return;
650    }
651    /*
652     * The foreach variable list actually has a spurious word ")" at the end of
653     * the w_fe list.  Thus we are at the of the list if one word beyond this
654     * is 0.
655     */
656    if (!whyles->w_fe[1]) {
657	dobreak(NULL, NULL);
658	return;
659    }
660    setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
661    bseek(&whyles->w_start);
662}
663
664void
665dorepeat(Char **v, struct command *kp)
666{
667    int i = 1;
668
669    do {
670	i *= getn(v[1]);
671	lshift(v, 2);
672    } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
673    if (noexec)
674	i = 1;
675
676    if (setintr) {
677	pintr_disabled++;
678	cleanup_push(&pintr_disabled, disabled_cleanup);
679    }
680    while (i > 0) {
681	if (setintr && pintr_disabled == 1) {
682	    cleanup_until(&pintr_disabled);
683	    pintr_disabled++;
684	    cleanup_push(&pintr_disabled, disabled_cleanup);
685	}
686	reexecute(kp);
687	--i;
688    }
689    if (setintr && pintr_disabled == 1)
690        cleanup_until(&pintr_disabled);
691    donefds();
692}
693
694/*ARGSUSED*/
695void
696doswbrk(Char **v, struct command *c)
697{
698    USE(v);
699    USE(c);
700    if (!noexec)
701	search(TC_BRKSW, 0, NULL);
702}
703
704int
705srchx(Char *cp)
706{
707    struct srch *sp, *sp1, *sp2;
708    int i;
709
710    /*
711     * Ignore keywords inside heredocs
712     */
713    if (inheredoc)
714	return -1;
715
716    /*
717     * Binary search Sp1 is the beginning of the current search range. Sp2 is
718     * one past the end.
719     */
720    for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
721	sp = sp1 + ((sp2 - sp1) >> 1);
722	if ((i = *cp - *sp->s_name) == 0 &&
723	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
724	    return sp->s_value;
725	if (i < 0)
726	    sp2 = sp;
727	else
728	    sp1 = sp + 1;
729    }
730    return (-1);
731}
732
733static const char *
734isrchx(int n)
735{
736    struct srch *sp, *sp2;
737
738    for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
739	if (sp->s_value == n)
740	    return (sp->s_name);
741    return ("");
742}
743
744
745static int Stype;
746static Char *Sgoal;
747
748static void
749search(int type, int level, Char *goal)
750{
751    struct Strbuf word = Strbuf_INIT;
752    Char *cp;
753    struct whyle *wp;
754    int wlevel = 0;
755    struct wordent *histent = NULL, *ohistent = NULL;
756
757    Stype = type;
758    Sgoal = goal;
759    if (type == TC_GOTO) {
760	struct Ain a;
761	a.type = TCSH_F_SEEK;
762	a.f_seek = 0;
763	a.a_seek = 0;
764	bseek(&a);
765    }
766    cleanup_push(&word, Strbuf_cleanup);
767    do {
768
769	if (intty) {
770	    histent = xmalloc(sizeof(*histent));
771	    ohistent = xmalloc(sizeof(*histent));
772	    ohistent->word = STRNULL;
773	    ohistent->next = histent;
774	    histent->prev = ohistent;
775	}
776
777	if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
778	    printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
779	/* xprintf("? "), flush(); */
780	(void) getword(&word);
781	Strbuf_terminate(&word);
782
783	if (intty && Strlen(word.s) > 0) {
784	    histent->word = Strsave(word.s);
785	    histent->next = xmalloc(sizeof(*histent));
786	    histent->next->prev = histent;
787	    histent = histent->next;
788	}
789
790	switch (srchx(word.s)) {
791
792	case TC_ELSE:
793	    if (level == 0 && type == TC_IF)
794		goto end;
795	    break;
796
797	case TC_IF:
798	    while (getword(&word)) {
799		if (intty) {
800		    histent->word = Strsave(word.s);
801		    histent->next = xmalloc(sizeof(*histent));
802		    histent->next->prev = histent;
803		    histent = histent->next;
804		}
805		continue;
806	    }
807
808	    if ((type == TC_IF || type == TC_ELSE) &&
809		eq(word.s, STRthen))
810		level++;
811	    break;
812
813	case TC_ENDIF:
814	    if (type == TC_IF || type == TC_ELSE)
815		level--;
816	    break;
817
818	case TC_FOREACH:
819	case TC_WHILE:
820	    wlevel++;
821	    if (type == TC_BREAK)
822		level++;
823	    break;
824
825	case TC_END:
826	    if (type == TC_BRKSW) {
827		if (wlevel == 0) {
828		    wp = whyles;
829		    if (wp) {
830			    whyles = wp->w_next;
831			    wpfree(wp);
832		    }
833		}
834	    }
835	    if (type == TC_BREAK)
836		level--;
837	    wlevel--;
838	    break;
839
840	case TC_SWITCH:
841	    if (type == TC_SWITCH || type == TC_BRKSW)
842		level++;
843	    break;
844
845	case TC_ENDSW:
846	    if (type == TC_SWITCH || type == TC_BRKSW)
847		level--;
848	    break;
849
850	case TC_LABEL:
851	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
852		level = -1;
853	    break;
854
855	default:
856	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
857		break;
858	    if (word.len == 0 || word.s[word.len - 1] != ':')
859		break;
860	    word.s[--word.len] = 0;
861	    if ((type == TC_GOTO && eq(word.s, goal)) ||
862		(type == TC_SWITCH && eq(word.s, STRdefault)))
863		level = -1;
864	    break;
865
866	case TC_CASE:
867	    if (type != TC_SWITCH || level != 0)
868		break;
869	    (void) getword(&word);
870	    if (word.len != 0 && word.s[word.len - 1] == ':')
871		word.s[--word.len] = 0;
872	    cp = strip(Dfix1(word.s));
873	    cleanup_push(cp, xfree);
874	    if (Gmatch(goal, cp))
875		level = -1;
876	    cleanup_until(cp);
877	    break;
878
879	case TC_DEFAULT:
880	    if (type == TC_SWITCH && level == 0)
881		level = -1;
882	    break;
883	}
884	if (intty) {
885	    ohistent->prev = histgetword(histent);
886	    ohistent->prev->next = ohistent;
887	    savehist(ohistent, 0);
888	    freelex(ohistent);
889	    xfree(ohistent);
890	} else
891	    (void) getword(NULL);
892    } while (level >= 0);
893 end:
894    cleanup_until(&word);
895}
896
897static struct wordent *
898histgetword(struct wordent *histent)
899{
900    int first;
901    eChar c, d;
902    int e;
903    struct Strbuf *tmp;
904    tmp = xmalloc(sizeof(*tmp));
905    tmp->size = 0;
906    tmp->s = NULL;
907    c = readc(1);
908    d = 0;
909    e = 0;
910    for (;;) {
911	tmp->len = 0;
912	Strbuf_terminate (tmp);
913	while (c == ' ' || c == '\t')
914	    c = readc(1);
915	if (c == '#')
916	    do
917		c = readc(1);
918	    while (c != CHAR_ERR && c != '\n');
919	if (c == CHAR_ERR)
920	    goto past;
921	if (c == '\n')
922	    goto nl;
923	unreadc(c);
924	first = 1;
925	do {
926	    e = (c == '\\');
927	    c = readc(1);
928	    if (c == '\\' && !e) {
929		if ((c = readc(1)) == '\n') {
930		    e = 1;
931		    c = ' ';
932		} else {
933		    unreadc(c);
934		    c = '\\';
935		}
936	    }
937	    if ((c == '\'' || c == '"') && !e) {
938		if (d == 0)
939		    d = c;
940		else if (d == c)
941		    d = 0;
942	    }
943	    if (c == CHAR_ERR)
944		goto past;
945
946	    Strbuf_append1(tmp, (Char) c);
947
948	    if (!first && !d && c == '(' && !e) {
949		break;
950	    }
951	    first = 0;
952	} while (d || e || (c != ' ' && c != '\t' && c != '\n'));
953	tmp->len--;
954	if (tmp->len) {
955	    Strbuf_terminate(tmp);
956	    histent->word = Strsave(tmp->s);
957	    histent->next = xmalloc(sizeof (*histent));
958	    histent->next->prev = histent;
959	    histent = histent->next;
960	}
961	if (c == '\n') {
962	nl:
963	    tmp->len = 0;
964	    Strbuf_append1(tmp, (Char) c);
965	    Strbuf_terminate(tmp);
966	    histent->word = Strsave(tmp->s);
967	    return histent;
968	}
969    }
970
971past:
972    switch (Stype) {
973
974    case TC_IF:
975	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
976	break;
977
978    case TC_ELSE:
979	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
980	break;
981
982    case TC_BRKSW:
983    case TC_SWITCH:
984	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
985	break;
986
987    case TC_BREAK:
988	stderror(ERR_NAME | ERR_NOTFOUND, "end");
989	break;
990
991    case TC_GOTO:
992	setname(short2str(Sgoal));
993	stderror(ERR_NAME | ERR_NOTFOUND, "label");
994	break;
995
996    default:
997	break;
998    }
999    /* NOTREACHED */
1000    return NULL;
1001}
1002
1003static int
1004getword(struct Strbuf *wp)
1005{
1006    int found = 0, first;
1007    eChar c, d;
1008
1009    if (wp)
1010	wp->len = 0;
1011    c = readc(1);
1012    d = 0;
1013    do {
1014	while (c == ' ' || c == '\t')
1015	    c = readc(1);
1016	if (c == '#')
1017	    do
1018		c = readc(1);
1019	    while (c != CHAR_ERR && c != '\n');
1020	if (c == CHAR_ERR)
1021	    goto past;
1022	if (c == '\n') {
1023	    if (wp)
1024		break;
1025	    return (0);
1026	}
1027	unreadc(c);
1028	found = 1;
1029	first = 1;
1030	do {
1031	    c = readc(1);
1032	    if (c == '\\' && (c = readc(1)) == '\n')
1033		c = ' ';
1034	    if (c == '\'' || c == '"') {
1035		if (d == 0)
1036		    d = c;
1037		else if (d == c)
1038		    d = 0;
1039	    }
1040	    if (c == CHAR_ERR)
1041		goto past;
1042	    if (wp)
1043		Strbuf_append1(wp, (Char) c);
1044	    if (!d && c == ')') {
1045		if (!first && wp) {
1046		    goto past_word_end;
1047		} else {
1048		    if (wp) {
1049			wp->len = 1;
1050			Strbuf_terminate(wp);
1051		    }
1052		    return found;
1053		}
1054	    }
1055	    if (!first && !d && c == '(') {
1056		if (wp)
1057		    goto past_word_end;
1058		else
1059		    break;
1060	    }
1061	    first = 0;
1062	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
1063    } while (wp == 0);
1064
1065 past_word_end:
1066    unreadc(c);
1067    if (found) {
1068	wp->len--;
1069	Strbuf_terminate(wp);
1070    }
1071
1072    return (found);
1073
1074past:
1075    switch (Stype) {
1076
1077    case TC_IF:
1078	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
1079	break;
1080
1081    case TC_ELSE:
1082	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
1083	break;
1084
1085    case TC_BRKSW:
1086    case TC_SWITCH:
1087	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
1088	break;
1089
1090    case TC_BREAK:
1091	stderror(ERR_NAME | ERR_NOTFOUND, "end");
1092	break;
1093
1094    case TC_GOTO:
1095	setname(short2str(Sgoal));
1096	stderror(ERR_NAME | ERR_NOTFOUND, "label");
1097	break;
1098
1099    default:
1100	break;
1101    }
1102    /* NOTREACHED */
1103    return (0);
1104}
1105
1106static void
1107toend(void)
1108{
1109    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
1110	search(TC_BREAK, 0, NULL);
1111	btell(&whyles->w_end);
1112	whyles->w_end.f_seek--;
1113    }
1114    else {
1115	bseek(&whyles->w_end);
1116    }
1117    wfree();
1118}
1119
1120static void
1121wpfree(struct whyle *wp)
1122{
1123	if (wp->w_fe0)
1124	    blkfree(wp->w_fe0);
1125	xfree(wp->w_fename);
1126	xfree(wp);
1127}
1128
1129void
1130wfree(void)
1131{
1132    struct Ain    o;
1133    struct whyle *nwp;
1134#ifdef lint
1135    nwp = NULL;	/* sun lint is dumb! */
1136#endif
1137
1138#ifdef FDEBUG
1139    static const char foo[] = "IAFE";
1140#endif /* FDEBUG */
1141
1142    btell(&o);
1143
1144#ifdef FDEBUG
1145    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
1146	    foo[o.type + 1], o.a_seek, o.f_seek);
1147#endif /* FDEBUG */
1148
1149    for (; whyles; whyles = nwp) {
1150	struct whyle *wp = whyles;
1151	nwp = wp->w_next;
1152
1153#ifdef FDEBUG
1154	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
1155		foo[wp->w_start.type+1],
1156		wp->w_start.a_seek, wp->w_start.f_seek);
1157	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
1158		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
1159#endif /* FDEBUG */
1160
1161	/*
1162	 * XXX: We free loops that have different seek types.
1163	 */
1164	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
1165	    wp->w_start.type == o.type) {
1166	    if (wp->w_end.type == TCSH_F_SEEK) {
1167		if (o.f_seek >= wp->w_start.f_seek &&
1168		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
1169		    break;
1170	    }
1171	    else {
1172		if (o.a_seek >= wp->w_start.a_seek &&
1173		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
1174		    break;
1175	    }
1176	}
1177
1178	wpfree(wp);
1179    }
1180}
1181
1182/*ARGSUSED*/
1183void
1184doecho(Char **v, struct command *c)
1185{
1186    USE(c);
1187    xecho(' ', v);
1188}
1189
1190/*ARGSUSED*/
1191void
1192doglob(Char **v, struct command *c)
1193{
1194    USE(c);
1195    xecho(0, v);
1196    flush();
1197}
1198
1199static void
1200xecho(int sep, Char **v)
1201{
1202    Char *cp, **globbed = NULL;
1203    int     nonl = 0;
1204    int	    echo_style = ECHO_STYLE;
1205    struct varent *vp;
1206
1207    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
1208	vp->vec[0] != NULL) {
1209	if (Strcmp(vp->vec[0], STRbsd) == 0)
1210	    echo_style = BSD_ECHO;
1211	else if (Strcmp(vp->vec[0], STRsysv) == 0)
1212	    echo_style = SYSV_ECHO;
1213	else if (Strcmp(vp->vec[0], STRboth) == 0)
1214	    echo_style = BOTH_ECHO;
1215	else if (Strcmp(vp->vec[0], STRnone) == 0)
1216	    echo_style = NONE_ECHO;
1217    }
1218
1219    v++;
1220    if (*v == 0)
1221	goto done;
1222    if (setintr) {
1223	int old_pintr_disabled;
1224	pintr_push_enable(&old_pintr_disabled);
1225	v = glob_all_or_error(v);
1226	cleanup_until(&old_pintr_disabled);
1227    } else {
1228	v = glob_all_or_error(v);
1229    }
1230    globbed = v;
1231    if (globbed != NULL)
1232	cleanup_push(globbed, blk_cleanup);
1233
1234    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
1235	nonl++, v++;
1236
1237    while ((cp = *v++) != 0) {
1238	Char c;
1239
1240	if (setintr) {
1241	    int old_pintr_disabled;
1242
1243	    pintr_push_enable(&old_pintr_disabled);
1244	    cleanup_until(&old_pintr_disabled);
1245	}
1246	while ((c = *cp++) != 0) {
1247	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
1248		switch (c = *cp++) {
1249		case 'a':
1250		    c = '\a';
1251		    break;
1252		case 'b':
1253		    c = '\b';
1254		    break;
1255		case 'c':
1256		    nonl = 1;
1257		    goto done;
1258		case 'e':
1259#if 0			/* Windows does not understand \e */
1260		    c = '\e';
1261#else
1262		    c = CTL_ESC('\033');
1263#endif
1264		    break;
1265		case 'f':
1266		    c = '\f';
1267		    break;
1268		case 'n':
1269		    c = '\n';
1270		    break;
1271		case 'r':
1272		    c = '\r';
1273		    break;
1274		case 't':
1275		    c = '\t';
1276		    break;
1277		case 'v':
1278		    c = '\v';
1279		    break;
1280		case '\\':
1281		    c = '\\';
1282		    break;
1283		case '0':
1284		    c = 0;
1285		    if (*cp >= '0' && *cp < '8')
1286			c = c * 8 + *cp++ - '0';
1287		    if (*cp >= '0' && *cp < '8')
1288			c = c * 8 + *cp++ - '0';
1289		    if (*cp >= '0' && *cp < '8')
1290			c = c * 8 + *cp++ - '0';
1291		    break;
1292		case '\0':
1293		    c = '\\';
1294		    cp--;
1295		    break;
1296		default:
1297		    xputchar('\\' | QUOTE);
1298		    break;
1299		}
1300	    }
1301	    xputwchar(c | QUOTE);
1302
1303	}
1304	if (*v)
1305	    xputchar(sep | QUOTE);
1306    }
1307done:
1308    if (sep && nonl == 0)
1309	xputchar('\n');
1310    else
1311	flush();
1312    if (globbed != NULL)
1313	cleanup_until(globbed);
1314}
1315
1316/* check whether an environment variable should invoke 'set_locale()' */
1317static int
1318islocale_var(Char *var)
1319{
1320    static Char *locale_vars[] = {
1321	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1322	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
1323    };
1324    Char **v;
1325
1326    for (v = locale_vars; *v; ++v)
1327	if (eq(var, *v))
1328	    return 1;
1329    return 0;
1330}
1331
1332static void
1333xlate_cr_cleanup(void *dummy)
1334{
1335    USE(dummy);
1336    xlate_cr = 0;
1337}
1338
1339/*ARGSUSED*/
1340void
1341doprintenv(Char **v, struct command *c)
1342{
1343    Char   *e;
1344
1345    USE(c);
1346    v++;
1347    if (*v == 0) {
1348	Char **ep;
1349
1350	xlate_cr = 1;
1351	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1352	for (ep = STR_environ; *ep; ep++) {
1353	    if (setintr) {
1354		int old_pintr_disabled;
1355
1356		pintr_push_enable(&old_pintr_disabled);
1357		cleanup_until(&old_pintr_disabled);
1358	    }
1359	    xprintf("%S\n", *ep);
1360	}
1361	cleanup_until(&xlate_cr);
1362    }
1363    else if ((e = tgetenv(*v)) != NULL) {
1364	int old_output_raw;
1365
1366	old_output_raw = output_raw;
1367	output_raw = 1;
1368	cleanup_push(&old_output_raw, output_raw_restore);
1369	xprintf("%S\n", e);
1370	cleanup_until(&old_output_raw);
1371    }
1372    else
1373	setcopy(STRstatus, STR1, VAR_READWRITE);
1374}
1375
1376/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
1377   (and anything else with a modern compiler) */
1378
1379/*ARGSUSED*/
1380void
1381dosetenv(Char **v, struct command *c)
1382{
1383    Char   *vp, *lp;
1384
1385    USE(c);
1386    if (*++v == 0) {
1387	doprintenv(--v, 0);
1388	return;
1389    }
1390
1391    vp = *v++;
1392    lp = vp;
1393
1394    if (!letter(*lp))
1395	stderror(ERR_NAME | ERR_VARBEGIN);
1396    do {
1397	lp++;
1398    } while (alnum(*lp) || *lp == '.');
1399    if (*lp != '\0')
1400	stderror(ERR_NAME | ERR_VARALNUM);
1401
1402    if ((lp = *v++) == 0)
1403	lp = STRNULL;
1404
1405    lp = globone(lp, G_APPEND);
1406    cleanup_push(lp, xfree);
1407    tsetenv(vp, lp);
1408    if (eq(vp, STRKPATH)) {
1409        importpath(lp);
1410	dohash(NULL, NULL);
1411	cleanup_until(lp);
1412	return;
1413    }
1414
1415#ifdef apollo
1416    if (eq(vp, STRSYSTYPE)) {
1417	dohash(NULL, NULL);
1418	cleanup_until(lp);
1419	return;
1420    }
1421#endif /* apollo */
1422
1423    /* dspkanji/dspmbyte autosetting */
1424    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
1425#if defined(DSPMBYTE)
1426    if (eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
1427	autoset_dspmbyte(lp);
1428    }
1429#endif
1430
1431    if (islocale_var(vp)) {
1432#ifdef NLS
1433	int     k;
1434
1435# ifdef SETLOCALEBUG
1436	dont_free = 1;
1437# endif /* SETLOCALEBUG */
1438	(void) setlocale(LC_ALL, "");
1439# ifdef LC_COLLATE
1440	(void) setlocale(LC_COLLATE, "");
1441# endif
1442# ifdef LC_CTYPE
1443	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1444# endif /* LC_CTYPE */
1445# if defined(AUTOSET_KANJI)
1446        autoset_kanji();
1447# endif /* AUTOSET_KANJI */
1448# ifdef NLS_CATALOGS
1449#  ifdef LC_MESSAGES
1450	(void) setlocale(LC_MESSAGES, "");
1451#  endif /* LC_MESSAGES */
1452	nlsclose();
1453	nlsinit();
1454# endif /* NLS_CATALOGS */
1455# ifdef SETLOCALEBUG
1456	dont_free = 0;
1457# endif /* SETLOCALEBUG */
1458# ifdef STRCOLLBUG
1459	fix_strcoll_bug();
1460# endif /* STRCOLLBUG */
1461	tw_cmd_free();	/* since the collation sequence has changed */
1462	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
1463	    continue;
1464	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
1465#else /* !NLS */
1466	AsciiOnly = 0;
1467#endif /* NLS */
1468	NLSMapsAreInited = 0;
1469	ed_Init();
1470	if (MapsAreInited && !NLSMapsAreInited)
1471	    ed_InitNLSMaps();
1472	cleanup_until(lp);
1473	return;
1474    }
1475
1476#ifdef NLS_CATALOGS
1477    if (eq(vp, STRNLSPATH)) {
1478	nlsclose();
1479	nlsinit();
1480    }
1481#endif
1482
1483    if (eq(vp, STRNOREBIND)) {
1484	NoNLSRebind = 1;
1485	MapsAreInited = 0;
1486	NLSMapsAreInited = 0;
1487	ed_InitMaps();
1488	cleanup_until(lp);
1489	return;
1490    }
1491#ifdef WINNT_NATIVE
1492    if (eq(vp, STRtcshlang)) {
1493	nlsinit();
1494	cleanup_until(lp);
1495	return;
1496    }
1497#endif /* WINNT_NATIVE */
1498    if (eq(vp, STRKTERM)) {
1499	char *t;
1500
1501	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1502	cleanup_ignore(lp);
1503	cleanup_until(lp);
1504	t = short2str(lp);
1505	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
1506	    editing = 1;
1507	    noediting = 0;
1508	    setNS(STRedit);
1509	}
1510	GotTermCaps = 0;
1511	ed_Init();
1512	return;
1513    }
1514
1515    if (eq(vp, STRKHOME)) {
1516	Char *canon;
1517	/*
1518	 * convert to canonical pathname (possibly resolving symlinks)
1519	 */
1520	cleanup_ignore(lp);
1521	canon = dcanon(lp, lp);
1522	cleanup_push(canon, xfree);
1523	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1524	cleanup_ignore(canon);
1525	cleanup_until(canon);
1526
1527	/* fix directory stack for new tilde home */
1528	dtilde();
1529	return;
1530    }
1531
1532    if (eq(vp, STRKSHLVL)) {
1533	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1534	cleanup_ignore(lp);
1535	cleanup_until(lp);
1536	return;
1537    }
1538
1539    if (eq(vp, STRKUSER)) {
1540	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1541	cleanup_ignore(lp);
1542	cleanup_until(lp);
1543	return;
1544    }
1545
1546    if (eq(vp, STRKGROUP)) {
1547	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1548	cleanup_ignore(lp);
1549	cleanup_until(lp);
1550	return;
1551    }
1552
1553#ifdef COLOR_LS_F
1554    if (eq(vp, STRLS_COLORS)) {
1555        parseLS_COLORS(lp);
1556	cleanup_until(lp);
1557	return;
1558    }
1559    if (eq(vp, STRLSCOLORS)) {
1560        parseLSCOLORS(lp);
1561	cleanup_until(lp);
1562	return;
1563    }
1564#endif /* COLOR_LS_F */
1565
1566#ifdef SIG_WINDOW
1567    /*
1568     * Load/Update $LINES $COLUMNS
1569     */
1570    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
1571	eq(vp, STRTERMCAP)) {
1572	cleanup_until(lp);
1573	check_window_size(1);
1574	return;
1575    }
1576
1577    /*
1578     * Change the size to the one directed by $LINES and $COLUMNS
1579     */
1580    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
1581#if 0
1582	GotTermCaps = 0;
1583#endif
1584	cleanup_until(lp);
1585	ed_Init();
1586	return;
1587    }
1588#endif /* SIG_WINDOW */
1589    cleanup_until(lp);
1590}
1591
1592/*ARGSUSED*/
1593void
1594dounsetenv(Char **v, struct command *c)
1595{
1596    Char  **ep, *p, *n, *name;
1597    int     i, maxi;
1598
1599    USE(c);
1600    /*
1601     * Find the longest environment variable
1602     */
1603    for (maxi = 0, ep = STR_environ; *ep; ep++) {
1604	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
1605	    continue;
1606	if (i > maxi)
1607	    maxi = i;
1608    }
1609
1610    name = xmalloc((maxi + 1) * sizeof(Char));
1611    cleanup_push(name, xfree);
1612
1613    while (++v && *v)
1614	for (maxi = 1; maxi;)
1615	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
1616		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
1617		    continue;
1618		*n = '\0';
1619		if (!Gmatch(name, *v))
1620		    continue;
1621		maxi = 1;
1622
1623		/* Unset the name. This wasn't being done until
1624		 * later but most of the stuff following won't
1625		 * work (particularly the setlocale() and getenv()
1626		 * stuff) as intended until the name is actually
1627		 * removed. (sg)
1628		 */
1629		Unsetenv(name);
1630
1631		if (eq(name, STRNOREBIND)) {
1632		    NoNLSRebind = 0;
1633		    MapsAreInited = 0;
1634		    NLSMapsAreInited = 0;
1635		    ed_InitMaps();
1636		}
1637#ifdef apollo
1638		else if (eq(name, STRSYSTYPE))
1639		    dohash(NULL, NULL);
1640#endif /* apollo */
1641		else if (islocale_var(name)) {
1642#ifdef NLS
1643		    int     k;
1644
1645# ifdef SETLOCALEBUG
1646		    dont_free = 1;
1647# endif /* SETLOCALEBUG */
1648		    (void) setlocale(LC_ALL, "");
1649# ifdef LC_COLLATE
1650		    (void) setlocale(LC_COLLATE, "");
1651# endif
1652# ifdef LC_CTYPE
1653		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1654# endif /* LC_CTYPE */
1655# ifdef NLS_CATALOGS
1656#  ifdef LC_MESSAGES
1657		    (void) setlocale(LC_MESSAGES, "");
1658#  endif /* LC_MESSAGES */
1659		    nlsclose();
1660		    nlsinit();
1661# endif /* NLS_CATALOGS */
1662# ifdef SETLOCALEBUG
1663		    dont_free = 0;
1664# endif /* SETLOCALEBUG */
1665# ifdef STRCOLLBUG
1666		    fix_strcoll_bug();
1667# endif /* STRCOLLBUG */
1668		    tw_cmd_free();/* since the collation sequence has changed */
1669		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
1670			continue;
1671		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
1672#else /* !NLS */
1673		    AsciiOnly = getenv("LANG") == NULL &&
1674			getenv("LC_CTYPE") == NULL;
1675#endif /* NLS */
1676		    NLSMapsAreInited = 0;
1677		    ed_Init();
1678		    if (MapsAreInited && !NLSMapsAreInited)
1679			ed_InitNLSMaps();
1680
1681		}
1682#ifdef WINNT_NATIVE
1683		else if (eq(name,(STRtcshlang))) {
1684		    nls_dll_unload();
1685		    nlsinit();
1686		}
1687#endif /* WINNT_NATIVE */
1688#ifdef COLOR_LS_F
1689		else if (eq(name, STRLS_COLORS))
1690		    parseLS_COLORS(n);
1691		else if (eq(name, STRLSCOLORS))
1692		    parseLSCOLORS(n);
1693#endif /* COLOR_LS_F */
1694#ifdef NLS_CATALOGS
1695		else if (eq(name, STRNLSPATH)) {
1696		    nlsclose();
1697		    nlsinit();
1698		}
1699#endif
1700		/*
1701		 * start again cause the environment changes
1702		 */
1703		break;
1704	    }
1705    cleanup_until(name);
1706}
1707
1708void
1709tsetenv(const Char *name, const Char *val)
1710{
1711#ifdef SETENV_IN_LIB
1712/*
1713 * XXX: This does not work right, since tcsh cannot track changes to
1714 * the environment this way. (the builtin setenv without arguments does
1715 * not print the right stuff neither does unsetenv). This was for Mach,
1716 * it is not needed anymore.
1717 */
1718#undef setenv
1719    char   *cname;
1720
1721    if (name == NULL)
1722	return;
1723    cname = strsave(short2str(name));
1724    setenv(cname, short2str(val), 1);
1725    xfree(cname);
1726#else /* !SETENV_IN_LIB */
1727    Char **ep = STR_environ;
1728    const Char *ccp;
1729    Char *cp, *dp;
1730    Char   *blk[2];
1731    Char  **oep = ep;
1732
1733#ifdef WINNT_NATIVE
1734    nt_set_env(name,val);
1735#endif /* WINNT_NATIVE */
1736    for (; *ep; ep++) {
1737#ifdef WINNT_NATIVE
1738	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1739				ccp++, dp++)
1740#else
1741	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
1742#endif /* WINNT_NATIVE */
1743	    continue;
1744	if (*ccp != 0 || *dp != '=')
1745	    continue;
1746	cp = Strspl(STRequal, val);
1747	xfree(*ep);
1748	*ep = strip(Strspl(name, cp));
1749	xfree(cp);
1750	blkfree((Char **) environ);
1751	environ = short2blk(STR_environ);
1752	return;
1753    }
1754    cp = Strspl(name, STRequal);
1755    blk[0] = strip(Strspl(cp, val));
1756    xfree(cp);
1757    blk[1] = 0;
1758    STR_environ = blkspl(STR_environ, blk);
1759    blkfree((Char **) environ);
1760    environ = short2blk(STR_environ);
1761    xfree(oep);
1762#endif /* SETENV_IN_LIB */
1763}
1764
1765void
1766Unsetenv(Char *name)
1767{
1768    Char **ep = STR_environ;
1769    Char *cp, *dp;
1770    Char **oep = ep;
1771
1772#ifdef WINNT_NATIVE
1773	nt_set_env(name,NULL);
1774#endif /*WINNT_NATIVE */
1775    for (; *ep; ep++) {
1776	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
1777	    continue;
1778	if (*cp != 0 || *dp != '=')
1779	    continue;
1780	cp = *ep;
1781	*ep = 0;
1782	STR_environ = blkspl(STR_environ, ep + 1);
1783	blkfree((Char **) environ);
1784	environ = short2blk(STR_environ);
1785	*ep = cp;
1786	xfree(cp);
1787	xfree(oep);
1788	return;
1789    }
1790}
1791
1792/*ARGSUSED*/
1793void
1794doumask(Char **v, struct command *c)
1795{
1796    Char *cp = v[1];
1797    int i;
1798
1799    USE(c);
1800    if (cp == 0) {
1801	i = (int)umask(0);
1802	(void) umask(i);
1803	xprintf("%o\n", i);
1804	return;
1805    }
1806    i = 0;
1807    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
1808	i = i * 8 + *cp++ - '0';
1809    if (*cp || i < 0 || i > 0777)
1810	stderror(ERR_NAME | ERR_MASK);
1811    (void) umask(i);
1812}
1813
1814#ifndef HAVENOLIMIT
1815# ifndef BSDLIMIT
1816   typedef long RLIM_TYPE;
1817#  ifdef _OSD_POSIX /* BS2000 */
1818#   include <ulimit.h>
1819#  endif
1820#  ifndef RLIM_INFINITY
1821#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
1822    extern RLIM_TYPE ulimit();
1823#   endif /* ! _MINIX && !__clipper__ */
1824#   define RLIM_INFINITY 0x003fffff
1825#   define RLIMIT_FSIZE 1
1826#  endif /* RLIM_INFINITY */
1827#  ifdef aiws
1828#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
1829#   define RLIMIT_DATA	3
1830#   define RLIMIT_STACK 1005
1831#  else /* aiws */
1832#   define toset(a) ((a) + 1)
1833#  endif /* aiws */
1834# else /* BSDLIMIT */
1835#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1836    typedef rlim_t RLIM_TYPE;
1837#  else
1838#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
1839     typedef rlim_t RLIM_TYPE;
1840#   else
1841#    if defined(_SX)
1842      typedef long long RLIM_TYPE;
1843#    else /* !_SX */
1844      typedef unsigned long RLIM_TYPE;
1845#    endif /* _SX */
1846#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
1847#  endif /* BSD4_4 && !__386BSD__  */
1848# endif /* BSDLIMIT */
1849
1850# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
1851/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
1852/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
1853#  ifndef RLIMIT_CPU
1854#   define RLIMIT_CPU		0
1855#   define RLIMIT_FSIZE		1
1856#   define RLIMIT_DATA		2
1857#   define RLIMIT_STACK		3
1858#   define RLIMIT_CORE		4
1859#   define RLIMIT_RSS		5
1860#   define RLIMIT_NOFILE	6
1861#  endif /* RLIMIT_CPU */
1862#  ifndef RLIM_INFINITY
1863#   define RLIM_INFINITY	0x7fffffff
1864#  endif /* RLIM_INFINITY */
1865   /*
1866    * old versions of HP/UX counted limits in 512 bytes
1867    */
1868#  ifndef SIGRTMIN
1869#   define FILESIZE512
1870#  endif /* SIGRTMIN */
1871# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
1872
1873# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
1874/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
1875/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
1876/* than include both and get warnings, we define the extra SVR4 limits here. */
1877/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
1878/* RLIMIT_VMEM based on it? */
1879#  ifndef RLIMIT_VMEM
1880#   define RLIMIT_VMEM	6
1881#  endif
1882#  ifndef RLIMIT_AS
1883#   define RLIMIT_AS	RLIMIT_VMEM
1884#  endif
1885# endif /* SYSVREL > 3 && BSDLIMIT */
1886
1887# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
1888#  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
1889#   define RLIMIT_VMEM	RLIMIT_AS
1890#  endif
1891/*
1892 * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
1893 * Linux headers: When the left hand does not know what the right hand does.
1894 */
1895#  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
1896#   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
1897#  endif
1898# endif
1899
1900struct limits limits[] =
1901{
1902# ifdef RLIMIT_CPU
1903    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
1904# endif /* RLIMIT_CPU */
1905
1906# ifdef RLIMIT_FSIZE
1907#  ifndef aiws
1908    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
1909#  else
1910    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
1911#  endif /* aiws */
1912# endif /* RLIMIT_FSIZE */
1913
1914# ifdef RLIMIT_DATA
1915    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
1916# endif /* RLIMIT_DATA */
1917
1918# ifdef RLIMIT_STACK
1919#  ifndef aiws
1920    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
1921#  else
1922    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
1923#  endif /* aiws */
1924# endif /* RLIMIT_STACK */
1925
1926# ifdef RLIMIT_CORE
1927    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
1928# endif /* RLIMIT_CORE */
1929
1930# ifdef RLIMIT_RSS
1931    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
1932# endif /* RLIMIT_RSS */
1933
1934# ifdef RLIMIT_UMEM
1935    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
1936# endif /* RLIMIT_UMEM */
1937
1938# ifdef RLIMIT_VMEM
1939    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
1940# endif /* RLIMIT_VMEM */
1941
1942# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1943    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1944# endif /* RLIMIT_HEAP */
1945
1946# ifdef RLIMIT_NOFILE
1947    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
1948# endif /* RLIMIT_NOFILE */
1949
1950# ifdef RLIMIT_NPTS
1951    { RLIMIT_NPTS,	"pseudoterminals", 1,	""		},
1952# endif /* RLIMIT_NPTS */
1953
1954# ifdef RLIMIT_KQUEUES
1955    { RLIMIT_KQUEUES,	"kqueues",	1,	""		},
1956# endif /* RLIMIT_KQUEUES */
1957
1958# ifdef RLIMIT_CONCUR
1959    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
1960# endif /* RLIMIT_CONCUR */
1961
1962# ifdef RLIMIT_MEMLOCK
1963    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
1964# endif /* RLIMIT_MEMLOCK */
1965
1966# ifdef RLIMIT_NPROC
1967    { RLIMIT_NPROC,	"maxproc",	1,	""		},
1968# endif /* RLIMIT_NPROC */
1969
1970# ifdef RLIMIT_NTHR
1971    { RLIMIT_NTHR,	"maxthread",	1,	""		},
1972# endif /* RLIMIT_NTHR */
1973
1974# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
1975    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1976# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
1977
1978# ifdef RLIMIT_SBSIZE
1979    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1980# endif /* RLIMIT_SBSIZE */
1981
1982# ifdef RLIMIT_SWAP
1983    { RLIMIT_SWAP,	"swapsize",	1024,	"kbytes"	},
1984# endif /* RLIMIT_SWAP */
1985
1986# ifdef RLIMIT_LOCKS
1987    { RLIMIT_LOCKS,	"maxlocks",	1,	""		},
1988# endif /* RLIMIT_LOCKS */
1989
1990# ifdef RLIMIT_POSIXLOCKS
1991    { RLIMIT_POSIXLOCKS,"posixlocks",	1,	""		},
1992# endif /* RLIMIT_POSIXLOCKS */
1993
1994# ifdef RLIMIT_SIGPENDING
1995    { RLIMIT_SIGPENDING,"maxsignal",	1,	""		},
1996# endif /* RLIMIT_SIGPENDING */
1997
1998# ifdef RLIMIT_MSGQUEUE
1999    { RLIMIT_MSGQUEUE,	"maxmessage",	1,	""		},
2000# endif /* RLIMIT_MSGQUEUE */
2001
2002# ifdef RLIMIT_NICE
2003    { RLIMIT_NICE,	"maxnice",	1,	""		},
2004# endif /* RLIMIT_NICE */
2005
2006# ifdef RLIMIT_RTPRIO
2007    { RLIMIT_RTPRIO,	"maxrtprio",	1,	""		},
2008# endif /* RLIMIT_RTPRIO */
2009
2010# ifdef RLIMIT_RTTIME
2011    { RLIMIT_RTTIME,	"maxrttime",	1,	"usec"		},
2012# endif /* RLIMIT_RTTIME */
2013
2014    { -1, 		NULL, 		0, 	NULL		}
2015};
2016
2017static struct limits *findlim	(Char *);
2018static RLIM_TYPE getval		(struct limits *, Char **);
2019static int strtail		(Char *, const char *);
2020static void limtail		(Char *, const char *);
2021static void limtail2		(Char *, const char *, const char *);
2022static void plim		(struct limits *, int);
2023static int setlim		(struct limits *, int, RLIM_TYPE);
2024
2025#ifdef convex
2026static  RLIM_TYPE
2027restrict_limit(double value)
2028{
2029    /*
2030     * is f too large to cope with? return the maximum or minimum int
2031     */
2032    if (value > (double) INT_MAX)
2033	return (RLIM_TYPE) INT_MAX;
2034    else if (value < (double) INT_MIN)
2035	return (RLIM_TYPE) INT_MIN;
2036    else
2037	return (RLIM_TYPE) value;
2038}
2039#else /* !convex */
2040# define restrict_limit(x)	((RLIM_TYPE) (x))
2041#endif /* convex */
2042
2043
2044static struct limits *
2045findlim(Char *cp)
2046{
2047    struct limits *lp, *res;
2048
2049    res = NULL;
2050    for (lp = limits; lp->limconst >= 0; lp++)
2051	if (prefix(cp, str2short(lp->limname))) {
2052	    if (res)
2053		stderror(ERR_NAME | ERR_AMBIG);
2054	    res = lp;
2055	}
2056    if (res)
2057	return (res);
2058    stderror(ERR_NAME | ERR_LIMIT);
2059    /* NOTREACHED */
2060    return (0);
2061}
2062
2063/*ARGSUSED*/
2064void
2065dolimit(Char **v, struct command *c)
2066{
2067    struct limits *lp;
2068    RLIM_TYPE limit;
2069    int    hard = 0;
2070
2071    USE(c);
2072    v++;
2073    if (*v && eq(*v, STRmh)) {
2074	hard = 1;
2075	v++;
2076    }
2077    if (*v == 0) {
2078	for (lp = limits; lp->limconst >= 0; lp++)
2079	    plim(lp, hard);
2080	return;
2081    }
2082    lp = findlim(v[0]);
2083    if (v[1] == 0) {
2084	plim(lp, hard);
2085	return;
2086    }
2087    limit = getval(lp, v + 1);
2088    if (setlim(lp, hard, limit) < 0)
2089	stderror(ERR_SILENT);
2090}
2091
2092static  RLIM_TYPE
2093getval(struct limits *lp, Char **v)
2094{
2095    float f;
2096    Char   *cp = *v++;
2097
2098    f = atof(short2str(cp));
2099
2100# ifdef convex
2101    /*
2102     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
2103     * strike
2104     */
2105    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
2106	stderror(ERR_NAME | ERR_TOOLARGE);
2107    }
2108# endif /* convex */
2109
2110    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
2111	cp++;
2112    if (*cp == 0) {
2113	if (*v == 0)
2114	    return restrict_limit((f * lp->limdiv) + 0.5);
2115	cp = *v;
2116    }
2117    switch (*cp) {
2118# ifdef RLIMIT_CPU
2119    case ':':
2120	if (lp->limconst != RLIMIT_CPU)
2121	    goto badscal;
2122	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
2123    case 'h':
2124	if (lp->limconst != RLIMIT_CPU)
2125	    goto badscal;
2126	limtail(cp, "hours");
2127	f *= 3600.0;
2128	break;
2129# endif /* RLIMIT_CPU */
2130    case 'm':
2131# ifdef RLIMIT_CPU
2132	if (lp->limconst == RLIMIT_CPU) {
2133	    limtail(cp, "minutes");
2134	    f *= 60.0;
2135	    break;
2136	}
2137# endif /* RLIMIT_CPU */
2138	limtail2(cp, "megabytes", "mbytes");
2139	f *= 1024.0 * 1024.0;
2140	break;
2141# ifdef RLIMIT_CPU
2142    case 's':
2143	if (lp->limconst != RLIMIT_CPU)
2144	    goto badscal;
2145	limtail(cp, "seconds");
2146	break;
2147# endif /* RLIMIT_CPU */
2148    case 'G':
2149	*cp = 'g';
2150	/*FALLTHROUGH*/
2151    case 'g':
2152# ifdef RLIMIT_CPU
2153	if (lp->limconst == RLIMIT_CPU)
2154	    goto badscal;
2155# endif /* RLIMIT_CPU */
2156	limtail2(cp, "gigabytes", "gbytes");
2157	f *= 1024.0 * 1024.0 * 1024.0;
2158	break;
2159    case 'M':
2160# ifdef RLIMIT_CPU
2161	if (lp->limconst == RLIMIT_CPU)
2162	    goto badscal;
2163# endif /* RLIMIT_CPU */
2164	*cp = 'm';
2165	limtail2(cp, "megabytes", "mbytes");
2166	f *= 1024.0 * 1024.0;
2167	break;
2168    case 'k':
2169# ifdef RLIMIT_CPU
2170	if (lp->limconst == RLIMIT_CPU)
2171	    goto badscal;
2172# endif /* RLIMIT_CPU */
2173	limtail2(cp, "kilobytes", "kbytes");
2174	f *= 1024.0;
2175	break;
2176    case 'b':
2177# ifdef RLIMIT_CPU
2178	if (lp->limconst == RLIMIT_CPU)
2179	    goto badscal;
2180# endif /* RLIMIT_CPU */
2181	limtail(cp, "blocks");
2182	f *= 512.0;
2183	break;
2184    case 'u':
2185	limtail(cp, "unlimited");
2186	return ((RLIM_TYPE) RLIM_INFINITY);
2187    default:
2188# ifdef RLIMIT_CPU
2189badscal:
2190# endif /* RLIMIT_CPU */
2191	stderror(ERR_NAME | ERR_SCALEF);
2192    }
2193# ifdef convex
2194    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
2195# else
2196    f += 0.5;
2197    if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
2198	return ((RLIM_TYPE) RLIM_INFINITY);
2199    else
2200	return ((RLIM_TYPE) f);
2201# endif /* convex */
2202}
2203
2204static int
2205strtail(Char *cp, const char *str)
2206{
2207    while (*cp && *cp == (Char)*str)
2208	cp++, str++;
2209    return (*cp != '\0');
2210}
2211
2212static void
2213limtail(Char *cp, const char *str)
2214{
2215    if (strtail(cp, str))
2216	stderror(ERR_BADSCALE, str);
2217}
2218
2219static void
2220limtail2(Char *cp, const char *str1, const char *str2)
2221{
2222    if (strtail(cp, str1) && strtail(cp, str2))
2223	stderror(ERR_BADSCALE, str1);
2224}
2225
2226/*ARGSUSED*/
2227static void
2228plim(struct limits *lp, int hard)
2229{
2230# ifdef BSDLIMIT
2231    struct rlimit rlim;
2232# endif /* BSDLIMIT */
2233    RLIM_TYPE limit;
2234    int     xdiv = lp->limdiv;
2235
2236    xprintf("%-13.13s", lp->limname);
2237
2238# ifndef BSDLIMIT
2239    limit = ulimit(lp->limconst, 0);
2240#  ifdef aiws
2241    if (lp->limconst == RLIMIT_DATA)
2242	limit -= 0x20000000;
2243#  endif /* aiws */
2244# else /* BSDLIMIT */
2245    (void) getrlimit(lp->limconst, &rlim);
2246    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
2247# endif /* BSDLIMIT */
2248
2249# if !defined(BSDLIMIT) || defined(FILESIZE512)
2250    /*
2251     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
2252     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
2253     */
2254    if (lp->limconst == RLIMIT_FSIZE) {
2255	if (limit >= (RLIM_INFINITY / 512))
2256	    limit = RLIM_INFINITY;
2257	else
2258	    xdiv = (xdiv == 1024 ? 2 : 1);
2259    }
2260# endif /* !BSDLIMIT || FILESIZE512 */
2261
2262    if (limit == RLIM_INFINITY)
2263	xprintf("unlimited");
2264    else
2265# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2266    if (lp->limconst == RLIMIT_CPU &&
2267        (unsigned long)limit >= 0x7ffffffdUL)
2268	xprintf("unlimited");
2269    else
2270# endif
2271# ifdef RLIMIT_CPU
2272    if (lp->limconst == RLIMIT_CPU)
2273	psecs(limit);
2274    else
2275# endif /* RLIMIT_CPU */
2276	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
2277    xputchar('\n');
2278}
2279
2280/*ARGSUSED*/
2281void
2282dounlimit(Char **v, struct command *c)
2283{
2284    struct limits *lp;
2285    int    lerr = 0;
2286    int    hard = 0;
2287    int	   force = 0;
2288
2289    USE(c);
2290    while (*++v && **v == '-') {
2291	Char   *vp = *v;
2292	while (*++vp)
2293	    switch (*vp) {
2294	    case 'f':
2295		force = 1;
2296		break;
2297	    case 'h':
2298		hard = 1;
2299		break;
2300	    default:
2301		stderror(ERR_ULIMUS);
2302		break;
2303	    }
2304    }
2305
2306    if (*v == 0) {
2307	for (lp = limits; lp->limconst >= 0; lp++)
2308	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
2309		lerr++;
2310	if (!force && lerr)
2311	    stderror(ERR_SILENT);
2312	return;
2313    }
2314    while (*v) {
2315	lp = findlim(*v++);
2316	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
2317	    stderror(ERR_SILENT);
2318    }
2319}
2320
2321static int
2322setlim(struct limits *lp, int hard, RLIM_TYPE limit)
2323{
2324# ifdef BSDLIMIT
2325    struct rlimit rlim;
2326
2327    (void) getrlimit(lp->limconst, &rlim);
2328
2329#  ifdef FILESIZE512
2330    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
2331    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
2332	limit /= 512;
2333#  endif /* FILESIZE512 */
2334    if (hard)
2335	rlim.rlim_max = limit;
2336    else if (limit == RLIM_INFINITY && euid != 0)
2337	rlim.rlim_cur = rlim.rlim_max;
2338    else
2339	rlim.rlim_cur = limit;
2340
2341    if (rlim.rlim_cur > rlim.rlim_max)
2342	rlim.rlim_max = rlim.rlim_cur;
2343
2344    if (setrlimit(lp->limconst, &rlim) < 0) {
2345# else /* BSDLIMIT */
2346    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
2347	limit /= 512;
2348# ifdef aiws
2349    if (lp->limconst == RLIMIT_DATA)
2350	limit += 0x20000000;
2351# endif /* aiws */
2352    if (ulimit(toset(lp->limconst), limit) < 0) {
2353# endif /* BSDLIMIT */
2354        int err;
2355        char *op, *type;
2356
2357	err = errno;
2358	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2359		     	CGETS(15, 3, "set"));
2360	cleanup_push(op, xfree);
2361	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2362	cleanup_push(type, xfree);
2363	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2364	    lp->limname, op, type, strerror(err));
2365	cleanup_until(op);
2366	return (-1);
2367    }
2368    return (0);
2369}
2370
2371#endif /* !HAVENOLIMIT */
2372
2373/*ARGSUSED*/
2374void
2375dosuspend(Char **v, struct command *c)
2376{
2377#ifdef BSDJOBS
2378    struct sigaction old;
2379#endif /* BSDJOBS */
2380
2381    USE(c);
2382    USE(v);
2383
2384    if (loginsh)
2385	stderror(ERR_SUSPLOG);
2386    untty();
2387
2388#ifdef BSDJOBS
2389    sigaction(SIGTSTP, NULL, &old);
2390    signal(SIGTSTP, SIG_DFL);
2391    (void) kill(0, SIGTSTP);
2392    /* the shell stops here */
2393    sigaction(SIGTSTP, &old, NULL);
2394#else /* !BSDJOBS */
2395    stderror(ERR_JOBCONTROL);
2396#endif /* BSDJOBS */
2397
2398#ifdef BSDJOBS
2399    if (tpgrp != -1) {
2400	if (grabpgrp(FSHTTY, opgrp) == -1)
2401	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
2402	(void) setpgid(0, shpgrp);
2403	(void) tcsetpgrp(FSHTTY, shpgrp);
2404    }
2405#endif /* BSDJOBS */
2406    (void) setdisc(FSHTTY);
2407}
2408
2409/* This is the dreaded EVAL built-in.
2410 *   If you don't fiddle with file descriptors, and reset didfds,
2411 *   this command will either ignore redirection inside or outside
2412 *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
2413 *   The stuff here seems to work, but I did it by trial and error rather
2414 *   than really knowing what was going on.  If tpgrp is zero, we are
2415 *   probably a background eval, e.g. "eval date &", and we want to
2416 *   make sure that any processes we start stay in our pgrp.
2417 *   This is also the case for "time eval date" -- stay in same pgrp.
2418 *   Otherwise, under stty tostop, processes will stop in the wrong
2419 *   pgrp, with no way for the shell to get them going again.  -IAN!
2420 */
2421
2422struct doeval_state
2423{
2424    Char **evalvec, *evalp;
2425    int didfds;
2426#ifndef CLOSE_ON_EXEC
2427    int didcch;
2428#endif
2429    int saveIN, saveOUT, saveDIAG;
2430    int SHIN, SHOUT, SHDIAG;
2431};
2432
2433static void
2434doeval_cleanup(void *xstate)
2435{
2436    struct doeval_state *state;
2437
2438    state = xstate;
2439    evalvec = state->evalvec;
2440    evalp = state->evalp;
2441    doneinp = 0;
2442#ifndef CLOSE_ON_EXEC
2443    didcch = state->didcch;
2444#endif /* CLOSE_ON_EXEC */
2445    didfds = state->didfds;
2446    if (state->saveIN != SHIN)
2447	xclose(SHIN);
2448    if (state->saveOUT != SHOUT)
2449	xclose(SHOUT);
2450    if (state->saveDIAG != SHDIAG)
2451	xclose(SHDIAG);
2452    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2453    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2454    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2455    if (didfds) {
2456	close_on_exec(dcopy(SHIN, 0), 1);
2457	close_on_exec(dcopy(SHOUT, 1), 1);
2458	close_on_exec(dcopy(SHDIAG, 2), 1);
2459    }
2460}
2461
2462static Char **Ggv;
2463/*ARGSUSED*/
2464void
2465doeval(Char **v, struct command *c)
2466{
2467    struct doeval_state state;
2468    int gflag, my_reenter;
2469    Char **gv;
2470    jmp_buf_t osetexit;
2471
2472    USE(c);
2473    v++;
2474    if (*v == 0)
2475	return;
2476    gflag = tglob(v);
2477    if (gflag) {
2478	gv = v = globall(v, gflag);
2479	if (v == 0)
2480	    stderror(ERR_NOMATCH);
2481	cleanup_push(gv, blk_cleanup);
2482	v = copyblk(v);
2483    }
2484    else {
2485	gv = NULL;
2486	v = copyblk(v);
2487	trim(v);
2488    }
2489
2490    Ggv = gv;
2491    state.evalvec = evalvec;
2492    state.evalp = evalp;
2493    state.didfds = didfds;
2494#ifndef CLOSE_ON_EXEC
2495    state.didcch = didcch;
2496#endif /* CLOSE_ON_EXEC */
2497    state.SHIN = SHIN;
2498    state.SHOUT = SHOUT;
2499    state.SHDIAG = SHDIAG;
2500
2501    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2502    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2503    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2504
2505    cleanup_push(&state, doeval_cleanup);
2506
2507    getexit(osetexit);
2508
2509    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
2510#ifdef cray
2511    my_reenter = 1;             /* assume non-zero return val */
2512    if (setexit() == 0) {
2513	my_reenter = 0;         /* Oh well, we were wrong */
2514#else /* !cray */
2515    if ((my_reenter = setexit()) == 0) {
2516#endif /* cray */
2517	evalvec = v;
2518	evalp = 0;
2519	(void)close_on_exec(SHIN = dcopy(0, -1), 1);
2520	(void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2521	(void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
2522#ifndef CLOSE_ON_EXEC
2523	didcch = 0;
2524#endif /* CLOSE_ON_EXEC */
2525	didfds = 0;
2526	gv = Ggv;
2527	process(0);
2528	Ggv = gv;
2529    }
2530
2531    if (my_reenter == 0) {
2532	cleanup_until(&state);
2533	if (Ggv)
2534	    cleanup_until(Ggv);
2535    }
2536
2537    resexit(osetexit);
2538    if (my_reenter)
2539	stderror(ERR_SILENT);
2540}
2541
2542/*************************************************************************/
2543/* print list of builtin commands */
2544
2545static void
2546lbuffed_cleanup (void *dummy)
2547{
2548    USE(dummy);
2549    lbuffed = 1;
2550}
2551
2552/*ARGSUSED*/
2553void
2554dobuiltins(Char **v, struct command *c)
2555{
2556    /* would use print_by_column() in tw.parse.c but that assumes
2557     * we have an array of Char * to pass.. (sg)
2558     */
2559    const struct biltins *b;
2560    int row, col, columns, rows;
2561    unsigned int w, maxwidth;
2562
2563    USE(c);
2564    USE(v);
2565    lbuffed = 0;		/* turn off line buffering */
2566    cleanup_push(&lbuffed, lbuffed_cleanup);
2567
2568    /* find widest string */
2569    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
2570	maxwidth = max(maxwidth, strlen(b->bname));
2571    ++maxwidth;					/* for space */
2572
2573    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
2574    if (!columns)
2575	columns = 1;
2576    rows = (nbfunc + (columns - 1)) / columns;
2577
2578    for (b = bfunc, row = 0; row < rows; row++) {
2579	for (col = 0; col < columns; col++) {
2580	    if (b < &bfunc[nbfunc]) {
2581		w = strlen(b->bname);
2582		xprintf("%s", b->bname);
2583		if (col < (columns - 1))	/* Not last column? */
2584		    for (; w < maxwidth; w++)
2585			xputchar(' ');
2586		++b;
2587	    }
2588	}
2589	if (row < (rows - 1)) {
2590	    if (Tty_raw_mode)
2591		xputchar('\r');
2592	    xputchar('\n');
2593	}
2594    }
2595#ifdef WINNT_NATIVE
2596    nt_print_builtins(maxwidth);
2597#else
2598    if (Tty_raw_mode)
2599	xputchar('\r');
2600    xputchar('\n');
2601#endif /* WINNT_NATIVE */
2602
2603    cleanup_until(&lbuffed);		/* turn back on line buffering */
2604    flush();
2605}
2606
2607#ifdef NLS_CATALOGS
2608char *
2609xcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2610{
2611    char *res;
2612
2613    errno = 0;
2614    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2615	handle_pending_signals();
2616	errno = 0;
2617    }
2618    return res;
2619}
2620
2621
2622# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2623char *
2624iconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2625{
2626    static char *buf = NULL;
2627    static size_t buf_size = 0;
2628
2629    char *orig, *dest, *p;
2630    ICONV_CONST char *src;
2631    size_t src_size, dest_size;
2632
2633    orig = xcatgets(ctd, set_id, msg_id, s);
2634    if (catgets_iconv == (iconv_t)-1 || orig == s)
2635        return orig;
2636    src = orig;
2637    src_size = strlen(src) + 1;
2638    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2639	return orig;
2640    dest = buf;
2641    while (src_size != 0) {
2642        dest_size = buf + buf_size - dest;
2643	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2644	    == (size_t)-1) {
2645	    switch (errno) {
2646	    case E2BIG:
2647		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2648		    return orig;
2649		buf_size *= 2;
2650		dest = p + (dest - buf);
2651		buf = p;
2652		break;
2653
2654	    case EILSEQ: case EINVAL: default:
2655		return orig;
2656	    }
2657	}
2658    }
2659    return buf;
2660}
2661# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2662#endif /* NLS_CATALOGS */
2663
2664void
2665nlsinit(void)
2666{
2667#ifdef NLS_CATALOGS
2668    static const char default_catalog[] = "tcsh";
2669
2670    char *catalog = (char *)(intptr_t)default_catalog;
2671
2672    if (adrof(STRcatalog) != NULL)
2673	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
2674#ifdef NL_CAT_LOCALE /* POSIX-compliant. */
2675    /*
2676     * Check if LC_MESSAGES is set in the environment and use it, if so.
2677     * If not, fall back to the setting of LANG.
2678     */
2679    catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
2680#else /* pre-POSIX */
2681# ifndef MCLoadBySet
2682#  define MCLoadBySet 0
2683#  endif
2684    catd = catopen(catalog, MCLoadBySet);
2685#endif
2686    if (catalog != default_catalog)
2687	xfree(catalog);
2688#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2689    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2690    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2691				xcatgets(catd, 255, 1, "UTF-8"));
2692#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2693#endif /* NLS_CATALOGS */
2694#ifdef WINNT_NATIVE
2695    nls_dll_init();
2696#endif /* WINNT_NATIVE */
2697    errinit();		/* init the errorlist in correct locale */
2698    mesginit();		/* init the messages for signals */
2699    dateinit();		/* init the messages for dates */
2700    editinit();		/* init the editor messages */
2701    terminit();		/* init the termcap messages */
2702}
2703
2704void
2705nlsclose(void)
2706{
2707#ifdef NLS_CATALOGS
2708#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2709    if (catgets_iconv != (iconv_t)-1) {
2710	iconv_close(catgets_iconv);
2711	catgets_iconv = (iconv_t)-1;
2712    }
2713#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2714    if (catd != (nl_catd)-1) {
2715	/*
2716	 * catclose can call other functions which can call longjmp
2717	 * making us re-enter this code. Prevent infinite recursion
2718	 * by resetting catd. Problem reported and solved by:
2719	 * Gerhard Niklasch
2720	 */
2721	nl_catd oldcatd = catd;
2722	catd = (nl_catd)-1;
2723	while (catclose(oldcatd) == -1 && errno == EINTR)
2724	    handle_pending_signals();
2725    }
2726#endif /* NLS_CATALOGS */
2727}
2728
2729int
2730getYN(const char *prompt)
2731{
2732    int doit;
2733    char c;
2734
2735    xprintf("%s", prompt);
2736    flush();
2737    (void) force_read(SHIN, &c, sizeof(c));
2738    /*
2739     * Perhaps we should use the yesexpr from the
2740     * actual locale
2741     */
2742    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
2743    while (c != '\n' && force_read(SHIN, &c, sizeof(c)) == sizeof(c))
2744	continue;
2745    return doit;
2746}
2747