mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This takes another step towards cleaning up the build system. We now do not even pretend to be theoretically compatible with pre-1989 K&R C compilers or with C++ compilers. In practice, this had already been broken for many years due to bit rot. Commit46593a89already removed the license handling enormity that depended on proto, so now we can cleanly remove it altogether. But we do need to leave some backwards compatibility stubs to keep the build system compatible with older AST code; it should remain possible to build older ksh versions with the current build system (the bin/ and src/cmd/INIT/ directories) for testing purposes. So as of now there is no more __MANGLE__d rubbish in your generated header files. This is only about a quarter of a century overdue... This commit also includes a huge amount of code cleanup to remove thousands of unused K&R C fallbacks and other cruft, particularly in libast. This code base should now be a little easier to understand for people who are familiar with a modern(ish) C standard. ratz is now also removed; this was a standalone and simplified 2005 version of gunzip. As of6137b99a, none of our code uses it, even theoretically. And the real g(un)zip is now everywhere. src/cmd/INIT/proto.c, src/cmd/INIT/ratz.c: - Removed. COPYRIGHT: - Remove zlib license; this only applied to ratz. bin/package, src/cmd/INIT/package.sh: - Related cleanups. - Unset LC_ALL before invoking a new shell, respecting the user's locale again and avoiding multibyte character corruption on the command line. src/cmd/INIT/proto.sh: - Add stub for backwards compatibility with Mamfiles that depend on proto. It does nothing but pass input without modification and is now installed as the new arch/*/bin/proto by src/cmd/INIT/Mamfile. src/cmd/INIT/iffe.sh: - Ignore the proto-related -e (--package) and -p (--prototyped) options; keep parsing them for backwards compatibility. - Trim the macros passed to every test to their standard C versions, removing K&R C and C++ versions. These are now considered to be for backwards compatibility only. src/cmd/INIT/iffe.tst: - Remove proto(1) mangling code. By the way, iffe can be regression-tested as follows: $ bin/package use # set up environment in a child shell $ regress src/cmd/INIT/iffe.tst $ exit # leave package environment src/cmd/INIT/make.probe, src/cmd/INIT/probe.win32: - Remove code to handle C++. src/lib/libast/features/common: - As in iffe.sh above, trim macros designed for compatibility with C++ and ancient C compilers to their standard C versions and comment that they are for backwards compatibility with AST code. This is needed to keep all the old ast and ksh code compiling. src/cmd/ksh93/sh/init.c, src/cmd/ksh93/sh/name.c: - Clarify libshell ABI compatibility function versions of macros. A "proto workaround" comment in the original code mislead me into thinking this had something to do with the removed proto(1), but it's unrelated. Call the workaround macro BYPASS_MACRO instead. src/cmd/ksh93/include/defs.h: - sh_sigcheck() macro: allow &sh as an argument: parenthesise shp. src/cmd/ksh93/sh/nvtype.c: - Remove unused nv_mkstruct() function. (re:d0a5cab1) **/features/*: - Remove obsolete iffe 'set prototyped' option. **/Mamfile: - Remove all references to the ast/prototyped.h header. - Remove all use of the proto command. Simply copy instead. *** 850-ish source files: *** - Remove all '#pragma prototyped' directives. - Remove all C++ compat code conditional upon defined(__cplusplus). - Remove all use of the _ARG_ macro, which on standard C expands to its argument: #define _ARG_(x) x (on K&R C, it expanded to nothing) - Remove all use of _BEGIN_EXTERNS_ and _END_EXTERNS_ macros (empty on standard C; this was for C++ compatibility) - Reduce all #if __STD_C (standard code) #else (K&R code) #endif blocks to the standard code only, without use of the macro. - Same for _STD_ macro which seems to have had the same function. - Change all instances of 'Void_t' to standard 'void'.
697 lines
16 KiB
C
697 lines
16 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
/*
|
|
* Fault handling routines
|
|
*
|
|
* David Korn
|
|
* AT&T Labs
|
|
*
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <fcin.h>
|
|
#include "io.h"
|
|
#include "history.h"
|
|
#include "shlex.h"
|
|
#include "variables.h"
|
|
#include "jobs.h"
|
|
#include "path.h"
|
|
#include "builtins.h"
|
|
#include "ulimit.h"
|
|
|
|
#define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV)
|
|
|
|
static char indone;
|
|
static int cursig = -1;
|
|
|
|
#if !_std_malloc
|
|
# include <vmalloc.h>
|
|
#endif
|
|
#if defined(VMFL)
|
|
/*
|
|
* This exception handler is called after vmalloc() unlocks the region
|
|
*/
|
|
static int malloc_done(Vmalloc_t* vm, int type, void* val, Vmdisc_t* dp)
|
|
{
|
|
dp->exceptf = 0;
|
|
sh_exit(SH_EXITSIG);
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Most signals caught or ignored by the shell come here
|
|
*/
|
|
void sh_fault(register int sig)
|
|
{
|
|
register Shell_t *shp = sh_getinterp();
|
|
register int flag=0;
|
|
register char *trap;
|
|
register struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
|
int action=0;
|
|
/* reset handler */
|
|
if(!(sig&SH_TRAP))
|
|
signal(sig, sh_fault);
|
|
sig &= ~SH_TRAP;
|
|
#ifdef SIGWINCH
|
|
if(sig==SIGWINCH)
|
|
{
|
|
int rows=0, cols=0;
|
|
int32_t v;
|
|
astwinsize(2,&rows,&cols);
|
|
if(v = cols)
|
|
nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY);
|
|
if(v = rows)
|
|
nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY);
|
|
shp->winch++;
|
|
}
|
|
#endif /* SIGWINCH */
|
|
trap = shp->st.trapcom[sig];
|
|
if(shp->savesig)
|
|
{
|
|
/* critical region, save and process later */
|
|
if(!(shp->sigflag[sig]&SH_SIGIGNORE))
|
|
shp->savesig = sig;
|
|
return;
|
|
}
|
|
if(sig==SIGALRM && shp->bltinfun==b_sleep)
|
|
{
|
|
if(trap && *trap)
|
|
{
|
|
shp->trapnote |= SH_SIGTRAP;
|
|
shp->sigflag[sig] |= SH_SIGTRAP;
|
|
}
|
|
return;
|
|
}
|
|
if(shp->subshell && trap && sig!=SIGINT && sig!=SIGQUIT && sig!=SIGWINCH && sig!=SIGCONT)
|
|
{
|
|
shp->exitval = SH_EXITSIG|sig;
|
|
sh_subfork();
|
|
shp->exitval = 0;
|
|
return;
|
|
}
|
|
/* handle ignored signals */
|
|
if(trap && *trap==0)
|
|
return;
|
|
flag = shp->sigflag[sig]&~SH_SIGOFF;
|
|
if(!trap)
|
|
{
|
|
if(sig==SIGINT && (shp->trapnote&SH_SIGIGNORE))
|
|
return;
|
|
if(flag&SH_SIGIGNORE)
|
|
{
|
|
if(shp->subshell)
|
|
shp->ignsig = sig;
|
|
sigrelease(sig);
|
|
return;
|
|
}
|
|
if(flag&SH_SIGDONE)
|
|
{
|
|
void *ptr=0;
|
|
if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! shp->subshell)
|
|
{
|
|
/* check for TERM signal between fork/exec */
|
|
if(sig==SIGTERM && job.in_critical)
|
|
shp->trapnote |= SH_SIGTERM;
|
|
return;
|
|
}
|
|
shp->lastsig = sig;
|
|
sigrelease(sig);
|
|
if(pp->mode != SH_JMPSUB)
|
|
{
|
|
if(pp->mode < SH_JMPSUB)
|
|
pp->mode = shp->subshell?SH_JMPSUB:SH_JMPFUN;
|
|
else
|
|
pp->mode = SH_JMPEXIT;
|
|
}
|
|
if(shp->subshell)
|
|
sh_exit(SH_EXITSIG);
|
|
if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1))))
|
|
{
|
|
if(ptr)
|
|
free(ptr);
|
|
sh_done(shp,sig);
|
|
}
|
|
/* mark signal and continue */
|
|
shp->trapnote |= SH_SIGSET;
|
|
if(sig <= shp->gd->sigmax)
|
|
shp->sigflag[sig] |= SH_SIGSET;
|
|
#if defined(VMFL)
|
|
if(abortsig(sig))
|
|
{
|
|
/* abort inside malloc, process when malloc returns */
|
|
/* VMFL defined when using vmalloc() */
|
|
Vmdisc_t* dp = vmdisc(Vmregion,0);
|
|
if(dp)
|
|
dp->exceptf = malloc_done;
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
errno = 0;
|
|
if(pp->mode==SH_JMPCMD || (pp->mode==1 && shp->bltinfun) && !(flag&SH_SIGIGNORE))
|
|
shp->lastsig = sig;
|
|
if(trap)
|
|
{
|
|
/*
|
|
* propagate signal to foreground group
|
|
*/
|
|
if(sig==SIGHUP && job.curpgid)
|
|
killpg(job.curpgid,SIGHUP);
|
|
flag = SH_SIGTRAP;
|
|
}
|
|
else
|
|
{
|
|
shp->lastsig = sig;
|
|
flag = SH_SIGSET;
|
|
#ifdef SIGTSTP
|
|
if(sig==SIGTSTP)
|
|
{
|
|
shp->trapnote |= SH_SIGTSTP;
|
|
if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
|
|
{
|
|
sigrelease(sig);
|
|
sh_exit(SH_EXITSIG);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* SIGTSTP */
|
|
}
|
|
#ifdef ERROR_NOTIFY
|
|
if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun)
|
|
action = (*shp->bltinfun)(-sig,(char**)0,(void*)0);
|
|
if(action>0)
|
|
return;
|
|
#endif
|
|
if(shp->bltinfun && shp->bltindata.notify)
|
|
{
|
|
shp->bltindata.sigset = 1;
|
|
return;
|
|
}
|
|
shp->trapnote |= flag;
|
|
if(sig <= shp->gd->sigmax)
|
|
shp->sigflag[sig] |= flag;
|
|
if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
|
|
{
|
|
if(action<0)
|
|
return;
|
|
sigrelease(sig);
|
|
sh_exit(SH_EXITSIG);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initialize signal handling
|
|
*/
|
|
void sh_siginit(void *ptr)
|
|
{
|
|
Shell_t *shp = (Shell_t*)ptr;
|
|
register int sig, n;
|
|
register const struct shtable2 *tp = shtab_signals;
|
|
sig_begin();
|
|
/* find the largest signal number in the table */
|
|
#if defined(SIGRTMIN) && defined(SIGRTMAX)
|
|
if ((n = SIGRTMIN) > 0 && (sig = SIGRTMAX) > n && sig < SH_TRAP)
|
|
{
|
|
shp->gd->sigruntime[SH_SIGRTMIN] = n;
|
|
shp->gd->sigruntime[SH_SIGRTMAX] = sig;
|
|
}
|
|
#endif /* SIGRTMIN && SIGRTMAX */
|
|
n = SIGTERM;
|
|
while(*tp->sh_name)
|
|
{
|
|
sig = (tp->sh_number&((1<<SH_SIGBITS)-1));
|
|
if (!(sig-- & SH_TRAP))
|
|
{
|
|
if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME)
|
|
sig = shp->gd->sigruntime[sig];
|
|
if(sig>n && sig<SH_TRAP)
|
|
n = sig;
|
|
}
|
|
tp++;
|
|
}
|
|
shp->gd->sigmax = n++;
|
|
shp->st.trapcom = (char**)sh_calloc(n,sizeof(char*));
|
|
shp->sigflag = (unsigned char*)sh_calloc(n,1);
|
|
shp->gd->sigmsg = (char**)sh_calloc(n,sizeof(char*));
|
|
for(tp=shtab_signals; sig=tp->sh_number; tp++)
|
|
{
|
|
n = (sig>>SH_SIGBITS);
|
|
if((sig &= ((1<<SH_SIGBITS)-1)) > (shp->gd->sigmax+1))
|
|
continue;
|
|
sig--;
|
|
if(n&SH_SIGRUNTIME)
|
|
sig = shp->gd->sigruntime[sig];
|
|
if(sig>=0)
|
|
{
|
|
shp->sigflag[sig] = n;
|
|
if(*tp->sh_name)
|
|
shp->gd->sigmsg[sig] = (char*)tp->sh_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Turn on trap handler for signal <sig>
|
|
*/
|
|
void sh_sigtrap(register int sig)
|
|
{
|
|
register int flag;
|
|
void (*fun)(int);
|
|
sh.st.otrapcom = 0;
|
|
if(sig==0)
|
|
sh_sigdone();
|
|
else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF)))
|
|
{
|
|
/* don't set signal if already set or off by parent */
|
|
if((fun=signal(sig,sh_fault))==SIG_IGN)
|
|
{
|
|
signal(sig,SIG_IGN);
|
|
flag |= SH_SIGOFF;
|
|
}
|
|
else
|
|
{
|
|
flag |= SH_SIGFAULT;
|
|
if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault)
|
|
signal(sig,fun);
|
|
}
|
|
flag &= ~(SH_SIGSET|SH_SIGTRAP);
|
|
sh.sigflag[sig] = flag;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* set signal handler so sh_done is called for all caught signals
|
|
*/
|
|
void sh_sigdone(void)
|
|
{
|
|
register int flag, sig = shgd->sigmax;
|
|
sh.sigflag[0] |= SH_SIGFAULT;
|
|
for(sig=shgd->sigmax; sig>0; sig--)
|
|
{
|
|
flag = sh.sigflag[sig];
|
|
if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF)))
|
|
sh_sigtrap(sig);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore to default signals
|
|
* Free the trap strings if mode is non-zero
|
|
* If mode>1 then ignored traps cause signal to be ignored
|
|
*/
|
|
void sh_sigreset(register int mode)
|
|
{
|
|
register char *trap;
|
|
register int flag, sig=sh.st.trapmax;
|
|
while(sig-- > 0)
|
|
{
|
|
if(trap=sh.st.trapcom[sig])
|
|
{
|
|
flag = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET);
|
|
if(*trap)
|
|
{
|
|
if(mode)
|
|
free(trap);
|
|
sh.st.trapcom[sig] = 0;
|
|
}
|
|
else if(sig && mode>1)
|
|
{
|
|
if(sig!=SIGCHLD)
|
|
signal(sig,SIG_IGN);
|
|
flag &= ~SH_SIGFAULT;
|
|
flag |= SH_SIGOFF;
|
|
}
|
|
sh.sigflag[sig] = flag;
|
|
}
|
|
}
|
|
for(sig=SH_DEBUGTRAP; sig>=0; sig--)
|
|
{
|
|
if(trap=sh.st.trap[sig])
|
|
{
|
|
if(mode)
|
|
free(trap);
|
|
sh.st.trap[sig] = 0;
|
|
}
|
|
|
|
}
|
|
sh.st.trapcom[0] = 0;
|
|
if(mode)
|
|
sh.st.trapmax = 0;
|
|
sh.trapnote=0;
|
|
}
|
|
|
|
/*
|
|
* free up trap if set and restore signal handler if modified
|
|
*/
|
|
void sh_sigclear(register int sig)
|
|
{
|
|
register int flag = sh.sigflag[sig];
|
|
register char *trap;
|
|
sh.st.otrapcom=0;
|
|
if(!(flag&SH_SIGFAULT))
|
|
return;
|
|
flag &= ~(SH_SIGTRAP|SH_SIGSET);
|
|
if(trap=sh.st.trapcom[sig])
|
|
{
|
|
if(!sh.subshell)
|
|
free(trap);
|
|
sh.st.trapcom[sig]=0;
|
|
}
|
|
sh.sigflag[sig] = flag;
|
|
}
|
|
|
|
/*
|
|
* check for traps
|
|
*/
|
|
|
|
void sh_chktrap(Shell_t* shp)
|
|
{
|
|
register int sig=shp->st.trapmax;
|
|
register char *trap;
|
|
if(!(shp->trapnote&~SH_SIGIGNORE))
|
|
sig=0;
|
|
shp->trapnote &= ~SH_SIGTRAP;
|
|
/* execute errexit trap first */
|
|
if(sh_isstate(SH_ERREXIT) && shp->exitval)
|
|
{
|
|
int sav_trapnote = shp->trapnote;
|
|
shp->trapnote &= ~SH_SIGSET;
|
|
if(shp->st.trap[SH_ERRTRAP])
|
|
{
|
|
trap = shp->st.trap[SH_ERRTRAP];
|
|
shp->st.trap[SH_ERRTRAP] = 0;
|
|
sh_trap(trap,0);
|
|
shp->st.trap[SH_ERRTRAP] = trap;
|
|
}
|
|
shp->trapnote = sav_trapnote;
|
|
if(sh_isoption(SH_ERREXIT))
|
|
{
|
|
struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
|
pp->mode = SH_JMPEXIT;
|
|
sh_exit(shp->exitval);
|
|
}
|
|
}
|
|
if(shp->sigflag[SIGALRM]&SH_SIGALRM)
|
|
sh_timetraps(shp);
|
|
#if SHOPT_BGX
|
|
if((shp->sigflag[SIGCHLD]&SH_SIGTRAP) && shp->st.trapcom[SIGCHLD])
|
|
job_chldtrap(shp,shp->st.trapcom[SIGCHLD],1);
|
|
#endif /* SHOPT_BGX */
|
|
while(--sig>=0)
|
|
{
|
|
if(sig==cursig)
|
|
continue;
|
|
#if SHOPT_BGX
|
|
if(sig==SIGCHLD)
|
|
continue;
|
|
#endif /* SHOPT_BGX */
|
|
if(shp->sigflag[sig]&SH_SIGTRAP)
|
|
{
|
|
shp->sigflag[sig] &= ~SH_SIGTRAP;
|
|
if(trap=shp->st.trapcom[sig])
|
|
{
|
|
cursig = sig;
|
|
sh_trap(trap,0);
|
|
cursig = -1;
|
|
/* If we're in a PS2 prompt, then we just parsed and executed a trap in the middle of parsing
|
|
* another command, so the lexer state is overwritten. Escape to avoid crashing the lexer. */
|
|
if(sh.nextprompt == 2)
|
|
{
|
|
fcclose(); /* force lexer to abort partial command */
|
|
sh.nextprompt = 1; /* next display prompt is PS1 */
|
|
sh.lastsig = sig; /* make sh_exit() set $? to signal exit status */
|
|
sh_exit(SH_EXITSIG); /* start a new command line */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* parse and execute the given trap string, stream or tree depending on mode
|
|
* mode==0 for string, mode==1 for stream, mode==2 for parse tree
|
|
* The return value is the exit status of the trap action.
|
|
*/
|
|
int sh_trap(const char *trap, int mode)
|
|
{
|
|
Shell_t *shp = sh_getinterp();
|
|
int jmpval, savxit = shp->exitval, savxit_return;
|
|
int was_history = sh_isstate(SH_HISTORY);
|
|
int was_verbose = sh_isstate(SH_VERBOSE);
|
|
int staktop = staktell();
|
|
char *savptr = stakfreeze(0);
|
|
struct checkpt buff;
|
|
Fcin_t savefc;
|
|
fcsave(&savefc);
|
|
sh_offstate(SH_HISTORY);
|
|
sh_offstate(SH_VERBOSE);
|
|
shp->intrap++;
|
|
sh_pushcontext(shp,&buff,SH_JMPTRAP);
|
|
jmpval = sigsetjmp(buff.buff,0);
|
|
if(jmpval == 0)
|
|
{
|
|
if(mode==2)
|
|
sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT));
|
|
else
|
|
{
|
|
Sfio_t *sp;
|
|
if(mode)
|
|
sp = (Sfio_t*)trap;
|
|
else
|
|
sp = sfopen(NIL(Sfio_t*),trap,"s");
|
|
sh_eval(sp,0);
|
|
}
|
|
}
|
|
else if(indone)
|
|
{
|
|
if(jmpval==SH_JMPSCRIPT)
|
|
indone=0;
|
|
else
|
|
{
|
|
if(jmpval==SH_JMPEXIT)
|
|
savxit = shp->exitval;
|
|
jmpval=SH_JMPTRAP;
|
|
}
|
|
}
|
|
sh_popcontext(shp,&buff);
|
|
shp->intrap--;
|
|
sfsync(shp->outpool);
|
|
savxit_return = shp->exitval;
|
|
if(jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
|
|
shp->exitval=savxit;
|
|
stakset(savptr,staktop);
|
|
fcrestore(&savefc);
|
|
if(was_history)
|
|
sh_onstate(SH_HISTORY);
|
|
if(was_verbose)
|
|
sh_onstate(SH_VERBOSE);
|
|
exitset();
|
|
if(jmpval>SH_JMPTRAP && (((struct checkpt*)shp->jmpbuffer)->prev || ((struct checkpt*)shp->jmpbuffer)->mode==SH_JMPSCRIPT))
|
|
siglongjmp(*shp->jmplist,jmpval);
|
|
return(savxit_return);
|
|
}
|
|
|
|
/*
|
|
* exit the current scope and jump to an earlier one based on pp->mode
|
|
*/
|
|
void sh_exit(register int xno)
|
|
{
|
|
Shell_t *shp = sh_getinterp();
|
|
register struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
|
register int sig=0;
|
|
register Sfio_t* pool;
|
|
/* POSIX requires exit status >= 2 for error in 'test'/'[' */
|
|
if(xno == 1 && (shp->bltindata.bnode==SYSTEST || shp->bltindata.bnode==SYSBRACKET))
|
|
shp->exitval = 2;
|
|
else
|
|
shp->exitval = xno;
|
|
if(xno==SH_EXITSIG)
|
|
shp->exitval |= (sig=shp->lastsig);
|
|
if(pp && pp->mode>1)
|
|
cursig = -1;
|
|
#ifdef SIGTSTP
|
|
if((shp->trapnote&SH_SIGTSTP) && job.jobcontrol)
|
|
{
|
|
/* ^Z detected by the shell */
|
|
shp->trapnote = 0;
|
|
shp->sigflag[SIGTSTP] = 0;
|
|
if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK))
|
|
return;
|
|
if(sh_isstate(SH_TIMING))
|
|
return;
|
|
/* Handles ^Z for shell builtins, subshells, and functs */
|
|
shp->lastsig = 0;
|
|
sh_onstate(SH_MONITOR);
|
|
sh_offstate(SH_STOPOK);
|
|
shp->trapnote = 0;
|
|
shp->forked = 1;
|
|
if(sh_isstate(SH_INTERACTIVE) && (sig=sh_fork(shp,0,NIL(int*))))
|
|
{
|
|
job.curpgid = 0;
|
|
job.parent = (pid_t)-1;
|
|
job_wait(sig);
|
|
shp->forked = 0;
|
|
job.parent = 0;
|
|
shp->sigflag[SIGTSTP] = 0;
|
|
/* wait for child to stop */
|
|
shp->exitval = (SH_EXITSIG|SIGTSTP);
|
|
/* return to prompt mode */
|
|
pp->mode = SH_JMPERREXIT;
|
|
}
|
|
else
|
|
{
|
|
if(shp->subshell)
|
|
sh_subfork();
|
|
/* script or child process; put to sleep */
|
|
sh_offstate(SH_STOPOK);
|
|
sh_offstate(SH_MONITOR);
|
|
shp->sigflag[SIGTSTP] = 0;
|
|
/* stop child job */
|
|
killpg(job.curpgid,SIGTSTP);
|
|
/* child resumes */
|
|
job_clear();
|
|
shp->exitval = (xno&SH_EXITMASK);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* SIGTSTP */
|
|
/* unlock output pool */
|
|
sh_offstate(SH_NOTRACK);
|
|
if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE)))
|
|
pool = shp->outpool; /* can't happen? */
|
|
sfclrlock(pool);
|
|
#ifdef SIGPIPE
|
|
if(shp->lastsig==SIGPIPE)
|
|
sfpurge(pool);
|
|
#endif /* SIGPIPE */
|
|
sfclrlock(sfstdin);
|
|
if(!pp)
|
|
sh_done(shp,sig);
|
|
shp->arithrecursion = 0;
|
|
shp->prefix = 0;
|
|
#if SHOPT_TYPEDEF
|
|
shp->mktype = 0;
|
|
#endif /* SHOPT_TYPEDEF */
|
|
if(job.in_critical)
|
|
job_unlock();
|
|
if(pp->mode == SH_JMPSCRIPT && !pp->prev)
|
|
sh_done(shp,sig);
|
|
if(pp->mode)
|
|
siglongjmp(pp->buff,pp->mode);
|
|
}
|
|
|
|
static void array_notify(Namval_t *np, void *data)
|
|
{
|
|
Namarr_t *ap = nv_arrayptr(np);
|
|
NOT_USED(data);
|
|
if(ap && ap->fun)
|
|
(*ap->fun)(np, 0, NV_AFREE);
|
|
}
|
|
|
|
/*
|
|
* This is the exit routine for the shell
|
|
*/
|
|
|
|
noreturn void sh_done(void *ptr, register int sig)
|
|
{
|
|
Shell_t *shp = (Shell_t*)ptr;
|
|
register char *t;
|
|
register int savxit = shp->exitval;
|
|
shp->trapnote = 0;
|
|
indone=1;
|
|
if(sig)
|
|
savxit = SH_EXITSIG|sig;
|
|
if(shp->userinit)
|
|
(*shp->userinit)(shp, -1);
|
|
if(t=shp->st.trapcom[0])
|
|
{
|
|
shp->st.trapcom[0]=0; /* should free but not long */
|
|
sh_trap(t,0);
|
|
savxit = shp->exitval;
|
|
}
|
|
else
|
|
{
|
|
/* avoid recursive call for set -e */
|
|
sh_offstate(SH_ERREXIT);
|
|
sh_chktrap(shp);
|
|
}
|
|
nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY);
|
|
sh_freeup(shp);
|
|
#if SHOPT_ACCT
|
|
sh_accend();
|
|
#endif /* SHOPT_ACCT */
|
|
#if SHOPT_VSH || SHOPT_ESH
|
|
if(mbwide()
|
|
#if SHOPT_ESH
|
|
|| sh_isoption(SH_EMACS)
|
|
|| sh_isoption(SH_GMACS)
|
|
#endif
|
|
#if SHOPT_VSH
|
|
|| sh_isoption(SH_VI)
|
|
#endif
|
|
)
|
|
tty_cooked(-1);
|
|
#endif /* SHOPT_VSH || SHOPT_ESH */
|
|
#ifdef JOBS
|
|
if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP)))
|
|
job_walk(sfstderr, job_hup, SIGHUP, NIL(char**));
|
|
#endif /* JOBS */
|
|
job_close(shp);
|
|
if(nv_search("VMTRACE", shp->var_tree,0))
|
|
strmatch((char*)0,(char*)0);
|
|
sfsync((Sfio_t*)sfstdin);
|
|
sfsync((Sfio_t*)shp->outpool);
|
|
sfsync((Sfio_t*)sfstdout);
|
|
if(savxit&SH_EXITSIG && (savxit&SH_EXITMASK) == shp->lastsig)
|
|
sig = savxit&SH_EXITMASK;
|
|
if(sig)
|
|
{
|
|
/* generate fault termination code */
|
|
if(RLIMIT_CORE!=RLIMIT_UNKNOWN)
|
|
{
|
|
#ifdef _lib_getrlimit
|
|
struct rlimit rlp;
|
|
getrlimit(RLIMIT_CORE,&rlp);
|
|
rlp.rlim_cur = 0;
|
|
setrlimit(RLIMIT_CORE,&rlp);
|
|
#else
|
|
vlimit(RLIMIT_CORE,0);
|
|
#endif
|
|
}
|
|
signal(sig,SIG_DFL);
|
|
sigrelease(sig);
|
|
kill(shgd->current_pid,sig);
|
|
pause();
|
|
}
|
|
#if SHOPT_KIA
|
|
if(sh_isoption(SH_NOEXEC))
|
|
kiaclose((Lex_t*)shp->lex_context);
|
|
#endif /* SHOPT_KIA */
|
|
|
|
/* Exit with portable 8-bit status (128 + signum) if last child process exits due to signal */
|
|
if (savxit & SH_EXITSIG)
|
|
savxit = savxit & ~SH_EXITSIG | 0200;
|
|
|
|
exit(savxit&SH_EXITMASK);
|
|
}
|