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