diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/main.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/main.c')
-rw-r--r-- | src/cmd/ksh93/sh/main.c | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/main.c b/src/cmd/ksh93/sh/main.c new file mode 100644 index 0000000..85948a6 --- /dev/null +++ b/src/cmd/ksh93/sh/main.c @@ -0,0 +1,787 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2012 AT&T Intellectual Property * +* 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> * +* * +***********************************************************************/ +#pragma prototyped +/* + * UNIX shell + * + * S. R. Bourne + * Rewritten By David Korn + * AT&T Labs + * + */ + +#include <ast.h> +#include <sfio.h> +#include <stak.h> +#include <ls.h> +#include <fcin.h> +#include "defs.h" +#include "variables.h" +#include "path.h" +#include "io.h" +#include "jobs.h" +#include "shlex.h" +#include "shnodes.h" +#include "history.h" +#include "timeout.h" +#include "FEATURE/time" +#include "FEATURE/pstat" +#include "FEATURE/execargs" +#include "FEATURE/externs" +#ifdef _hdr_nc +# include <nc.h> +#endif /* _hdr_nc */ + +#define CMD_LENGTH 64 + +/* These routines are referenced by this module */ +static void exfile(Shell_t*, Sfio_t*,int); +static void chkmail(Shell_t *shp, char*); +#if defined(_lib_fork) && !defined(_NEXT_SOURCE) + static void fixargs(char**,int); +#else +# define fixargs(a,b) +#endif + +#ifndef environ + extern char **environ; +#endif + +static struct stat lastmail; +static time_t mailtime; +static char beenhere = 0; + +#ifdef _lib_sigvec + void clearsigmask(register int sig) + { + struct sigvec vec; + if(sigvec(sig,NIL(struct sigvec*),&vec)>=0 && vec.sv_mask) + { + vec.sv_mask = 0; + sigvec(sig,&vec,NIL(struct sigvec*)); + } + } +#endif /* _lib_sigvec */ + +#ifdef _lib_fts_notify +# include <fts.h> + /* check for interrupts during tree walks */ + static int fts_sigcheck(FTS* fp, FTSENT* ep, void* context) + { + Shell_t *shp = (Shell_t*)context; + NOT_USED(fp); + NOT_USED(ep); + if(shp->trapnote&SH_SIGSET) + { + errno = EINTR; + return(-1); + } + return(0); + } +#endif /* _lib_fts_notify */ + +#ifdef PATH_BFPATH +#define PATHCOMP NIL(Pathcomp_t*) +#else +#define PATHCOMP "" +#endif + +/* + * search for file and exfile() it if it exists + * 1 returned if file found, 0 otherwise + */ + +int sh_source(Shell_t *shp, Sfio_t *iop, const char *file) +{ + char* oid; + char* nid; + int fd; + + if (!file || !*file || (fd = path_open(shp,file, PATHCOMP)) < 0) + { + REGRESS(source, "sh_source", ("%s:ENOENT", file)); + return 0; + } + oid = error_info.id; + nid = error_info.id = strdup(file); + shp->st.filename = path_fullname(shp,stakptr(PATH_OFFSET)); + REGRESS(source, "sh_source", ("%s", file)); + exfile(shp, iop, fd); + error_info.id = oid; + free(nid); + return 1; +} + +#ifdef S_ISSOCK +#define REMOTE(m) (S_ISSOCK(m)||!(m)) +#else +#define REMOTE(m) !(m) +#endif + +int sh_main(int ac, char *av[], Shinit_f userinit) +{ + register char *name; + register int fdin; + register Sfio_t *iop; + register Shell_t *shp; + struct stat statb; + int i, rshflag; /* set for restricted shell */ + char *command; + free(malloc(64*1024)); +#ifdef _lib_sigvec + /* This is to clear mask that may be left on by rlogin */ + clearsigmask(SIGALRM); + clearsigmask(SIGHUP); + clearsigmask(SIGCHLD); +#endif /* _lib_sigvec */ +#ifdef _hdr_nc + _NutConf(_NC_SET_SUFFIXED_SEARCHING, 1); +#endif /* _hdr_nc */ + fixargs(av,0); + shp = sh_init(ac,av,userinit); + time(&mailtime); + if(rshflag=sh_isoption(SH_RESTRICTED)) + sh_offoption(SH_RESTRICTED); +#ifdef _lib_fts_notify + fts_notify(fts_sigcheck,(void*)shp); +#endif /* _lib_fts_notify */ + if(sigsetjmp(*((sigjmp_buf*)shp->jmpbuffer),0)) + { + /* begin script execution here */ + sh_reinit((char**)0); + shp->gd->pid = getpid(); + shp->gd->ppid = getppid(); + } + shp->fn_depth = shp->dot_depth = 0; + command = error_info.id; + /* set pidname '$$' */ + srand(shp->gd->pid&0x7fff); + if(nv_isnull(PS4NOD)) + nv_putval(PS4NOD,e_traceprompt,NV_RDONLY); + path_pwd(shp,1); + iop = (Sfio_t*)0; +#if SHOPT_BRACEPAT + sh_onoption(SH_BRACEEXPAND); +#endif + if((beenhere++)==0) + { + sh_onstate(SH_PROFILE); + ((Lex_t*)shp->lex_context)->nonstandard = 0; + if(shp->gd->ppid==1) + shp->login_sh++; + if(shp->login_sh >= 2) + sh_onoption(SH_LOGIN_SHELL); + /* decide whether shell is interactive */ + if(!sh_isoption(SH_INTERACTIVE) && !sh_isoption(SH_TFLAG) && !sh_isoption(SH_CFLAG) && + sh_isoption(SH_SFLAG) && tty_check(0) && tty_check(ERRIO)) + sh_onoption(SH_INTERACTIVE); + if(sh_isoption(SH_INTERACTIVE)) + { + sh_onoption(SH_BGNICE); + sh_onoption(SH_RC); + } + if(!sh_isoption(SH_RC) && (sh_isoption(SH_BASH) && !sh_isoption(SH_POSIX) +#if SHOPT_REMOTE + || !fstat(0, &statb) && REMOTE(statb.st_mode) +#endif + )) + sh_onoption(SH_RC); + for(i=0; i<elementsof(shp->offoptions.v); i++) + shp->options.v[i] &= ~shp->offoptions.v[i]; + if(sh_isoption(SH_INTERACTIVE)) + { +#ifdef SIGXCPU + signal(SIGXCPU,SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + signal(SIGXFSZ,SIG_DFL); +#endif /* SIGXFSZ */ + sh_onoption(SH_MONITOR); + } + job_init(shp,sh_isoption(SH_LOGIN_SHELL)); + if(sh_isoption(SH_LOGIN_SHELL)) + { + /* system profile */ + sh_source(shp, iop, e_sysprofile); + if(!sh_isoption(SH_NOUSRPROFILE) && !sh_isoption(SH_PRIVILEGED)) + { + char **files = shp->gd->login_files; + while ((name = *files++) && !sh_source(shp, iop, sh_mactry(shp,name))); + } + } + /* make sure PWD is set up correctly */ + path_pwd(shp,1); + if(!sh_isoption(SH_NOEXEC)) + { + if(!sh_isoption(SH_NOUSRPROFILE) && !sh_isoption(SH_PRIVILEGED) && sh_isoption(SH_RC)) + { +#if SHOPT_BASH + if(sh_isoption(SH_BASH) && !sh_isoption(SH_POSIX)) + { +#if SHOPT_SYSRC + sh_source(shp, iop, e_bash_sysrc); +#endif + sh_source(shp, iop, shp->gd->rcfile ? shp->gd->rcfile : sh_mactry(shp,(char*)e_bash_rc)); + } + else +#endif + { + if(name = sh_mactry(shp,nv_getval(ENVNOD))) + name = *name ? strdup(name) : (char*)0; +#if SHOPT_SYSRC + if(!strmatch(name, "?(.)/./*")) + sh_source(shp, iop, e_sysrc); +#endif + if(name) + { + sh_source(shp, iop, name); + free(name); + } + } + } + else if(sh_isoption(SH_INTERACTIVE) && sh_isoption(SH_PRIVILEGED)) + sh_source(shp, iop, e_suidprofile); + } + shp->st.cmdname = error_info.id = command; + sh_offstate(SH_PROFILE); + if(rshflag) + sh_onoption(SH_RESTRICTED); + /* open input file if specified */ + if(shp->comdiv) + { + shell_c: + iop = sfnew(NIL(Sfio_t*),shp->comdiv,strlen(shp->comdiv),0,SF_STRING|SF_READ); + } + else + { + name = error_info.id; + error_info.id = shp->shname; + if(sh_isoption(SH_SFLAG)) + fdin = 0; + else + { + char *sp; + /* open stream should have been passed into shell */ + if(strmatch(name,e_devfdNN)) + { +#if !_WINIX + char *cp; + int type; +#endif + fdin = (int)strtol(name+8, (char**)0, 10); + if(fstat(fdin,&statb)<0) + errormsg(SH_DICT,ERROR_system(1),e_open,name); +#if !_WINIX + /* + * try to undo effect of solaris 2.5+ + * change for argv for setuid scripts + */ + if(((type = sh_type(cp = av[0])) & SH_TYPE_SH) && (!(name = nv_getval(L_ARGNOD)) || !((type = sh_type(cp = name)) & SH_TYPE_SH))) + { + av[0] = (type & SH_TYPE_LOGIN) ? cp : path_basename(cp); + /* exec to change $0 for ps */ + execv(pathshell(),av); + /* exec fails */ + shp->st.dolv[0] = av[0]; + fixargs(shp->st.dolv,1); + } +#endif + name = av[0]; + sh_offoption(SH_VERBOSE); + sh_offoption(SH_XTRACE); + } + else + { + int isdir = 0; + if((fdin=sh_open(name,O_RDONLY,0))>=0 &&(fstat(fdin,&statb)<0 || S_ISDIR(statb.st_mode))) + { + close(fdin); + isdir = 1; + fdin = -1; + } + else + shp->st.filename = path_fullname(shp,name); + sp = 0; + if(fdin < 0 && !strchr(name,'/')) + { +#ifdef PATH_BFPATH + if(path_absolute(shp,name,NIL(Pathcomp_t*))) + sp = stakptr(PATH_OFFSET); +#else + sp = path_absolute(shp,name,NIL(char*)); +#endif + if(sp) + { + if((fdin=sh_open(sp,O_RDONLY,0))>=0) + shp->st.filename = path_fullname(shp,sp); + } + } + if(fdin<0) + { + if(isdir) + errno = EISDIR; + error_info.id = av[0]; + if(sp || errno!=ENOENT) + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_open,name); + /* try sh -c 'name "$@"' */ + sh_onoption(SH_CFLAG); + shp->comdiv = (char*)malloc(strlen(name)+7); + name = strcopy(shp->comdiv,name); + if(shp->st.dolc) + strcopy(name," \"$@\""); + goto shell_c; + } + if(fdin==0) + fdin = sh_iomovefd(fdin); + } + shp->readscript = shp->shname; + } + error_info.id = name; + shp->comdiv--; +#if SHOPT_ACCT + sh_accinit(); + if(fdin != 0) + sh_accbegin(error_info.id); +#endif /* SHOPT_ACCT */ + } + } + else + { + fdin = shp->infd; + fixargs(shp->st.dolv,1); + } + if(sh_isoption(SH_INTERACTIVE)) + sh_onstate(SH_INTERACTIVE); + nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY); + exfile(shp,iop,fdin); + sh_done(shp,0); + /* NOTREACHED */ + return(0); +} + +/* + * iop is not null when the input is a string + * fdin is the input file descriptor + */ + +static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) +{ + time_t curtime; + Shnode_t *t; + int maxtry=IOMAXTRY, tdone=0, execflags; + int states,jmpval; + struct checkpt buff; + sh_pushcontext(shp,&buff,SH_JMPERREXIT); + /* open input stream */ + nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); + if(!iop) + { + if(fno > 0) + { + int r; + if(fno < 10 && ((r=sh_fcntl(fno,F_DUPFD,10))>=10)) + { + shp->fdstatus[r] = shp->fdstatus[fno]; + sh_close(fno); + fno = r; + } + fcntl(fno,F_SETFD,FD_CLOEXEC); + shp->fdstatus[fno] |= IOCLEX; + iop = sh_iostream((void*)shp,fno); + } + else + iop = sfstdin; + } + else + fno = -1; + shp->infd = fno; + if(sh_isstate(SH_INTERACTIVE)) + { + if(nv_isnull(PS1NOD)) + nv_putval(PS1NOD,(shp->gd->euserid?e_stdprompt:e_supprompt),NV_RDONLY); + sh_sigdone(); + if(sh_histinit((void*)shp)) + sh_onoption(SH_HISTORY); + } + else + { + if(!sh_isstate(SH_PROFILE)) + { + buff.mode = SH_JMPEXIT; + sh_onoption(SH_TRACKALL); + sh_offoption(SH_MONITOR); + } + sh_offstate(SH_INTERACTIVE); + sh_offstate(SH_MONITOR); + sh_offstate(SH_HISTORY); + sh_offoption(SH_HISTORY); + } + states = sh_getstate(); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval) + { + Sfio_t *top; + sh_iorestore((void*)shp,0,jmpval); + hist_flush(shp->gd->hist_ptr); + sfsync(shp->outpool); + shp->st.execbrk = shp->st.breakcnt = 0; + /* check for return from profile or env file */ + if(sh_isstate(SH_PROFILE) && (jmpval==SH_JMPFUN || jmpval==SH_JMPEXIT)) + { + sh_setstate(states); + goto done; + } + if(!sh_isoption(SH_INTERACTIVE) || sh_isstate(SH_FORKED) || (jmpval > SH_JMPERREXIT && job_close(shp) >=0)) + { + sh_offstate(SH_INTERACTIVE); + sh_offstate(SH_MONITOR); + goto done; + } + /* skip over remaining input */ + if(top = fcfile()) + { + while(fcget()>0); + fcclose(); + while(top=sfstack(iop,SF_POPSTACK)) + sfclose(top); + } + /* make sure that we own the terminal */ +#ifdef SIGTSTP + tcsetpgrp(job.fd,shp->gd->pid); +#endif /* SIGTSTP */ + } + /* error return here */ + sfclrerr(iop); + sh_setstate(states); + shp->st.optindex = 1; + opt_info.offset = 0; + shp->st.loopcnt = 0; + shp->trapnote = 0; + shp->intrap = 0; + error_info.line = 1; + shp->inlineno = 1; + shp->binscript = 0; + if(sfeof(iop)) + goto eof_or_error; + /* command loop */ + while(1) + { + shp->nextprompt = 1; + sh_freeup(shp); + stakset(NIL(char*),0); + exitset(); + sh_offstate(SH_STOPOK); + sh_offstate(SH_ERREXIT); + sh_offstate(SH_VERBOSE); + sh_offstate(SH_TIMING); + sh_offstate(SH_GRACE); + sh_offstate(SH_TTYWAIT); + if(sh_isoption(SH_VERBOSE)) + sh_onstate(SH_VERBOSE); + sh_onstate(SH_ERREXIT); + /* -eim flags don't apply to profiles */ + if(sh_isstate(SH_PROFILE)) + { + sh_offstate(SH_INTERACTIVE); + sh_offstate(SH_ERREXIT); + sh_offstate(SH_MONITOR); + } + if(sh_isstate(SH_INTERACTIVE) && !tdone) + { + register char *mail; +#ifdef JOBS + sh_offstate(SH_MONITOR); + if(sh_isoption(SH_MONITOR)) + sh_onstate(SH_MONITOR); + if(job.pwlist) + { + job_walk(sfstderr,job_list,JOB_NFLAG,(char**)0); + job_wait((pid_t)0); + } +#endif /* JOBS */ + if((mail=nv_getval(MAILPNOD)) || (mail=nv_getval(MAILNOD))) + { + time(&curtime); + if ((curtime - mailtime) >= sh_mailchk) + { + chkmail(shp,mail); + mailtime = curtime; + } + } + if(shp->gd->hist_ptr) + hist_eof(shp->gd->hist_ptr); + /* sets timeout for command entry */ + shp->timeout = shp->st.tmout; +#if SHOPT_TIMEOUT + if(shp->timeout <= 0 || shp->timeout > SHOPT_TIMEOUT) + shp->timeout = SHOPT_TIMEOUT; +#endif /* SHOPT_TIMEOUT */ + shp->inlineno = 1; + error_info.line = 1; + shp->exitval = 0; + shp->trapnote = 0; + if(buff.mode == SH_JMPEXIT) + { + buff.mode = SH_JMPERREXIT; +#ifdef DEBUG + errormsg(SH_DICT,ERROR_warn(0),"%d: mode changed to JMP_EXIT",getpid()); +#endif + } + } + errno = 0; + if(tdone || !sfreserve(iop,0,0)) + { + eof_or_error: + if(sh_isstate(SH_INTERACTIVE) && !sferror(iop)) + { + if(--maxtry>0 && sh_isoption(SH_IGNOREEOF) && + !sferror(sfstderr) && (shp->fdstatus[fno]&IOTTY)) + { + sfclrerr(iop); + errormsg(SH_DICT,0,e_logout); + continue; + } + else if(job_close(shp)<0) + continue; + } + if(errno==0 && sferror(iop) && --maxtry>0) + { + sfclrlock(iop); + sfclrerr(iop); + continue; + } + goto done; + } + maxtry = IOMAXTRY; + if(sh_isstate(SH_INTERACTIVE) && shp->gd->hist_ptr) + { + job_wait((pid_t)0); + hist_eof(shp->gd->hist_ptr); + sfsync(sfstderr); + } + if(sh_isoption(SH_HISTORY)) + sh_onstate(SH_HISTORY); + job.waitall = job.curpgid = 0; + error_info.flags |= ERROR_INTERACTIVE; + t = (Shnode_t*)sh_parse(shp,iop,0); + if(!sh_isstate(SH_INTERACTIVE) && !sh_isoption(SH_CFLAG)) + error_info.flags &= ~ERROR_INTERACTIVE; + shp->readscript = 0; + if(sh_isstate(SH_INTERACTIVE) && shp->gd->hist_ptr) + hist_flush(shp->gd->hist_ptr); + sh_offstate(SH_HISTORY); + if(t) + { + execflags = sh_state(SH_ERREXIT)|sh_state(SH_INTERACTIVE); + /* The last command may not have to fork */ + if(!sh_isstate(SH_PROFILE) && sh_isoption(SH_CFLAG) && + (fno<0 || !(shp->fdstatus[fno]&(IOTTY|IONOSEEK))) + && !sfreserve(iop,0,0)) + { + execflags |= sh_state(SH_NOFORK); + } + shp->st.execbrk = 0; + sh_exec(t,execflags); + if(shp->forked) + { + sh_offstate(SH_INTERACTIVE); + goto done; + } + /* This is for sh -t */ + if(sh_isoption(SH_TFLAG) && !sh_isstate(SH_PROFILE)) + tdone++; + } + } +done: + sh_popcontext(shp,&buff); + if(sh_isstate(SH_INTERACTIVE)) + { + sfputc(sfstderr,'\n'); + job_close(shp); + } + if(jmpval == SH_JMPSCRIPT) + siglongjmp(*shp->jmplist,jmpval); + else if(jmpval == SH_JMPEXIT) + sh_done(shp,0); + if(fno>0) + sh_close(fno); + if(shp->st.filename) + free((void*)shp->st.filename); + shp->st.filename = 0; +} + + +/* prints out messages if files in list have been modified since last call */ +static void chkmail(Shell_t *shp, char *files) +{ + register char *cp,*sp,*qp; + register char save; + struct argnod *arglist=0; + int offset = staktell(); + char *savstak=stakptr(0); + struct stat statb; + if(*(cp=files) == 0) + return; + sp = cp; + do + { + /* skip to : or end of string saving first '?' */ + for(qp=0;*sp && *sp != ':';sp++) + if((*sp == '?' || *sp=='%') && qp == 0) + qp = sp; + save = *sp; + *sp = 0; + /* change '?' to end-of-string */ + if(qp) + *qp = 0; + do + { + /* see if time has been modified since last checked + * and the access time <= the modification time + */ + if(stat(cp,&statb) >= 0 && statb.st_mtime >= mailtime + && statb.st_atime <= statb.st_mtime) + { + /* check for directory */ + if(!arglist && S_ISDIR(statb.st_mode)) + { + /* generate list of directory entries */ + path_complete(shp,cp,"/*",&arglist); + } + else + { + /* + * If the file has shrunk, + * or if the size is zero + * then don't print anything + */ + if(statb.st_size && + ( statb.st_ino != lastmail.st_ino + || statb.st_dev != lastmail.st_dev + || statb.st_size > lastmail.st_size)) + { + /* save and restore $_ */ + char *save = shp->lastarg; + shp->lastarg = cp; + errormsg(SH_DICT,0,sh_mactry(shp,qp?qp+1:(char*)e_mailmsg)); + shp->lastarg = save; + } + lastmail = statb; + break; + } + } + if(arglist) + { + cp = arglist->argval; + arglist = arglist->argchn.ap; + } + else + cp = 0; + } + while(cp); + if(qp) + *qp = '?'; + *sp++ = save; + cp = sp; + } + while(save); + stakset(savstak,offset); +} + +#undef EXECARGS +#undef PSTAT +#if defined(_hdr_execargs) && defined(pdp11) +# include <execargs.h> +# define EXECARGS 1 +#endif + +#if defined(_lib_pstat) && defined(_sys_pstat) +# include <sys/pstat.h> +# define PSTAT 1 +#endif + +#if defined(_lib_fork) && !defined(_NEXT_SOURCE) +/* + * fix up command line for ps command + * mode is 0 for initialization + */ +static void fixargs(char **argv, int mode) +{ +#if EXECARGS + *execargs=(char *)argv; +#else + static char *buff; + static int command_len; + register char *cp; + int offset=0,size; +# ifdef PSTAT + union pstun un; + if(mode==0) + { + struct pst_static st; + un.pst_static = &st; + if(pstat(PSTAT_STATIC, un, sizeof(struct pst_static), 1, 0)<0) + return; + command_len = st.command_length; + return; + } + stakseek(command_len+2); + buff = stakseek(0); +# else + if(mode==0) + { + buff = argv[0]; + while(cp = *argv++) + command_len += strlen(cp)+1; + if(environ && *environ==buff+command_len) + { + for(argv=environ; cp = *argv; cp++) + { + if(command_len > CMD_LENGTH) + { + command_len = CMD_LENGTH; + break; + } + *argv++ = strdup(cp); + command_len += strlen(cp)+1; + } + } + command_len -= 1; + return; + } +# endif /* PSTAT */ + if(command_len==0) + return; + while((cp = *argv++) && offset < command_len) + { + if(offset + (size=strlen(cp)) >= command_len) + size = command_len - offset; + memcpy(buff+offset,cp,size); + offset += size; + buff[offset++] = ' '; + } + buff[offset-1] = 0; +# ifdef PSTAT + un.pst_command = stakptr(0); + pstat(PSTAT_SETCMD,un,0,0,0); +# endif /* PSTAT */ +#endif /* EXECARGS */ +} +#endif /* _lib_fork */ |