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