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