/* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.162 2011/02/26 00:07:06 christos Exp $ */ /* * sh.func.c: csh builtin functions */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sh.h" RCSID("$tcsh: sh.func.c,v 3.162 2011/02/26 00:07:06 christos Exp $") #include "ed.h" #include "tw.h" #include "tc.h" #ifdef WINNT_NATIVE #include "nt.const.h" #endif /* WINNT_NATIVE */ #if defined (NLS_CATALOGS) && defined(HAVE_ICONV) static iconv_t catgets_iconv; /* Or (iconv_t)-1 */ #endif /* * C shell */ extern int MapsAreInited; extern int NLSMapsAreInited; extern int GotTermCaps; static int zlast = -1; static void islogin (void); static void preread (void); static void doagain (void); static const char *isrchx (int); static void search (int, int, Char *); static int getword (struct Strbuf *); static struct wordent *histgetword (struct wordent *); static void toend (void); static void xecho (int, Char **); static int islocale_var (Char *); static void wpfree (struct whyle *); const struct biltins * isbfunc(struct command *t) { Char *cp = t->t_dcom[0]; const struct biltins *bp, *bp1, *bp2; static struct biltins label = {"", dozip, 0, 0}; static struct biltins foregnd = {"%job", dofg1, 0, 0}; static struct biltins backgnd = {"%job &", dobg1, 0, 0}; /* * We never match a builtin that has quoted the first * character; this has been the traditional way to escape * builtin commands. */ if (*cp & QUOTE) return NULL; if (*cp != ':' && lastchr(cp) == ':') { label.bname = short2str(cp); return (&label); } if (*cp == '%') { if (t->t_dflg & F_AMPERSAND) { t->t_dflg &= ~F_AMPERSAND; backgnd.bname = short2str(cp); return (&backgnd); } foregnd.bname = short2str(cp); return (&foregnd); } #ifdef WARP /* * This is a perhaps kludgy way to determine if the warp builtin is to be * acknowledged or not. If checkwarp() fails, then we are to assume that * the warp command is invalid, and carry on as we would handle any other * non-builtin command. -- JDK 2/4/88 */ if (eq(STRwarp, cp) && !checkwarp()) { return (0); /* this builtin disabled */ } #endif /* WARP */ /* * Binary search Bp1 is the beginning of the current search range. Bp2 is * one past the end. */ for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) { int i; bp = bp1 + ((bp2 - bp1) >> 1); if ((i = ((char) *cp) - *bp->bname) == 0 && (i = StrQcmp(cp, str2short(bp->bname))) == 0) return bp; if (i < 0) bp2 = bp; else bp1 = bp + 1; } #ifdef WINNT_NATIVE return nt_check_additional_builtins(cp); #endif /*WINNT_NATIVE*/ return (0); } void func(struct command *t, const struct biltins *bp) { int i; xechoit(t->t_dcom); setname(bp->bname); i = blklen(t->t_dcom) - 1; if (i < bp->minargs) stderror(ERR_NAME | ERR_TOOFEW); if (i > bp->maxargs) stderror(ERR_NAME | ERR_TOOMANY); (*bp->bfunct) (t->t_dcom, t); } /*ARGSUSED*/ void doonintr(Char **v, struct command *c) { Char *cp; Char *vv = v[1]; USE(c); if (parintr.sa_handler == SIG_IGN) return; if (setintr && intty) stderror(ERR_NAME | ERR_TERMINAL); cp = gointr; gointr = 0; xfree(cp); if (vv == 0) { if (setintr) sigset_interrupting(SIGINT, queue_pintr); else (void) signal(SIGINT, SIG_DFL); gointr = 0; } else if (eq((vv = strip(vv)), STRminus)) { (void) signal(SIGINT, SIG_IGN); gointr = Strsave(STRminus); } else { gointr = Strsave(vv); sigset_interrupting(SIGINT, queue_pintr); } } /*ARGSUSED*/ void donohup(Char **v, struct command *c) { USE(c); USE(v); if (intty) stderror(ERR_NAME | ERR_TERMINAL); if (setintr == 0) { (void) signal(SIGHUP, SIG_IGN); phup_disabled = 1; #ifdef CC submit(getpid()); #endif /* CC */ } } /*ARGSUSED*/ void dohup(Char **v, struct command *c) { USE(c); USE(v); if (intty) stderror(ERR_NAME | ERR_TERMINAL); if (setintr == 0) (void) signal(SIGHUP, SIG_DFL); } /*ARGSUSED*/ void dozip(Char **v, struct command *c) { USE(c); USE(v); } /*ARGSUSED*/ void dofiletest(Char **v, struct command *c) { Char **globbed, **fileptr, *ftest, *res; USE(c); if (*(ftest = *++v) != '-') stderror(ERR_NAME | ERR_FILEINQ); ++v; v = glob_all_or_error(v); globbed = v; cleanup_push(globbed, blk_cleanup); while (*(fileptr = v++) != '\0') { res = filetest(ftest, &fileptr, 0); cleanup_push(res, xfree); xprintf("%S", res); cleanup_until(res); if (*v) xprintf(" "); } xprintf("\n"); cleanup_until(globbed); } void prvars(void) { plist(&shvhed, VAR_ALL); } /*ARGSUSED*/ void doalias(Char **v, struct command *c) { struct varent *vp; Char *p; USE(c); v++; p = *v++; if (p == 0) plist(&aliases, VAR_ALL); else if (*v == 0) { vp = adrof1(strip(p), &aliases); if (vp && vp->vec) blkpr(vp->vec), xputchar('\n'); } else { if (eq(p, STRalias) || eq(p, STRunalias)) { setname(short2str(p)); stderror(ERR_NAME | ERR_DANGER); } set1(strip(p), saveblk(v), &aliases, VAR_READWRITE); tw_cmd_free(); } } /*ARGSUSED*/ void unalias(Char **v, struct command *c) { USE(c); unset1(v, &aliases); tw_cmd_free(); } /*ARGSUSED*/ void dologout(Char **v, struct command *c) { USE(c); USE(v); islogin(); goodbye(NULL, NULL); } /*ARGSUSED*/ void dologin(Char **v, struct command *c) { #ifdef WINNT_NATIVE USE(c); USE(v); #else /* !WINNT_NATIVE */ char **p = short2blk(v); USE(c); cleanup_push((Char **)p, blk_cleanup); islogin(); rechist(NULL, adrof(STRsavehist) != NULL); sigaction(SIGTERM, &parterm, NULL); (void) execv(_PATH_BIN_LOGIN, p); (void) execv(_PATH_USRBIN_LOGIN, p); cleanup_until((Char **)p); untty(); xexit(1); #endif /* !WINNT_NATIVE */ } #ifdef NEWGRP /*ARGSUSED*/ void donewgrp(Char **v, struct command *c) { char **p; if (chkstop == 0 && setintr) panystop(0); sigaction(SIGTERM, &parterm, NULL); p = short2blk(v); /* * From Beto Appleton (beto@aixwiz.austin.ibm.com) * Newgrp can take 2 arguments... */ (void) execv(_PATH_BIN_NEWGRP, p); (void) execv(_PATH_USRBIN_NEWGRP, p); blkfree((Char **) p); untty(); xexit(1); } #endif /* NEWGRP */ static void islogin(void) { if (chkstop == 0 && setintr) panystop(0); if (loginsh) return; stderror(ERR_NOTLOGIN); } void doif(Char **v, struct command *kp) { int i; Char **vv; v++; i = noexec ? 1 : expr(&v); vv = v; if (*vv == NULL) stderror(ERR_NAME | ERR_EMPTYIF); if (eq(*vv, STRthen)) { if (*++vv) stderror(ERR_NAME | ERR_IMPRTHEN); setname(short2str(STRthen)); /* * If expression was zero, then scan to else , otherwise just fall into * following code. */ if (!i) search(TC_IF, 0, NULL); return; } /* * Simple command attached to this if. Left shift the node in this tree, * munging it so we can reexecute it. */ if (i) { lshift(kp->t_dcom, vv - kp->t_dcom); reexecute(kp); donefds(); } } /* * Reexecute a command, being careful not * to redo i/o redirection, which is already set up. */ void reexecute(struct command *kp) { kp->t_dflg &= F_SAVE; kp->t_dflg |= F_REPEAT; /* * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set * pgrp's as the jobs would then have no way to get the tty (we can't give * it to them, and our parent wouldn't know their pgrp, etc. */ execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE); } /*ARGSUSED*/ void doelse (Char **v, struct command *c) { USE(c); USE(v); if (!noexec) search(TC_ELSE, 0, NULL); } /*ARGSUSED*/ void dogoto(Char **v, struct command *c) { Char *lp; USE(c); lp = globone(v[1], G_ERROR); cleanup_push(lp, xfree); if (!noexec) gotolab(lp); cleanup_until(lp); } void gotolab(Char *lab) { struct whyle *wp; /* * While we still can, locate any unknown ends of existing loops. This * obscure code is the WORST result of the fact that we don't really parse. */ zlast = TC_GOTO; for (wp = whyles; wp; wp = wp->w_next) if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) { search(TC_BREAK, 0, NULL); btell(&wp->w_end); } else { bseek(&wp->w_end); } search(TC_GOTO, 0, lab); /* * Eliminate loops which were exited. */ wfree(); } /*ARGSUSED*/ void doswitch(Char **v, struct command *c) { Char *cp, *lp; USE(c); v++; if (!*v || *(*v++) != '(') stderror(ERR_SYNTAX); cp = **v == ')' ? STRNULL : *v++; if (*(*v++) != ')') v--; if (*v) stderror(ERR_SYNTAX); lp = globone(cp, G_ERROR); cleanup_push(lp, xfree); if (!noexec) search(TC_SWITCH, 0, lp); cleanup_until(lp); } /*ARGSUSED*/ void dobreak(Char **v, struct command *c) { USE(v); USE(c); if (whyles == NULL) stderror(ERR_NAME | ERR_NOTWHILE); if (!noexec) toend(); } /*ARGSUSED*/ void doexit(Char **v, struct command *c) { USE(c); if (chkstop == 0 && (intty || intact) && evalvec == 0) panystop(0); /* * Don't DEMAND parentheses here either. */ v++; if (*v) { setv(STRstatus, putn(expr(&v)), VAR_READWRITE); if (*v) stderror(ERR_NAME | ERR_EXPRESSION); } btoeof(); #if 0 if (intty) #endif /* Always close, why only on ttys? */ xclose(SHIN); } /*ARGSUSED*/ void doforeach(Char **v, struct command *c) { Char *cp, *sp; struct whyle *nwp; int gflag; USE(c); v++; cp = sp = strip(*v); if (!letter(*cp)) stderror(ERR_NAME | ERR_VARBEGIN); do { cp++; } while (alnum(*cp)); if (*cp != '\0') stderror(ERR_NAME | ERR_VARALNUM); cp = *v++; if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') stderror(ERR_NAME | ERR_NOPAREN); v++; gflag = tglob(v); if (gflag) { v = globall(v, gflag); if (v == 0 && !noexec) stderror(ERR_NAME | ERR_NOMATCH); } else { v = saveblk(v); trim(v); } nwp = xcalloc(1, sizeof *nwp); nwp->w_fe = nwp->w_fe0 = v; btell(&nwp->w_start); nwp->w_fename = Strsave(cp); nwp->w_next = whyles; nwp->w_end.type = TCSH_F_SEEK; whyles = nwp; /* * Pre-read the loop so as to be more comprehensible to a terminal user. */ zlast = TC_FOREACH; if (intty) preread(); if (!noexec) doagain(); } /*ARGSUSED*/ void dowhile(Char **v, struct command *c) { int status; int again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) && whyles->w_fename == 0; USE(c); v++; /* * Implement prereading here also, taking care not to evaluate the * expression before the loop has been read up from a terminal. */ if (noexec) status = 0; else if (intty && !again) status = !exp0(&v, 1); else status = !expr(&v); if (*v && !noexec) stderror(ERR_NAME | ERR_EXPRESSION); if (!again) { struct whyle *nwp = xcalloc(1, sizeof(*nwp)); nwp->w_start = lineloc; nwp->w_end.type = TCSH_F_SEEK; nwp->w_end.f_seek = 0; nwp->w_end.a_seek = 0; nwp->w_next = whyles; whyles = nwp; zlast = TC_WHILE; if (intty) { /* * The tty preread */ preread(); doagain(); return; } } if (status) /* We ain't gonna loop no more, no more! */ toend(); } static void preread(void) { int old_pintr_disabled; whyles->w_end.type = TCSH_I_SEEK; if (setintr) pintr_push_enable(&old_pintr_disabled); search(TC_BREAK, 0, NULL); /* read the expression in */ if (setintr) cleanup_until(&old_pintr_disabled); btell(&whyles->w_end); } /*ARGSUSED*/ void doend(Char **v, struct command *c) { USE(v); USE(c); if (!whyles) stderror(ERR_NAME | ERR_NOTWHILE); btell(&whyles->w_end); if (!noexec) doagain(); } /*ARGSUSED*/ void docontin(Char **v, struct command *c) { USE(v); USE(c); if (!whyles) stderror(ERR_NAME | ERR_NOTWHILE); if (!noexec) doagain(); } static void doagain(void) { /* Repeating a while is simple */ if (whyles->w_fename == 0) { bseek(&whyles->w_start); return; } /* * The foreach variable list actually has a spurious word ")" at the end of * the w_fe list. Thus we are at the of the list if one word beyond this * is 0. */ if (!whyles->w_fe[1]) { dobreak(NULL, NULL); return; } setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE); bseek(&whyles->w_start); } void dorepeat(Char **v, struct command *kp) { int i = 1; do { i *= getn(v[1]); lshift(v, 2); } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0); if (noexec) i = 1; if (setintr) { pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } while (i > 0) { if (setintr && pintr_disabled == 1) { cleanup_until(&pintr_disabled); pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } reexecute(kp); --i; } if (setintr && pintr_disabled == 1) cleanup_until(&pintr_disabled); donefds(); } /*ARGSUSED*/ void doswbrk(Char **v, struct command *c) { USE(v); USE(c); if (!noexec) search(TC_BRKSW, 0, NULL); } int srchx(Char *cp) { struct srch *sp, *sp1, *sp2; int i; /* * Ignore keywords inside heredocs */ if (inheredoc) return -1; /* * Binary search Sp1 is the beginning of the current search range. Sp2 is * one past the end. */ for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) { sp = sp1 + ((sp2 - sp1) >> 1); if ((i = *cp - *sp->s_name) == 0 && (i = Strcmp(cp, str2short(sp->s_name))) == 0) return sp->s_value; if (i < 0) sp2 = sp; else sp1 = sp + 1; } return (-1); } static const char * isrchx(int n) { struct srch *sp, *sp2; for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++) if (sp->s_value == n) return (sp->s_name); return (""); } static int Stype; static Char *Sgoal; static void search(int type, int level, Char *goal) { struct Strbuf word = Strbuf_INIT; Char *cp; struct whyle *wp; int wlevel = 0; struct wordent *histent = NULL, *ohistent = NULL; Stype = type; Sgoal = goal; if (type == TC_GOTO) { struct Ain a; a.type = TCSH_F_SEEK; a.f_seek = 0; a.a_seek = 0; bseek(&a); } cleanup_push(&word, Strbuf_cleanup); do { if (intty) { histent = xmalloc(sizeof(*histent)); ohistent = xmalloc(sizeof(*histent)); ohistent->word = STRNULL; ohistent->next = histent; histent->prev = ohistent; } if (intty && fseekp == feobp && aret == TCSH_F_SEEK) printprompt(1, isrchx(type == TC_BREAK ? zlast : type)); /* xprintf("? "), flush(); */ (void) getword(&word); Strbuf_terminate(&word); if (intty && Strlen(word.s) > 0) { histent->word = Strsave(word.s); histent->next = xmalloc(sizeof(*histent)); histent->next->prev = histent; histent = histent->next; } switch (srchx(word.s)) { case TC_ELSE: if (level == 0 && type == TC_IF) goto end; break; case TC_IF: while (getword(&word)) continue; if ((type == TC_IF || type == TC_ELSE) && eq(word.s, STRthen)) level++; break; case TC_ENDIF: if (type == TC_IF || type == TC_ELSE) level--; break; case TC_FOREACH: case TC_WHILE: wlevel++; if (type == TC_BREAK) level++; break; case TC_END: if (type == TC_BRKSW) { if (wlevel == 0) { wp = whyles; if (wp) { whyles = wp->w_next; wpfree(wp); } } } if (type == TC_BREAK) level--; wlevel--; break; case TC_SWITCH: if (type == TC_SWITCH || type == TC_BRKSW) level++; break; case TC_ENDSW: if (type == TC_SWITCH || type == TC_BRKSW) level--; break; case TC_LABEL: if (type == TC_GOTO && getword(&word) && eq(word.s, goal)) level = -1; break; default: if (type != TC_GOTO && (type != TC_SWITCH || level != 0)) break; if (word.len == 0 || word.s[word.len - 1] != ':') break; word.s[--word.len] = 0; if ((type == TC_GOTO && eq(word.s, goal)) || (type == TC_SWITCH && eq(word.s, STRdefault))) level = -1; break; case TC_CASE: if (type != TC_SWITCH || level != 0) break; (void) getword(&word); if (word.len != 0 && word.s[word.len - 1] == ':') word.s[--word.len] = 0; cp = strip(Dfix1(word.s)); cleanup_push(cp, xfree); if (Gmatch(goal, cp)) level = -1; cleanup_until(cp); break; case TC_DEFAULT: if (type == TC_SWITCH && level == 0) level = -1; break; } if (intty) { ohistent->prev = histgetword(histent); ohistent->prev->next = ohistent; savehist(ohistent, 0); freelex(ohistent); xfree(ohistent); } else (void) getword(NULL); } while (level >= 0); end: cleanup_until(&word); } static struct wordent * histgetword(struct wordent *histent) { int found = 0, first; eChar c, d; int e; struct Strbuf *tmp; tmp = xmalloc(sizeof(*tmp)); tmp->size = 0; tmp->s = NULL; c = readc(1); d = 0; e = 0; for (;;) { tmp->len = 0; Strbuf_terminate (tmp); while (c == ' ' || c == '\t') c = readc(1); if (c == '#') do c = readc(1); while (c != CHAR_ERR && c != '\n'); if (c == CHAR_ERR) goto past; if (c == '\n') goto nl; unreadc(c); found = 1; first = 1; do { e = (c == '\\'); c = readc(1); if (c == '\\' && !e) { if ((c = readc(1)) == '\n') { e = 1; c = ' '; } else { unreadc(c); c = '\\'; } } if ((c == '\'' || c == '"') && !e) { if (d == 0) d = c; else if (d == c) d = 0; } if (c == CHAR_ERR) goto past; Strbuf_append1(tmp, (Char) c); if (!first && !d && c == '(' && !e) { break; } first = 0; } while (d || e || (c != ' ' && c != '\t' && c != '\n')); tmp->len--; if (tmp->len) { Strbuf_terminate(tmp); histent->word = Strsave(tmp->s); histent->next = xmalloc(sizeof (*histent)); histent->next->prev = histent; histent = histent->next; } if (c == '\n') { nl: tmp->len = 0; Strbuf_append1(tmp, (Char) c); Strbuf_terminate(tmp); histent->word = Strsave(tmp->s); return histent; } } past: switch (Stype) { case TC_IF: stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); break; case TC_ELSE: stderror(ERR_NAME | ERR_NOTFOUND, "endif"); break; case TC_BRKSW: case TC_SWITCH: stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); break; case TC_BREAK: stderror(ERR_NAME | ERR_NOTFOUND, "end"); break; case TC_GOTO: setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "label"); break; default: break; } /* NOTREACHED */ return NULL; } static int getword(struct Strbuf *wp) { int found = 0, first; eChar c, d; if (wp) wp->len = 0; c = readc(1); d = 0; do { while (c == ' ' || c == '\t') c = readc(1); if (c == '#') do c = readc(1); while (c != CHAR_ERR && c != '\n'); if (c == CHAR_ERR) goto past; if (c == '\n') { if (wp) break; return (0); } unreadc(c); found = 1; first = 1; do { c = readc(1); if (c == '\\' && (c = readc(1)) == '\n') c = ' '; if (c == '\'' || c == '"') { if (d == 0) d = c; else if (d == c) d = 0; } if (c == CHAR_ERR) goto past; if (wp) Strbuf_append1(wp, (Char) c); if (!first && !d && c == '(') { if (wp) goto past_word_end; else break; } first = 0; } while ((d || (c != ' ' && c != '\t')) && c != '\n'); } while (wp == 0); past_word_end: unreadc(c); if (found) { wp->len--; Strbuf_terminate(wp); } return (found); past: switch (Stype) { case TC_IF: stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); break; case TC_ELSE: stderror(ERR_NAME | ERR_NOTFOUND, "endif"); break; case TC_BRKSW: case TC_SWITCH: stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); break; case TC_BREAK: stderror(ERR_NAME | ERR_NOTFOUND, "end"); break; case TC_GOTO: setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "label"); break; default: break; } /* NOTREACHED */ return (0); } static void toend(void) { if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) { search(TC_BREAK, 0, NULL); btell(&whyles->w_end); whyles->w_end.f_seek--; } else { bseek(&whyles->w_end); } wfree(); } static void wpfree(struct whyle *wp) { if (wp->w_fe0) blkfree(wp->w_fe0); xfree(wp->w_fename); xfree(wp); } void wfree(void) { struct Ain o; struct whyle *nwp; #ifdef lint nwp = NULL; /* sun lint is dumb! */ #endif #ifdef FDEBUG static const char foo[] = "IAFE"; #endif /* FDEBUG */ btell(&o); #ifdef FDEBUG xprintf("o->type %c o->a_seek %d o->f_seek %d\n", foo[o.type + 1], o.a_seek, o.f_seek); #endif /* FDEBUG */ for (; whyles; whyles = nwp) { struct whyle *wp = whyles; nwp = wp->w_next; #ifdef FDEBUG xprintf("start->type %c start->a_seek %d start->f_seek %d\n", foo[wp->w_start.type+1], wp->w_start.a_seek, wp->w_start.f_seek); xprintf("end->type %c end->a_seek %d end->f_seek %d\n", foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek); #endif /* FDEBUG */ /* * XXX: We free loops that have different seek types. */ if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type && wp->w_start.type == o.type) { if (wp->w_end.type == TCSH_F_SEEK) { if (o.f_seek >= wp->w_start.f_seek && (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek)) break; } else { if (o.a_seek >= wp->w_start.a_seek && (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek)) break; } } wpfree(wp); } } /*ARGSUSED*/ void doecho(Char **v, struct command *c) { USE(c); xecho(' ', v); } /*ARGSUSED*/ void doglob(Char **v, struct command *c) { USE(c); xecho(0, v); flush(); } static void xecho(int sep, Char **v) { Char *cp, **globbed = NULL; int nonl = 0; int echo_style = ECHO_STYLE; struct varent *vp; if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL && vp->vec[0] != NULL) { if (Strcmp(vp->vec[0], STRbsd) == 0) echo_style = BSD_ECHO; else if (Strcmp(vp->vec[0], STRsysv) == 0) echo_style = SYSV_ECHO; else if (Strcmp(vp->vec[0], STRboth) == 0) echo_style = BOTH_ECHO; else if (Strcmp(vp->vec[0], STRnone) == 0) echo_style = NONE_ECHO; } v++; if (*v == 0) goto done; if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); v = glob_all_or_error(v); cleanup_until(&old_pintr_disabled); } else { v = glob_all_or_error(v); } globbed = v; if (globbed != NULL) cleanup_push(globbed, blk_cleanup); if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn)) nonl++, v++; while ((cp = *v++) != 0) { Char c; if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); cleanup_until(&old_pintr_disabled); } while ((c = *cp++) != 0) { if ((echo_style & SYSV_ECHO) != 0 && c == '\\') { switch (c = *cp++) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'c': nonl = 1; goto done; case 'e': #if 0 /* Windows does not understand \e */ c = '\e'; #else c = CTL_ESC('\033'); #endif break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\\': c = '\\'; break; case '0': c = 0; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; break; case '\0': c = '\\'; cp--; break; default: xputchar('\\' | QUOTE); break; } } xputwchar(c | QUOTE); } if (*v) xputchar(sep | QUOTE); } done: if (sep && nonl == 0) xputchar('\n'); else flush(); if (globbed != NULL) cleanup_until(globbed); } /* check whether an environment variable should invoke 'set_locale()' */ static int islocale_var(Char *var) { static Char *locale_vars[] = { STRLANG, STRLC_ALL, STRLC_CTYPE, STRLC_NUMERIC, STRLC_TIME, STRLC_COLLATE, STRLC_MESSAGES, STRLC_MONETARY, 0 }; Char **v; for (v = locale_vars; *v; ++v) if (eq(var, *v)) return 1; return 0; } static void xlate_cr_cleanup(void *dummy) { USE(dummy); xlate_cr = 0; } /*ARGSUSED*/ void doprintenv(Char **v, struct command *c) { Char *e; USE(c); v++; if (*v == 0) { Char **ep; xlate_cr = 1; cleanup_push(&xlate_cr, xlate_cr_cleanup); for (ep = STR_environ; *ep; ep++) { if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); cleanup_until(&old_pintr_disabled); } xprintf("%S\n", *ep); } cleanup_until(&xlate_cr); } else if ((e = tgetenv(*v)) != NULL) { int old_output_raw; old_output_raw = output_raw; output_raw = 1; cleanup_push(&old_output_raw, output_raw_restore); xprintf("%S\n", e); cleanup_until(&old_output_raw); } else setcopy(STRstatus, STR1, VAR_READWRITE); } /* from "Karl Berry." -- for NeXT things (and anything else with a modern compiler) */ /*ARGSUSED*/ void dosetenv(Char **v, struct command *c) { Char *vp, *lp; USE(c); if (*++v == 0) { doprintenv(--v, 0); return; } vp = *v++; lp = vp; if (!letter(*lp)) stderror(ERR_NAME | ERR_VARBEGIN); do { lp++; } while (alnum(*lp)); if (*lp != '\0') stderror(ERR_NAME | ERR_VARALNUM); if ((lp = *v++) == 0) lp = STRNULL; lp = globone(lp, G_APPEND); cleanup_push(lp, xfree); tsetenv(vp, lp); if (eq(vp, STRKPATH)) { importpath(lp); dohash(NULL, NULL); cleanup_until(lp); return; } #ifdef apollo if (eq(vp, STRSYSTYPE)) { dohash(NULL, NULL); cleanup_until(lp); return; } #endif /* apollo */ /* dspkanji/dspmbyte autosetting */ /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */ #if defined(DSPMBYTE) if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) { autoset_dspmbyte(lp); } #endif if (islocale_var(vp)) { #ifdef NLS int k; # ifdef SETLOCALEBUG dont_free = 1; # endif /* SETLOCALEBUG */ (void) setlocale(LC_ALL, ""); # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ # endif /* LC_CTYPE */ # if defined(AUTOSET_KANJI) autoset_kanji(); # endif /* AUTOSET_KANJI */ # ifdef NLS_CATALOGS # ifdef LC_MESSAGES (void) setlocale(LC_MESSAGES, ""); # endif /* LC_MESSAGES */ nlsclose(); nlsinit(); # endif /* NLS_CATALOGS */ # ifdef SETLOCALEBUG dont_free = 0; # endif /* SETLOCALEBUG */ # ifdef STRCOLLBUG fix_strcoll_bug(); # endif /* STRCOLLBUG */ tw_cmd_free(); /* since the collation sequence has changed */ for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++) continue; AsciiOnly = MB_CUR_MAX == 1 && k > 0377; #else /* !NLS */ AsciiOnly = 0; #endif /* NLS */ NLSMapsAreInited = 0; ed_Init(); if (MapsAreInited && !NLSMapsAreInited) ed_InitNLSMaps(); cleanup_until(lp); return; } #ifdef NLS_CATALOGS if (eq(vp, STRNLSPATH)) { nlsclose(); nlsinit(); } #endif if (eq(vp, STRNOREBIND)) { NoNLSRebind = 1; MapsAreInited = 0; NLSMapsAreInited = 0; ed_InitMaps(); cleanup_until(lp); return; } #ifdef WINNT_NATIVE if (eq(vp, STRtcshlang)) { nlsinit(); cleanup_until(lp); return; } #endif /* WINNT_NATIVE */ if (eq(vp, STRKTERM)) { char *t; setv(STRterm, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); t = short2str(lp); if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) { editing = 1; noediting = 0; setNS(STRedit); } GotTermCaps = 0; ed_Init(); return; } if (eq(vp, STRKHOME)) { Char *canon; /* * convert to canonical pathname (possibly resolving symlinks) */ canon = dcanon(lp, lp); cleanup_ignore(lp); cleanup_until(lp); cleanup_push(canon, xfree); setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(canon); cleanup_until(canon); /* fix directory stack for new tilde home */ dtilde(); return; } if (eq(vp, STRKSHLVL)) { setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } if (eq(vp, STRKUSER)) { setv(STRuser, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } if (eq(vp, STRKGROUP)) { setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } #ifdef COLOR_LS_F if (eq(vp, STRLS_COLORS)) { parseLS_COLORS(lp); cleanup_until(lp); return; } #endif /* COLOR_LS_F */ #ifdef SIG_WINDOW /* * Load/Update $LINES $COLUMNS */ if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) || eq(vp, STRTERMCAP)) { cleanup_until(lp); check_window_size(1); return; } /* * Change the size to the one directed by $LINES and $COLUMNS */ if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) { #if 0 GotTermCaps = 0; #endif cleanup_until(lp); ed_Init(); return; } #endif /* SIG_WINDOW */ cleanup_until(lp); } /*ARGSUSED*/ void dounsetenv(Char **v, struct command *c) { Char **ep, *p, *n, *name; int i, maxi; USE(c); /* * Find the longest environment variable */ for (maxi = 0, ep = STR_environ; *ep; ep++) { for (i = 0, p = *ep; *p && *p != '='; p++, i++) continue; if (i > maxi) maxi = i; } name = xmalloc((maxi + 1) * sizeof(Char)); cleanup_push(name, xfree); while (++v && *v) for (maxi = 1; maxi;) for (maxi = 0, ep = STR_environ; *ep; ep++) { for (n = name, p = *ep; *p && *p != '='; *n++ = *p++) continue; *n = '\0'; if (!Gmatch(name, *v)) continue; maxi = 1; /* Unset the name. This wasn't being done until * later but most of the stuff following won't * work (particularly the setlocale() and getenv() * stuff) as intended until the name is actually * removed. (sg) */ Unsetenv(name); if (eq(name, STRNOREBIND)) { NoNLSRebind = 0; MapsAreInited = 0; NLSMapsAreInited = 0; ed_InitMaps(); } #ifdef apollo else if (eq(name, STRSYSTYPE)) dohash(NULL, NULL); #endif /* apollo */ else if (islocale_var(name)) { #ifdef NLS int k; # ifdef SETLOCALEBUG dont_free = 1; # endif /* SETLOCALEBUG */ (void) setlocale(LC_ALL, ""); # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ # endif /* LC_CTYPE */ # ifdef NLS_CATALOGS # ifdef LC_MESSAGES (void) setlocale(LC_MESSAGES, ""); # endif /* LC_MESSAGES */ nlsclose(); nlsinit(); # endif /* NLS_CATALOGS */ # ifdef SETLOCALEBUG dont_free = 0; # endif /* SETLOCALEBUG */ # ifdef STRCOLLBUG fix_strcoll_bug(); # endif /* STRCOLLBUG */ tw_cmd_free();/* since the collation sequence has changed */ for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++) continue; AsciiOnly = MB_CUR_MAX == 1 && k > 0377; #else /* !NLS */ AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; #endif /* NLS */ NLSMapsAreInited = 0; ed_Init(); if (MapsAreInited && !NLSMapsAreInited) ed_InitNLSMaps(); } #ifdef WINNT_NATIVE else if (eq(name,(STRtcshlang))) { nls_dll_unload(); nlsinit(); } #endif /* WINNT_NATIVE */ #ifdef COLOR_LS_F else if (eq(name, STRLS_COLORS)) parseLS_COLORS(n); #endif /* COLOR_LS_F */ #ifdef NLS_CATALOGS else if (eq(name, STRNLSPATH)) { nlsclose(); nlsinit(); } #endif /* * start again cause the environment changes */ break; } cleanup_until(name); } void tsetenv(const Char *name, const Char *val) { #ifdef SETENV_IN_LIB /* * XXX: This does not work right, since tcsh cannot track changes to * the environment this way. (the builtin setenv without arguments does * not print the right stuff neither does unsetenv). This was for Mach, * it is not needed anymore. */ #undef setenv char *cname; if (name == NULL) return; cname = strsave(short2str(name)); setenv(cname, short2str(val), 1); xfree(cname); #else /* !SETENV_IN_LIB */ Char **ep = STR_environ; const Char *ccp; Char *cp, *dp; Char *blk[2]; Char **oep = ep; #ifdef WINNT_NATIVE nt_set_env(name,val); #endif /* WINNT_NATIVE */ for (; *ep; ep++) { #ifdef WINNT_NATIVE for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp); ccp++, dp++) #else for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++) #endif /* WINNT_NATIVE */ continue; if (*ccp != 0 || *dp != '=') continue; cp = Strspl(STRequal, val); xfree(*ep); *ep = strip(Strspl(name, cp)); xfree(cp); blkfree((Char **) environ); environ = short2blk(STR_environ); return; } cp = Strspl(name, STRequal); blk[0] = strip(Strspl(cp, val)); xfree(cp); blk[1] = 0; STR_environ = blkspl(STR_environ, blk); blkfree((Char **) environ); environ = short2blk(STR_environ); xfree(oep); #endif /* SETENV_IN_LIB */ } void Unsetenv(Char *name) { Char **ep = STR_environ; Char *cp, *dp; Char **oep = ep; #ifdef WINNT_NATIVE nt_set_env(name,NULL); #endif /*WINNT_NATIVE */ for (; *ep; ep++) { for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) continue; if (*cp != 0 || *dp != '=') continue; cp = *ep; *ep = 0; STR_environ = blkspl(STR_environ, ep + 1); blkfree((Char **) environ); environ = short2blk(STR_environ); *ep = cp; xfree(cp); xfree(oep); return; } } /*ARGSUSED*/ void doumask(Char **v, struct command *c) { Char *cp = v[1]; int i; USE(c); if (cp == 0) { i = (int)umask(0); (void) umask(i); xprintf("%o\n", i); return; } i = 0; while (Isdigit(*cp) && *cp != '8' && *cp != '9') i = i * 8 + *cp++ - '0'; if (*cp || i < 0 || i > 0777) stderror(ERR_NAME | ERR_MASK); (void) umask(i); } #ifndef HAVENOLIMIT # ifndef BSDLIMIT typedef long RLIM_TYPE; # ifdef _OSD_POSIX /* BS2000 */ # include # endif # ifndef RLIM_INFINITY # if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY) extern RLIM_TYPE ulimit(); # endif /* ! _MINIX && !__clipper__ */ # define RLIM_INFINITY 0x003fffff # define RLIMIT_FSIZE 1 # endif /* RLIM_INFINITY */ # ifdef aiws # define toset(a) (((a) == 3) ? 1004 : (a) + 1) # define RLIMIT_DATA 3 # define RLIMIT_STACK 1005 # else /* aiws */ # define toset(a) ((a) + 1) # endif /* aiws */ # else /* BSDLIMIT */ # if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__) typedef rlim_t RLIM_TYPE; # else # if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3) typedef rlim_t RLIM_TYPE; # else # if defined(_SX) typedef long long RLIM_TYPE; # else /* !_SX */ typedef unsigned long RLIM_TYPE; # endif /* _SX */ # endif /* SOLARIS2 || (sgi && SYSVREL > 3) */ # endif /* BSD4_4 && !__386BSD__ */ # endif /* BSDLIMIT */ # if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT) /* Yes hpux8.0 has limits but does not make them public */ /* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */ # ifndef RLIMIT_CPU # define RLIMIT_CPU 0 # define RLIMIT_FSIZE 1 # define RLIMIT_DATA 2 # define RLIMIT_STACK 3 # define RLIMIT_CORE 4 # define RLIMIT_RSS 5 # define RLIMIT_NOFILE 6 # endif /* RLIMIT_CPU */ # ifndef RLIM_INFINITY # define RLIM_INFINITY 0x7fffffff # endif /* RLIM_INFINITY */ /* * old versions of HP/UX counted limits in 512 bytes */ # ifndef SIGRTMIN # define FILESIZE512 # endif /* SIGRTMIN */ # endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */ # if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX) /* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */ /* sh.h. However, some SVR4 limits are defined in . Rather */ /* than include both and get warnings, we define the extra SVR4 limits here. */ /* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */ /* RLIMIT_VMEM based on it? */ # ifndef RLIMIT_VMEM # define RLIMIT_VMEM 6 # endif # ifndef RLIMIT_AS # define RLIMIT_AS RLIMIT_VMEM # endif # endif /* SYSVREL > 3 && BSDLIMIT */ # if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) # if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM) # define RLIMIT_VMEM RLIMIT_AS # endif /* * Oh well, has it, but does not * Linux headers: When the left hand does not know what the right hand does. */ # if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME) # define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1) # endif # endif struct limits limits[] = { # ifdef RLIMIT_CPU { RLIMIT_CPU, "cputime", 1, "seconds" }, # endif /* RLIMIT_CPU */ # ifdef RLIMIT_FSIZE # ifndef aiws { RLIMIT_FSIZE, "filesize", 1024, "kbytes" }, # else { RLIMIT_FSIZE, "filesize", 512, "blocks" }, # endif /* aiws */ # endif /* RLIMIT_FSIZE */ # ifdef RLIMIT_DATA { RLIMIT_DATA, "datasize", 1024, "kbytes" }, # endif /* RLIMIT_DATA */ # ifdef RLIMIT_STACK # ifndef aiws { RLIMIT_STACK, "stacksize", 1024, "kbytes" }, # else { RLIMIT_STACK, "stacksize", 1024 * 1024, "kbytes"}, # endif /* aiws */ # endif /* RLIMIT_STACK */ # ifdef RLIMIT_CORE { RLIMIT_CORE, "coredumpsize", 1024, "kbytes" }, # endif /* RLIMIT_CORE */ # ifdef RLIMIT_RSS { RLIMIT_RSS, "memoryuse", 1024, "kbytes" }, # endif /* RLIMIT_RSS */ # ifdef RLIMIT_UMEM { RLIMIT_UMEM, "memoryuse", 1024, "kbytes" }, # endif /* RLIMIT_UMEM */ # ifdef RLIMIT_VMEM { RLIMIT_VMEM, "vmemoryuse", 1024, "kbytes" }, # endif /* RLIMIT_VMEM */ # if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */ { RLIMIT_HEAP, "heapsize", 1024, "kbytes" }, # endif /* RLIMIT_HEAP */ # ifdef RLIMIT_NOFILE { RLIMIT_NOFILE, "descriptors", 1, "" }, # endif /* RLIMIT_NOFILE */ # ifdef RLIMIT_CONCUR { RLIMIT_CONCUR, "concurrency", 1, "thread(s)" }, # endif /* RLIMIT_CONCUR */ # ifdef RLIMIT_MEMLOCK { RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes" }, # endif /* RLIMIT_MEMLOCK */ # ifdef RLIMIT_NPROC { RLIMIT_NPROC, "maxproc", 1, "" }, # endif /* RLIMIT_NPROC */ # if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE) { RLIMIT_OFILE, "openfiles", 1, "" }, # endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */ # ifdef RLIMIT_SBSIZE { RLIMIT_SBSIZE, "sbsize", 1, "" }, # endif /* RLIMIT_SBSIZE */ #ifdef RLIMIT_POSIXLOCKS { RLIMIT_POSIXLOCKS, "posixlocks", 1, "" }, #endif /* RLIMIT_POSIXLOCKS */ # ifdef RLIMIT_SWAP { RLIMIT_SWAP, "swapsize", 1024, "kbytes" }, # endif /* RLIMIT_SWAP */ # ifdef RLIMIT_LOCKS { RLIMIT_LOCKS, "maxlocks", 1, "" }, # endif /* RLIMIT_LOCKS */ # ifdef RLIMIT_SIGPENDING { RLIMIT_SIGPENDING,"maxsignal", 1, "" }, # endif /* RLIMIT_SIGPENDING */ # ifdef RLIMIT_MSGQUEUE { RLIMIT_MSGQUEUE, "maxmessage", 1, "" }, # endif /* RLIMIT_MSGQUEUE */ # ifdef RLIMIT_NICE { RLIMIT_NICE, "maxnice", 1, "" }, # endif /* RLIMIT_NICE */ # ifdef RLIMIT_RTPRIO { RLIMIT_RTPRIO, "maxrtprio", 1, "" }, # endif /* RLIMIT_RTPRIO */ # ifdef RLIMIT_RTTIME { RLIMIT_RTTIME, "maxrttime", 1, "usec" }, # endif /* RLIMIT_RTTIME */ { -1, NULL, 0, NULL } }; static struct limits *findlim (Char *); static RLIM_TYPE getval (struct limits *, Char **); static int strtail (Char *, const char *); static void limtail (Char *, const char *); static void limtail2 (Char *, const char *, const char *); static void plim (struct limits *, int); static int setlim (struct limits *, int, RLIM_TYPE); #ifdef convex static RLIM_TYPE restrict_limit(double value) { /* * is f too large to cope with? return the maximum or minimum int */ if (value > (double) INT_MAX) return (RLIM_TYPE) INT_MAX; else if (value < (double) INT_MIN) return (RLIM_TYPE) INT_MIN; else return (RLIM_TYPE) value; } #else /* !convex */ # define restrict_limit(x) ((RLIM_TYPE) (x)) #endif /* convex */ static struct limits * findlim(Char *cp) { struct limits *lp, *res; res = NULL; for (lp = limits; lp->limconst >= 0; lp++) if (prefix(cp, str2short(lp->limname))) { if (res) stderror(ERR_NAME | ERR_AMBIG); res = lp; } if (res) return (res); stderror(ERR_NAME | ERR_LIMIT); /* NOTREACHED */ return (0); } /*ARGSUSED*/ void dolimit(Char **v, struct command *c) { struct limits *lp; RLIM_TYPE limit; int hard = 0; USE(c); v++; if (*v && eq(*v, STRmh)) { hard = 1; v++; } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) plim(lp, hard); return; } lp = findlim(v[0]); if (v[1] == 0) { plim(lp, hard); return; } limit = getval(lp, v + 1); if (setlim(lp, hard, limit) < 0) stderror(ERR_SILENT); } static RLIM_TYPE getval(struct limits *lp, Char **v) { float f; Char *cp = *v++; f = atof(short2str(cp)); # ifdef convex /* * is f too large to cope with. limit f to minint, maxint - X-6768 by * strike */ if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) { stderror(ERR_NAME | ERR_TOOLARGE); } # endif /* convex */ while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') cp++; if (*cp == 0) { if (*v == 0) return restrict_limit((f * lp->limdiv) + 0.5); cp = *v; } switch (*cp) { # ifdef RLIMIT_CPU case ':': if (lp->limconst != RLIMIT_CPU) goto badscal; return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1)))); case 'h': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "hours"); f *= 3600.0; break; # endif /* RLIMIT_CPU */ case 'm': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) { limtail(cp, "minutes"); f *= 60.0; break; } # endif /* RLIMIT_CPU */ limtail2(cp, "megabytes", "mbytes"); f *= 1024.0 * 1024.0; break; # ifdef RLIMIT_CPU case 's': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "seconds"); break; # endif /* RLIMIT_CPU */ case 'G': *cp = 'g'; /*FALLTHROUGH*/ case 'g': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail2(cp, "gigabytes", "gbytes"); f *= 1024.0 * 1024.0 * 1024.0; break; case 'M': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ *cp = 'm'; limtail2(cp, "megabytes", "mbytes"); f *= 1024.0 * 1024.0; break; case 'k': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail2(cp, "kilobytes", "kbytes"); f *= 1024.0; break; case 'b': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail(cp, "blocks"); f *= 512.0; break; case 'u': limtail(cp, "unlimited"); return ((RLIM_TYPE) RLIM_INFINITY); default: # ifdef RLIMIT_CPU badscal: # endif /* RLIMIT_CPU */ stderror(ERR_NAME | ERR_SCALEF); } # ifdef convex return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5)); # else f += 0.5; if (f > (float) ((RLIM_TYPE) RLIM_INFINITY)) return ((RLIM_TYPE) RLIM_INFINITY); else return ((RLIM_TYPE) f); # endif /* convex */ } static int strtail(Char *cp, const char *str) { while (*cp && *cp == (Char)*str) cp++, str++; return (*cp != '\0'); } static void limtail(Char *cp, const char *str) { if (strtail(cp, str)) stderror(ERR_BADSCALE, str); } static void limtail2(Char *cp, const char *str1, const char *str2) { if (strtail(cp, str1) && strtail(cp, str2)) stderror(ERR_BADSCALE, str1); } /*ARGSUSED*/ static void plim(struct limits *lp, int hard) { # ifdef BSDLIMIT struct rlimit rlim; # endif /* BSDLIMIT */ RLIM_TYPE limit; int xdiv = lp->limdiv; xprintf("%-13.13s", lp->limname); # ifndef BSDLIMIT limit = ulimit(lp->limconst, 0); # ifdef aiws if (lp->limconst == RLIMIT_DATA) limit -= 0x20000000; # endif /* aiws */ # else /* BSDLIMIT */ (void) getrlimit(lp->limconst, &rlim); limit = hard ? rlim.rlim_max : rlim.rlim_cur; # endif /* BSDLIMIT */ # if !defined(BSDLIMIT) || defined(FILESIZE512) /* * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024 * blocks. Note we cannot pre-multiply cause we might overflow (A/UX) */ if (lp->limconst == RLIMIT_FSIZE) { if (limit >= (RLIM_INFINITY / 512)) limit = RLIM_INFINITY; else xdiv = (xdiv == 1024 ? 2 : 1); } # endif /* !BSDLIMIT || FILESIZE512 */ if (limit == RLIM_INFINITY) xprintf("unlimited"); else # if defined(RLIMIT_CPU) && defined(_OSD_POSIX) if (lp->limconst == RLIMIT_CPU && (unsigned long)limit >= 0x7ffffffdUL) xprintf("unlimited"); else # endif # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) psecs(limit); else # endif /* RLIMIT_CPU */ xprintf("%ld %s", (long) (limit / xdiv), lp->limscale); xputchar('\n'); } /*ARGSUSED*/ void dounlimit(Char **v, struct command *c) { struct limits *lp; int lerr = 0; int hard = 0; int force = 0; USE(c); while (*++v && **v == '-') { Char *vp = *v; while (*++vp) switch (*vp) { case 'f': force = 1; break; case 'h': hard = 1; break; default: stderror(ERR_ULIMUS); break; } } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) lerr++; if (!force && lerr) stderror(ERR_SILENT); return; } while (*v) { lp = findlim(*v++); if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force) stderror(ERR_SILENT); } } static int setlim(struct limits *lp, int hard, RLIM_TYPE limit) { # ifdef BSDLIMIT struct rlimit rlim; (void) getrlimit(lp->limconst, &rlim); # ifdef FILESIZE512 /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */ if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE) limit /= 512; # endif /* FILESIZE512 */ if (hard) rlim.rlim_max = limit; else if (limit == RLIM_INFINITY && euid != 0) rlim.rlim_cur = rlim.rlim_max; else rlim.rlim_cur = limit; if (rlim.rlim_cur > rlim.rlim_max) rlim.rlim_max = rlim.rlim_cur; if (setrlimit(lp->limconst, &rlim) < 0) { # else /* BSDLIMIT */ if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE) limit /= 512; # ifdef aiws if (lp->limconst == RLIMIT_DATA) limit += 0x20000000; # endif /* aiws */ if (ulimit(toset(lp->limconst), limit) < 0) { # endif /* BSDLIMIT */ int err; char *op, *type; err = errno; op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") : CGETS(15, 3, "set")); cleanup_push(op, xfree); type = strsave(hard ? CGETS(15, 4, " hard") : ""); cleanup_push(type, xfree); xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname, lp->limname, op, type, strerror(err)); cleanup_until(op); return (-1); } return (0); } #endif /* !HAVENOLIMIT */ /*ARGSUSED*/ void dosuspend(Char **v, struct command *c) { #ifdef BSDJOBS struct sigaction old; #endif /* BSDJOBS */ USE(c); USE(v); if (loginsh) stderror(ERR_SUSPLOG); untty(); #ifdef BSDJOBS sigaction(SIGTSTP, NULL, &old); signal(SIGTSTP, SIG_DFL); (void) kill(0, SIGTSTP); /* the shell stops here */ sigaction(SIGTSTP, &old, NULL); #else /* !BSDJOBS */ stderror(ERR_JOBCONTROL); #endif /* BSDJOBS */ #ifdef BSDJOBS if (tpgrp != -1) { if (grabpgrp(FSHTTY, opgrp) == -1) stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno)); (void) setpgid(0, shpgrp); (void) tcsetpgrp(FSHTTY, shpgrp); } #endif /* BSDJOBS */ (void) setdisc(FSHTTY); } /* This is the dreaded EVAL built-in. * If you don't fiddle with file descriptors, and reset didfds, * this command will either ignore redirection inside or outside * its arguments, e.g. eval "date >x" vs. eval "date" >x * The stuff here seems to work, but I did it by trial and error rather * than really knowing what was going on. If tpgrp is zero, we are * probably a background eval, e.g. "eval date &", and we want to * make sure that any processes we start stay in our pgrp. * This is also the case for "time eval date" -- stay in same pgrp. * Otherwise, under stty tostop, processes will stop in the wrong * pgrp, with no way for the shell to get them going again. -IAN! */ struct doeval_state { Char **evalvec, *evalp; int didfds; #ifndef CLOSE_ON_EXEC int didcch; #endif int saveIN, saveOUT, saveDIAG; int SHIN, SHOUT, SHDIAG; }; static void doeval_cleanup(void *xstate) { struct doeval_state *state; state = xstate; evalvec = state->evalvec; evalp = state->evalp; doneinp = 0; #ifndef CLOSE_ON_EXEC didcch = state->didcch; #endif /* CLOSE_ON_EXEC */ didfds = state->didfds; xclose(SHIN); xclose(SHOUT); xclose(SHDIAG); close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1); close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1); close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1); } static Char **Ggv; /*ARGSUSED*/ void doeval(Char **v, struct command *c) { struct doeval_state state; int gflag, my_reenter; Char **gv; jmp_buf_t osetexit; USE(c); v++; if (*v == 0) return; gflag = tglob(v); if (gflag) { gv = v = globall(v, gflag); if (v == 0) stderror(ERR_NOMATCH); cleanup_push(gv, blk_cleanup); v = copyblk(v); } else { gv = NULL; v = copyblk(v); trim(v); } Ggv = gv; state.evalvec = evalvec; state.evalp = evalp; state.didfds = didfds; #ifndef CLOSE_ON_EXEC state.didcch = didcch; #endif /* CLOSE_ON_EXEC */ state.SHIN = SHIN; state.SHOUT = SHOUT; state.SHDIAG = SHDIAG; (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1); (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1); (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1); cleanup_push(&state, doeval_cleanup); getexit(osetexit); /* PWP: setjmp/longjmp bugfix for optimizing compilers */ #ifdef cray my_reenter = 1; /* assume non-zero return val */ if (setexit() == 0) { my_reenter = 0; /* Oh well, we were wrong */ #else /* !cray */ if ((my_reenter = setexit()) == 0) { #endif /* cray */ evalvec = v; evalp = 0; (void)close_on_exec(SHIN = dcopy(0, -1), 1); (void)close_on_exec(SHOUT = dcopy(1, -1), 1); (void)close_on_exec(SHDIAG = dcopy(2, -1), 1); #ifndef CLOSE_ON_EXEC didcch = 0; #endif /* CLOSE_ON_EXEC */ didfds = 0; gv = Ggv; process(0); Ggv = gv; } if (my_reenter == 0) { cleanup_until(&state); if (Ggv) cleanup_until(Ggv); } resexit(osetexit); if (my_reenter) stderror(ERR_SILENT); } /*************************************************************************/ /* print list of builtin commands */ static void lbuffed_cleanup (void *dummy) { USE(dummy); lbuffed = 1; } /*ARGSUSED*/ void dobuiltins(Char **v, struct command *c) { /* would use print_by_column() in tw.parse.c but that assumes * we have an array of Char * to pass.. (sg) */ const struct biltins *b; int row, col, columns, rows; unsigned int w, maxwidth; USE(c); USE(v); lbuffed = 0; /* turn off line buffering */ cleanup_push(&lbuffed, lbuffed_cleanup); /* find widest string */ for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b) maxwidth = max(maxwidth, strlen(b->bname)); ++maxwidth; /* for space */ columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ if (!columns) columns = 1; rows = (nbfunc + (columns - 1)) / columns; for (b = bfunc, row = 0; row < rows; row++) { for (col = 0; col < columns; col++) { if (b < &bfunc[nbfunc]) { w = strlen(b->bname); xprintf("%s", b->bname); if (col < (columns - 1)) /* Not last column? */ for (; w < maxwidth; w++) xputchar(' '); ++b; } } if (row < (rows - 1)) { if (Tty_raw_mode) xputchar('\r'); xputchar('\n'); } } #ifdef WINNT_NATIVE nt_print_builtins(maxwidth); #else if (Tty_raw_mode) xputchar('\r'); xputchar('\n'); #endif /* WINNT_NATIVE */ cleanup_until(&lbuffed); /* turn back on line buffering */ flush(); } #ifdef NLS_CATALOGS char * xcatgets(nl_catd ctd, int set_id, int msg_id, const char *s) { char *res; errno = 0; while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) { handle_pending_signals(); errno = 0; } return res; } # if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) char * iconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s) { static char *buf = NULL; static size_t buf_size = 0; char *orig, *dest, *p; ICONV_CONST char *src; size_t src_size, dest_size; orig = xcatgets(ctd, set_id, msg_id, s); if (catgets_iconv == (iconv_t)-1 || orig == s) return orig; src = orig; src_size = strlen(src) + 1; if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL) return orig; dest = buf; while (src_size != 0) { dest_size = buf + buf_size - dest; if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size) == (size_t)-1) { switch (errno) { case E2BIG: if ((p = xrealloc(buf, buf_size * 2)) == NULL) return orig; buf_size *= 2; dest = p + (dest - buf); buf = p; break; case EILSEQ: case EINVAL: default: return orig; } } } return buf; } # endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ #endif /* NLS_CATALOGS */ void nlsinit(void) { #ifdef NLS_CATALOGS static const char default_catalog[] = "tcsh"; char *catalog = (char *)(intptr_t)default_catalog; if (adrof(STRcatalog) != NULL) catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog))); #ifdef NL_CAT_LOCALE /* POSIX-compliant. */ /* * Check if LC_MESSAGES is set in the environment and use it, if so. * If not, fall back to the setting of LANG. */ catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0); #else /* pre-POSIX */ # ifndef MCLoadBySet # define MCLoadBySet 0 # endif catd = catopen(catalog, MCLoadBySet); #endif if (catalog != default_catalog) xfree(catalog); #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */ catgets_iconv = iconv_open (nl_langinfo (CODESET), xcatgets(catd, 255, 1, "UTF-8")); #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ #endif /* NLS_CATALOGS */ #ifdef WINNT_NATIVE nls_dll_init(); #endif /* WINNT_NATIVE */ errinit(); /* init the errorlist in correct locale */ mesginit(); /* init the messages for signals */ dateinit(); /* init the messages for dates */ editinit(); /* init the editor messages */ terminit(); /* init the termcap messages */ } void nlsclose(void) { #ifdef NLS_CATALOGS #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) if (catgets_iconv != (iconv_t)-1) { iconv_close(catgets_iconv); catgets_iconv = (iconv_t)-1; } #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ if (catd != (nl_catd)-1) { /* * catclose can call other functions which can call longjmp * making us re-enter this code. Prevent infinite recursion * by resetting catd. Problem reported and solved by: * Gerhard Niklasch */ nl_catd oldcatd = catd; catd = (nl_catd)-1; while (catclose(oldcatd) == -1 && errno == EINTR) handle_pending_signals(); } #endif /* NLS_CATALOGS */ }