void sh_applyopts(Shell_t *shp, Shopt_t newflags) { // Cannot set -n for interactive shells since there is no way out. if (sh_isoption(shp, SH_INTERACTIVE)) off_option(&newflags, SH_NOEXEC); if (is_option(&newflags, SH_PRIVILEGED)) on_option(&newflags, SH_NOUSRPROFILE); int is_privileged = is_option(&newflags, SH_PRIVILEGED) != sh_isoption(shp, SH_PRIVILEGED); int is_privileged_off = is_option(&(shp->arg_context)->sh->offoptions, SH_PRIVILEGED); if ((!sh_isstate(shp, SH_INIT) && is_privileged) || (sh_isstate(shp, SH_INIT) && is_privileged_off && shp->gd->userid != shp->gd->euserid)) { if (!is_option(&newflags, SH_PRIVILEGED)) { if (setuid(shp->gd->userid) < 0) { error(ERROR_system(0), "setuid(%d) failed", shp->gd->userid); return; } if (setgid(shp->gd->groupid) < 0) { error(ERROR_system(0), "setgid(%d) failed", shp->gd->groupid); return; } if (shp->gd->euserid == 0) { shp->gd->euserid = shp->gd->userid; shp->gd->egroupid = shp->gd->groupid; } } else if ((shp->gd->userid != shp->gd->euserid && setuid(shp->gd->euserid) < 0) || (shp->gd->groupid != shp->gd->egroupid && setgid(shp->gd->egroupid) < 0) || (shp->gd->userid == shp->gd->euserid && shp->gd->groupid == shp->gd->egroupid)) { off_option(&newflags, SH_PRIVILEGED); } } #if SHOPT_BASH on_option(&newflags, SH_CMDHIST); on_option(&newflags, SH_CHECKHASH); on_option(&newflags, SH_EXECFAIL); on_option(&newflags, SH_EXPAND_ALIASES); on_option(&newflags, SH_HISTAPPEND); on_option(&newflags, SH_INTERACTIVE_COMM); on_option(&newflags, SH_LITHIST); on_option(&newflags, SH_NOEMPTYCMDCOMPL); if (!is_option(&newflags, SH_XPG_ECHO) && sh_isoption(shp, SH_XPG_ECHO)) { astconf("UNIVERSE", 0, "ucb"); } if (is_option(&newflags, SH_XPG_ECHO) && !sh_isoption(shp, SH_XPG_ECHO)) { astconf("UNIVERSE", 0, "att"); } if (is_option(&newflags, SH_HISTORY2) && !sh_isoption(shp, SH_HISTORY2)) { sh_onstate(shp, SH_HISTORY); sh_onoption(shp, SH_HISTORY); } if (!is_option(&newflags, SH_HISTORY2) && sh_isoption(shp, SH_HISTORY2)) { sh_offstate(shp, SH_HISTORY); sh_offoption(shp, SH_HISTORY); } #endif shp->options = newflags; }
int b_return(register int n, register char *argv[],Shbltin_t *context) { register char *arg; register Shell_t *shp = context->shp; struct checkpt *pp = (struct checkpt*)shp->jmplist; const char *options = (**argv=='r'?sh_optreturn:sh_optexit); while((n = optget(argv,options))) switch(n) { case ':': if(!strmatch(argv[opt_info.index],"[+-]+([0-9])")) errormsg(SH_DICT,2, "%s", opt_info.arg); goto done; case '?': errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); return(2); } done: if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); pp->mode = (**argv=='e'?SH_JMPEXIT:SH_JMPFUN); argv += opt_info.index; n = (((arg= *argv)?(int)strtol(arg, (char**)0, 10):shp->oldexit)); if(n<0 || n==256 || n > SH_EXITMASK+shp->gd->sigmax) n &= ((unsigned int)n)&SH_EXITMASK; /* return outside of function, dotscript and profile is exit */ if(shp->fn_depth==0 && shp->dot_depth==0 && !sh_isstate(shp,SH_PROFILE)) pp->mode = SH_JMPEXIT; sh_exit(shp,shp->savexit=n); return(1); }
// // Builtin `bg`. // int b_bg(int n, char *argv[], Shbltin_t *context) { int flag = **argv; Shell_t *shp = context->shp; const char *optstr = sh_optbg; if (*argv[0] == 'f') { optstr = sh_optfg; } else if (*argv[0] == 'd') { optstr = sh_optdisown; } while ((n = optget(argv, optstr))) { switch (n) { case ':': { errormsg(SH_DICT, 2, "%s", opt_info.arg); break; } case '?': { errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); __builtin_unreachable(); } default: { break; } } } if (error_info.errors) { errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NULL)); __builtin_unreachable(); } argv += opt_info.index; if (!sh_isoption(shp, SH_MONITOR) || !job.jobcontrol) { if (sh_isstate(shp, SH_INTERACTIVE)) { errormsg(SH_DICT, ERROR_exit(1), e_no_jctl); __builtin_unreachable(); } return 1; } if (flag == 'd' && *argv == 0) argv = NULL; if (job_walk(shp, sfstdout, job_switch, flag, argv)) { errormsg(SH_DICT, ERROR_exit(1), e_no_job); __builtin_unreachable(); } return shp->exitval; }
// // Open the history file. If HISTNAME is not given and userid==0 then no history file. If login_sh // and HISTFILE is longer than HIST_MAX bytes then it is cleaned up. // // hist_open() returns 1, if history file is opened. // int sh_histinit(void *sh_context) { Shell_t *shp = sh_context; int fd; History_t *hp; char *histname; char *fname = NULL; int histmask, maxlines, hist_start = 0; char *cp; off_t hsize = 0; shgd->hist_ptr = hist_ptr; if (shgd->hist_ptr) return 1; if (!(histname = nv_getval(HISTFILE))) { int offset = stktell(shp->stk); cp = nv_getval(HOME); if (cp) sfputr(shp->stk, cp, -1); sfputr(shp->stk, hist_fname, 0); stkseek(shp->stk, offset); histname = stkptr(shp->stk, offset); } #if 0 // TODO: Figure out if this should be enabled. Originally excluded via `#ifdef future`. if (hp = wasopen) { // Reuse history file if same name. wasopen = 0; shgd->hist_ptr = hist_ptr = hp; if (strcmp(histname, hp->histname) == 0) { return 1; } else { hist_free(); } } #endif // future retry: cp = path_relative(shp, histname); if (!histinit) histmode = S_IRUSR | S_IWUSR; if ((fd = open(cp, O_BINARY | O_APPEND | O_RDWR | O_CREAT | O_CLOEXEC, histmode)) >= 0) { hsize = lseek(fd, (off_t)0, SEEK_END); } if ((unsigned)fd < 10) { int n; if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) { sh_close(fd); fd = n; } } // Make sure that file has history file format. if (hsize && hist_check(fd)) { sh_close(fd); hsize = 0; if (unlink(cp) >= 0) goto retry; fd = -1; } // Don't allow root a history_file in /tmp. if (fd < 0 && shgd->userid) { fname = ast_temp_file(NULL, NULL, &fd, O_APPEND | O_CLOEXEC); if (!fname) return 0; } if (fd < 0) return 0; // Set the file to close-on-exec. (void)fcntl(fd, F_SETFD, FD_CLOEXEC); cp = nv_getval(HISTSIZE); if (cp) { maxlines = (unsigned)strtol(cp, NULL, 10); } else { maxlines = HIST_DFLT; } for (histmask = 16; histmask <= maxlines; histmask <<= 1) { ; // empty loop } histmask -= 1; hp = calloc(1, sizeof(History_t) + histmask * sizeof(off_t)); if (!hp) { sh_close(fd); return 0; } shgd->hist_ptr = hist_ptr = hp; hp->histshell = shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp = sfnew(NULL, NULL, HIST_BSIZE, fd, SF_READ | SF_WRITE | SF_APPENDWR | SF_SHARE); hp->histind = 1; hp->histcmds[1] = 2; hp->histcnt = 2; hp->histname = strdup(histname); hp->histdisc = hist_disc; if (hsize == 0) { // Put special characters at front of file. sfwrite(hp->histfp, (char *)hist_stamp, 2); sfsync(hp->histfp); } else { // Initialize history list. int first, last; off_t mark, size = (HIST_MAX / 4) + maxlines * HIST_LINE; hp->histind = first = hist_nearend(hp, hp->histfp, hsize - size); histinit = 1; hist_eof(hp); // this sets histind to last command if ((hist_start = (last = (int)hp->histind) - maxlines) <= 0) hist_start = 1; mark = hp->histmarker; while (first > hist_start) { size += size; first = hist_nearend(hp, hp->histfp, hsize - size); hp->histind = first; } histinit = hist_start; hist_eof(hp); if (!histinit) { sfseek(hp->histfp, hp->histcnt = hsize, SEEK_SET); hp->histind = last; hp->histmarker = mark; } histinit = 0; } if (fname) { unlink(fname); free(fname); } if (hist_clean(fd) && hist_start > 1 && hsize > HIST_MAX) { #ifdef DEBUG sfprintf(sfstderr, "%d: hist_trim hsize=%d\n", getpid(), hsize); sfsync(sfstderr); #endif // DEBUG hp = hist_trim(hp, (int)hp->histind - maxlines); } sfdisc(hp->histfp, &hp->histdisc); STORE_VT((HISTCUR)->nvalue, i32p, &hp->histind); sh_timeradd(1000L * (HIST_RECENT - 30), 1, hist_touch, hp->histname); hp->auditfp = NULL; char buff[SF_BUFSIZE]; if (!sh_isstate(shp, SH_INTERACTIVE)) return 1; hp->auditmask = sh_checkaudit(hp, AUDIT_FILE, buff, sizeof(buff)); if (!hp->auditmask) return 1; if ((fd = sh_open(buff, O_BINARY | O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)) >= 0 && fd < 10) { int n; if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) { sh_close(fd); fd = n; } } if (fd >= 0) { (void)fcntl(fd, F_SETFD, FD_CLOEXEC); hp->tty = strdup(isatty(2) ? ttyname(2) : "notty"); hp->auditfp = sfnew(NULL, NULL, -1, fd, SF_WRITE); } return 1; }
int b_hist(int argc,char *argv[], void *extra) { register History_t *hp; register char *arg; register int flag,fdo; register Shell_t *shp = ((Shbltin_t*)extra)->shp; Sfio_t *outfile; char *fname; int range[2], incr, index2, indx= -1; char *edit = 0; /* name of editor */ char *replace = 0; /* replace old=new */ int lflag = 0, nflag = 0, rflag = 0; #if SHOPT_HISTEXPAND int pflag = 0; #endif Histloc_t location; NOT_USED(argc); if(!sh_histinit((void*)shp)) errormsg(SH_DICT,ERROR_system(1),e_histopen); hp = shp->gd->hist_ptr; while((flag = optget(argv,sh_opthist))) switch(flag) { case 'e': edit = opt_info.arg; break; case 'n': nflag++; break; case 'l': lflag++; break; case 'r': rflag++; break; case 's': edit = "-"; break; #if SHOPT_HISTEXPAND case 'p': pflag++; break; #endif case 'N': if(indx<=0) { if((flag = hist_max(hp) - opt_info.num-1) < 0) flag = 1; range[++indx] = flag; break; } case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); break; case '?': errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); break; } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); argv += (opt_info.index-1); #if SHOPT_HISTEXPAND if(pflag) { hist_cancel(hp); pflag = 0; while(arg=argv[1]) { flag = hist_expand(arg,&replace); if(!(flag & HIST_ERROR)) sfputr(sfstdout, replace, '\n'); else pflag = 1; if(replace) free(replace); argv++; } return pflag; } #endif flag = indx; while(flag<1 && (arg=argv[1])) { /* look for old=new argument */ if(!replace && strchr(arg+1,'=')) { replace = arg; argv++; continue; } else if(isdigit(*arg) || *arg == '-') { /* see if completely numeric */ do arg++; while(isdigit(*arg)); if(*arg==0) { arg = argv[1]; range[++flag] = (int)strtol(arg, (char**)0, 10); if(*arg == '-') range[flag] += (hist_max(hp)-1); argv++; continue; } } /* search for last line starting with string */ location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1); if((range[++flag] = location.hist_command) < 0) errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]); argv++; } if(flag <0) { /* set default starting range */ if(lflag) { flag = hist_max(hp)-16; if(flag<1) flag = 1; } else flag = hist_max(hp)-2; range[0] = flag; flag = 0; } index2 = hist_min(hp); if(range[0]<index2) range[0] = index2; if(flag==0) /* set default termination range */ range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]); if(range[1]>=(flag=(hist_max(hp) - !lflag))) range[1] = flag; /* check for valid ranges */ if(range[1]<index2 || range[0]>=flag) errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]); if(edit && *edit=='-' && range[0]!=range[1]) errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg); /* now list commands from range[rflag] to range[1-rflag] */ incr = 1; flag = rflag>0; if(range[1-flag] < range[flag]) incr = -1; if(lflag) { outfile = sfstdout; arg = "\n\t"; } else { if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*)))) errormsg(SH_DICT,ERROR_exit(1),e_create,""); if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0) errormsg(SH_DICT,ERROR_system(1),e_create,fname); outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE); arg = "\n"; nflag++; } while(1) { if(nflag==0) sfprintf(outfile,"%d\t",range[flag]); else if(lflag) sfputc(outfile,'\t'); hist_list(shp->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg); if(lflag) sh_sigcheck(shp); if(range[flag] == range[1-flag]) break; range[flag] += incr; } if(lflag) return(0); sfclose(outfile); hist_eof(hp); arg = edit; if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD)))) arg = (char*)e_defedit; #ifdef apollo /* * Code to support the FC using the pad editor. * Exampled of how to use: HISTEDIT=pad */ if (strcmp (arg, "pad") == 0) { extern int pad_create(char*); sh_close(fdo); fdo = pad_create(fname); pad_wait(fdo); unlink(fname); strcat(fname, ".bak"); unlink(fname); lseek(fdo,(off_t)0,SEEK_SET); } else { #endif /* apollo */ if(*arg != '-') { char *com[3]; com[0] = arg; com[1] = fname; com[2] = 0; error_info.errors = sh_eval(sh_sfeval(com),0); } fdo = sh_chkopen(fname); unlink(fname); free((void*)fname); #ifdef apollo } #endif /* apollo */ /* don't history fc itself unless forked */ error_info.flags |= ERROR_SILENT; if(!sh_isstate(SH_FORKED)) hist_cancel(hp); sh_onstate(SH_HISTORY); sh_onstate(SH_VERBOSE); /* echo lines as read */ if(replace) hist_subst(error_info.id,fdo,replace); else if(error_info.errors == 0) { char buff[IOBSIZE+1]; Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); /* read in and run the command */ if(shp->hist_depth++ > HIST_RECURSE) errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); sh_eval(iop,1); shp->hist_depth--; }
struct argnod *sh_argprocsub(Shell_t *shp, struct argnod *argp) { // Argument of the form <(cmd) or >(cmd). struct argnod *ap; int nn, monitor, fd, pv[3]; int subshell = shp->subshell; pid_t pid0; ap = (struct argnod *)stkseek(shp->stk, ARGVAL); ap->argflag |= ARG_MAKE; ap->argflag &= ~ARG_RAW; fd = argp->argflag & ARG_RAW; if (fd == 0 && shp->subshell) sh_subtmpfile(shp); #if has_dev_fd sfwrite(shp->stk, e_devfdNN, 8); pv[2] = 0; sh_pipe(pv); sfputr(shp->stk, fmtbase((long)pv[fd], 10, 0), 0); #else // has_dev_fd pv[0] = -1; shp->fifo = ast_temp_path("ksh.fifo"); if (mkfifo(shp->fifo, S_IRUSR | S_IWUSR)) abort(); sfputr(shp->stk, shp->fifo, 0); #endif // has_dev_fd ap = (struct argnod *)stkfreeze(shp->stk, 0); shp->inpipe = shp->outpipe = NULL; monitor = (sh_isstate(shp, SH_MONITOR) != 0); if (monitor) sh_offstate(shp, SH_MONITOR); shp->subshell = 0; #if has_dev_fd #if USE_SPAWN if (shp->vex || (shp->vex = spawnvex_open(0))) { spawnvex_add(shp->vex, pv[fd], pv[fd], 0, 0); } else #endif // USE_SPAWN fcntl(pv[fd], F_SETFD, 0); shp->fdstatus[pv[fd]] &= ~IOCLEX; #endif // has_dev_fd pid0 = shp->procsub ? *shp->procsub : 0; if (fd) { if (!shp->procsub) { shp->nprocsub = 4; shp->procsub = procsub = calloc(1, shp->nprocsub * sizeof(pid_t)); } else { nn = procsub - shp->procsub; if (nn >= shp->nprocsub) { shp->nprocsub += 3; shp->procsub = realloc(shp->procsub, shp->nprocsub * sizeof(pid_t)); procsub = shp->procsub + nn; } } if (pid0) *shp->procsub = 0; shp->inpipe = pv; sh_exec(shp, (Shnode_t *)argp->argchn.ap, (int)sh_isstate(shp, SH_ERREXIT)); if (pid0) *shp->procsub = pid0; *procsub++ = job.lastpost; } else { shp->outpipe = pv; sh_exec(shp, (Shnode_t *)argp->argchn.ap, (int)sh_isstate(shp, SH_ERREXIT)); } shp->subshell = subshell; if (monitor) sh_onstate(shp, SH_MONITOR); #if has_dev_fd sh_close(pv[1 - fd]); sh_iosave(shp, -pv[fd], shp->topfd, NULL); #else free(shp->fifo); shp->fifo = NULL; #endif // has_dev_fd return ap; }
/* * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set * Use sfpkrd() to poll() or select() to wait for input if possible * Unfortunately, systems that get interrupted from slow reads update * this access time for for the terminal (in violation of POSIX). * The fixtime() macro, resets the time to the time at entry in * this case. This is not necessary for systems that can handle * sfpkrd() correctly (i,e., those that support poll() or select() */ int ed_read(void *context, int fd, char *buff, int size, int reedit) { register Edit_t *ep = (Edit_t*)context; register int rv= -1; register int delim = ((ep->e_raw&RAWMODE)?nttyparm.c_cc[VEOL]:'\n'); Shell_t *shp = ep->sh; int mode = -1; int (*waitevent)(int,long,int) = shp->gd->waitevent; if(ep->e_raw==ALTMODE) mode = 1; if(size < 0) { mode = 1; size = -size; } sh_onstate(SH_TTYWAIT); errno = EINTR; shp->gd->waitevent = 0; while(rv<0 && errno==EINTR) { if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP)) goto done; if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS))) { Edpos_t lastpos; int n, rows, newsize; /* move cursor to start of first line */ ed_putchar(ep,'\r'); ed_flush(ep); astwinsize(2,&rows,&newsize); n = (ep->e_plen+ep->e_cur)/++ep->e_winsz; while(n--) ed_putstring(ep,CURSOR_UP); if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz)) { /* clear the current command line */ n = lastpos.line; while(lastpos.line--) { ed_nputchar(ep,ep->e_winsz,' '); ed_putchar(ep,'\n'); } ed_nputchar(ep,ep->e_winsz,' '); while(n--) ed_putstring(ep,CURSOR_UP); } ep->sh->winch = 0; ed_flush(ep); sh_delay(.05); astwinsize(2,&rows,&newsize); ep->e_winsz = newsize-1; if(ep->e_winsz < MINWINDOW) ep->e_winsz = MINWINDOW; if(!ep->e_multiline && ep->e_wsize < MAXLINE) ep->e_wsize = ep->e_winsz-2; ep->e_nocrnl=1; if(*ep->e_vi_insert) { buff[0] = ESC; buff[1] = cntl('L'); buff[2] = 'a'; return(3); } if(sh_isoption(SH_EMACS) || sh_isoption(SH_VI)) buff[0] = cntl('L'); return(1); } else ep->sh->winch = 0; /* an interrupt that should be ignored */ errno = 0; if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0) rv = sfpkrd(fd,buff,size,delim,-1L,mode); } if(rv < 0) { #ifdef _hdr_utime # define fixtime() if(isdevtty)utime(ep->e_tty,&utimes) int isdevtty=0; struct stat statb; struct utimbuf utimes; if(errno==0 && !ep->e_tty) { if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0) { ep->e_tty_ino = statb.st_ino; ep->e_tty_dev = statb.st_dev; } } if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev) { utimes.actime = statb.st_atime; utimes.modtime = statb.st_mtime; isdevtty=1; } #else # define fixtime() #endif /* _hdr_utime */ while(1) { rv = read(fd,buff,size); if(rv>=0 || errno!=EINTR) break; if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP)) goto done; /* an interrupt that should be ignored */ fixtime(); } } else if(rv>=0 && mode>0) rv = read(fd,buff,rv>0?rv:1); done: shp->gd->waitevent = waitevent; sh_offstate(SH_TTYWAIT); return(rv); }
static int whence(Shell_t *shp,char **argv, register int flags) { register const char *name; register Namval_t *np; register const char *cp; register int aflag,r=0; register const char *msg; int tofree; Dt_t *root; Namval_t *nq; char *notused; Pathcomp_t *pp=0; int notrack = 1; if(flags&Q_FLAG) flags &= ~A_FLAG; while(name= *argv++) { tofree=0; aflag = ((flags&A_FLAG)!=0); cp = 0; np = 0; if(flags&P_FLAG) goto search; if(flags&Q_FLAG) goto bltins; /* reserved words first */ if(sh_lookup(name,shtab_reserved)) { sfprintf(sfstdout,"%s%s\n",name,(flags&V_FLAG)?sh_translate(is_reserved):""); if(!aflag) continue; aflag++; } /* non-tracked aliases */ if((np=nv_search(name,shp->alias_tree,0)) && !nv_isnull(np) && !(notrack=nv_isattr(np,NV_TAGGED)) && (cp=nv_getval(np))) { if(flags&V_FLAG) { if(nv_isattr(np,NV_EXPORT)) msg = sh_translate(is_xalias); else msg = sh_translate(is_alias); sfprintf(sfstdout,msg,name); } sfputr(sfstdout,sh_fmtq(cp),'\n'); if(!aflag) continue; cp = 0; aflag++; } /* built-ins and functions next */ bltins: root = (flags&F_FLAG)?shp->bltin_tree:shp->fun_tree; if(np= nv_bfsearch(name, root, &nq, ¬used)) { if(is_abuiltin(np) && nv_isnull(np)) goto search; cp = ""; if(flags&V_FLAG) { if(nv_isnull(np)) cp = sh_translate(is_ufunction); else if(is_abuiltin(np)) { if(nv_isattr(np,BLT_SPC)) cp = sh_translate(is_spcbuiltin); else cp = sh_translate(is_builtin); } else cp = sh_translate(is_function); } if(flags&Q_FLAG) continue; sfprintf(sfstdout,"%s%s\n",name,cp); if(!aflag) continue; cp = 0; aflag++; } search: if(sh_isstate(SH_DEFPATH)) { cp=0; notrack=1; } do { if(path_search(shp,name,&pp,2+(aflag>1))) { cp = name; if((flags&P_FLAG) && *cp!='/') cp = 0; } else { cp = stakptr(PATH_OFFSET); if(*cp==0) cp = 0; else if(*cp!='/') { cp = path_fullname(shp,cp); tofree=1; } } if(flags&Q_FLAG) { pp = 0; r |= !cp; } else if(cp) { if(flags&V_FLAG) { if(*cp!= '/') { if(!np && (np=nv_search(name,shp->track_tree,0))) sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),path_pwd(shp,0),cp); else if(!np || nv_isnull(np)) sfprintf(sfstdout,"%s%s\n",name,sh_translate(is_ufunction)); continue; } sfputr(sfstdout,sh_fmtq(name),' '); /* built-in version of program */ if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) msg = sh_translate(is_builtver); /* tracked aliases next */ else if(aflag>1 || !notrack || strchr(name,'/')) msg = sh_translate("is"); else msg = sh_translate(is_talias); sfputr(sfstdout,msg,' '); } sfputr(sfstdout,sh_fmtq(cp),'\n'); if(aflag) { if(aflag<=1) aflag++; if (pp) pp = pp->next; } else pp = 0; if(tofree) { free((char*)cp); tofree = 0; } } else if(aflag<=1) { r |= 1; if(flags&V_FLAG) errormsg(SH_DICT,ERROR_exit(0),e_found,sh_fmtq(name)); } } while(pp); } return(r); }
/* * open the history file * if HISTNAME is not given and userid==0 then no history file. * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is * cleaned up. * hist_open() returns 1, if history file is open */ int sh_histinit(void *sh_context) { Shell_t *shp = (Shell_t*)sh_context; register int fd; register History_t *hp; register char *histname; char *fname=0; int histmask, maxlines, hist_start=0; register char *cp; register off_t hsize = 0; if(shgd->hist_ptr=hist_ptr) return(1); if(!(histname = nv_getval(HISTFILE))) { int offset = staktell(); if(cp=nv_getval(HOME)) stakputs(cp); stakputs(hist_fname); stakputc(0); stakseek(offset); histname = stakptr(offset); } #ifdef future if(hp=wasopen) { /* reuse history file if same name */ wasopen = 0; shgd->hist_ptr = hist_ptr = hp; if(strcmp(histname,hp->histname)==0) return(1); else hist_free(); } #endif retry: cp = path_relative(shp,histname); if(!histinit) histmode = S_IRUSR|S_IWUSR; if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) { hsize=lseek(fd,(off_t)0,SEEK_END); } if((unsigned)fd <=2) { int n; if((n=fcntl(fd,F_DUPFD,10))>=0) { close(fd); fd=n; } } /* make sure that file has history file format */ if(hsize && hist_check(fd)) { close(fd); hsize = 0; if(unlink(cp)>=0) goto retry; fd = -1; } if(fd < 0) { #if KSHELL /* don't allow root a history_file in /tmp */ if(shgd->userid) #endif /* KSHELL */ { if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) return(0); fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); } } if(fd<0) return(0); /* set the file to close-on-exec */ fcntl(fd,F_SETFD,FD_CLOEXEC); if(cp=nv_getval(HISTSIZE)) maxlines = (unsigned)strtol(cp, (char**)0, 10); else maxlines = HIST_DFLT; for(histmask=16;histmask <= maxlines; histmask <<=1 ); if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) { close(fd); return(0); } shgd->hist_ptr = hist_ptr = hp; hp->histshell = (void*)shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); hp->histind = 1; hp->histcmds[1] = 2; hp->histcnt = 2; hp->histname = strdup(histname); hp->histdisc = hist_disc; if(hsize==0) { /* put special characters at front of file */ sfwrite(hp->histfp,(char*)hist_stamp,2); sfsync(hp->histfp); } /* initialize history list */ else { int first,last; off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); histinit = 1; hist_eof(hp); /* this sets histind to last command */ if((hist_start = (last=(int)hp->histind)-maxlines) <=0) hist_start = 1; mark = hp->histmarker; while(first > hist_start) { size += size; first = hist_nearend(hp,hp->histfp,hsize-size); hp->histind = first; } histinit = hist_start; hist_eof(hp); if(!histinit) { sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); hp->histind = last; hp->histmarker = mark; } histinit = 0; } if(fname) { unlink(fname); free((void*)fname); } if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) { #ifdef DEBUG sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); sfsync(sfstderr); #endif /* DEBUG */ hp = hist_trim(hp,(int)hp->histind-maxlines); } sfdisc(hp->histfp,&hp->histdisc); #if KSHELL (HISTCUR)->nvalue.lp = (&hp->histind); #endif /* KSHELL */ sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); #if SHOPT_ACCTFILE if(sh_isstate(SH_INTERACTIVE)) acctinit(hp); #endif /* SHOPT_ACCTFILE */ #if SHOPT_AUDIT { char buff[SF_BUFSIZE]; hp->auditfp = 0; if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) { if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) { int n; if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) { sh_close(fd); fd = n; } } if(fd>=0) { fcntl(fd,F_SETFD,FD_CLOEXEC); hp->tty = strdup(ttyname(2)); hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); } } } #endif return(1); }
void bash_init(int mode) { Shell_t *shp = &sh; Sfio_t *iop; Namval_t *np; int n=0,xtrace,verbose; if(mode>0) goto reinit; if(mode < 0) { /* termination code */ if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX)) sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout)); return; } if(sh_isstate(SH_PREINIT)) { /* pre-init stage */ if(sh_isoption(SH_RESTRICTED)) sh_onoption(SH_RESTRICTED2); sh_onoption(SH_HISTORY2); sh_onoption(SH_INTERACTIVE_COMM); sh_onoption(SH_SOURCEPATH); sh_onoption(SH_HISTAPPEND); sh_onoption(SH_CMDHIST); sh_onoption(SH_LITHIST); sh_onoption(SH_NOEMPTYCMDCOMPL); if(shp->login_sh==2) sh_onoption(SH_LOGIN_SHELL); if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0) sh_onoption(SH_POSIX); if(strcmp(astconf("UNIVERSE",0,0),"att")==0) sh_onoption(SH_XPG_ECHO); else sh_offoption(SH_XPG_ECHO); if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0) sh_onoption(SH_PHYSICAL); else sh_offoption(SH_PHYSICAL); /* add builtins */ sh_addbuiltin("shopt", b_shopt, &sh); /* set up some variables needed for --version * needs to go here because --version option is parsed before the init script. */ if(np=nv_open("HOSTTYPE",shp->var_tree,0)) nv_putval(np, BASH_HOSTTYPE, NV_NOFREE); if(np=nv_open("MACHTYPE",shp->var_tree,0)) nv_putval(np, BASH_MACHTYPE, NV_NOFREE); if(np=nv_open("BASH_VERSION",shp->var_tree,0)) nv_putval(np, BASH_VERSION, NV_NOFREE); if(np=nv_open("BASH_VERSINFO",shp->var_tree,0)) { char *argv[7]; argv[0] = BASH_MAJOR; argv[1] = BASH_MINOR; argv[2] = BASH_PATCH; argv[3] = BASH_BUILD; argv[4] = BASH_RELEASE; argv[5] = BASH_MACHTYPE; argv[6] = 0; nv_setvec(np, 0, 6, argv); nv_onattr(np,NV_RDONLY); } return; } /* rest of init stage */ /* restrict BASH_ENV */ if(np=nv_open("BASH_ENV",shp->var_tree,0)) { const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT); Namfun_t *fp = calloc(dp->dsize,1); fp->disc = dp; nv_disc(np, fp, 0); } /* open GLOBIGNORE node */ if(np=nv_open("GLOBIGNORE",shp->var_tree,0)) { const Namdisc_t *dp = &SH_GLOBIGNORE_disc; Namfun_t *fp = calloc(dp->dsize,1); fp->disc = dp; nv_disc(np, fp, 0); } /* set startup files */ n=0; if(sh_isoption(SH_LOGIN_SHELL)) { if(!sh_isoption(SH_POSIX)) { login_files[n++] = (char*)e_bash_profile; login_files[n++] = (char*)e_bash_login; } login_files[n++] = (char*)e_profile; } shp->login_files = login_files; reinit: xtrace = sh_isoption(SH_XTRACE); sh_offoption(SH_XTRACE); verbose = sh_isoption(SH_VERBOSE); sh_offoption(SH_VERBOSE); if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD)) nv_offattr(np,NV_RDONLY); iop = sfopen(NULL, bash_pre_rc, "s"); sh_eval(iop,0); if(xtrace) sh_offoption(SH_XTRACE); if(verbose) sh_offoption(SH_VERBOSE); }
int b_dot_cmd(int n, char *argv[], Shbltin_t *context) { char *script; Namval_t *np; int jmpval; Shell_t *shp = context->shp; struct sh_scoped savst, *prevscope = shp->st.self; int fd; char *filename = NULL; char *buffer = NULL; struct dolnod *saveargfor = NULL; volatile struct dolnod *argsave = NULL; checkpt_t buff; Sfio_t *iop = NULL; short level; Optdisc_t disc; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; opt_info.disc = &disc; while ((n = optget(argv, sh_optdot))) { switch (n) { case ':': { errormsg(SH_DICT, 2, "%s", opt_info.arg); break; } case '?': { errormsg(SH_DICT, ERROR_usage(0), "%s", opt_info.arg); return 2; } default: { break; } } } argv += opt_info.index; script = *argv; if (error_info.errors || !script) { errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NULL)); __builtin_unreachable(); } if (shp->dot_depth + 1 > DOTMAX) { errormsg(SH_DICT, ERROR_exit(1), e_toodeep, script); __builtin_unreachable(); } np = shp->posix_fun; if (!np) { // Check for KornShell style function first. np = nv_search(script, shp->fun_tree, 0); if (np && is_afunction(np) && !nv_isattr(np, NV_FPOSIX)) { if (!FETCH_VT(np->nvalue, ip)) { // TODO: Replace this with a comment explaining why the return value of this // path_search() call is ignored. At the time I wrote this (2019-03-16) no unit test // exercises this statement. I added the void cast to silence Coverity Scan 253792. (void)path_search(shp, script, NULL, 0); if (FETCH_VT(np->nvalue, ip)) { if (nv_isattr(np, NV_FPOSIX)) np = NULL; } else { errormsg(SH_DICT, ERROR_exit(1), e_found, script); __builtin_unreachable(); } } } else { np = NULL; } if (!np) { fd = path_open(shp, script, path_get(shp, script)); if (fd < 0) { errormsg(SH_DICT, ERROR_system(1), e_open, script); __builtin_unreachable(); } filename = path_fullname(shp, stkptr(shp->stk, PATH_OFFSET)); } } *prevscope = shp->st; shp->st.lineno = np ? ((struct functnod *)nv_funtree(np))->functline : 1; shp->st.var_local = shp->st.save_tree = shp->var_tree; if (filename) { shp->st.filename = filename; shp->st.lineno = 1; } level = shp->fn_depth + shp->dot_depth + 1; nv_putval(SH_LEVELNOD, (char *)&level, NV_INT16); shp->st.prevst = prevscope; shp->st.self = &savst; shp->topscope = (Shscope_t *)shp->st.self; prevscope->save_tree = shp->var_tree; if (np) { struct Ufunction *rp = FETCH_VT(np->nvalue, rp); shp->st.filename = strdup(rp->fname ? rp->fname : ""); } nv_putval(SH_PATHNAMENOD, shp->st.filename, NV_NOFREE); shp->posix_fun = NULL; if (np || argv[1]) argsave = sh_argnew(shp, argv, &saveargfor); sh_pushcontext(shp, &buff, SH_JMPDOT); jmpval = sigsetjmp(buff.buff, 0); if (jmpval == 0) { shp->dot_depth++; if (np) { sh_exec(shp, (Shnode_t *)(nv_funtree(np)), sh_isstate(shp, SH_ERREXIT)); } else { buffer = malloc(IOBSIZE + 1); iop = sfnew(NULL, buffer, IOBSIZE, fd, SF_READ); sh_offstate(shp, SH_NOFORK); sh_eval(shp, iop, sh_isstate(shp, SH_PROFILE) ? SH_FUNEVAL : 0); } } sh_popcontext(shp, &buff); if (buffer) free(buffer); if (!np) { free(shp->st.filename); shp->st.filename = NULL; } shp->dot_depth--; if ((np || argv[1]) && jmpval != SH_JMPSCRIPT) { sh_argreset(shp, (struct dolnod *)argsave, saveargfor); } else { prevscope->dolc = shp->st.dolc; prevscope->dolv = shp->st.dolv; } if (shp->st.self != &savst) *shp->st.self = shp->st; // Only restore the top Shscope_t portion for posix functions. memcpy(&shp->st, prevscope, sizeof(Shscope_t)); shp->topscope = (Shscope_t *)prevscope; nv_putval(SH_PATHNAMENOD, shp->st.filename, NV_NOFREE); if (jmpval && jmpval != SH_JMPFUN) siglongjmp(shp->jmplist->buff, jmpval); return shp->exitval; }
// // mode = 0: init, called two times // before parsing shell args with SH_PREINIT state turned on // second time after sh_init() is through and with SH_PREINIT state turned off // mode > 1: re-init // mode < 0: shutdown // void bash_init(Shell_t *shp, int mode) { Sfio_t *iop; Namval_t *np; int n = 0, xtrace, verbose; if (mode > 0) goto reinit; if (mode < 0) { // termination code if (sh_isoption(shp, SH_LOGIN_SHELL) && !sh_isoption(shp, SH_POSIX)) { sh_source(shp, NULL, sh_mactry(shp, (char *)e_bash_logout)); } return; } if (sh_isstate(shp, SH_PREINIT)) { // pre-init stage if (sh_isoption(shp, SH_RESTRICTED)) sh_onoption(shp, SH_RESTRICTED2); sh_onoption(shp, SH_HISTORY2); sh_onoption(shp, SH_INTERACTIVE_COMM); sh_onoption(shp, SH_SOURCEPATH); sh_onoption(shp, SH_HISTAPPEND); sh_onoption(shp, SH_CMDHIST); sh_onoption(shp, SH_LITHIST); sh_onoption(shp, SH_NOEMPTYCMDCOMPL); sh_onoption(shp, SH_POSIX); if (shp->login_sh == 2) sh_onoption(shp, SH_LOGIN_SHELL); if (strcmp(astconf("UNIVERSE", 0, 0), "att") == 0) { sh_onoption(shp, SH_XPG_ECHO); } else { sh_offoption(shp, SH_XPG_ECHO); } sh_offoption(shp, SH_PHYSICAL); // Add builtins. sh_addbuiltin(shp, "shopt", b_shopt, &sh); sh_addbuiltin(shp, "enable", b_builtin, &sh); // Set up some variables needed for --version. // Needs to go here because --version option is parsed before the init script. #if 0 /* This was causing a core dump when running set to display all variables */ if(np=nv_open("HOSTTYPE",shp->var_tree,0)) nv_putval(np, BASH_HOSTTYPE, NV_NOFREE); #endif np = nv_open("MACHTYPE", shp->var_tree, 0); if (np) nv_putval(np, BASH_MACHTYPE, NV_NOFREE); np = nv_open("BASH_VERSION", shp->var_tree, 0); if (np) nv_putval(np, BASH_VERSION, NV_NOFREE); np = nv_open("BASH_VERSINFO", shp->var_tree, 0); if (np) { char *argv[7]; argv[0] = BASH_MAJOR; argv[1] = BASH_MINOR; argv[2] = BASH_PATCH; argv[3] = BASH_BUILD; argv[4] = BASH_RELEASE; argv[5] = BASH_MACHTYPE; argv[6] = 0; nv_setvec(np, 0, 6, argv); nv_onattr(np, NV_RDONLY); } return; } // Rest of init stage. // Restrict BASH_ENV. np = nv_open("BASH_ENV", shp->var_tree, 0); if (np) { const Namdisc_t *dp = nv_discfun(DISCFUN_RESTRICT); Namfun_t *fp = calloc(dp->dsize, 1); fp->disc = dp; nv_disc(np, fp, DISC_NOOP); } // Open GLOBIGNORE node. np = nv_open("GLOBIGNORE", shp->var_tree, 0); if (np) { const Namdisc_t *dp = &SH_GLOBIGNORE_disc; Namfun_t *fp = calloc(dp->dsize, 1); fp->disc = dp; nv_disc(np, fp, DISC_NOOP); } np = nv_open("BASH_EXECUTION_STRING", shp->var_tree, 0); if (np) { np->nvalue.cp = shp->comdiv; nv_onattr(np, NV_NOFREE); } // Set startup files. n = 0; if (sh_isoption(shp, SH_LOGIN_SHELL)) { if (!sh_isoption(shp, SH_POSIX)) { shp->gd->login_files[n++] = (char *)e_bash_profile; shp->gd->login_files[n++] = (char *)e_bash_login; } shp->gd->login_files[n++] = (char *)e_profile; } shp->gd->login_files = login_files; reinit: xtrace = sh_isoption(shp, SH_XTRACE); sh_offoption(shp, SH_XTRACE); verbose = sh_isoption(shp, SH_VERBOSE); sh_offoption(shp, SH_VERBOSE); np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD); if (np) nv_offattr(np, NV_RDONLY); iop = sfopen(NULL, bash_pre_rc, "s"); sh_eval(shp, iop, 0); if (xtrace) sh_offoption(shp, SH_XTRACE); if (verbose) sh_offoption(shp, SH_VERBOSE); }
int path_expand(Shell_t *shp, const char *pattern, struct argnod **arghead) { glob_t gdata; struct argnod *ap; glob_t *gp = &gdata; int flags, extra = 0; #if SHOPT_BASH int off; char *sp, *cp, *cp2; #endif sh_stats(STAT_GLOBS); memset(gp, 0, sizeof(gdata)); flags = GLOB_GROUP | GLOB_AUGMENTED | GLOB_NOCHECK | GLOB_NOSORT | GLOB_STACK | GLOB_LIST | GLOB_DISC; if (sh_isoption(shp, SH_MARKDIRS)) flags |= GLOB_MARK; if (sh_isoption(shp, SH_GLOBSTARS)) flags |= GLOB_STARSTAR; #if SHOPT_BASH #if 0 if(sh_isoption(shp,SH_BASH) && !sh_isoption(shp,SH_EXTGLOB)) flags &= ~GLOB_AUGMENTED; #endif if (sh_isoption(shp, SH_NULLGLOB)) flags &= ~GLOB_NOCHECK; if (sh_isoption(shp, SH_NOCASEGLOB)) flags |= GLOB_ICASE; #endif if (sh_isstate(shp, SH_COMPLETE)) { extra += scantree(shp, shp->alias_tree, pattern, arghead); extra += scantree(shp, shp->fun_tree, pattern, arghead); gp->gl_nextdir = nextdir; flags |= GLOB_COMPLETE; flags &= ~GLOB_NOCHECK; } #if SHOPT_BASH off = stktell(shp->stk); if (off) sp = stkfreeze(shp->stk, 0); if (sh_isoption(shp, SH_BASH)) { // For bash, FIGNORE is a colon separated list of suffixes to ignore // when doing filename/command completion. GLOBIGNORE is similar to ksh // FIGNORE, but colon separated instead of being an augmented shell // pattern. Generate shell patterns out of those here. if (sh_isstate(shp, SH_FCOMPLETE)) { cp = nv_getval(sh_scoped(shp, FIGNORENOD)); } else { static Namval_t *GLOBIGNORENOD; if (!GLOBIGNORENOD) GLOBIGNORENOD = nv_open("GLOBIGNORE", shp->var_tree, 0); cp = nv_getval(sh_scoped(shp, GLOBIGNORENOD)); } if (cp) { flags |= GLOB_AUGMENTED; sfputr(shp->stk, "@(", -1); if (!sh_isstate(shp, SH_FCOMPLETE)) { sfputr(shp->stk, cp, -1); for (cp = stkptr(shp->stk, off); *cp; cp++) { if (*cp == ':') *cp = '|'; } } else { cp2 = strtok(cp, ":"); if (!cp2) cp2 = cp; do { sfputc(shp->stk, '*'); sfputr(shp->stk, cp2, -1); cp2 = strtok(NULL, ":"); if (cp2) { *(cp2 - 1) = ':'; sfputc(shp->stk, '|'); } } while (cp2); } sfputc(shp->stk, ')'); gp->gl_fignore = stkfreeze(shp->stk, 1); } else if (!sh_isstate(shp, SH_FCOMPLETE) && sh_isoption(shp, SH_DOTGLOB)) { gp->gl_fignore = ""; } } else #endif gp->gl_fignore = nv_getval(sh_scoped(shp, FIGNORENOD)); if (suflen) gp->gl_suffix = sufstr; gp->gl_intr = &shp->trapnote; suflen = 0; if (strncmp(pattern, "~(N", 3) == 0) flags &= ~GLOB_NOCHECK; ast_glob(pattern, flags, 0, gp); #if SHOPT_BASH if (off) { stkset(shp->stk, sp, off); } else { stkseek(shp->stk, 0); } #endif sh_sigcheck(shp); for (ap = (struct argnod *)gp->gl_list; ap; ap = ap->argnxt.ap) { ap->argchn.ap = ap->argnxt.ap; if (!ap->argnxt.ap) ap->argchn.ap = *arghead; } if (gp->gl_list) *arghead = (struct argnod *)gp->gl_list; return gp->gl_pathc + extra; }