fcc023545e
address space available to processes. this limit exists in most other modern unix variants, and like most of them, our defaults are unlimited. remove the old mmap / rlimit.datasize hack. - adds the VMCMD_STACK flag to all the stack-creation vmcmd callers. it is currently unused, but was added a few years ago. - add a pair of new process size values to kinfo_proc2{}. one is the total size of the process memory map, and the other is the total size adjusted for unused stack space (since most processes have a lot of this...) - patch sh, and csh to notice RLIMIT_AS. (in some cases, the alias RLIMIT_VMEM was already present and used if availble.) - patch ps, top and systat to notice the new k_vm_vsize member of kinfo_proc2{}. - update irix, svr4, svr4_32, linux and osf1 emulations to support this information. (freebsd could be done, but that it's best left as part of the full-update of compat/freebsd.) this addresses PR 7897. it also gives correct memory usage values, which have never been entirely correct (since mmap), and have been very incorrect since jemalloc() was enabled. tested on i386 and sparc64, build tested on several other platforms. thanks to many folks for feedback and testing but most espcially chuq and yamt for critical suggestions that lead to this patch not having a special ugliness i wasn't happy with anyway :-)
1455 lines
29 KiB
C
1455 lines
29 KiB
C
/* $NetBSD: func.c,v 1.37 2009/03/29 01:02:48 mrg Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1980, 1991, 1993
|
|
* 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 <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)func.c 8.1 (Berkeley) 5/31/93";
|
|
#else
|
|
__RCSID("$NetBSD: func.c,v 1.37 2009/03/29 01:02:48 mrg Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "csh.h"
|
|
#include "extern.h"
|
|
#include "pathnames.h"
|
|
|
|
extern char **environ;
|
|
extern int progprintf(int, char **);
|
|
|
|
static void islogin(void);
|
|
static void reexecute(struct command *);
|
|
static void preread(void);
|
|
static void doagain(void);
|
|
static void search(int, int, Char *);
|
|
static int getword(Char *);
|
|
static int keyword(Char *);
|
|
static void toend(void);
|
|
static void xecho(int, Char **);
|
|
static void Unsetenv(Char *);
|
|
static void wpfree(struct whyle *);
|
|
|
|
struct biltins *
|
|
isbfunc(struct command *t)
|
|
{
|
|
static struct biltins label = {"", dozip, 0, 0};
|
|
static struct biltins foregnd = {"%job", dofg1, 0, 0};
|
|
static struct biltins backgnd = {"%job &", dobg1, 0, 0};
|
|
struct biltins *bp, *bp1, *bp2;
|
|
Char *cp;
|
|
|
|
cp = t->t_dcom[0];
|
|
|
|
if (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);
|
|
}
|
|
/*
|
|
* 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 = *cp - *bp->bname) == 0 &&
|
|
(i = Strcmp(cp, str2short(bp->bname))) == 0)
|
|
return bp;
|
|
if (i < 0)
|
|
bp2 = bp;
|
|
else
|
|
bp1 = bp + 1;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
func(struct command *t, 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);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doonintr(Char **v, struct command *t)
|
|
{
|
|
Char *cp, *vv;
|
|
sigset_t nsigset;
|
|
|
|
vv = v[1];
|
|
if (parintr == SIG_IGN)
|
|
return;
|
|
if (setintr && intty)
|
|
stderror(ERR_NAME | ERR_TERMINAL);
|
|
cp = gointr;
|
|
gointr = 0;
|
|
xfree((ptr_t) cp);
|
|
if (vv == 0) {
|
|
if (setintr) {
|
|
sigemptyset(&nsigset);
|
|
(void)sigaddset(&nsigset, SIGINT);
|
|
(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
|
|
} 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);
|
|
(void)signal(SIGINT, pintr);
|
|
}
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
donohup(Char **v, struct command *t)
|
|
{
|
|
if (intty)
|
|
stderror(ERR_NAME | ERR_TERMINAL);
|
|
if (setintr == 0) {
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
}
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dozip(Char **v, struct command *t)
|
|
{
|
|
;
|
|
}
|
|
|
|
void
|
|
prvars(void)
|
|
{
|
|
plist(&shvhed);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doalias(Char **v, struct command *t)
|
|
{
|
|
struct varent *vp;
|
|
Char *p;
|
|
|
|
v++;
|
|
p = *v++;
|
|
if (p == 0)
|
|
plist(&aliases);
|
|
else if (*v == 0) {
|
|
vp = adrof1(strip(p), &aliases);
|
|
if (vp) {
|
|
blkpr(cshout, vp->vec);
|
|
(void) fputc('\n', cshout);
|
|
}
|
|
}
|
|
else {
|
|
if (eq(p, STRalias) || eq(p, STRunalias)) {
|
|
setname(vis_str(p));
|
|
stderror(ERR_NAME | ERR_DANGER);
|
|
}
|
|
set1(strip(p), saveblk(v), &aliases);
|
|
}
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
unalias(Char **v, struct command *t)
|
|
{
|
|
unset1(v, &aliases);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dologout(Char **v, struct command *t)
|
|
{
|
|
islogin();
|
|
goodbye();
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dologin(Char **v, struct command *t)
|
|
{
|
|
islogin();
|
|
rechist();
|
|
(void)signal(SIGTERM, parterm);
|
|
(void)execl(_PATH_LOGIN, "login", short2str(v[1]), NULL);
|
|
untty();
|
|
xexit(1);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
static void
|
|
islogin(void)
|
|
{
|
|
if (chkstop == 0 && setintr)
|
|
panystop(0);
|
|
if (loginsh)
|
|
return;
|
|
stderror(ERR_NOTLOGIN);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void
|
|
doif(Char **v, struct command *kp)
|
|
{
|
|
Char **vv;
|
|
int i;
|
|
|
|
v++;
|
|
i = expr(&v);
|
|
vv = v;
|
|
if (*vv == NULL)
|
|
stderror(ERR_NAME | ERR_EMPTYIF);
|
|
if (eq(*vv, STRthen)) {
|
|
if (*++vv)
|
|
stderror(ERR_NAME | ERR_IMPRTHEN);
|
|
setname(vis_str(STRthen));
|
|
/*
|
|
* If expression was zero, then scan to else, otherwise just fall into
|
|
* following code.
|
|
*/
|
|
if (!i)
|
|
search(T_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.
|
|
*/
|
|
static 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);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doelse(Char **v, struct command *t)
|
|
{
|
|
search(T_ELSE, 0, NULL);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dogoto(Char **v, struct command *t)
|
|
{
|
|
Char *lp;
|
|
|
|
gotolab(lp = globone(v[1], G_ERROR));
|
|
xfree((ptr_t) 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.
|
|
*/
|
|
for (wp = whyles; wp; wp = wp->w_next)
|
|
if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) {
|
|
search(T_BREAK, 0, NULL);
|
|
btell(&wp->w_end);
|
|
}
|
|
else
|
|
bseek(&wp->w_end);
|
|
search(T_GOTO, 0, lab);
|
|
/*
|
|
* Eliminate loops which were exited.
|
|
*/
|
|
wfree();
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doswitch(Char **v, struct command *t)
|
|
{
|
|
Char *cp, *lp;
|
|
|
|
v++;
|
|
if (!*v || *(*v++) != '(')
|
|
stderror(ERR_SYNTAX);
|
|
cp = **v == ')' ? STRNULL : *v++;
|
|
if (*(*v++) != ')')
|
|
v--;
|
|
if (*v)
|
|
stderror(ERR_SYNTAX);
|
|
search(T_SWITCH, 0, lp = globone(cp, G_ERROR));
|
|
xfree((ptr_t) lp);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dobreak(Char **v, struct command *t)
|
|
{
|
|
if (whyles)
|
|
toend();
|
|
else
|
|
stderror(ERR_NAME | ERR_NOTWHILE);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doexit(Char **v, struct command *t)
|
|
{
|
|
if (chkstop == 0 && (intty || intact) && evalvec == 0)
|
|
panystop(0);
|
|
/*
|
|
* Don't DEMAND parentheses here either.
|
|
*/
|
|
v++;
|
|
if (*v) {
|
|
set(STRstatus, putn(expr(&v)));
|
|
if (*v)
|
|
stderror(ERR_NAME | ERR_EXPRESSION);
|
|
}
|
|
btoeof();
|
|
if (intty)
|
|
(void) close(SHIN);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doforeach(Char **v, struct command *t)
|
|
{
|
|
struct whyle *nwp;
|
|
Char *cp, *sp;
|
|
|
|
v++;
|
|
sp = cp = strip(*v);
|
|
if (!letter(*sp))
|
|
stderror(ERR_NAME | ERR_VARBEGIN);
|
|
while (*cp && alnum(*cp))
|
|
cp++;
|
|
if (*cp)
|
|
stderror(ERR_NAME | ERR_VARALNUM);
|
|
if ((cp - sp) > MAXVARLEN)
|
|
stderror(ERR_NAME | ERR_VARTOOLONG);
|
|
cp = *v++;
|
|
if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
|
|
stderror(ERR_NAME | ERR_NOPAREN);
|
|
v++;
|
|
gflag = 0, tglob(v);
|
|
v = globall(v);
|
|
if (v == 0)
|
|
stderror(ERR_NAME | ERR_NOMATCH);
|
|
nwp = (struct whyle *) xcalloc(1, sizeof *nwp);
|
|
nwp->w_fe = nwp->w_fe0 = v;
|
|
gargv = 0;
|
|
btell(&nwp->w_start);
|
|
nwp->w_fename = Strsave(cp);
|
|
nwp->w_next = whyles;
|
|
nwp->w_end.type = F_SEEK;
|
|
whyles = nwp;
|
|
/*
|
|
* Pre-read the loop so as to be more comprehensible to a terminal user.
|
|
*/
|
|
if (intty)
|
|
preread();
|
|
doagain();
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dowhile(Char **v, struct command *t)
|
|
{
|
|
int status;
|
|
int again;
|
|
|
|
again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) &&
|
|
whyles->w_fename == 0;
|
|
v++;
|
|
/*
|
|
* Implement prereading here also, taking care not to evaluate the
|
|
* expression before the loop has been read up from a terminal.
|
|
*/
|
|
if (intty && !again)
|
|
status = !exp0(&v, 1);
|
|
else
|
|
status = !expr(&v);
|
|
if (*v)
|
|
stderror(ERR_NAME | ERR_EXPRESSION);
|
|
if (!again) {
|
|
struct whyle *nwp =
|
|
(struct whyle *)xcalloc(1, sizeof(*nwp));
|
|
|
|
nwp->w_start = lineloc;
|
|
nwp->w_end.type = F_SEEK;
|
|
nwp->w_end.f_seek = 0;
|
|
nwp->w_next = whyles;
|
|
whyles = nwp;
|
|
if (intty) {
|
|
/*
|
|
* The tty preread
|
|
*/
|
|
preread();
|
|
doagain();
|
|
return;
|
|
}
|
|
}
|
|
if (status)
|
|
/* We ain't gonna loop no more, no more! */
|
|
toend();
|
|
}
|
|
|
|
static void
|
|
preread(void)
|
|
{
|
|
sigset_t nsigset;
|
|
|
|
whyles->w_end.type = I_SEEK;
|
|
if (setintr) {
|
|
sigemptyset(&nsigset);
|
|
(void) sigaddset(&nsigset, SIGINT);
|
|
(void) sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
|
|
}
|
|
|
|
search(T_BREAK, 0, NULL); /* read the expression in */
|
|
if (setintr)
|
|
(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
|
|
btell(&whyles->w_end);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doend(Char **v, struct command *t)
|
|
{
|
|
if (!whyles)
|
|
stderror(ERR_NAME | ERR_NOTWHILE);
|
|
btell(&whyles->w_end);
|
|
doagain();
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
docontin(Char **v, struct command *t)
|
|
{
|
|
if (!whyles)
|
|
stderror(ERR_NAME | ERR_NOTWHILE);
|
|
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;
|
|
}
|
|
set(whyles->w_fename, Strsave(*whyles->w_fe++));
|
|
bseek(&whyles->w_start);
|
|
}
|
|
|
|
void
|
|
dorepeat(Char **v, struct command *kp)
|
|
{
|
|
int i;
|
|
sigset_t nsigset;
|
|
|
|
i = getn(v[1]);
|
|
if (setintr) {
|
|
sigemptyset(&nsigset);
|
|
(void)sigaddset(&nsigset, SIGINT);
|
|
(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
|
|
}
|
|
lshift(v, 2);
|
|
while (i > 0) {
|
|
if (setintr)
|
|
(void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
|
|
reexecute(kp);
|
|
--i;
|
|
}
|
|
donefds();
|
|
if (setintr)
|
|
(void) sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doswbrk(Char **v, struct command *t)
|
|
{
|
|
search(T_BRKSW, 0, NULL);
|
|
}
|
|
|
|
int
|
|
srchx(Char *cp)
|
|
{
|
|
struct srch *sp, *sp1, *sp2;
|
|
int i;
|
|
|
|
/*
|
|
* 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 Char Stype;
|
|
static Char *Sgoal;
|
|
|
|
/*VARARGS2*/
|
|
static void
|
|
search(int type, int level, Char *goal)
|
|
{
|
|
Char wordbuf[BUFSIZE];
|
|
Char *aword, *cp;
|
|
struct whyle *wp;
|
|
int wlevel = 0;
|
|
|
|
aword = wordbuf;
|
|
Stype = type;
|
|
Sgoal = goal;
|
|
if (type == T_GOTO) {
|
|
struct Ain a;
|
|
a.type = F_SEEK;
|
|
a.f_seek = 0;
|
|
bseek(&a);
|
|
}
|
|
do {
|
|
if (intty && fseekp == feobp && aret == F_SEEK)
|
|
(void)fprintf(cshout, "? "), (void)fflush(cshout);
|
|
aword[0] = 0;
|
|
(void)getword(aword);
|
|
switch (srchx(aword)) {
|
|
case T_CASE:
|
|
if (type != T_SWITCH || level != 0)
|
|
break;
|
|
(void) getword(aword);
|
|
if (lastchr(aword) == ':')
|
|
aword[Strlen(aword) - 1] = 0;
|
|
cp = strip(Dfix1(aword));
|
|
if (Gmatch(goal, cp))
|
|
level = -1;
|
|
xfree((ptr_t) cp);
|
|
break;
|
|
case T_DEFAULT:
|
|
if (type == T_SWITCH && level == 0)
|
|
level = -1;
|
|
break;
|
|
case T_ELSE:
|
|
if (level == 0 && type == T_IF)
|
|
return;
|
|
break;
|
|
case T_END:
|
|
if (type == T_BRKSW) {
|
|
if (wlevel == 0) {
|
|
wp = whyles;
|
|
if (wp) {
|
|
whyles = wp->w_next;
|
|
wpfree(wp);
|
|
}
|
|
}
|
|
}
|
|
if (type == T_BREAK)
|
|
level--;
|
|
wlevel--;
|
|
break;
|
|
case T_ENDIF:
|
|
if (type == T_IF || type == T_ELSE)
|
|
level--;
|
|
break;
|
|
case T_ENDSW:
|
|
if (type == T_SWITCH || type == T_BRKSW)
|
|
level--;
|
|
break;
|
|
case T_IF:
|
|
while (getword(aword))
|
|
continue;
|
|
if ((type == T_IF || type == T_ELSE) &&
|
|
eq(aword, STRthen))
|
|
level++;
|
|
break;
|
|
case T_LABEL:
|
|
if (type == T_GOTO && getword(aword) && eq(aword, goal))
|
|
level = -1;
|
|
break;
|
|
case T_SWITCH:
|
|
if (type == T_SWITCH || type == T_BRKSW)
|
|
level++;
|
|
break;
|
|
case T_FOREACH:
|
|
case T_WHILE:
|
|
wlevel++;
|
|
if (type == T_BREAK)
|
|
level++;
|
|
break;
|
|
default:
|
|
if (type != T_GOTO && (type != T_SWITCH || level != 0))
|
|
break;
|
|
if (lastchr(aword) != ':')
|
|
break;
|
|
aword[Strlen(aword) - 1] = 0;
|
|
if ((type == T_GOTO && eq(aword, goal)) ||
|
|
(type == T_SWITCH && eq(aword, STRdefault)))
|
|
level = -1;
|
|
break;
|
|
}
|
|
(void) getword(NULL);
|
|
} while (level >= 0);
|
|
}
|
|
|
|
static void
|
|
wpfree(struct whyle *wp)
|
|
{
|
|
if (wp->w_fe0)
|
|
blkfree(wp->w_fe0);
|
|
if (wp->w_fename)
|
|
xfree((ptr_t) wp->w_fename);
|
|
xfree((ptr_t) wp);
|
|
}
|
|
|
|
static int
|
|
getword(Char *wp)
|
|
{
|
|
int c, d, found, kwd;
|
|
Char *owp;
|
|
|
|
c = readc(1);
|
|
d = 0;
|
|
found = 0;
|
|
kwd = 0;
|
|
owp = wp;
|
|
do {
|
|
while (c == ' ' || c == '\t')
|
|
c = readc(1);
|
|
if (c == '#')
|
|
do
|
|
c = readc(1);
|
|
while (c >= 0 && c != '\n');
|
|
if (c < 0)
|
|
goto past;
|
|
if (c == '\n') {
|
|
if (wp)
|
|
break;
|
|
return (0);
|
|
}
|
|
unreadc(c);
|
|
found = 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 < 0)
|
|
goto past;
|
|
if (wp) {
|
|
*wp++ = c;
|
|
*wp = 0; /* end the string b4 test */
|
|
}
|
|
} while ((d || (!(kwd = keyword(owp)) && c != ' '
|
|
&& c != '\t')) && c != '\n');
|
|
} while (wp == 0);
|
|
|
|
/*
|
|
* if we have read a keyword ( "if", "switch" or "while" ) then we do not
|
|
* need to unreadc the look-ahead char
|
|
*/
|
|
if (!kwd) {
|
|
unreadc(c);
|
|
if (found)
|
|
*--wp = 0;
|
|
}
|
|
|
|
return (found);
|
|
|
|
past:
|
|
switch (Stype) {
|
|
case T_BREAK:
|
|
stderror(ERR_NAME | ERR_NOTFOUND, "end");
|
|
/* NOTREACHED */
|
|
case T_ELSE:
|
|
stderror(ERR_NAME | ERR_NOTFOUND, "endif");
|
|
/* NOTREACHED */
|
|
case T_GOTO:
|
|
setname(vis_str(Sgoal));
|
|
stderror(ERR_NAME | ERR_NOTFOUND, "label");
|
|
/* NOTREACHED */
|
|
case T_IF:
|
|
stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
|
|
/* NOTREACHED */
|
|
case T_BRKSW:
|
|
case T_SWITCH:
|
|
stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
|
|
/* NOTREACHED */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* keyword(wp) determines if wp is one of the built-n functions if,
|
|
* switch or while. It seems that when an if statement looks like
|
|
* "if(" then getword above sucks in the '(' and so the search routine
|
|
* never finds what it is scanning for. Rather than rewrite doword, I hack
|
|
* in a test to see if the string forms a keyword. Then doword stops
|
|
* and returns the word "if" -strike
|
|
*/
|
|
|
|
static int
|
|
keyword(Char *wp)
|
|
{
|
|
static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
|
|
static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'};
|
|
static Char STRif[] = {'i', 'f', '\0'};
|
|
|
|
if (!wp)
|
|
return (0);
|
|
|
|
if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0)
|
|
|| (Strcmp(wp, STRswitch) == 0))
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
toend(void)
|
|
{
|
|
if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) {
|
|
search(T_BREAK, 0, NULL);
|
|
btell(&whyles->w_end);
|
|
whyles->w_end.f_seek--;
|
|
}
|
|
else
|
|
bseek(&whyles->w_end);
|
|
wfree();
|
|
}
|
|
|
|
void
|
|
wfree(void)
|
|
{
|
|
struct Ain o;
|
|
struct whyle *nwp;
|
|
|
|
btell(&o);
|
|
|
|
for (; whyles; whyles = nwp) {
|
|
struct whyle *wp = whyles;
|
|
nwp = wp->w_next;
|
|
|
|
/*
|
|
* We free loops that have different seek types.
|
|
*/
|
|
if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type &&
|
|
wp->w_start.type == o.type) {
|
|
if (wp->w_end.type == 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);
|
|
}
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doecho(Char **v, struct command *t)
|
|
{
|
|
xecho(' ', v);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doglob(Char **v, struct command *t)
|
|
{
|
|
xecho(0, v);
|
|
(void)fflush(cshout);
|
|
}
|
|
|
|
static void
|
|
xecho(int sep, Char **v)
|
|
{
|
|
Char *cp;
|
|
sigset_t nsigset;
|
|
int nonl;
|
|
|
|
nonl = 0;
|
|
if (setintr) {
|
|
sigemptyset(&nsigset);
|
|
(void)sigaddset(&nsigset, SIGINT);
|
|
(void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
|
|
}
|
|
v++;
|
|
if (*v == 0)
|
|
goto done;
|
|
gflag = 0, tglob(v);
|
|
if (gflag) {
|
|
v = globall(v);
|
|
if (v == 0)
|
|
stderror(ERR_NAME | ERR_NOMATCH);
|
|
}
|
|
else {
|
|
v = gargv = saveblk(v);
|
|
trim(v);
|
|
}
|
|
if (sep == ' ' && *v && eq(*v, STRmn))
|
|
nonl++, v++;
|
|
while ((cp = *v++) != NULL) {
|
|
int c;
|
|
|
|
while ((c = *cp++) != '\0')
|
|
(void)vis_fputc(c | QUOTE, cshout);
|
|
|
|
if (*v)
|
|
(void)vis_fputc(sep | QUOTE, cshout);
|
|
}
|
|
done:
|
|
if (sep && nonl == 0)
|
|
(void)fputc('\n', cshout);
|
|
else
|
|
(void)fflush(cshout);
|
|
if (setintr)
|
|
(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
|
|
if (gargv)
|
|
blkfree(gargv), gargv = 0;
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dosetenv(Char **v, struct command *t)
|
|
{
|
|
Char *lp, *vp;
|
|
sigset_t nsigset;
|
|
|
|
v++;
|
|
if ((vp = *v++) == 0) {
|
|
Char **ep;
|
|
|
|
if (setintr) {
|
|
sigemptyset(&nsigset);
|
|
(void)sigaddset(&nsigset, SIGINT);
|
|
(void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
|
|
}
|
|
for (ep = STR_environ; *ep; ep++)
|
|
(void)fprintf(cshout, "%s\n", vis_str(*ep));
|
|
return;
|
|
}
|
|
if ((lp = *v++) == 0)
|
|
lp = STRNULL;
|
|
Setenv(vp, lp = globone(lp, G_APPEND));
|
|
if (eq(vp, STRPATH)) {
|
|
importpath(lp);
|
|
dohash(NULL, NULL);
|
|
}
|
|
else if (eq(vp, STRLANG) || eq(vp, STRLC_CTYPE)) {
|
|
#ifdef NLS
|
|
int k;
|
|
|
|
(void)setlocale(LC_ALL, "");
|
|
for (k = 0200; k <= 0377 && !Isprint(k); k++)
|
|
continue;
|
|
AsciiOnly = k > 0377;
|
|
#else
|
|
AsciiOnly = 0;
|
|
#endif /* NLS */
|
|
}
|
|
xfree((ptr_t) lp);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dounsetenv(Char **v, struct command *t)
|
|
{
|
|
static Char *name = NULL;
|
|
Char **ep, *p, *n;
|
|
int i, maxi;
|
|
|
|
if (name)
|
|
xfree((ptr_t) name);
|
|
/*
|
|
* 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 = (Char *)xmalloc((size_t) (maxi + 1) * sizeof(Char));
|
|
|
|
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;
|
|
if (eq(name, STRLANG) || eq(name, STRLC_CTYPE)) {
|
|
#ifdef NLS
|
|
int k;
|
|
|
|
(void) setlocale(LC_ALL, "");
|
|
for (k = 0200; k <= 0377 && !Isprint(k); k++)
|
|
continue;
|
|
AsciiOnly = k > 0377;
|
|
#else
|
|
AsciiOnly = getenv("LANG") == NULL &&
|
|
getenv("LC_CTYPE") == NULL;
|
|
#endif /* NLS */
|
|
}
|
|
/*
|
|
* Delete name, and start again cause the environment changes
|
|
*/
|
|
Unsetenv(name);
|
|
break;
|
|
}
|
|
xfree((ptr_t) name);
|
|
name = NULL;
|
|
}
|
|
|
|
void
|
|
Setenv(Char *name, Char *val)
|
|
{
|
|
Char *blk[2], *cp, *dp, **ep, **oep;
|
|
|
|
ep = STR_environ;
|
|
oep = ep;
|
|
|
|
for (; *ep; ep++) {
|
|
for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
|
|
continue;
|
|
if (*cp != 0 || *dp != '=')
|
|
continue;
|
|
cp = Strspl(STRequal, val);
|
|
xfree((ptr_t)* ep);
|
|
*ep = strip(Strspl(name, cp));
|
|
xfree((ptr_t)cp);
|
|
blkfree((Char **)environ);
|
|
environ = short2blk(STR_environ);
|
|
return;
|
|
}
|
|
cp = Strspl(name, STRequal);
|
|
blk[0] = strip(Strspl(cp, val));
|
|
xfree((ptr_t)cp);
|
|
blk[1] = 0;
|
|
STR_environ = blkspl(STR_environ, blk);
|
|
blkfree((Char **)environ);
|
|
environ = short2blk(STR_environ);
|
|
xfree((ptr_t) oep);
|
|
}
|
|
|
|
static void
|
|
Unsetenv(Char *name)
|
|
{
|
|
Char *cp, *dp, **ep, **oep;
|
|
|
|
ep = STR_environ;
|
|
oep = ep;
|
|
|
|
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);
|
|
environ = short2blk(STR_environ);
|
|
*ep = cp;
|
|
xfree((ptr_t) cp);
|
|
xfree((ptr_t) oep);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doumask(Char **v, struct command *t)
|
|
{
|
|
Char *cp;
|
|
int i;
|
|
|
|
cp = v[1];
|
|
if (cp == 0) {
|
|
i = umask(0);
|
|
(void)umask(i);
|
|
(void)fprintf(cshout, "%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);
|
|
}
|
|
|
|
typedef rlim_t RLIM_TYPE;
|
|
|
|
static const struct limits {
|
|
int limconst;
|
|
const char *limname;
|
|
int limdiv;
|
|
const char *limscale;
|
|
} limits[] = {
|
|
{ RLIMIT_CPU, "cputime", 1, "seconds" },
|
|
{ RLIMIT_FSIZE, "filesize", 1024, "kbytes" },
|
|
{ RLIMIT_DATA, "datasize", 1024, "kbytes" },
|
|
{ RLIMIT_STACK, "stacksize", 1024, "kbytes" },
|
|
{ RLIMIT_CORE, "coredumpsize", 1024, "kbytes" },
|
|
{ RLIMIT_RSS, "memoryuse", 1024, "kbytes" },
|
|
{ RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes" },
|
|
{ RLIMIT_NPROC, "maxproc", 1, "" },
|
|
{ RLIMIT_NOFILE, "openfiles", 1, "" },
|
|
{ RLIMIT_SBSIZE, "sbsize", 1, "bytes" },
|
|
{ RLIMIT_AS, "vmemoryuse", 1024, "kbytes" },
|
|
{ -1, NULL, 0, NULL }
|
|
};
|
|
|
|
static const struct limits *findlim(Char *);
|
|
static RLIM_TYPE getval(const struct limits *, Char **);
|
|
static void limtail(Char *, const char *);
|
|
static void plim(const struct limits *, Char);
|
|
static int setlim(const struct limits *, Char, RLIM_TYPE);
|
|
|
|
static const struct limits *
|
|
findlim(Char *cp)
|
|
{
|
|
const struct limits *lp, *res;
|
|
|
|
res = (struct limits *) 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 */
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dolimit(Char **v, struct command *t)
|
|
{
|
|
const struct limits *lp;
|
|
RLIM_TYPE limit;
|
|
char hard;
|
|
|
|
hard = 0;
|
|
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(const struct limits *lp, Char **v)
|
|
{
|
|
Char *cp;
|
|
float f;
|
|
|
|
cp = *v++;
|
|
f = atof(short2str(cp));
|
|
|
|
while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
|
|
cp++;
|
|
if (*cp == 0) {
|
|
if (*v == 0)
|
|
return ((RLIM_TYPE)((f + 0.5) * lp->limdiv));
|
|
cp = *v;
|
|
}
|
|
switch (*cp) {
|
|
case ':':
|
|
if (lp->limconst != RLIMIT_CPU)
|
|
goto badscal;
|
|
return ((RLIM_TYPE)(f * 60.0 + atof(short2str(cp + 1))));
|
|
case 'M':
|
|
if (lp->limconst == RLIMIT_CPU)
|
|
goto badscal;
|
|
*cp = 'm';
|
|
limtail(cp, "megabytes");
|
|
f *= 1024.0 * 1024.0;
|
|
break;
|
|
case 'h':
|
|
if (lp->limconst != RLIMIT_CPU)
|
|
goto badscal;
|
|
limtail(cp, "hours");
|
|
f *= 3600.0;
|
|
break;
|
|
case 'k':
|
|
if (lp->limconst == RLIMIT_CPU)
|
|
goto badscal;
|
|
limtail(cp, "kbytes");
|
|
f *= 1024.0;
|
|
break;
|
|
case 'm':
|
|
if (lp->limconst == RLIMIT_CPU) {
|
|
limtail(cp, "minutes");
|
|
f *= 60.0;
|
|
break;
|
|
}
|
|
*cp = 'm';
|
|
limtail(cp, "megabytes");
|
|
f *= 1024.0 * 1024.0;
|
|
break;
|
|
case 's':
|
|
if (lp->limconst != RLIMIT_CPU)
|
|
goto badscal;
|
|
limtail(cp, "seconds");
|
|
break;
|
|
case 'u':
|
|
limtail(cp, "unlimited");
|
|
return (RLIM_INFINITY);
|
|
default:
|
|
badscal:
|
|
stderror(ERR_NAME | ERR_SCALEF);
|
|
/* NOTREACHED */
|
|
}
|
|
f += 0.5;
|
|
if (f > (float) RLIM_INFINITY)
|
|
return RLIM_INFINITY;
|
|
else
|
|
return ((RLIM_TYPE)f);
|
|
}
|
|
|
|
static void
|
|
limtail(Char *cp, const char *str)
|
|
{
|
|
while (*cp && *cp == *str)
|
|
cp++, str++;
|
|
if (*cp)
|
|
stderror(ERR_BADSCALE, str);
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
plim(const struct limits *lp, Char hard)
|
|
{
|
|
struct rlimit rlim;
|
|
RLIM_TYPE limit;
|
|
|
|
(void)fprintf(cshout, "%-13.13s", lp->limname);
|
|
|
|
(void)getrlimit(lp->limconst, &rlim);
|
|
limit = hard ? rlim.rlim_max : rlim.rlim_cur;
|
|
|
|
if (limit == RLIM_INFINITY)
|
|
(void)fprintf(cshout, "unlimited");
|
|
else if (lp->limconst == RLIMIT_CPU)
|
|
psecs((long) limit);
|
|
else
|
|
(void)fprintf(cshout, "%ld %s", (long) (limit / lp->limdiv),
|
|
lp->limscale);
|
|
(void)fputc('\n', cshout);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dounlimit(Char **v, struct command *t)
|
|
{
|
|
const struct limits *lp;
|
|
int lerr;
|
|
Char hard;
|
|
|
|
lerr = 0;
|
|
hard = 0;
|
|
v++;
|
|
if (*v && eq(*v, STRmh)) {
|
|
hard = 1;
|
|
v++;
|
|
}
|
|
if (*v == 0) {
|
|
for (lp = limits; lp->limconst >= 0; lp++)
|
|
if (setlim(lp, hard, (RLIM_TYPE)RLIM_INFINITY) < 0)
|
|
lerr++;
|
|
if (lerr)
|
|
stderror(ERR_SILENT);
|
|
return;
|
|
}
|
|
while (*v) {
|
|
lp = findlim(*v++);
|
|
if (setlim(lp, hard, (RLIM_TYPE)RLIM_INFINITY) < 0)
|
|
stderror(ERR_SILENT);
|
|
}
|
|
}
|
|
|
|
static int
|
|
setlim(const struct limits *lp, Char hard, RLIM_TYPE limit)
|
|
{
|
|
struct rlimit rlim;
|
|
|
|
(void)getrlimit(lp->limconst, &rlim);
|
|
|
|
if (hard)
|
|
rlim.rlim_max = limit;
|
|
else if (limit == RLIM_INFINITY && geteuid() != 0)
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
else
|
|
rlim.rlim_cur = limit;
|
|
|
|
if (rlim.rlim_max < rlim.rlim_cur)
|
|
rlim.rlim_max = rlim.rlim_cur;
|
|
|
|
if (setrlimit(lp->limconst, &rlim) < 0) {
|
|
(void)fprintf(csherr, "%s: %s: Can't %s%s limit (%s)\n", bname,
|
|
lp->limname, limit == RLIM_INFINITY ? "remove" : "set",
|
|
hard ? " hard" : "", strerror(errno));
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
dosuspend(Char **v, struct command *t)
|
|
{
|
|
int ctpgrp;
|
|
void (*old)(int);
|
|
|
|
if (loginsh)
|
|
stderror(ERR_SUSPLOG);
|
|
untty();
|
|
|
|
old = signal(SIGTSTP, SIG_DFL);
|
|
(void)kill(0, SIGTSTP);
|
|
/* the shell stops here */
|
|
(void)signal(SIGTSTP, old);
|
|
|
|
if (tpgrp != -1) {
|
|
retry:
|
|
ctpgrp = tcgetpgrp(FSHTTY);
|
|
if (ctpgrp != opgrp) {
|
|
old = signal(SIGTTIN, SIG_DFL);
|
|
(void)kill(0, SIGTTIN);
|
|
(void)signal(SIGTTIN, old);
|
|
goto retry;
|
|
}
|
|
(void)setpgid(0, shpgrp);
|
|
(void)tcsetpgrp(FSHTTY, shpgrp);
|
|
}
|
|
}
|
|
|
|
/* 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!
|
|
*/
|
|
static Char **gv = NULL;
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doeval(Char **v, struct command *t)
|
|
{
|
|
jmp_buf osetexit;
|
|
Char *oevalp, **oevalvec, **savegv;
|
|
int my_reenter, odidfds, oSHERR, oSHIN, oSHOUT, saveERR, saveIN, saveOUT;
|
|
|
|
savegv = gv;
|
|
UNREGISTER(v);
|
|
|
|
oevalvec = evalvec;
|
|
oevalp = evalp;
|
|
odidfds = didfds;
|
|
oSHIN = SHIN;
|
|
oSHOUT = SHOUT;
|
|
oSHERR = SHERR;
|
|
|
|
v++;
|
|
if (*v == 0)
|
|
return;
|
|
gflag = 0, tglob(v);
|
|
if (gflag) {
|
|
gv = v = globall(v);
|
|
gargv = 0;
|
|
if (v == 0)
|
|
stderror(ERR_NOMATCH);
|
|
v = copyblk(v);
|
|
}
|
|
else {
|
|
gv = NULL;
|
|
v = copyblk(v);
|
|
trim(v);
|
|
}
|
|
|
|
saveIN = dcopy(SHIN, -1);
|
|
saveOUT = dcopy(SHOUT, -1);
|
|
saveERR = dcopy(SHERR, -1);
|
|
|
|
getexit(osetexit);
|
|
|
|
if ((my_reenter = setexit()) == 0) {
|
|
evalvec = v;
|
|
evalp = 0;
|
|
SHIN = dcopy(0, -1);
|
|
SHOUT = dcopy(1, -1);
|
|
SHERR = dcopy(2, -1);
|
|
didfds = 0;
|
|
process(0);
|
|
}
|
|
|
|
evalvec = oevalvec;
|
|
evalp = oevalp;
|
|
doneinp = 0;
|
|
didfds = odidfds;
|
|
if (SHIN != -1)
|
|
(void)close(SHIN);
|
|
if (SHOUT != -1)
|
|
(void)close(SHOUT);
|
|
if (SHERR != -1)
|
|
(void)close(SHERR);
|
|
SHIN = dmove(saveIN, oSHIN);
|
|
SHOUT = dmove(saveOUT, oSHOUT);
|
|
SHERR = dmove(saveERR, oSHERR);
|
|
if (gv)
|
|
blkfree(gv), gv = NULL;
|
|
resexit(osetexit);
|
|
gv = savegv;
|
|
if (my_reenter)
|
|
stderror(ERR_SILENT);
|
|
}
|
|
|
|
void
|
|
/*ARGSUSED*/
|
|
doprintf(Char **v, struct command *t)
|
|
{
|
|
char **c;
|
|
int ret;
|
|
|
|
ret = progprintf(blklen(v), c = short2blk(v));
|
|
(void)fflush(cshout);
|
|
(void)fflush(csherr);
|
|
|
|
blkfree((Char **) c);
|
|
if (ret)
|
|
stderror(ERR_SILENT);
|
|
}
|