Rework addition of Symbian splash screen to reduce the source impact (uses SVG from Bug 2414)
Notes: by using the OPTION SOURCEDIR parameter in the mifconv extension instructions, I can
arrange to use the same source file name in sfimage, without having to export over the original
Nokia file. This means that the name inside splashscreen.mbg is the same, which removes the need
for the conditional compilation in SplashScreen.cpp, and gets rid of sf_splashscreen.mmp.
/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
/*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1997 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
#include "zsh.mdh"
#include "exec.pro"
#ifdef __SYMBIAN32__
#ifdef __WINSCW__
#pragma warn_unusedarg off
#pragma warn_possunwant off
#endif//__WINSCW__
#endif//__SYMBIAN32__
#ifdef __SYMBIAN32__
#include <pthread.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "dummy.h"
#endif //__SYMBIAN32__
#ifdef __SYMBIAN32__
typedef struct
{
Eprog p;
int dont_change_job;
int exiting;
} st_exec;
#endif // __SYMBIAN32__
/* used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. */
/**/
int noerrexit;
/* suppress error messages */
/**/
mod_export int noerrs;
/* do not save history on exec and exit */
/**/
int nohistsave;
/* error/break flag */
/**/
mod_export int errflag;
/* Status of return from a trap */
/**/
int trapreturn;
/* != 0 if this is a subshell */
/**/
int subsh;
/* != 0 if we have a return pending */
/**/
mod_export int retflag;
/**/
long lastval2;
/* The table of file descriptors. A table element is zero if the *
* corresponding fd is not used by the shell. It is greater than *
* 1 if the fd is used by a <(...) or >(...) substitution and 1 if *
* it is an internal file descriptor which must be closed before *
* executing an external command. The first ten elements of the *
* table is not used. A table element is set by movefd and cleard *
* by zclose. */
/**/
char *fdtable;
/* The allocated size of fdtable */
/**/
int fdtable_size;
/* The highest fd that marked with nonzero in fdtable */
/**/
int max_zsh_fd;
/* input fd from the coprocess */
/**/
mod_export int coprocin;
/* output fd from the coprocess */
/**/
mod_export int coprocout;
/* != 0 if the line editor is active */
/**/
mod_export int zleactive;
/* pid of process undergoing 'process substitution' */
/**/
pid_t cmdoutpid;
/* exit status of process undergoing 'process substitution' */
/**/
int cmdoutval;
/* The context in which a shell function is called, see SFC_* in zsh.h. */
/**/
mod_export int sfcontext;
/* Stack to save some variables before executing a signal handler function */
/**/
struct execstack *exstack;
/* Stack with names of functions currently active. */
/**/
mod_export Funcstack funcstack;
#define execerr() if (!forked) { lastval = 1; goto done; } else _exit(1)
static LinkList args;
static int doneps4;
static char *STTYval;
/* Execution functions. */
static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
execcursh, exectime, execfuncdef, execfor, execselect,
execwhile, execrepeat, execcase, execif, execcond,
execarith, execautofn, exectry
};
/* structure for command builtin for when it is used with -v or -V */
static struct builtin commandbn =
BUILTIN(0, 0, bin_whence, 0, -1, BIN_COMMAND, "vV", NULL);
/* parse string into a list */
/**/
mod_export Eprog
parse_string(char *s)
{
Eprog p;
int oldlineno = lineno;
lexsave();
inpush(s, INP_LINENO, NULL);
strinbeg(0);
lineno = 1;
p = parse_list();
lineno = oldlineno;
if (tok == LEXERR && !lastval)
lastval = 1;
strinend();
inpop();
lexrestore();
return p;
}
/**/
#ifdef HAVE_GETRLIMIT
/* the resource limits for the shell and its children */
/**/
mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS];
/**/
mod_export int
zsetlimit(int limnum, char *nam)
{
if (limits[limnum].rlim_max != current_limits[limnum].rlim_max ||
limits[limnum].rlim_cur != current_limits[limnum].rlim_cur) {
if (setrlimit(limnum, limits + limnum)) {
if (nam)
zwarnnam(nam, "setrlimit failed: %e", NULL, errno);
return -1;
}
current_limits[limnum] = limits[limnum];
}
return 0;
}
/**/
mod_export int
setlimits(char *nam)
{
int limnum;
int ret = 0;
for (limnum = 0; limnum < RLIM_NLIMITS; limnum++)
if (zsetlimit(limnum, nam))
ret++;
return ret;
}
/**/
#endif /* HAVE_GETRLIMIT */
/* fork and set limits */
/**/
#ifndef __SYMBIAN32__
static pid_t
zfork(struct timeval *tv)
{
pid_t pid;
struct timezone dummy_tz;
/*
* Is anybody willing to explain this test?
*/
if (thisjob != -1 && thisjob >= jobtabsize - 1 && !expandjobtab()) {
zerr("job table full", NULL, 0);
return -1;
}
if (tv)
gettimeofday(tv, &dummy_tz);
pid = fork();
if (pid == -1) {
zerr("fork failed: %e", NULL, errno);
return -1;
}
#ifdef HAVE_GETRLIMIT
if (!pid)
/* set resource limits for the child process */
setlimits(NULL);
#endif
return pid;
}
#endif //__SYMBIAN32__
/*
* Allen Edeln gebiet ich Andacht,
* Hohen und Niedern von Heimdalls Geschlecht;
* Ich will list_pipe's Wirken kuenden
* Die aeltesten Sagen, der ich mich entsinne...
*
* In most shells, if you do something like:
*
* cat foo | while read a; do grep $a bar; done
*
* the shell forks and executes the loop in the sub-shell thus created.
* In zsh this traditionally executes the loop in the current shell, which
* is nice to have if the loop does something to change the shell, like
* setting parameters or calling builtins.
* Putting the loop in a sub-shell makes life easy, because the shell only
* has to put it into the job-structure and then treats it as a normal
* process. Suspending and interrupting is no problem then.
* Some years ago, zsh either couldn't suspend such things at all, or
* it got really messed up when users tried to do it. As a solution, we
* implemented the list_pipe-stuff, which has since then become a reason
* for many nightmares.
* Pipelines like the one above are executed by the functions in this file
* which call each other (and sometimes recursively). The one above, for
* example would lead to a function call stack roughly like:
*
* execlist->execpline->execcmd->execwhile->execlist->execpline
*
* (when waiting for the grep, ignoring execpline2 for now). At this time,
* zsh has built two job-table entries for it: one for the cat and one for
* the grep. If the user hits ^Z at this point (and jobbing is used), the
* shell is notified that the grep was suspended. The list_pipe flag is
* used to tell the execpline where it was waiting that it was in a pipeline
* with a shell construct at the end (which may also be a shell function or
* several other things). When zsh sees the suspended grep, it forks to let
* the sub-shell execute the rest of the while loop. The parent shell walks
* up in the function call stack to the first execpline. There it has to find
* out that it has just forked and then has to add information about the sub-
* shell (its pid and the text for it) in the job entry of the cat. The pid
* is passed down in the list_pipe_pid variable.
* But there is a problem: the suspended grep is a child of the parent shell
* and can't be adopted by the sub-shell. So the parent shell also has to
* keep the information about this process (more precisely: this pipeline)
* by keeping the job table entry it created for it. The fact that there
* are two jobs which have to be treated together is remembered by setting
* the STAT_SUPERJOB flag in the entry for the cat-job (which now also
* contains a process-entry for the whole loop -- the sub-shell) and by
* setting STAT_SUBJOB in the job of the grep-job. With that we can keep
* sub-jobs from being displayed and we can handle an fg/bg on the super-
* job correctly. When the super-job is continued, the shell also wakes up
* the sub-job. But then, the grep will exit sometime. Now the parent shell
* has to remember not to try to wake it up again (in case of another ^Z).
* It also has to wake up the sub-shell (which suspended itself immediately
* after creation), so that the rest of the loop is executed by it.
* But there is more: when the sub-shell is created, the cat may already
* have exited, so we can't put the sub-shell in the process group of it.
* In this case, we put the sub-shell in the process group of the parent
* shell and in any case, the sub-shell has to put all commands executed
* by it into its own process group, because only this way the parent
* shell can control them since it only knows the process group of the sub-
* shell. Of course, this information is also important when putting a job
* in the foreground, where we have to attach its process group to the
* controlling tty.
* All this is made more difficult because we have to handle return values
* correctly. If the grep is signaled, its exit status has to be propagated
* back to the parent shell which needs it to set the exit status of the
* super-job. And of course, when the grep is signaled (including ^C), the
* loop has to be stopped, etc.
* The code for all this is distributed over three files (exec.c, jobs.c,
* and signals.c) and none of them is a simple one. So, all in all, there
* may still be bugs, but considering the complexity (with race conditions,
* signal handling, and all that), this should probably be expected.
*/
/**/
int list_pipe = 0, simple_pline = 0;
static pid_t list_pipe_pid;
static struct timeval list_pipe_start;
static int nowait, pline_level = 0;
static int list_pipe_child = 0, list_pipe_job;
static char list_pipe_text[JOBTEXTSIZE];
/* execute a current shell command */
/**/
static int
execcursh(Estate state, int do_exec)
{
Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
/* Skip word only used for try/always */
state->pc++;
if (!list_pipe && thisjob != list_pipe_job && !hasprocs(thisjob))
deletejob(jobtab + thisjob);
cmdpush(CS_CURSH);
execlist(state, 1, do_exec);
cmdpop();
state->pc = end;
return lastval;
}
/* execve after handling $_ and #! */
#define POUNDBANGLIMIT 64
/**/
static int
zexecve(char *pth, char **argv)
{
int eno =0;
#ifndef __SYMBIAN32__
static char buf[PATH_MAX * 2];
char **eep;
unmetafy(pth, NULL);
for (eep = argv; *eep; eep++)
if (*eep != pth)
unmetafy(*eep, NULL);
buf[0] = '_';
buf[1] = '=';
if (*pth == '/')
strcpy(buf + 2, pth);
else
sprintf(buf + 2, "%s/%s", pwd, pth);
zputenv(buf);
closedumps();
execve(pth, argv, environ);
/* If the execve returns (which in general shouldn't happen), *
* then check for an errno equal to ENOEXEC. This errno is set *
* if the process file has the appropriate access permission, *
* but has an invalid magic number in its header. */
if ((eno = errno) == ENOEXEC) {
char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0;
int fd, ct, t0;
if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) {
argv0 = *argv;
*argv = pth;
ct = read(fd, execvebuf, POUNDBANGLIMIT);
close(fd);
if (ct > 0) {
if (execvebuf[0] == '#') {
if (execvebuf[1] == '!') {
for (t0 = 0; t0 != ct; t0++)
if (execvebuf[t0] == '\n')
break;
while (inblank(execvebuf[t0]))
execvebuf[t0--] = '\0';
execvebuf[POUNDBANGLIMIT] = '\0';
for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
if (*ptr) {
*ptr = '\0';
argv[-2] = ptr2;
argv[-1] = ptr + 1;
execve(ptr2, argv - 2, environ);
} else {
argv[-1] = ptr2;
execve(ptr2, argv - 1, environ);
}
} else {
argv[-1] = "sh";
execve("/bin/sh", argv - 1, environ);
}
} else {
for (t0 = 0; t0 != ct; t0++)
if (!execvebuf[t0])
break;
if (t0 == ct) {
argv[-1] = "sh";
execve("/bin/sh", argv - 1, environ);
}
}
} else
eno = errno;
*argv = argv0;
} else
eno = errno;
}
/* restore the original arguments and path but do not bother with *
* null characters as these cannot be passed to external commands *
* anyway. So the result is truncated at the first null char. */
pth = metafy(pth, -1, META_NOALLOC);
for (eep = argv; *eep; eep++)
if (*eep != pth)
(void) metafy(*eep, -1, META_NOALLOC);
#endif//__SYMBIAN32__
return eno;
}
#define MAXCMDLEN (PATH_MAX*4)
/* test whether we really want to believe the error number */
/**/
static int
isgooderr(int e, char *dir)
{
/*
* Maybe the directory was unreadable, or maybe it wasn't
* even a directory.
*/
return ((e != EACCES || !access(dir, X_OK)) &&
e != ENOENT && e != ENOTDIR);
}
/* execute an external command */
/**/
void
execute(UNUSED(Cmdnam cmdname), int dash, int defpath)
{
Cmdnam cn;
char buf[MAXCMDLEN], buf2[MAXCMDLEN];
char *s, *z, *arg0;
char **argv, **pp;
int eno = 0, ee;
arg0 = (char *) peekfirst(args);
if (isset(RESTRICTED) && (strchr(arg0, '/') || defpath)) {
zerr("%s: restricted", arg0, 0);
_exit(1);
}
/* If the parameter STTY is set in the command's environment, *
* we first run the stty command with the value of this *
* parameter as it arguments. */
if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) {
LinkList exargs = args;
char *t = tricat("stty", " ", s);
STTYval = 0; /* this prevents infinite recursion */
zsfree(s);
args = NULL;
execstring(t, 1, 0);
zsfree(t);
args = exargs;
} else if (s) {
STTYval = 0;
zsfree(s);
}
/* If ARGV0 is in the commands environment, we use *
* that as argv[0] for this external command */
if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) {
setdata(firstnode(args), (void *) ztrdup(z));
delenvvalue(z - 6);
} else if (dash) {
/* Else if the pre-command `-' was given, we add `-' *
* to the front of argv[0] for this command. */
sprintf(buf2, "-%s", arg0);
setdata(firstnode(args), (void *) ztrdup(buf2));
}
argv = makecline(args);
closem(3);
child_unblock();
if ((int) strlen(arg0) >= PATH_MAX) {
zerr("command too long: %s", arg0, 0);
_exit(1);
}
for (s = arg0; *s; s++)
if (*s == '/') {
errno = zexecve(arg0, argv);
if (arg0 == s || unset(PATHDIRS) ||
(arg0[0] == '.' && (arg0 + 1 == s ||
(arg0[1] == '.' && arg0 + 2 == s)))) {
zerr("%e: %s", arg0, errno);
_exit((errno == EACCES || errno == ENOEXEC) ? 126 : 127);
}
break;
}
/* for command -p, search the default path */
if (defpath) {
char *s, pbuf[PATH_MAX];
char *dptr, *pe, *ps = DEFAULT_PATH;
for(;ps;ps = pe ? pe+1 : NULL) {
pe = strchr(ps, ':');
if (*ps == '/') {
s = pbuf;
if (pe)
struncpy(&s, ps, pe-ps);
else
strucpy(&s, ps);
*s++ = '/';
if ((s - pbuf) + strlen(arg0) >= PATH_MAX)
continue;
strucpy(&s, arg0);
if (iscom(pbuf))
break;
}
}
if (!ps) {
zerr("command not found: %s", arg0, 0);
_exit(127);
}
ee = zexecve(pbuf, argv);
if ((dptr = strrchr(pbuf, '/')))
*dptr = '\0';
if (isgooderr(ee, *pbuf ? pbuf : "/"))
eno = ee;
} else {
if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) {
char nn[PATH_MAX], *dptr;
if (cn->flags & HASHED)
strcpy(nn, cn->u.cmd);
else {
for (pp = path; pp < cn->u.name; pp++)
if (!**pp || (**pp == '.' && (*pp)[1] == '\0')) {
ee = zexecve(arg0, argv);
if (isgooderr(ee, *pp))
eno = ee;
} else if (**pp != '/') {
z = buf;
strucpy(&z, *pp);
*z++ = '/';
strcpy(z, arg0);
ee = zexecve(buf, argv);
if (isgooderr(ee, *pp))
eno = ee;
}
strcpy(nn, cn->u.name ? *(cn->u.name) : "");
strcat(nn, "/");
strcat(nn, cn->nam);
}
ee = zexecve(nn, argv);
if ((dptr = strrchr(nn, '/')))
*dptr = '\0';
if (isgooderr(ee, *nn ? nn : "/"))
eno = ee;
}
for (pp = path; *pp; pp++)
if (!(*pp)[0] || ((*pp)[0] == '.' && !(*pp)[1])) {
ee = zexecve(arg0, argv);
if (isgooderr(ee, *pp))
eno = ee;
} else {
z = buf;
strucpy(&z, *pp);
*z++ = '/';
strcpy(z, arg0);
ee = zexecve(buf, argv);
if (isgooderr(ee, *pp))
eno = ee;
}
}
if (eno)
zerr("%e: %s", arg0, eno);
else
zerr("command not found: %s", arg0, 0);
_exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
}
#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; }
/*
* Get the full pathname of an external command.
* If the second argument is zero, return the first argument if found;
* if non-zero, return the path using heap memory. (RET_IF_COM(X), above).
*/
/**/
mod_export char *
findcmd(char *arg0, int docopy)
{
char **pp;
char *z, *s, buf[MAXCMDLEN];
Cmdnam cn;
cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
if (!cn && isset(HASHCMDS))
cn = hashcmd(arg0, path);
if ((int) strlen(arg0) > PATH_MAX)
return NULL;
for (s = arg0; *s; s++)
if (*s == '/') {
RET_IF_COM(arg0);
if (arg0 == s || unset(PATHDIRS)) {
return NULL;
}
break;
}
if (cn) {
char nn[PATH_MAX];
if (cn->flags & HASHED)
strcpy(nn, cn->u.cmd);
else {
for (pp = path; pp < cn->u.name; pp++)
if (**pp != '/') {
z = buf;
if (**pp) {
strucpy(&z, *pp);
*z++ = '/';
}
strcpy(z, arg0);
RET_IF_COM(buf);
}
strcpy(nn, cn->u.name ? *(cn->u.name) : "");
strcat(nn, "/");
strcat(nn, cn->nam);
}
RET_IF_COM(nn);
}
for (pp = path; *pp; pp++) {
z = buf;
if (**pp) {
strucpy(&z, *pp);
*z++ = '/';
}
strcpy(z, arg0);
RET_IF_COM(buf);
}
return NULL;
}
/**/
int
iscom(char *s)
{
struct stat statbuf;
char *us = unmeta(s);
return (access(us, X_OK) == 0 && stat(us, &statbuf) >= 0 &&
S_ISREG(statbuf.st_mode));
}
/**/
int
isreallycom(Cmdnam cn)
{
char fullnam[MAXCMDLEN];
if (cn->flags & HASHED)
strcpy(fullnam, cn->u.cmd);
else if (!cn->u.name)
return 0;
else {
strcpy(fullnam, *(cn->u.name));
strcat(fullnam, "/");
strcat(fullnam, cn->nam);
}
return iscom(fullnam);
}
/**/
int
isrelative(char *s)
{
if (*s != '/')
return 1;
for (; *s; s++)
if (*s == '.' && s[-1] == '/' &&
(s[1] == '/' || s[1] == '\0' ||
(s[1] == '.' && (s[2] == '/' || s[2] == '\0'))))
return 1;
return 0;
}
/**/
mod_export Cmdnam
hashcmd(char *arg0, char **pp)
{
Cmdnam cn;
char *s, buf[PATH_MAX];
char **pq;
for (; *pp; pp++)
if (**pp == '/') {
s = buf;
strucpy(&s, *pp);
*s++ = '/';
if ((s - buf) + strlen(arg0) >= PATH_MAX)
continue;
strcpy(s, arg0);
if (iscom(buf))
break;
}
if (!*pp)
return NULL;
cn = (Cmdnam) zshcalloc(sizeof *cn);
cn->flags = 0;
cn->u.name = pp;
cmdnamtab->addnode(cmdnamtab, ztrdup(arg0), cn);
if (isset(HASHDIRS)) {
for (pq = pathchecked; pq <= pp; pq++)
hashdir(pq);
pathchecked = pp + 1;
}
return cn;
}
/* execute a string */
/**/
mod_export void
execstring(char *s, int dont_change_job, int exiting)
{
Eprog prog;
pushheap();
if ((prog = parse_string(s)))
execode(prog, dont_change_job, exiting);
popheap();
}
#ifdef __SYMBIAN32__
mod_export
void* execode_wrap(void* any)
{
st_exec *st=(st_exec*)any;
if(st)
execode(st->p, st->dont_change_job, st->exiting);
return NULL;
}
#endif //__SYMBIAN32__
/**/
mod_export void
execode(Eprog p, int dont_change_job, int exiting)
{
struct estate s;
s.prog = p;
s.pc = p->prog;
s.strs = p->strs;
useeprog(p); /* Mark as in use */
execlist(&s, dont_change_job, exiting);
freeeprog(p); /* Free if now unused */
}
/* Execute a simplified command. This is used to execute things that
* will run completely in the shell, so that we can by-pass all that
* nasty job-handling and redirection stuff in execpline and execcmd. */
/**/
static int
execsimple(Estate state)
{
wordcode code = *state->pc++;
int lv;
if (errflag)
return (lastval = 1);
/* In evaluated traps, don't modify the line number. */
if ((!intrap || trapisfunc) && !ineval && code)
lineno = code - 1;
code = wc_code(*state->pc++);
if (code == WC_ASSIGN) {
cmdoutval = 0;
addvars(state, state->pc - 1, 0);
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
}
lv = (lastval ? lastval : cmdoutval);
} else
lv = (execfuncs[code - WC_CURSH])(state, 0);
return lastval = lv;
}
/* Main routine for executing a list. *
* exiting means that the (sub)shell we are in is a definite goner *
* after the current list is finished, so we may be able to exec the *
* last command directly instead of forking. If dont_change_job is *
* nonzero, then restore the current job number after executing the *
* list. */
/**/
void
execlist(Estate state, int dont_change_job, int exiting)
{
static int donetrap;
Wordcode next;
wordcode code;
int ret, cj, csp, ltype;
int old_pline_level, old_list_pipe, oldlineno;
/*
* ERREXIT only forces the shell to exit if the last command in a &&
* or || fails. This is the case even if an earlier command is a
* shell function or other current shell structure, so we have to set
* noerrexit here if the sublist is not of type END.
*/
int oldnoerrexit = noerrexit;
cj = thisjob;
old_pline_level = pline_level;
old_list_pipe = list_pipe;
oldlineno = lineno;
if (sourcelevel && unset(SHINSTDIN))
pline_level = list_pipe = 0;
/* Loop over all sets of comands separated by newline, *
* semi-colon or ampersand (`sublists'). */
code = *state->pc++;
while (wc_code(code) == WC_LIST && !breaks && !retflag) {
ltype = WC_LIST_TYPE(code);
csp = cmdsp;
if (ltype & Z_SIMPLE) {
next = state->pc + WC_LIST_SKIP(code);
execsimple(state);
state->pc = next;
goto sublist_done;
}
/* Reset donetrap: this ensures that a trap is only *
* called once for each sublist that fails. */
donetrap = 0;
/* Loop through code followed by &&, ||, or end of sublist. */
code = *state->pc++;
while (wc_code(code) == WC_SUBLIST) {
next = state->pc + WC_SUBLIST_SKIP(code);
if (!oldnoerrexit)
noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END);
switch (WC_SUBLIST_TYPE(code)) {
case WC_SUBLIST_END:
/* End of sublist; just execute, ignoring status. */
if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE)
execsimple(state);
else
execpline(state, code, ltype, (ltype & Z_END) && exiting);
state->pc = next;
goto sublist_done;
break;
case WC_SUBLIST_AND:
/* If the return code is non-zero, we skip pipelines until *
* we find a sublist followed by ORNEXT. */
if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
execsimple(state) :
execpline(state, code, Z_SYNC, 0)))) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
while (wc_code(code) == WC_SUBLIST &&
WC_SUBLIST_TYPE(code) == WC_SUBLIST_AND) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
}
if (wc_code(code) != WC_SUBLIST) {
/* We've skipped to the end of the list, not executing *
* the final pipeline, so don't perform error handling *
* for this sublist. */
donetrap = 1;
goto sublist_done;
} else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
donetrap = 1;
/*
* Treat this in the same way as if we reached
* the end of the sublist normally.
*/
state->pc = next;
goto sublist_done;
}
}
cmdpush(CS_CMDAND);
break;
case WC_SUBLIST_OR:
/* If the return code is zero, we skip pipelines until *
* we find a sublist followed by ANDNEXT. */
if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
execsimple(state) :
execpline(state, code, Z_SYNC, 0)))) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
while (wc_code(code) == WC_SUBLIST &&
WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
}
if (wc_code(code) != WC_SUBLIST) {
/* We've skipped to the end of the list, not executing *
* the final pipeline, so don't perform error handling *
* for this sublist. */
donetrap = 1;
goto sublist_done;
} else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
donetrap = 1;
/*
* Treat this in the same way as if we reached
* the end of the sublist normally.
*/
state->pc = next;
goto sublist_done;
}
}
cmdpush(CS_CMDOR);
break;
}
state->pc = next;
code = *state->pc++;
}
state->pc--;
sublist_done:
noerrexit = oldnoerrexit;
if (sigtrapped[SIGDEBUG]) {
exiting = donetrap;
ret = lastval;
dotrap(SIGDEBUG);
lastval = ret;
donetrap = exiting;
noerrexit = oldnoerrexit;
}
cmdsp = csp;
/* Check whether we are suppressing traps/errexit *
* (typically in init scripts) and if we haven't *
* already performed them for this sublist. */
if (!noerrexit && !donetrap) {
if (sigtrapped[SIGZERR] && lastval) {
dotrap(SIGZERR);
donetrap = 1;
}
if (lastval) {
int errreturn = isset(ERRRETURN) &&
(isset(INTERACTIVE) || locallevel || sourcelevel);
int errexit = isset(ERREXIT) ||
(isset(ERRRETURN) && !errreturn);
if (errexit) {
if (sigtrapped[SIGEXIT])
dotrap(SIGEXIT);
if (mypid != getpid())
_exit(lastval);
else
exit(lastval);
}
if (errreturn) {
retflag = 1;
breaks = loops;
}
}
}
if (ltype & Z_END)
break;
code = *state->pc++;
}
pline_level = old_pline_level;
list_pipe = old_list_pipe;
lineno = oldlineno;
if (dont_change_job)
thisjob = cj;
}
/* Execute a pipeline. *
* last1 is a flag that this command is the last command in a shell *
* that is about to exit, so we can exec instead of forking. It gets *
* passed all the way down to execcmd() which actually makes the *
* decision. A 0 is always passed if the command is not the last in *
* the pipeline. This function assumes that the sublist is not NULL. *
* If last1 is zero but the command is at the end of a pipeline, we *
* pass 2 down to execcmd(). *
*/
/**/
static int
execpline(Estate state, wordcode slcode, int how, int last1)
{
int ipipe[2], opipe[2];
int pj, newjob;
int old_simple_pline = simple_pline;
int slflags = WC_SUBLIST_FLAGS(slcode);
wordcode code = *state->pc++;
static int lastwj, lpforked;
if (wc_code(code) != WC_PIPE)
return lastval = (slflags & WC_SUBLIST_NOT) != 0;
else if (slflags & WC_SUBLIST_NOT)
last1 = 0;
pj = thisjob;
ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
child_block();
/*
* Get free entry in job table and initialize it.
* This is currently the only call to initjob(), so this
* is also the only place where we can expand the job table
* under us.
*/
if ((thisjob = newjob = initjob()) == -1) {
child_unblock();
return 1;
}
if (how & Z_TIMED)
jobtab[thisjob].stat |= STAT_TIMED;
if (slflags & WC_SUBLIST_COPROC) {
how = Z_ASYNC;
if (coprocin >= 0) {
zclose(coprocin);
zclose(coprocout);
}
mpipe(ipipe);
mpipe(opipe);
coprocin = ipipe[0];
coprocout = opipe[1];
fdtable[coprocin] = fdtable[coprocout] = 0;
}
/* This used to set list_pipe_pid=0 unconditionally, but in things
* like `ls|if true; then sleep 20; cat; fi' where the sleep was
* stopped, the top-level execpline() didn't get the pid for the
* sub-shell because it was overwritten. */
if (!pline_level++) {
list_pipe_pid = 0;
nowait = 0;
simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END);
list_pipe_job = newjob;
}
lastwj = lpforked = 0;
execpline2(state, code, how, opipe[0], ipipe[1], last1);
pline_level--;
if (how & Z_ASYNC) {
lastwj = newjob;
if (thisjob == list_pipe_job)
list_pipe_job = 0;
jobtab[thisjob].stat |= STAT_NOSTTY;
if (slflags & WC_SUBLIST_COPROC) {
zclose(ipipe[1]);
zclose(opipe[0]);
}
if (how & Z_DISOWN) {
deletejob(jobtab + thisjob);
thisjob = -1;
}
else
spawnjob();
child_unblock();
return 0;
} else {
if (newjob != lastwj) {
Job jn = jobtab + newjob;
int updated;
if (newjob == list_pipe_job && list_pipe_child)
_exit(0);
lastwj = thisjob = newjob;
if (list_pipe || (pline_level && !(how & Z_TIMED)))
jn->stat |= STAT_NOPRINT;
if (nowait) {
if(!pline_level) {
struct process *pn, *qn;
curjob = newjob;
DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
addproc(list_pipe_pid, list_pipe_text, 0,
&list_pipe_start);
/* If the super-job contains only the sub-shell, the
sub-shell is the group leader. */
if (!jn->procs->next || lpforked == 2) {
jn->gleader = list_pipe_pid;
jn->stat |= STAT_SUBLEADER;
}
for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
if (WIFSTOPPED(pn->status))
break;
if (pn) {
for (qn = jn->procs; qn->next; qn = qn->next);
qn->status = pn->status;
}
jn->stat &= ~(STAT_DONE | STAT_NOPRINT);
jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED;
printjob(jn, !!isset(LONGLISTJOBS), 1);
}
else if (newjob != list_pipe_job)
deletejob(jn);
else
lastwj = -1;
}
errbrk_saved = 0;
for (; !nowait;) {
if (list_pipe_child) {
jn->stat |= STAT_NOPRINT;
makerunning(jn);
}
if (!(jn->stat & STAT_LOCKED)) {
updated = hasprocs(thisjob);
waitjobs();
child_block();
} else
updated = 0;
if (!updated &&
list_pipe_job && hasprocs(list_pipe_job) &&
!(jobtab[list_pipe_job].stat & STAT_STOPPED)) {
child_unblock();
child_block();
}
if (list_pipe_child &&
jn->stat & STAT_DONE &&
lastval2 & 0200)
killpg(mypgrp, lastval2 & ~0200);
if (!list_pipe_child && !lpforked && !subsh && jobbing &&
(list_pipe || last1 || pline_level) &&
((jn->stat & STAT_STOPPED) ||
(list_pipe_job && pline_level &&
(jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
#ifndef __SYMBIAN32__
pid_t pid;
int synch[2];
struct timeval bgtime;
pipe(synch);
if ((pid = zfork(&bgtime)) == -1) {
trashzle();
close(synch[0]);
close(synch[1]);
fprintf(stderr, "zsh: job can't be suspended\n");
fflush(stderr);
makerunning(jn);
killjb(jn, SIGCONT);
thisjob = newjob;
}
else if (pid) {
char dummy;
lpforked =
(killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
list_pipe_pid = pid;
list_pipe_start = bgtime;
nowait = errflag = 1;
breaks = loops;
close(synch[1]);
read(synch[0], &dummy, 1);
close(synch[0]);
/* If this job has finished, we leave it as a
* normal (non-super-) job. */
if (!(jn->stat & STAT_DONE)) {
jobtab[list_pipe_job].other = newjob;
jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
jn->other = pid;
}
if ((list_pipe || last1) && hasprocs(list_pipe_job))
killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
break;
}
else {
close(synch[0]);
entersubsh(Z_ASYNC, 0, 0, 0);
if (jobtab[list_pipe_job].procs) {
if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
== -1) {
setpgrp(0L, mypgrp = getpid());
}
} else
setpgrp(0L, mypgrp = getpid());
close(synch[1]);
kill(getpid(), SIGSTOP);
list_pipe = 0;
list_pipe_child = 1;
opts[INTERACTIVE] = 0;
if (errbrk_saved) {
errflag = prev_errflag;
breaks = prev_breaks;
}
break;
}
#else
if ((list_pipe || last1) && hasprocs(list_pipe_job))
killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
break;
#endif // __SYMBIAN32__
}
else if (subsh && jn->stat & STAT_STOPPED)
thisjob = newjob;
else
break;
}
child_unblock();
if (list_pipe && (lastval & 0200) && pj >= 0 &&
(!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
deletejob(jn);
jn = jobtab + pj;
if (jn->gleader)
killjb(jn, lastval & ~0200);
}
if (list_pipe_child ||
((jn->stat & STAT_DONE) &&
(list_pipe || (pline_level && !(jn->stat & STAT_SUBJOB)))))
deletejob(jn);
thisjob = pj;
}
if (slflags & WC_SUBLIST_NOT)
lastval = !lastval;
}
if (!pline_level)
simple_pline = old_simple_pline;
return lastval;
}
static int subsh_close = -1;
/* execute pipeline. This function assumes the `pline' is not NULL. */
/**/
static void
execpline2(Estate state, wordcode pcode,
int how, int input, int output, int last1)
{
int pipes[2];
if (breaks || retflag)
return;
/* In evaluated traps, don't modify the line number. */
if ((!intrap || trapisfunc) && !ineval && WC_PIPE_LINENO(pcode))
lineno = WC_PIPE_LINENO(pcode) - 1;
if (pline_level == 1) {
if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel))
strcpy(list_pipe_text,
getjobtext(state->prog,
state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ?
0 : 1)));
else
list_pipe_text[0] = '\0';
}
if (WC_PIPE_TYPE(pcode) == WC_PIPE_END)
#ifndef __SYMBIAN32__
execcmd(state, input, output, how, last1 ? 1 : 2);
#else
execcmd(state, input, 0, how, last1 ? 1 : 2);
#endif
else {
int old_list_pipe = list_pipe;
Wordcode next = state->pc + (*state->pc), pc;
wordcode code;
state->pc++;
for (pc = state->pc; wc_code(code = *pc) == WC_REDIR; pc += 3);
mpipe(pipes);
/* if we are doing "foo | bar" where foo is a current *
* shell command, do foo in a subshell and do the *
* rest of the pipeline in the current shell. */
if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) {
#ifndef __SYMBIAN32__
int synch[2];
pid_t pid;
struct timeval bgtime;
pipe(synch);
if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
} else if (pid) {
char dummy, *text;
text = getjobtext(state->prog, state->pc);
addproc(pid, text, 0, &bgtime);
close(synch[1]);
read(synch[0], &dummy, 1);
close(synch[0]);
} else {
zclose(pipes[0]);
close(synch[0]);
entersubsh(how, 2, 0, 0);
close(synch[1]);
execcmd(state, input, pipes[1], how, 0);
_exit(lastval);
}
#else
execcmd(state, input, pipes[1], how, 0);
#endif//__SYMBIAN32__
} else {
/* otherwise just do the pipeline normally. */
#ifndef __SYMBIAN32__
subsh_close = pipes[0];
execcmd(state, input, pipes[1], how, 0);
#else
execcmd(state, 1, 1, how, 0);
#endif
}
#ifndef __SYMBIAN32__
zclose(pipes[1]);
#endif
state->pc = next;
/* if another execpline() is invoked because the command is *
* a list it must know that we're already in a pipeline */
cmdpush(CS_PIPE);
list_pipe = 1;
#ifndef __SYMBIAN32__
execpline2(state, *state->pc++, how, pipes[0], output, last1);
#else
execpline2(state, *state->pc++, how, 1, output, last1);
#endif
list_pipe = old_list_pipe;
cmdpop();
#ifndef __SYMBIAN32__
zclose(pipes[0]);
#endif
subsh_close = -1;
}
}
/* make the argv array */
/**/
static char **
makecline(LinkList list)
{
LinkNode node;
char **argv, **ptr;
/* A bigger argv is necessary for executing scripts */
ptr = argv = 2 + (char **) hcalloc((countlinknodes(list) + 4) *
sizeof(char *));
if (isset(XTRACE)) {
if (!doneps4)
printprompt4();
for (node = firstnode(list); node; incnode(node)) {
*ptr++ = (char *)getdata(node);
quotedzputs(getdata(node), xtrerr);
if (nextnode(node))
fputc(' ', xtrerr);
}
fputc('\n', xtrerr);
fflush(xtrerr);
} else {
for (node = firstnode(list); node; incnode(node))
*ptr++ = (char *)getdata(node);
}
*ptr = NULL;
return (argv);
}
/**/
mod_export void
untokenize(char *s)
{
if (*s) {
int c;
while ((c = *s++))
if (itok(c)) {
char *p = s - 1;
if (c != Nularg)
*p++ = ztokens[c - Pound];
while ((c = *s++)) {
if (itok(c)) {
if (c != Nularg)
*p++ = ztokens[c - Pound];
} else
*p++ = c;
}
*p = '\0';
break;
}
}
}
/* Open a file for writing redirection */
/**/
static int
clobber_open(struct redir *f)
{
struct stat buf;
int fd, oerrno;
/* If clobbering, just open. */
if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type))
return open(unmeta(f->name),
O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666);
/* If not clobbering, attempt to create file exclusively. */
if ((fd = open(unmeta(f->name),
O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0)
return fd;
/* If that fails, we are still allowed to open non-regular files. *
* Try opening, and if it's a regular file then close it again *
* because we weren't supposed to open it. */
oerrno = errno;
if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) {
if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode))
return fd;
close(fd);
}
errno = oerrno;
return -1;
}
/* size of buffer for tee and cat processes */
#define TCBUFSIZE 4092
/* close an multio (success) */
/**/
static void
closemn(struct multio **mfds, int fd)
{
if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) {
struct multio *mn = mfds[fd];
int i;
#ifndef __SYMBIAN32__
char buf[TCBUFSIZE];
pid_t pid;
int len;
struct timeval bgtime;
if ((pid = zfork(&bgtime))) {
for (i = 0; i < mn->ct; i++)
zclose(mn->fds[i]);
zclose(mn->pipe);
if (pid == -1) {
mfds[fd] = NULL;
return;
}
mn->ct = 1;
mn->fds[0] = fd;
addproc(pid, NULL, 1, &bgtime);
return;
}
/* pid == 0 */
closeallelse(mn);
if (mn->rflag) {
/* tee process */
while ((len = read(mn->pipe, buf, TCBUFSIZE)) != 0) {
if (len < 0) {
if (errno == EINTR)
continue;
else
break;
}
for (i = 0; i < mn->ct; i++)
write(mn->fds[i], buf, len);
}
} else {
/* cat process */
for (i = 0; i < mn->ct; i++)
while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) {
if (len < 0) {
if (errno == EINTR)
continue;
else
break;
}
write(mn->pipe, buf, len);
}
}
_exit(0);
#else
for (i = 0; i < mn->ct; i++)
zclose(mn->fds[i]);
zclose(mn->pipe);
mn->ct = 1;
mn->fds[0] = fd;
return;
#endif
} else if (fd >= 0)
mfds[fd] = NULL;
}
/* close all the mnodes (failure) */
/**/
static void
closemnodes(struct multio **mfds)
{
int i, j;
for (i = 0; i < 10; i++)
if (mfds[i]) {
for (j = 0; j < mfds[i]->ct; j++)
zclose(mfds[i]->fds[j]);
mfds[i] = NULL;
}
}
/**/
static void
closeallelse(struct multio *mn)
{
int i, j;
long openmax;
openmax = zopenmax();
for (i = 0; i < openmax; i++)
if (mn->pipe != i) {
for (j = 0; j < mn->ct; j++)
if (mn->fds[j] == i)
break;
if (j == mn->ct)
zclose(i);
}
}
/* A multio is a list of fds associated with a certain fd. *
* Thus if you do "foo >bar >ble", the multio for fd 1 will have *
* two fds, the result of open("bar",...), and the result of *
* open("ble",....). */
/* Add a fd to an multio. fd1 must be < 10, and may be in any state. *
* fd2 must be open, and is `consumed' by this function. Note that *
* fd1 == fd2 is possible, and indicates that fd1 was really closed. *
* We effectively do `fd2 = movefd(fd2)' at the beginning of this *
* function, but in most cases we can avoid an extra dup by delaying *
* the movefd: we only >need< to move it if we're actually doing a *
* multiple redirection. */
/**/
static void
addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag)
{
int pipes[2];
if (!mfds[fd1] || unset(MULTIOS)) {
if(!mfds[fd1]) { /* starting a new multio */
mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio));
if (!forked && save[fd1] == -2)
save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1);
}
redup(fd2, fd1);
mfds[fd1]->ct = 1;
mfds[fd1]->fds[0] = fd1;
mfds[fd1]->rflag = rflag;
} else {
if (mfds[fd1]->rflag != rflag) {
zerr("file mode mismatch on fd %d", NULL, fd1);
return;
}
if (mfds[fd1]->ct == 1) { /* split the stream */
mfds[fd1]->fds[0] = movefd(fd1);
mfds[fd1]->fds[1] = movefd(fd2);
mpipe(pipes);
mfds[fd1]->pipe = pipes[1 - rflag];
redup(pipes[rflag], fd1);
mfds[fd1]->ct = 2;
} else { /* add another fd to an already split stream */
if(!(mfds[fd1]->ct % MULTIOUNIT)) {
int new = sizeof(struct multio) + sizeof(int) * mfds[fd1]->ct;
int old = new - sizeof(int) * MULTIOUNIT;
mfds[fd1] = hrealloc((char *)mfds[fd1], old, new);
}
mfds[fd1]->fds[mfds[fd1]->ct++] = movefd(fd2);
}
}
if (subsh_close >= 0 && !fdtable[subsh_close])
subsh_close = -1;
}
/**/
static void
addvars(Estate state, Wordcode pc, int export)
{
LinkList vl;
int xtr, isstr, htok = 0;
char **arr, **ptr, *name;
Wordcode opc = state->pc;
wordcode ac;
local_list1(svl);
xtr = isset(XTRACE);
if (xtr) {
printprompt4();
doneps4 = 1;
}
state->pc = pc;
while (wc_code(ac = *state->pc++) == WC_ASSIGN) {
name = ecgetstr(state, EC_DUPTOK, &htok);
if (htok)
untokenize(name);
if (xtr)
fprintf(xtrerr,
WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC ? "%s+=" : "%s=", name);
if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) {
init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok));
vl = &svl;
} else
vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok);
if (vl && htok) {
prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) :
PF_ASSIGN));
if (errflag) {
state->pc = opc;
return;
}
if (isset(GLOBASSIGN) || !isstr)
globlist(vl, 0);
if (errflag) {
state->pc = opc;
return;
}
}
if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) {
Param pm;
char *val;
int allexp;
if (empty(vl))
val = ztrdup("");
else {
untokenize(peekfirst(vl));
val = ztrdup(ugetnode(vl));
}
if (xtr) {
quotedzputs(val, xtrerr);
fputc(' ', xtrerr);
}
if (export && !strchr(name, '[')) {
if (export < 0 && isset(RESTRICTED) &&
(pm = (Param) paramtab->removenode(paramtab, name)) &&
(pm->flags & PM_RESTRICTED)) {
zerr("%s: restricted", pm->nam, 0);
zsfree(val);
state->pc = opc;
return;
}
if (strcmp(name, "STTY") == 0) {
zsfree(STTYval);
STTYval = ztrdup(val);
}
allexp = opts[ALLEXPORT];
opts[ALLEXPORT] = 1;
pm = assignsparam(name, val,
WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
opts[ALLEXPORT] = allexp;
} else
pm = assignsparam(name, val,
WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
if (errflag) {
state->pc = opc;
return;
}
continue;
}
if (vl) {
ptr = arr = (char **) zalloc(sizeof(char **) *
(countlinknodes(vl) + 1));
while (nonempty(vl))
*ptr++ = ztrdup((char *) ugetnode(vl));
} else
ptr = arr = (char **) zalloc(sizeof(char **));
*ptr = NULL;
if (xtr) {
fprintf(xtrerr, "( ");
for (ptr = arr; *ptr; ptr++) {
quotedzputs(*ptr, xtrerr);
fputc(' ', xtrerr);
}
fprintf(xtrerr, ") ");
}
assignaparam(name, arr, WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
if (errflag) {
state->pc = opc;
return;
}
}
state->pc = opc;
}
/**/
void
setunderscore(char *str)
{
if (str && *str) {
int l = strlen(str) + 1, nl = (l + 31) & ~31;
if (nl > underscorelen || (underscorelen - nl) > 64) {
zfree(underscore, underscorelen);
underscore = (char *) zalloc(underscorelen = nl);
}
strcpy(underscore, str);
underscoreused = l;
} else {
if (underscorelen > 128) {
zfree(underscore, underscorelen);
underscore = (char *) zalloc(underscorelen = 32);
}
*underscore = '\0';
underscoreused = 1;
}
}
/* These describe the type of expansions that need to be done on the words
* used in the thing we are about to execute. They are set in execcmd() and
* used in execsubst() which might be called from one of the functions
* called from execcmd() (like execfor() and so on). */
static int esprefork, esglob = 1;
/**/
void
execsubst(LinkList strs)
{
if (strs) {
prefork(strs, esprefork);
if (esglob) {
LinkList ostrs = strs;
globlist(strs, 0);
strs = ostrs;
}
}
}
#ifdef __SYMBIAN32__
extern int handlePipeCmds(char*, pid_t, int fds[3], int);
extern void clearfds(void);
#endif
/**/
static void
execcmd(Estate state, int input, int output, int how, int last1)
{
HashNode hn = NULL;
LinkNode node;
Redir fn;
struct multio *mfds[10];
char *text;
int save[10];
int fil, dfil, is_cursh, type, do_exec = 0, i, htok = 0;
int nullexec = 0, assign = 0, forked = 0;
int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
/* Various flags to the command. */
int cflags = 0, checked = 0, oautocont = opts[AUTOCONTINUE];
LinkList redir;
wordcode code;
Wordcode beg = state->pc, varspc;
FILE *oxtrerr = xtrerr;
static int op_fd=-1;
int thread_created=0;
doneps4 = 0;
redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
if (wc_code(*state->pc) == WC_ASSIGN) {
varspc = state->pc;
while (wc_code((code = *state->pc)) == WC_ASSIGN)
state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
3 : WC_ASSIGN_NUM(code) + 2);
} else
varspc = NULL;
code = *state->pc++;
type = wc_code(code);
/* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
* But for that we would need to check/change all builtins so that
* they don't modify their argument strings. */
args = (type == WC_SIMPLE ?
ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
for (i = 0; i < 10; i++) {
save[i] = -2;
mfds[i] = NULL;
}
/* If the command begins with `%', then assume it is a *
* reference to a job in the job table. */
if (type == WC_SIMPLE && args && nonempty(args) &&
*(char *)peekfirst(args) == '%') {
if (how & Z_DISOWN)
opts[AUTOCONTINUE] = 1;
pushnode(args, dupstring((how & Z_DISOWN)
? "disown" : (how & Z_ASYNC) ? "bg" : "fg"));
how = Z_SYNC;
}
/* If AUTORESUME is set, the command is SIMPLE, and doesn't have *
* any redirections, then check if it matches as a prefix of a *
* job currently in the job table. If it does, then we treat it *
* as a command to resume this job. */
if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) &&
args && nonempty(args) && (!redir || empty(redir)) && !input &&
!nextnode(firstnode(args))) {
if (unset(NOTIFY))
scanjobs();
if (findjobnam(peekfirst(args)) != -1)
pushnode(args, dupstring("fg"));
}
/* Check if it's a builtin needing automatic MAGIC_EQUALS_SUBST *
* handling. Things like typeset need this. We can't detect the *
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
* only works in simple cases. has_token() is called to make sure *
* this really is a simple case. */
if (type == WC_SIMPLE) {
while (args && nonempty(args)) {
char* cmdarg= (char *) peekfirst(args);
checked = !has_token(cmdarg);
if (!checked)
break;
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
is_shfunc = 1;
break;
}
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
checked = !(cflags & BINF_BUILTIN);
break;
}
if (!(hn->flags & BINF_PREFIX)) {
is_builtin = 1;
/* autoload the builtin if necessary */
if (!((Builtin) hn)->handlerfunc) {
load_module(((Builtin) hn)->optstr);
hn = builtintab->getnode(builtintab, cmdarg);
}
assign = (hn && (hn->flags & BINF_MAGICEQUALS));
break;
}
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
cflags |= hn->flags;
checked = 0;
if (cflags & BINF_COMMAND && nextnode(firstnode(args))) {
/* check for options to command builtin */
char *next = (char *) getdata(nextnode(firstnode(args)));
char *cmdopt;
if (next && *next == '-' && strlen(next) == 2 &&
(cmdopt = strchr("pvV", next[1])))
{
if (*cmdopt == 'p') {
uremnode(args, firstnode(args));
use_defpath = 1;
if (nextnode(firstnode(args)))
next = (char *) getdata(nextnode(firstnode(args)));
} else {
hn = (HashNode)&commandbn;
is_builtin = 1;
break;
}
}
if (!strcmp(next, "--"))
uremnode(args, firstnode(args));
}
uremnode(args, firstnode(args));
hn = NULL;
if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
break;
}
}
/* Do prefork substitutions */
esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0;
if (args && htok)
prefork(args, esprefork);
if (type == WC_SIMPLE) {
int unglobbed = 0;
for (;;) {
char *cmdarg;
if (!(cflags & BINF_NOGLOB))
while (!checked && !errflag && args && nonempty(args) &&
has_token((char *) peekfirst(args)))
zglob(args, firstnode(args), 0);
else if (!unglobbed) {
for (node = firstnode(args); node; incnode(node))
untokenize((char *) getdata(node));
unglobbed = 1;
}
/* Current shell should not fork unless the *
* exec occurs at the end of a pipeline. */
if ((cflags & BINF_EXEC) && last1)
do_exec = 1;
/* Empty command */
if (!args || empty(args)) {
if (redir && nonempty(redir)) {
if (do_exec) {
/* Was this "exec < foobar"? */
nullexec = 1;
break;
} else if (varspc) {
nullexec = 2;
break;
} else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] ||
(cflags & BINF_PREFIX)) {
zerr("redirection with no command", NULL, 0);
errflag = lastval = 1;
return;
} else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
if (!args)
args = newlinklist();
addlinknode(args, dupstring(":"));
} else if (readnullcmd && *readnullcmd &&
((Redir) peekfirst(redir))->type == REDIR_READ &&
!nextnode(firstnode(redir))) {
if (!args)
args = newlinklist();
addlinknode(args, dupstring(readnullcmd));
} else {
if (!args)
args = newlinklist();
addlinknode(args, dupstring(nullcmd));
}
} else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) {
lastval = 0;
return;
} else {
cmdoutval = 0;
if (varspc)
addvars(state, varspc, 0);
if (errflag)
lastval = errflag;
else
lastval = cmdoutval;
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
}
return;
}
} else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
zerrnam("exec", "%s: restricted",
(char *) getdata(firstnode(args)), 0);
lastval = 1;
return;
}
/*
* Quit looking for a command if:
* - there was an error; or
* - we checked the simple cases needing MAGIC_EQUAL_SUBST; or
* - we know we already found a builtin (because either:
* - we loaded a builtin from a module, or
* - we have determined there are options which would
* require us to use the "command" builtin); or
* - we aren't using POSIX and so BINF_COMMAND indicates a zsh
* precommand modifier is being used in place of the builtin
*/
if (errflag || checked || is_builtin ||
(unset(POSIXBUILTINS) && (cflags & BINF_COMMAND)))
break;
cmdarg = (char *) peekfirst(args);
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
is_shfunc = 1;
break;
}
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
if (cflags & BINF_BUILTIN) {
zwarn("no such builtin: %s", cmdarg, 0);
lastval = 1;
opts[AUTOCONTINUE] = oautocont;
return;
}
break;
}
if (!(hn->flags & BINF_PREFIX)) {
is_builtin = 1;
/* autoload the builtin if necessary */
if (!((Builtin) hn)->handlerfunc) {
load_module(((Builtin) hn)->optstr);
hn = builtintab->getnode(builtintab, cmdarg);
}
break;
}
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
cflags |= hn->flags;
uremnode(args, firstnode(args));
hn = NULL;
}
}
if (errflag) {
lastval = 1;
opts[AUTOCONTINUE] = oautocont;
return;
}
/* Get the text associated with this command. */
if ((how & Z_ASYNC) ||
(!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
text = getjobtext(state->prog, beg);
else
text = NULL;
/* Set up special parameter $_ */
setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : "");
/* Warn about "rm *" */
if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) &&
isset(SHINSTDIN) && args && nonempty(args) &&
nextnode(firstnode(args)) && !strcmp(peekfirst(args), "rm")) {
LinkNode node, next;
for (node = nextnode(firstnode(args)); node && !errflag; node = next) {
char *s = (char *) getdata(node);
int l = strlen(s);
next = nextnode(node);
if (s[0] == Star && !s[1]) {
if (!checkrmall(pwd))
uremnode(args, node);
} else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) {
char t = s[l - 2];
s[l - 2] = 0;
if (!checkrmall(s))
uremnode(args, node);
s[l - 2] = t;
}
}
if (!nextnode(firstnode(args)))
errflag = 1;
}
if (errflag) {
lastval = 1;
opts[AUTOCONTINUE] = oautocont;
return;
}
if (type == WC_SIMPLE && !nullexec) {
char *s;
char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
(!redir || empty(redir)) && args && !empty(args) &&
!nextnode(firstnode(args)) && *(char *)peekfirst(args));
DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c");
#ifndef __SYMBIAN32__
if (!hn) {
/* Resolve external commands */
char *cmdarg = (char *) peekfirst(args);
char **checkpath = pathchecked;
int dohashcmd = isset(HASHCMDS);
hn = cmdnamtab->getnode(cmdnamtab, cmdarg);
if (hn && trycd && !isreallycom((Cmdnam)hn)) {
if (!(((Cmdnam)hn)->flags & HASHED)) {
checkpath = path;
dohashcmd = 1;
}
cmdnamtab->removenode(cmdnamtab, cmdarg);
cmdnamtab->freenode(hn);
hn = NULL;
}
if (!hn && dohashcmd && strcmp(cmdarg, "..")) {
for (s = cmdarg; *s && *s != '/'; s++);
if (!*s)
hn = (HashNode) hashcmd(cmdarg, checkpath);
}
}
#endif //__SYMBIAN32__
/* If no command found yet, see if it *
* is a directory we should AUTOCD to. */
if (!hn && trycd && (s = cancd(peekfirst(args)))) {
peekfirst(args) = (void *) s;
pushnode(args, dupstring("cd"));
if ((hn = builtintab->getnode(builtintab, "cd")))
is_builtin = 1;
}
}
/* This is nonzero if the command is a current shell procedure? */
is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH);
/**************************************************************************
* Do we need to fork? We need to fork if: *
* 1) The command is supposed to run in the background. (or) *
* 2) There is no `exec' flag, and either: *
* a) This is a builtin or shell function with output piped somewhere. *
* b) This is an external command and we can't do a `fake exec'. *
* *
* A `fake exec' is possible if we have all the following conditions: *
* 1) last1 flag is 1. This indicates that the current shell will not *
* be needed after the current command. This is typically the case *
* when when the command is the last stage in a subshell, or is the *
* last command after the option `-c'. *
* 2) We don't have any traps set. *
* 3) We don't have any files to delete. *
* *
* The condition above for a `fake exec' will also work for a current *
* shell command such as a builtin, but doesn't really buy us anything *
* (doesn't save us a process), since it is already running in the *
* current shell. *
**************************************************************************/
if ((how & Z_ASYNC) ||
(!do_exec &&
(((is_builtin || is_shfunc) && output) ||
(!is_cursh && (last1 != 1 || nsigtrapped || havefiles()))))) {
// forked = 1;
if (sigtrapped[SIGINT] & ZSIG_IGNORED)
holdintr();
#ifdef HAVE_NICE
/* Check if we should run background jobs at a lower priority. */
if ((how & Z_ASYNC) && isset(BGNICE))
nice(5);
#endif /* HAVE_NICE */
} else if (is_cursh) {
/* This is a current shell procedure that didn't need to fork. *
* This includes current shell procedures that are being exec'ed, *
* as well as null execs. */
jobtab[thisjob].stat |= STAT_CURSH;
} else {
/* This is an exec (real or fake) for an external command. *
* Note that any form of exec means that the subshell is fake *
* (but we may be in a subshell already). */
is_exec = 1;
}
if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
LinkList oargs = args;
globlist(args, 0);
args = oargs;
}
if (errflag) {
lastval = 1;
goto err;
}
/* Make a copy of stderr for xtrace output before redirecting */
fflush(xtrerr);
if (isset(XTRACE) && xtrerr == stderr &&
(type < WC_SUBSH || type == WC_TIMED)) {
if (!(xtrerr = fdopen(movefd(dup(fileno(stderr))), "w")))
xtrerr = stderr;
else
fdtable[fileno(xtrerr)] = 3;
}
/* Add pipeline input/output to mnodes */
#ifndef __SYMBIAN32__
if (input)
{
addfd(forked, save, mfds, 0, input, 0);
}
if (output)
{
addfd(forked, save, mfds, 1, output, 1);
}
#endif
/* Do process substitutions */
if (redir)
spawnpipes(redir, nullexec);
/* Do io redirections */
while (redir && nonempty(redir)) {
fn = (Redir) ugetnode(redir);
DPUTS(fn->type == REDIR_HEREDOC || fn->type == REDIR_HEREDOCDASH,
"BUG: unexpanded here document");
if (fn->type == REDIR_INPIPE) {
if (fn->fd2 == -1) {
closemnodes(mfds);
fixfds(save);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fn->fd2, 0);
} else if (fn->type == REDIR_OUTPIPE) {
if (fn->fd2 == -1) {
closemnodes(mfds);
fixfds(save);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fn->fd2, 1);
} else {
if (fn->type != REDIR_HERESTR && xpandredir(fn, redir))
continue;
if (errflag) {
closemnodes(mfds);
fixfds(save);
execerr();
}
if (isset(RESTRICTED) && IS_WRITE_FILE(fn->type)) {
zwarn("writing redirection not allowed in restricted mode", NULL, 0);
execerr();
}
if (unset(EXECOPT))
continue;
switch(fn->type) {
case REDIR_HERESTR:
fil = getherestr(fn);
if (fil == -1) {
closemnodes(mfds);
fixfds(save);
if (errno != EINTR)
zwarn("%e", NULL, errno);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fil, 0);
break;
case REDIR_READ:
case REDIR_READWRITE:
if (fn->type == REDIR_READ)
fil = open(unmeta(fn->name), O_RDONLY | O_NOCTTY);
else
fil = open(unmeta(fn->name),
O_RDWR | O_CREAT | O_NOCTTY, 0666);
if (fil == -1) {
closemnodes(mfds);
fixfds(save);
if (errno != EINTR)
zwarn("%e: %s", fn->name, errno);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fil, 0);
/* If this is 'exec < file', read from stdin, *
* not terminal, unless `file' is a terminal. */
if (nullexec == 1 && fn->fd1 == 0 &&
isset(SHINSTDIN) && interact && !zleactive)
init_io();
break;
case REDIR_CLOSE:
if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2)
save[fn->fd1] = movefd(fn->fd1);
closemn(mfds, fn->fd1);
zclose(fn->fd1);
break;
case REDIR_MERGEIN:
case REDIR_MERGEOUT:
if (fn->fd2 < 10)
closemn(mfds, fn->fd2);
if (fn->fd2 > 9 &&
(fdtable[fn->fd2] ||
fn->fd2 == coprocin ||
fn->fd2 == coprocout)) {
fil = -1;
errno = EBADF;
} else {
int fd = fn->fd2;
if(fd == -2)
fd = (fn->type == REDIR_MERGEOUT) ? coprocout : coprocin;
fil = dup(fd);
}
if (fil == -1) {
char fdstr[4];
closemnodes(mfds);
fixfds(save);
if (fn->fd2 != -2)
sprintf(fdstr, "%d", fn->fd2);
zwarn("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, errno);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fil, fn->type == REDIR_MERGEOUT);
break;
default:
if (IS_APPEND_REDIR(fn->type))
fil = open(unmeta(fn->name),
(unset(CLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ?
O_WRONLY | O_APPEND | O_NOCTTY :
O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666);
else
fil = clobber_open(fn);
if(fil != -1 && IS_ERROR_REDIR(fn->type))
dfil = dup(fil);
else
dfil = 0;
if (fil == -1 || dfil == -1) {
if(fil != -1)
close(fil);
closemnodes(mfds);
fixfds(save);
if (errno != EINTR)
zwarn("%e: %s", fn->name, errno);
execerr();
}
addfd(forked, save, mfds, fn->fd1, fil, 1);
if(IS_ERROR_REDIR(fn->type))
addfd(forked, save, mfds, 2, dfil, 1);
break;
}
}
}
/* We are done with redirection. close the mnodes, *
* spawning tee/cat processes as necessary. */
for (i = 0; i < 10; i++)
if (mfds[i] && mfds[i]->ct >= 2)
closemn(mfds, i);
if (nullexec) {
if (nullexec == 1) {
/*
* If nullexec is 1 we specifically *don't* restore the original
* fd's before returning.
*/
for (i = 0; i < 10; i++)
if (save[i] != -2)
zclose(save[i]);
goto done;
}
/*
* If nullexec is 2, we have variables to add with the redirections
* in place.
*/
if (varspc)
addvars(state, varspc, 0);
lastval = errflag ? errflag : cmdoutval;
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
}
} else if (isset(EXECOPT) && !errflag) {
/*
* We delay the entersubsh() to here when we are exec'ing
* the current shell (including a fake exec to run a builtin then
* exit) in case there is an error return.
*/
if (is_exec)
entersubsh(how, (type != WC_SUBSH) ? 2 : 1, 1,
(do_exec || (type >= WC_CURSH && last1 == 1))
&& !forked);
if (type >= WC_CURSH) {
if (last1 == 1)
do_exec = 1;
lastval = (execfuncs[type - WC_CURSH])(state, do_exec);
} else if (is_builtin || is_shfunc) {
LinkList restorelist = 0, removelist = 0;
/* builtin or shell function */
if (!forked && ((cflags & BINF_COMMAND) ||
(unset(POSIXBUILTINS) && !assign) ||
(isset(POSIXBUILTINS) && !is_shfunc &&
!(hn->flags & BINF_PSPECIAL)))) {
if (varspc)
save_params(state, varspc, &restorelist, &removelist);
else
restorelist = removelist = NULL;
}
if (varspc) {
/* Export this if the command is a shell function,
* but not if it's a builtin.
*/
addvars(state, varspc, is_shfunc);
if (errflag) {
if (restorelist)
restore_params(restorelist, removelist);
lastval = 1;
fixfds(save);
goto done;
}
}
if (is_shfunc) {
/* It's a shell function */
#ifdef PATH_DEV_FD
int i;
for (i = 10; i <= max_zsh_fd; i++)
if (fdtable[i] > 1)
fdtable[i]++;
#endif
if (subsh_close >= 0)
zclose(subsh_close);
subsh_close = -1;
execshfunc((Shfunc) hn, args);
#ifdef PATH_DEV_FD
for (i = 10; i <= max_zsh_fd; i++)
if (fdtable[i] > 1)
if (--(fdtable[i]) <= 2)
zclose(i);
#endif
} else {
/* It's a builtin */
if (forked)
closem(1);
lastval = execbuiltin(args, (Builtin) hn, input, output);
#ifdef PATH_DEV_FD
closem(2);
#endif
fflush(stdout);
#ifdef __SYMBIAN32__
fflush(stderr);
#endif
if (save[1] == -2) {
if (ferror(stdout)) {
zwarn("write error: %e", NULL, errno);
clearerr(stdout);
}
} else
clearerr(stdout);
}
if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
lastval && !subsh) {
fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
}
if (do_exec) {
if (subsh)
_exit(lastval);
/* If we are exec'ing a command, and we are not in a subshell, *
* then check if we should save the history file. */
if (isset(RCS) && interact && !nohistsave)
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
exit(lastval);
}
if (restorelist)
restore_params(restorelist, removelist);
} else {
pid_t pid;
int fds[3];
struct timeval bgtime;
struct timezone dummy_tz;
char* arg0 = (char *) peekfirst(args);
gettimeofday(&bgtime, &dummy_tz);
if (!forked)
setiparam("SHLVL", --shlvl);
if (do_exec) {
/* If we are exec'ing a command, and we are not *
* in a subshell, then save the history file. */
if (!subsh && isset(RCS) && interact && !nohistsave)
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
}
if (varspc) {
addvars(state, varspc, -1);
if (errflag)
_exit(1);
}
closem(1);
if (coprocin)
zclose(coprocin);
if (coprocout)
zclose(coprocout);
#ifdef HAVE_GETRLIMIT
if (!forked)
setlimits(NULL);
#endif
if (how & Z_ASYNC) {
zsfree(STTYval);
STTYval = 0;
}
#ifdef __SYMBIAN32__
{
//find and execute the external command
const int data_size=512;
char data[data_size];
int read_cnt=0;
char** argv;
fd_set readfds;
int status=-1;
size_t oldcmdlen=0, newcmdlen=0;
struct timeval tv;
char *pcmd=NULL;
char* pfile=NULL;
//generate full path
argv=makecline(args);
pfile=argv[0];
//generate commands
while(++argv && *argv)
{
newcmdlen=strlen(*argv)+newcmdlen+1;
if(!pcmd)
pcmd=(char*)calloc(newcmdlen+1, sizeof(char));
else
pcmd=(char*)realloc(pcmd, newcmdlen+1);
sprintf(pcmd+oldcmdlen, "%s ", *argv);
oldcmdlen=newcmdlen;
}
//execute the external command
if ( (pid=popen3(pfile, pcmd, environ, fds)) == -1)
{
zerr("command not found: %s", arg0, 0);
opts[AUTOCONTINUE] = oautocont;
clearfds();
free(pcmd);
lastval=127;
return;
}
free(pcmd);
if(input || output)
{
handlePipeCmds(pfile, pid, fds, output);
}
else
{
//read the output from the executed command
for(;;)
{
int wait_pid=waitpid(pid, &status, WNOHANG);
if(pid == wait_pid || wait_pid == -1) //dont wait for the child to terminate
{
int max= MAX(fds[1], fds[2]);
FD_ZERO(&readfds);
FD_SET(fds[1], &readfds);
FD_SET(fds[2], &readfds);
//read available data, if child is terminated already.
for(;;)
{
tv.tv_sec = 1;
tv.tv_usec = 0;
if(select(max+1, &readfds, NULL, NULL, &tv) <=0)
break;
else
{
if(FD_ISSET(fds[1], &readfds))
{
memset(&data[0], 0, 512);
read_cnt=read(fds[1], &data[0], 512);
if(read_cnt>0)
write(1, data, read_cnt);
}
if(FD_ISSET(fds[2], &readfds))
{
memset(&data[0], 0, 512);
read_cnt=read(fds[2], &data[0], 512);
if(read_cnt>0)
write(2, data, read_cnt);
}
}
}
//Updating the Error status.
if(WIFEXITED(status))
lastval = WEXITSTATUS(status);
else if(WIFTERMINATED(status))
lastval = WTERMINATESTATUS(status);
else if(WIFPANICED(status))
lastval = WPANICCODE(status);
break;
}
else
{
int max= MAX(0, MAX(fds[1], fds[2]));
FD_ZERO(&readfds);
FD_SET(fds[1], &readfds);
FD_SET(fds[2], &readfds);
FD_SET(0, &readfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
if(select(max+1, &readfds, NULL, NULL, &tv) < 0)
break;
else
{
if(FD_ISSET(fds[1], &readfds))
{
memset(&data[0], 0, 512);
read_cnt=read(fds[1], &data[0], 512);
if(read_cnt>0)
write(1, data, read_cnt);
}
if(FD_ISSET(fds[2], &readfds))
{
memset(&data[0], 0, 512);
read_cnt=read(fds[2], &data[0], 512);
if(read_cnt>0)
write(2, data, read_cnt);
}
if(FD_ISSET(0, &readfds))
{
memset(&data[0], 0, 512);
read_cnt=read(0, &data[0], 512);
if(read_cnt>0)
write(fds[0], data, read_cnt);
}
}//else
} //else
}//for (;;)
close(fds[0]);
close(fds[1]);
close(fds[2]);
}
}
#endif
if (how & Z_ASYNC) {
lastpid = (zlong) pid;
} else if (!jobtab[thisjob].stty_in_env && varspc) {
/* search for STTY=... */
Wordcode p = varspc;
wordcode ac;
while (wc_code(ac = *p) == WC_ASSIGN) {
if (!strcmp(ecrawstr(state->prog, p + 1, NULL), "STTY")) {
jobtab[thisjob].stty_in_env = 1;
break;
}
p += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
3 : WC_ASSIGN_NUM(ac) + 2);
}
}
opts[AUTOCONTINUE] = oautocont;
fixfds(save);
return;
}
}
err:
if (forked)
_exit(lastval);
fixfds(save);
done:
if (xtrerr != oxtrerr) {
fil = fileno(xtrerr);
fclose(xtrerr);
xtrerr = oxtrerr;
zclose(fil);
}
zsfree(STTYval);
STTYval = 0;
opts[AUTOCONTINUE] = oautocont;
}
/* Arrange to have variables restored. */
/**/
static void
save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
{
Param pm;
char *s;
wordcode ac;
*restore_p = newlinklist();
*remove_p = newlinklist();
while (wc_code(ac = *pc) == WC_ASSIGN) {
s = ecrawstr(state->prog, pc + 1, NULL);
if ((pm = (Param) paramtab->getnode(paramtab, s))) {
if (pm->env)
delenv(pm);
if (!(pm->flags & PM_SPECIAL)) {
paramtab->removenode(paramtab, s);
} else if (!(pm->flags & PM_READONLY) &&
(unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) {
Param tpm = (Param) hcalloc(sizeof *tpm);
tpm->nam = pm->nam;
copyparam(tpm, pm, 1);
pm = tpm;
}
addlinknode(*remove_p, dupstring(s));
addlinknode(*restore_p, pm);
} else
addlinknode(*remove_p, dupstring(s));
pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
3 : WC_ASSIGN_NUM(ac) + 2);
}
}
/* Restore saved parameters after executing a shfunc or builtin */
/**/
static void
restore_params(LinkList restorelist, LinkList removelist)
{
Param pm;
char *s;
/* remove temporary parameters */
while ((s = (char *) ugetnode(removelist))) {
if ((pm = (Param) paramtab->getnode(paramtab, s)) &&
!(pm->flags & PM_SPECIAL)) {
pm->flags &= ~PM_READONLY;
unsetparam_pm(pm, 0, 0);
}
}
if (restorelist) {
/* restore saved parameters */
while ((pm = (Param) ugetnode(restorelist))) {
if (pm->flags & PM_SPECIAL) {
Param tpm = (Param) paramtab->getnode(paramtab, pm->nam);
DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) ||
!(pm->flags & PM_SPECIAL),
"BUG: in restoring special parameters");
tpm->flags = pm->flags;
switch (PM_TYPE(pm->flags)) {
case PM_SCALAR:
tpm->gsu.s->setfn(tpm, pm->u.str);
break;
case PM_INTEGER:
tpm->gsu.i->setfn(tpm, pm->u.val);
break;
case PM_EFLOAT:
case PM_FFLOAT:
tpm->gsu.f->setfn(tpm, pm->u.dval);
break;
case PM_ARRAY:
tpm->gsu.a->setfn(tpm, pm->u.arr);
break;
case PM_HASHED:
tpm->gsu.h->setfn(tpm, pm->u.hash);
break;
}
pm = tpm;
} else
paramtab->addnode(paramtab, pm->nam, pm);
if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->nam))))
addenv(pm, s);
}
}
}
/* restore fds after redirecting a builtin */
/**/
static void
fixfds(int *save)
{
int old_errno = errno;
int i;
for (i = 0; i != 10; i++)
if (save[i] != -2)
redup(save[i], i);
errno = old_errno;
}
/**/
int
forklevel;
/**/
static void
entersubsh(int how, int cl, int fake, int revertpgrp)
{
int sig, monitor;
if (cl != 2)
for (sig = 0; sig < VSIGCOUNT; sig++)
if (!(sigtrapped[sig] & ZSIG_FUNC))
unsettrap(sig);
if (!(monitor = isset(MONITOR))) {
if (how & Z_ASYNC) {
settrap(SIGINT, NULL);
settrap(SIGQUIT, NULL);
if (isatty(0)) {
close(0);
if (open("/dev/null", O_RDWR | O_NOCTTY)) {
zerr("can't open /dev/null: %e", NULL, errno);
_exit(1);
}
}
}
} else if (thisjob != -1 && cl) {
if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) {
if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 ||
killpg(jobtab[list_pipe_job].gleader, 0) == -1) {
jobtab[list_pipe_job].gleader =
jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid());
setpgrp(0L, jobtab[list_pipe_job].gleader);
if (how & Z_SYNC)
attachtty(jobtab[thisjob].gleader);
}
}
else if (!jobtab[thisjob].gleader ||
setpgrp(0L, jobtab[thisjob].gleader) == -1) {
jobtab[thisjob].gleader = getpid();
if (list_pipe_job != thisjob &&
!jobtab[list_pipe_job].gleader)
jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader;
setpgrp(0L, jobtab[thisjob].gleader);
if (how & Z_SYNC)
attachtty(jobtab[thisjob].gleader);
}
}
if (!fake)
subsh = 1;
if (revertpgrp && getpid() == mypgrp)
release_pgrp();
if (SHTTY != -1) {
shout = NULL;
zclose(SHTTY);
SHTTY = -1;
}
if (isset(MONITOR)) {
signal_default(SIGTTOU);
signal_default(SIGTTIN);
signal_default(SIGTSTP);
}
if (interact) {
signal_default(SIGTERM);
if (!(sigtrapped[SIGINT] & ZSIG_IGNORED))
signal_default(SIGINT);
}
if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED))
signal_default(SIGQUIT);
opts[MONITOR] = opts[USEZLE] = 0;
zleactive = 0;
if (cl)
clearjobtab(monitor);
get_usage();
forklevel = locallevel;
}
/* close internal shell fds */
/**/
mod_export void
closem(int how)
{
int i;
for (i = 10; i <= max_zsh_fd; i++)
if (fdtable[i] && (!how || fdtable[i] == how))
zclose(i);
}
/* convert here document into a here string */
/**/
char *
gethere(char *str, int typ)
{
char *buf;
int bsiz, qt = 0, strip = 0;
char *s, *t, *bptr, c;
for (s = str; *s; s++)
if (INULL(*s)) {
qt = 1;
break;
}
quotesubst(str);
untokenize(str);
if (typ == REDIR_HEREDOCDASH) {
strip = 1;
while (*str == '\t')
str++;
}
bptr = buf = zalloc(bsiz = 256);
for (;;) {
t = bptr;
while ((c = hgetc()) == '\t' && strip)
;
for (;;) {
if (bptr == buf + bsiz) {
buf = realloc(buf, 2 * bsiz);
t = buf + bsiz - (bptr - t);
bptr = buf + bsiz;
bsiz *= 2;
}
if (lexstop || c == '\n')
break;
*bptr++ = c;
c = hgetc();
}
*bptr = '\0';
if (!strcmp(t, str))
break;
if (lexstop) {
t = bptr;
break;
}
*bptr++ = '\n';
}
if (t > buf && t[-1] == '\n')
t--;
*t = '\0';
if (!qt) {
int ef = errflag;
parsestr(buf);
if (!errflag)
errflag = ef;
}
s = dupstring(buf);
zfree(buf, bsiz);
return s;
}
/* open here string fd */
/**/
static int
getherestr(struct redir *fn)
{
char *s, *t;
int fd, len;
t = fn->name;
singsub(&t);
untokenize(t);
unmetafy(t, &len);
t[len++] = '\n';
if ((fd = gettempfile(NULL, 1, &s)) < 0)
return -1;
write(fd, t, len);
close(fd);
fd = open(s, O_RDONLY | O_NOCTTY);
unlink(s);
return fd;
}
/* $(...) */
/**/
LinkList
getoutput(char *cmd, int qt)
{
Eprog prog;
int pipes[2];
#ifndef __SYMBIAN32__
pid_t pid;
#endif
Wordcode pc;
int fd;
LinkList retval;
if (!(prog = parse_string(cmd)))
return NULL;
pc = prog->prog;
if (prog != &dummy_eprog &&
wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) &&
wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) &&
WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END &&
wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END &&
wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == REDIR_READ &&
!pc[4] &&
wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) {
/* $(< word) */
int stream;
char *s = dupstring(ecrawstr(prog, pc + 5, NULL));
singsub(&s);
if (errflag)
return NULL;
untokenize(s);
if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
zerr("%e: %s", s, errno);
return NULL;
}
return readoutput(stream, qt);
}
#ifndef __SYMBIAN32__
mpipe(pipes);
child_block();
cmdoutval = 0;
if ((cmdoutpid = pid = zfork(NULL)) == -1) {
/* fork error */
zclose(pipes[0]);
zclose(pipes[1]);
errflag = 1;
cmdoutpid = 0;
child_unblock();
return NULL;
} else if (pid) {
LinkList retval;
zclose(pipes[1]);
retval = readoutput(pipes[0], qt);
fdtable[pipes[0]] = 0;
waitforpid(pid); /* unblocks */
lastval = cmdoutval;
return retval;
}
/* pid == 0 */
child_unblock();
zclose(pipes[0]);
redup(pipes[1], 1);
opts[MONITOR] = 0;
entersubsh(Z_SYNC, 1, 0, 0);
cmdpush(CS_CMDSUBST);
cmdpop();
close(1);
_exit(lastval);
zerr("exit returned in child!!", NULL, 0);
kill(getpid(), SIGKILL);
#endif // __SYMBIAN32__
//execute the command within the same process space
mpipe(pipes);
fd = dup(1); /* Preserve stdout */
fcntl(fd, F_SETFD, FD_CLOEXEC);
redup(pipes[1], 1);
execode(prog, 0, 1);
retval=readoutput(pipes[0], qt);
redup(fd, 1);
zclose(pipes[0]);
zclose(pipes[1]);
close(fd);
return retval;
}
/* read output of command substitution */
/**/
mod_export LinkList
readoutput(int in, int qt)
{
LinkList ret;
char *buf, *ptr;
int bsiz, cnt = 0;
char c;
FILE *fin;
#ifdef __SYMBIAN32__
struct timeval tv;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(in, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 1;
fin = fdopen(in, "r");
ret = newlinklist();
ptr = buf = (char *) hcalloc(bsiz = 64);
if(select(in+1, &readfds, NULL, NULL, &tv)==0)
{
goto dont_read;
}
#endif //__SYMBIAN32__
while ((read(in, &c, 1) >0) || errno == EINTR) {
#ifndef __SYMBIAN32__
if (c == EOF) {
errno = 0;
clearerr(fin);
continue;
}
#endif
if (imeta(c)) {
*ptr++ = Meta;
c ^= 32;
cnt++;
}
if (++cnt >= bsiz) {
char *pp = (char *) hcalloc(bsiz *= 2);
memcpy(pp, buf, cnt - 1);
ptr = (buf = pp) + cnt - 1;
}
*ptr++ = c;
#ifndef __SYMBIAN32__
if (ptr[-1]=='\n')
{
break;
}
#else
FD_ZERO(&readfds);
FD_SET(in, &readfds);
if(select(in+1, &readfds, NULL, NULL, &tv)==0)
{
break;
}
#endif
}
dont_read:
fclose(fin);
while (cnt && ptr[-1] == '\n')
ptr--, cnt--;
*ptr = '\0';
if (qt) {
if (!cnt) {
*ptr++ = Nularg;
*ptr = '\0';
}
addlinknode(ret, buf);
} else {
char **words = spacesplit(buf, 0, 1, 0);
while (*words) {
if (isset(GLOBSUBST))
shtokenize(*words);
addlinknode(ret, *words++);
}
}
return ret;
}
/**/
static Eprog
parsecmd(char *cmd)
{
char *str;
Eprog prog;
for (str = cmd + 2; *str && *str != Outpar; str++);
if (!*str || cmd[1] != Inpar) {
zerr("oops.", NULL, 0);
return NULL;
}
*str = '\0';
if (str[1] || !(prog = parse_string(cmd + 2))) {
zerr("parse error in process substitution", NULL, 0);
return NULL;
}
return prog;
}
/* =(...) */
/**/
char *
getoutputfile(char *cmd)
{
Eprog prog;
#ifndef __SYMBIAN32__
pid_t pid;
char *nam;
int fd;
if (thisjob == -1)
return NULL;
if (!(prog = parsecmd(cmd)))
return NULL;
if (!(nam = gettempname(NULL, 0)))
return NULL;
if (!jobtab[thisjob].filelist)
jobtab[thisjob].filelist = znewlinklist();
zaddlinknode(jobtab[thisjob].filelist, nam);
child_block();
fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
if (fd < 0 || (cmdoutpid = pid = zfork(NULL)) == -1) {
/* fork or open error */
child_unblock();
return nam;
} else if (pid) {
int os;
close(fd);
os = jobtab[thisjob].stat;
waitforpid(pid);
cmdoutval = 0;
jobtab[thisjob].stat = os;
return nam;
}
/* pid == 0 */
redup(fd, 1);
opts[MONITOR] = 0;
entersubsh(Z_SYNC, 1, 0, 0);
cmdpush(CS_CMDSUBST);
execode(prog, 0, 1);
cmdpop();
close(1);
_exit(lastval);
zerr("exit returned in child!!", NULL, 0);
kill(getpid(), SIGKILL);
return NULL;
#else
st_exec stexec;
pthread_t testThread;
if (!(prog = parsecmd(cmd)))
return NULL;
stexec.dont_change_job=0;
stexec.exiting=1;
stexec.p=prog;
pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
return NULL;
#endif
}
#if !defined(PATH_DEV_FD) && defined(HAVE_FIFOS)
/* get a temporary named pipe */
static char *
namedpipe(void)
{
char *tnam = gettempname(NULL, 1);
# ifdef HAVE_MKFIFO
if (mkfifo(tnam, 0600) < 0)
# else
if (mknod(tnam, 0010600, 0) < 0)
# endif
return NULL;
return tnam;
}
#endif /* ! PATH_DEV_FD && HAVE_FIFOS */
/* <(...) or >(...) */
/**/
char *
getproc(char *cmd)
{
#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD)
zerr("doesn't look like your system supports FIFOs.", NULL, 0);
return NULL;
#else
Eprog prog;
int out = *cmd == Inang;
#ifndef __SYMBIAN32__
char *pnam;
pid_t pid;
struct timeval bgtime;
#ifndef PATH_DEV_FD
int fd;
if (thisjob == -1)
return NULL;
if (!(pnam = namedpipe()))
return NULL;
if (!(prog = parsecmd(cmd)))
return NULL;
if (!jobtab[thisjob].filelist)
jobtab[thisjob].filelist = znewlinklist();
zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam));
if ((pid = zfork(&bgtime))) {
if (pid == -1)
return NULL;
if (!out)
addproc(pid, NULL, 1, &bgtime);
return pnam;
}
closem(0);
fd = open(pnam, out ? O_WRONLY | O_NOCTTY : O_RDONLY | O_NOCTTY);
if (fd == -1) {
zerr("can't open %s: %e", pnam, errno);
_exit(1);
}
entersubsh(Z_ASYNC, 1, 0, 0);
redup(fd, out);
#else /* PATH_DEV_FD */
int pipes[2];
if (thisjob == -1)
return NULL;
pnam = hcalloc(strlen(PATH_DEV_FD) + 6);
if (!(prog = parsecmd(cmd)))
return NULL;
mpipe(pipes);
if ((pid = zfork(&bgtime))) {
sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]);
zclose(pipes[out]);
if (pid == -1)
{
zclose(pipes[!out]);
return NULL;
}
fdtable[pipes[!out]] = 2;
if (!out)
{
addproc(pid, NULL, 1, &bgtime);
}
return pnam;
}
entersubsh(Z_ASYNC, 1, 0, 0);
redup(pipes[out], out);
closem(0); /* this closes pipes[!out] as well */
#endif /* PATH_DEV_FD */
cmdpush(CS_CMDSUBST);
execode(prog, 0, 1);
cmdpop();
zclose(out);
_exit(lastval);
return NULL;
#else /*__SYMBIAN32__*/
st_exec stexec;
pthread_t testThread;
if (!(prog = parsecmd(cmd)))
return NULL;
stexec.dont_change_job=0;
stexec.exiting=1;
stexec.p=prog;
pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
return NULL;
#endif // __SYMBIAN32__
#endif /* HAVE_FIFOS and PATH_DEV_FD not defined */
}
/*
* > >(...) or < <(...) (does not use named pipes)
*
* If the second argument is 1, this is part of
* an "exec < <(...)" or "exec > >(...)" and we shouldn't
* wait for the job to finish before continuing.
*/
/**/
static int
getpipe(char *cmd, int nullexec)
{
Eprog prog;
int out = *cmd == Inang;
#ifndef __SYMBIAN32__
pid_t pid;
struct timeval bgtime;
int pipes[2];
if (!(prog = parsecmd(cmd)))
return -1;
mpipe(pipes);
if ((pid = zfork(&bgtime))) {
zclose(pipes[out]);
if (pid == -1) {
zclose(pipes[!out]);
return -1;
}
if (!nullexec)
addproc(pid, NULL, 1, &bgtime);
return pipes[!out];
}
entersubsh(Z_ASYNC, 1, 0, 0);
redup(pipes[out], out);
closem(0); /* this closes pipes[!out] as well */
cmdpush(CS_CMDSUBST);
execode(prog, 0, 1);
cmdpop();
_exit(lastval);
#else
st_exec stexec;
pthread_t testThread;
if (!(prog = parsecmd(cmd)))
return -1;
stexec.dont_change_job=0;
stexec.exiting=1;
stexec.p=prog;
pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
#endif //__SYMBIAN32__
return 0;
}
/* open pipes with fds >= 10 */
/**/
static void
mpipe(int *pp)
{
pipe(pp);
pp[0] = movefd(pp[0]);
pp[1] = movefd(pp[1]);
}
/*
* Do process substitution with redirection
*
* If the second argument is 1, this is part of
* an "exec < <(...)" or "exec > >(...)" and we shouldn't
* wait for the job to finish before continuing.
*/
/**/
static void
spawnpipes(LinkList l, int nullexec)
{
LinkNode n;
Redir f;
char *str;
n = firstnode(l);
for (; n; incnode(n)) {
f = (Redir) getdata(n);
if (f->type == REDIR_OUTPIPE || f->type == REDIR_INPIPE) {
str = f->name;
f->fd2 = getpipe(str, nullexec);
}
}
}
extern int tracingcond;
/* evaluate a [[ ... ]] */
/**/
static int
execcond(Estate state, UNUSED(int do_exec))
{
int stat;
state->pc--;
if (isset(XTRACE)) {
printprompt4();
fprintf(xtrerr, "[[");
tracingcond++;
}
cmdpush(CS_COND);
stat = evalcond(state, NULL);
/*
* 2 indicates a syntax error. For compatibility, turn this
* into a shell error.
*/
if (stat == 2)
errflag = 1;
cmdpop();
if (isset(XTRACE)) {
fprintf(xtrerr, " ]]\n");
fflush(xtrerr);
tracingcond--;
}
return stat;
}
/* evaluate a ((...)) arithmetic command */
/**/
static int
execarith(Estate state, UNUSED(int do_exec))
{
char *e;
mnumber val = zero_mnumber;
int htok = 0;
if (isset(XTRACE)) {
printprompt4();
fprintf(xtrerr, "((");
}
cmdpush(CS_MATH);
e = ecgetstr(state, EC_DUPTOK, &htok);
if (htok)
singsub(&e);
if (isset(XTRACE))
fprintf(xtrerr, " %s", e);
val = matheval(e);
cmdpop();
if (isset(XTRACE)) {
fprintf(xtrerr, " ))\n");
fflush(xtrerr);
}
errflag = 0;
/* should test for fabs(val.u.d) < epsilon? */
return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
}
/* perform time ... command */
/**/
static int
exectime(Estate state, UNUSED(int do_exec))
{
int jb;
jb = thisjob;
if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
shelltime();
return 0;
}
execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
thisjob = jb;
return lastval;
}
/* Define a shell function */
/**/
static int
execfuncdef(Estate state, UNUSED(int do_exec))
{
Shfunc shf;
char *s;
int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0;
Wordcode beg = state->pc, end;
Eprog prog;
Patprog *pp;
LinkList names;
end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
if (!(names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
state->pc = end;
return 0;
}
nprg = end - beg;
sbeg = *state->pc++;
nstrs = *state->pc++;
npats = *state->pc++;
nprg = (end - state->pc);
plen = nprg * sizeof(wordcode);
len = plen + (npats * sizeof(Patprog)) + nstrs;
if (htok)
execsubst(names);
while ((s = (char *) ugetnode(names))) {
prog = (Eprog) zalloc(sizeof(*prog));
prog->npats = npats;
prog->nref = 1; /* allocated from permanent storage */
prog->len = len;
if (state->prog->dump) {
prog->flags = EF_MAP;
incrdumpcount(state->prog->dump);
prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
prog->prog = state->pc;
prog->strs = state->strs + sbeg;
prog->dump = state->prog->dump;
} else {
prog->flags = EF_REAL;
prog->pats = pp = (Patprog *) zalloc(len);
prog->prog = (Wordcode) (prog->pats + npats);
prog->strs = (char *) (prog->prog + nprg);
prog->dump = NULL;
memcpy(prog->prog, state->pc, plen);
memcpy(prog->strs, state->strs + sbeg, nstrs);
}
for (i = npats; i--; pp++)
*pp = dummy_patprog1;
prog->shf = NULL;
shf = (Shfunc) zalloc(sizeof(*shf));
shf->funcdef = prog;
shf->flags = 0;
/* is this shell function a signal trap? */
if (!strncmp(s, "TRAP", 4) &&
(signum = getsignum(s + 4)) != -1) {
if (settrap(signum, shf->funcdef)) {
freeeprog(shf->funcdef);
zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
}
sigtrapped[signum] |= ZSIG_FUNC;
/*
* Remove the old node explicitly in case it has
* an alternative name
*/
removetrapnode(signum);
}
shfunctab->addnode(shfunctab, ztrdup(s), shf);
}
state->pc = end;
return 0;
}
/* Main entry point to execute a shell function. */
/**/
static void
execshfunc(Shfunc shf, LinkList args)
{
LinkList last_file_list = NULL;
unsigned char *ocs;
int ocsp, osfc;
if (errflag)
return;
if (!list_pipe && thisjob != list_pipe_job && !hasprocs(thisjob)) {
/* Without this deletejob the process table *
* would be filled by a recursive function. */
last_file_list = jobtab[thisjob].filelist;
jobtab[thisjob].filelist = NULL;
deletejob(jobtab + thisjob);
}
if (isset(XTRACE)) {
LinkNode lptr;
printprompt4();
if (args)
for (lptr = firstnode(args); lptr; incnode(lptr)) {
if (lptr != firstnode(args))
fputc(' ', xtrerr);
quotedzputs((char *)getdata(lptr), xtrerr);
}
fputc('\n', xtrerr);
fflush(xtrerr);
}
ocs = cmdstack;
ocsp = cmdsp;
cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
cmdsp = 0;
if ((osfc = sfcontext) == SFC_NONE)
sfcontext = SFC_DIRECT;
doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0);
sfcontext = osfc;
free(cmdstack);
cmdstack = ocs;
cmdsp = ocsp;
if (!list_pipe)
deletefilelist(last_file_list);
}
/* Function to execute the special type of command that represents an *
* autoloaded shell function. The command structure tells us which *
* function it is. This function is actually called as part of the *
* execution of the autoloaded function itself, so when the function *
* has been autoloaded, its list is just run with no frills. */
/**/
static int
execautofn(Estate state, UNUSED(int do_exec))
{
Shfunc shf;
char *oldscriptname;
if (!(shf = loadautofn(state->prog->shf, 1, 0)))
return 1;
oldscriptname = scriptname;
scriptname = dupstring(shf->nam);
execode(shf->funcdef, 1, 0);
scriptname = oldscriptname;
return lastval;
}
/**/
Shfunc
loadautofn(Shfunc shf, int fksh, int autol)
{
int noalias = noaliases, ksh = 1;
Eprog prog;
pushheap();
noaliases = (shf->flags & PM_UNALIASED);
prog = getfpfunc(shf->nam, &ksh);
noaliases = noalias;
if (ksh == 1) {
ksh = fksh;
if (ksh == 1)
ksh = (shf->flags & PM_KSHSTORED) ? 2 :
(shf->flags & PM_ZSHSTORED) ? 0 : 1;
}
if (prog == &dummy_eprog) {
/* We're not actually in the function; decrement locallevel */
locallevel--;
zwarn("%s: function definition file not found", shf->nam, 0);
locallevel++;
popheap();
return NULL;
}
if (!prog)
return NULL;
if (ksh == 2 || (ksh == 1 && isset(KSHAUTOLOAD))) {
if (autol) {
prog->flags |= EF_RUN;
freeeprog(shf->funcdef);
if (prog->flags & EF_MAP)
shf->funcdef = prog;
else
shf->funcdef = dupeprog(prog, 0);
shf->flags &= ~PM_UNDEFINED;
} else {
VARARR(char, n, strlen(shf->nam) + 1);
strcpy(n, shf->nam);
execode(prog, 1, 0);
shf = (Shfunc) shfunctab->getnode(shfunctab, n);
if (!shf || (shf->flags & PM_UNDEFINED)) {
/* We're not actually in the function; decrement locallevel */
locallevel--;
zwarn("%s: function not defined by file", n, 0);
locallevel++;
popheap();
return NULL;
}
}
} else {
freeeprog(shf->funcdef);
if (prog->flags & EF_MAP)
shf->funcdef = stripkshdef(prog, shf->nam);
else
shf->funcdef = dupeprog(stripkshdef(prog, shf->nam), 0);
shf->flags &= ~PM_UNDEFINED;
}
popheap();
return shf;
}
/*
* execute a shell function
*
* If noreturnval is nonzero, then reset the current return
* value (lastval) to its value before the shell function
* was executed. However, in any case return the status value
* from the function (i.e. if noreturnval is not set, this
* will be the same as lastval).
*/
/**/
mod_export int
doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
{
char **tab, **x, *oargv0;
int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
int *oldpipestats = NULL;
char saveopts[OPT_SIZE], *oldscriptname = scriptname, *fname = dupstring(name);
int obreaks;
struct funcstack fstack;
#ifdef MAX_FUNCTION_DEPTH
static int funcdepth;
#endif
pushheap();
oargv0 = NULL;
obreaks = breaks;;
if (trapreturn < 0)
trapreturn--;
oldlastval = lastval;
oldnumpipestats = numpipestats;
if (noreturnval) {
/*
* Easiest to use the heap here since we're bracketed
* immediately by a pushheap/popheap pair.
*/
size_t bytes = sizeof(int)*numpipestats;
oldpipestats = (int *)zhalloc(bytes);
memcpy(oldpipestats, pipestats, bytes);
}
starttrapscope();
tab = pparams;
if (!(flags & PM_UNDEFINED))
scriptname = dupstring(name);
oldzoptind = zoptind;
zoptind = 1;
oldoptcind = optcind;
optcind = 0;
/* We need to save the current options even if LOCALOPTIONS is *
* not currently set. That's because if it gets set in the *
* function we need to restore the original options on exit. */
memcpy(saveopts, opts, sizeof(opts));
if (flags & PM_TAGGED)
opts[XTRACE] = 1;
opts[PRINTEXITVALUE] = 0;
if (doshargs) {
LinkNode node;
node = doshargs->first;
pparams = x = (char **) zshcalloc(((sizeof *x) *
(1 + countlinknodes(doshargs))));
if (isset(FUNCTIONARGZERO)) {
oargv0 = argzero;
argzero = ztrdup((char *) node->dat);
}
node = node->next;
for (; node; node = node->next, x++)
*x = ztrdup((char *) node->dat);
} else {
pparams = (char **) zshcalloc(sizeof *pparams);
if (isset(FUNCTIONARGZERO)) {
oargv0 = argzero;
argzero = ztrdup(argzero);
}
}
#ifdef MAX_FUNCTION_DEPTH
if(++funcdepth > MAX_FUNCTION_DEPTH)
{
zerr("maximum nested function level reached", NULL, 0);
goto undoshfunc;
}
#endif
fstack.name = dupstring(name);
fstack.prev = funcstack;
funcstack = &fstack;
if (prog->flags & EF_RUN) {
Shfunc shf;
prog->flags &= ~EF_RUN;
runshfunc(prog, NULL, fstack.name);
if (!(shf = (Shfunc) shfunctab->getnode(shfunctab,
(name = fname)))) {
zwarn("%s: function not defined by file", name, 0);
if (noreturnval)
errflag = 1;
else
lastval = 1;
goto doneshfunc;
}
prog = shf->funcdef;
}
runshfunc(prog, wrappers, fstack.name);
doneshfunc:
funcstack = fstack.prev;
#ifdef MAX_FUNCTION_DEPTH
undoshfunc:
--funcdepth;
#endif
if (retflag) {
retflag = 0;
breaks = obreaks;
}
freearray(pparams);
if (oargv0) {
zsfree(argzero);
argzero = oargv0;
}
pparams = tab;
optcind = oldoptcind;
zoptind = oldzoptind;
scriptname = oldscriptname;
if (isset(LOCALOPTIONS)) {
/* restore all shell options except PRIVILEGED and RESTRICTED */
saveopts[PRIVILEGED] = opts[PRIVILEGED];
saveopts[RESTRICTED] = opts[RESTRICTED];
memcpy(opts, saveopts, sizeof(opts));
} else {
/* just restore a couple. */
opts[XTRACE] = saveopts[XTRACE];
opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
}
endtrapscope();
if (trapreturn < -1)
trapreturn++;
ret = lastval;
if (noreturnval) {
lastval = oldlastval;
numpipestats = oldnumpipestats;
memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats);
}
popheap();
if (exit_pending) {
if (locallevel) {
/* Still functions to return: force them to do so. */
retflag = 1;
breaks = loops;
} else {
/*
* All functions finished: time to exit the shell.
* We already did the `stopmsg' test when the
* exit command was handled.
*/
stopmsg = 1;
zexit(exit_pending >> 1, 0);
}
}
return ret;
}
/* This finally executes a shell function and any function wrappers *
* defined by modules. This works by calling the wrapper function which *
* in turn has to call back this function with the arguments it gets. */
/**/
mod_export void
runshfunc(Eprog prog, FuncWrap wrap, char *name)
{
int cont;
VARARR(char, ou, underscoreused);
memcpy(ou, underscore, underscoreused);
while (wrap) {
wrap->module->wrapper++;
cont = wrap->handler(prog, wrap->next, name);
wrap->module->wrapper--;
if (!wrap->module->wrapper &&
(wrap->module->flags & MOD_UNLOAD))
unload_module(wrap->module, NULL);
if (!cont)
return;
wrap = wrap->next;
}
startparamscope();
execode(prog, 1, 0);
setunderscore(ou);
endparamscope();
}
/* Search fpath for an undefined function. Finds the file, and returns the *
* list of its contents. */
/**/
Eprog
getfpfunc(char *s, int *ksh)
{
char **pp, buf[PATH_MAX];
off_t len;
off_t rlen;
char *d;
Eprog r;
int fd;
pp = fpath;
for (; *pp; pp++) {
if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX)
continue;
if (**pp)
sprintf(buf, "%s/%s", *pp, s);
else
strcpy(buf, s);
if ((r = try_dump_file(*pp, s, buf, ksh)))
return r;
unmetafy(buf, NULL);
if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) {
if ((len = lseek(fd, 0, 2)) != -1) {
d = (char *) zalloc(len + 1);
lseek(fd, 0, 0);
if ((rlen = read(fd, d, len)) >= 0) {
char *oldscriptname = scriptname;
close(fd);
d[rlen] = '\0';
d = metafy(d, rlen, META_REALLOC);
scriptname = dupstring(s);
r = parse_string(d);
scriptname = oldscriptname;
zfree(d, len + 1);
return r;
} else
close(fd);
zfree(d, len + 1);
} else
close(fd);
}
}
return &dummy_eprog;
}
/* Handle the most common type of ksh-style autoloading, when doing a *
* zsh-style autoload. Given the list read from an autoload file, and the *
* name of the function being defined, check to see if the file consists *
* entirely of a single definition for that function. If so, use the *
* contents of that definition. Otherwise, use the entire file. */
/**/
Eprog
stripkshdef(Eprog prog, char *name)
{
Wordcode pc = prog->prog;
wordcode code;
if (!prog)
return NULL;
code = *pc++;
if (wc_code(code) != WC_LIST ||
(WC_LIST_TYPE(code) & (Z_SYNC|Z_END|Z_SIMPLE)) != (Z_SYNC|Z_END|Z_SIMPLE))
return prog;
pc++;
code = *pc++;
if (wc_code(code) != WC_FUNCDEF ||
*pc != 1 || strcmp(name, ecrawstr(prog, pc + 1, NULL)))
return prog;
{
Eprog ret;
Wordcode end = pc + WC_FUNCDEF_SKIP(code);
int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i;
Patprog *pp;
pc += 5;
nprg = end - pc;
plen = nprg * sizeof(wordcode);
len = plen + (npats * sizeof(Patprog)) + nstrs;
if (prog->flags & EF_MAP) {
ret = prog;
free(prog->pats);
ret->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
ret->prog = pc;
ret->strs = prog->strs + sbeg;
} else {
ret = (Eprog) zhalloc(sizeof(*ret));
ret->flags = EF_HEAP;
ret->pats = pp = (Patprog *) zhalloc(len);
ret->prog = (Wordcode) (ret->pats + npats);
ret->strs = (char *) (ret->prog + nprg);
memcpy(ret->prog, pc, plen);
memcpy(ret->strs, prog->strs + sbeg, nstrs);
ret->dump = NULL;
}
ret->len = len;
ret->npats = npats;
for (i = npats; i--; pp++)
*pp = dummy_patprog1;
ret->shf = NULL;
return ret;
}
}
/* check to see if AUTOCD applies here */
/**/
static char *
cancd(char *s)
{
int nocdpath = s[0] == '.' &&
(s[1] == '/' || !s[1] || (s[1] == '.' && (s[2] == '/' || !s[1])));
char *t;
if (*s != '/') {
char sbuf[PATH_MAX], **cp;
if (cancd2(s))
return s;
if (access(unmeta(s), X_OK) == 0)
return NULL;
if (!nocdpath)
for (cp = cdpath; *cp; cp++) {
if (strlen(*cp) + strlen(s) + 1 >= PATH_MAX)
continue;
if (**cp)
sprintf(sbuf, "%s/%s", *cp, s);
else
strcpy(sbuf, s);
if (cancd2(sbuf)) {
doprintdir = -1;
return dupstring(sbuf);
}
}
if ((t = cd_able_vars(s))) {
if (cancd2(t)) {
doprintdir = -1;
return t;
}
}
return NULL;
}
return cancd2(s) ? s : NULL;
}
/**/
static int
cancd2(char *s)
{
struct stat buf;
char *us, *us2 = NULL;
int ret;
/*
* If CHASEDOTS and CHASELINKS are not set, we want to rationalize the
* path by removing foo/.. combinations in the logical rather than
* the physical path. If either is set, we test the physical path.
*/
if (!isset(CHASEDOTS) && !isset(CHASELINKS)) {
if (*s != '/')
us = tricat(pwd[1] ? pwd : "", "/", s);
else
us = ztrdup(s);
fixdir(us2 = us);
} else
us = unmeta(s);
ret = !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode));
if (us2)
free(us2);
return ret;
}
/**/
void
execsave(void)
{
struct execstack *es;
es = (struct execstack *) malloc(sizeof(struct execstack));
es->args = args;
es->list_pipe_pid = list_pipe_pid;
es->nowait = nowait;
es->pline_level = pline_level;
es->list_pipe_child = list_pipe_child;
es->list_pipe_job = list_pipe_job;
strcpy(es->list_pipe_text, list_pipe_text);
es->lastval = lastval;
es->noeval = noeval;
es->badcshglob = badcshglob;
es->cmdoutpid = cmdoutpid;
es->cmdoutval = cmdoutval;
es->trapreturn = trapreturn;
es->noerrs = noerrs;
es->subsh_close = subsh_close;
es->underscore = ztrdup(underscore);
es->next = exstack;
exstack = es;
noerrs = cmdoutpid = 0;
}
/**/
void
execrestore(void)
{
struct execstack *en;
DPUTS(!exstack, "BUG: execrestore() without execsave()");
args = exstack->args;
list_pipe_pid = exstack->list_pipe_pid;
nowait = exstack->nowait;
pline_level = exstack->pline_level;
list_pipe_child = exstack->list_pipe_child;
list_pipe_job = exstack->list_pipe_job;
strcpy(list_pipe_text, exstack->list_pipe_text);
lastval = exstack->lastval;
noeval = exstack->noeval;
badcshglob = exstack->badcshglob;
cmdoutpid = exstack->cmdoutpid;
cmdoutval = exstack->cmdoutval;
trapreturn = exstack->trapreturn;
noerrs = exstack->noerrs;
subsh_close = exstack->subsh_close;
setunderscore(exstack->underscore);
zsfree(exstack->underscore);
en = exstack->next;
free(exstack);
exstack = en;
}