int B_login(int argc,char *argv[],void *extra) { struct checkpt *pp; register struct login *logp=0; register Shell_t *shp; const char *pname; if(argc) shp = ((Shbltin_t*)extra)->shp; else { logp = (struct login*)extra; shp = logp->sh; } pp = (struct checkpt*)shp->jmplist; if(sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[0]); else { register struct argnod *arg=shp->envlist; register Namval_t* np; register char *cp; if(shp->subshell && !shp->subshare) sh_subfork(); if(logp && logp->clear) { #ifdef _ENV_H env_close(shp->env); shp->env = env_open((char**)0,3); #else nv_scan(shp->var_tree,noexport,0,NV_EXPORT,NV_EXPORT); #endif } while(arg) { if((cp=strchr(arg->argval,'=')) && (*cp=0,np=nv_search(arg->argval,shp->var_tree,0))) { nv_onattr(np,NV_EXPORT); sh_envput(shp->env,np); } if(cp) *cp = '='; arg=arg->argnxt.ap; } pname = argv[0]; if(logp && logp->arg0) argv[0] = logp->arg0; #ifdef JOBS if(job_close(shp) < 0) return(1); #endif /* JOBS */ /* force bad exec to terminate shell */ pp->mode = SH_JMPEXIT; sh_sigreset(2); sh_freeup(shp); path_exec(pname,argv,NIL(struct argnod*)); sh_done(shp,0); } return(1); }
static pid_t path_pfexecve(Shell_t *shp,const char *path, char *argv[],char *const envp[],int spawn) { #if SHOPT_PFSH char resolvedpath[PATH_MAX + 1]; pid_t pid; if(spawn) { while((pid = vfork()) < 0) _sh_fork(shp,pid, 0, (int*)0); if(pid) return(pid); } if(!sh_isoption(SH_PFSH)) return(execve(path, argv, envp)); /* Solaris implements realpath(3C) using the resolvepath(2) */ /* system call so we can save us to call access(2) first */ /* we can exec the command directly instead of via pfexec(1) if */ /* there is a matching entry without attributes in exec_attr(4) */ if(!path_xattr(shp,path,resolvedpath)) return(execve(path, argv, envp)); --argv; argv[0] = argv[1]; argv[1] = resolvedpath; return(execve("/usr/bin/pfexec", argv, envp)); #else return(execve(path, argv, envp)); #endif }
static_fn int infof(Opt_t *op, Sfio_t *sp, const char *s, Optdisc_t *dp) { UNUSED(op); UNUSED(dp); #if SHOPT_BASH Shell_t *shp = sh_getinterp(); extern const char sh_bash1[], sh_bash2[]; if (strcmp(s, "bash1") == 0) { if (sh_isoption(shp, SH_BASH)) sfputr(sp, sh_bash1, -1); } else if (strcmp(s, "bash2") == 0) { if (sh_isoption(shp, SH_BASH)) sfputr(sp, sh_bash2, -1); } else if (*s == ':' && sh_isoption(shp, SH_BASH)) { sfputr(sp, s, -1); } else #endif if (*s != ':') { sfputr(sp, sh_set, -1); } return 1; }
// Returns the value of $-. char *sh_argdolminus(Shell_t *shp) { Arg_t *ap = shp->arg_context; const char *cp = optksh; char *flagp = ap->flagadr; while (cp < &optksh[NUM_OPTS]) { int n = flagval[cp - optksh]; if (sh_isoption(shp, n)) *flagp++ = *cp; cp++; } *flagp = 0; return ap->flagadr; }
static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) { #if SHOPT_BASH extern const char sh_bash1[], sh_bash2[]; if(strcmp(s,"bash1")==0) { if(sh_isoption(SH_BASH)) sfputr(sp,sh_bash1,-1); } else if(strcmp(s,"bash2")==0) { if(sh_isoption(SH_BASH)) sfputr(sp,sh_bash2,-1); } else if(*s==':' && sh_isoption(SH_BASH)) sfputr(sp,s,-1); else #endif if(*s!=':') sfputr(sp,sh_set,-1); return(1); }
// // Builtin `login`. // int B_login(int argc, char *argv[], Shbltin_t *context) { checkpt_t *pp; struct login *logp = NULL; Shell_t *shp; const char *pname; if (argc) { shp = context->shp; } else { logp = (struct login *)context; shp = logp->sh; } pp = shp->jmplist; if (sh_isoption(shp, SH_RESTRICTED)) { errormsg(SH_DICT, ERROR_exit(1), e_restricted, argv[0]); __builtin_unreachable(); } else { struct argnod *arg = shp->envlist; Namval_t *np; char *cp; if (shp->subshell && !shp->subshare) sh_subfork(); if (logp && logp->clear) { nv_scan(shp->var_tree, noexport, 0, NV_EXPORT, NV_EXPORT); } while (arg) { cp = strchr(arg->argval, '='); if (cp && (*cp = 0, np = nv_search(arg->argval, shp->var_tree, 0))) { nv_onattr(np, NV_EXPORT); sh_envput(shp, np); } if (cp) *cp = '='; arg = arg->argnxt.ap; } pname = argv[0]; if (logp && logp->arg0) argv[0] = logp->arg0; #ifdef JOBS if (job_close(shp) < 0) return 1; #endif // JOBS // Force bad exec to terminate shell. pp->mode = SH_JMPEXIT; sh_sigreset(shp, 2); sh_freeup(shp); path_exec(shp, pname, argv, NULL); sh_done(shp, 0); } return 1; }
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; }
// // 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; }
// // Enter the fc command on the current history line. // int ed_fulledit(Edit_t *ep) { char *cp; if (!shgd->hist_ptr) return -1; // Use EDITOR on current command. if (ep->e_hline == ep->e_hismax) { if (ep->e_eol < 0) return -1; ep->e_inbuf[ep->e_eol + 1] = 0; ed_external(ep->e_inbuf, (char *)ep->e_inbuf); sfwrite(shgd->hist_ptr->histfp, (char *)ep->e_inbuf, ep->e_eol + 1); sh_onstate(ep->sh, SH_HISTORY); hist_flush(shgd->hist_ptr); } cp = stpcpy((char *)ep->e_inbuf, e_runvi); cp = stpcpy(cp, fmtbase((long)ep->e_hline, 10, 0)); ep->e_eol = ((unsigned char *)cp - (unsigned char *)ep->e_inbuf) - (sh_isoption(ep->sh, SH_VI) != 0); return 0; }
/* * command is called with argc==0 when checking for -V or -v option * In this case return 0 when -v or -V or unknown option, otherwise * the shift count to the command is returned */ int b_command(register int argc,char *argv[],Shbltin_t *context) { register int n, flags=0; register Shell_t *shp = context->shp; opt_info.index = opt_info.offset = 0; while((n = optget(argv,sh_optcommand))) switch(n) { case 'p': if(sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,"-p"); sh_onstate(SH_DEFPATH); break; case 'v': flags |= X_FLAG; break; case 'V': flags |= V_FLAG; break; case 'x': shp->xargexit = 1; break; case ':': if(argc==0) return(0); errormsg(SH_DICT,2, "%s", opt_info.arg); break; case '?': if(argc==0) return(0); errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); break; } if(argc==0) return(flags?0:opt_info.index); argv += opt_info.index; if(error_info.errors || !*argv) errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); return(whence(shp,argv, flags)); }
/* * Enter the fc command on the current history line */ int ed_fulledit(Edit_t *ep) { register char *cp; if(!shgd->hist_ptr) return(-1); /* use EDITOR on current command */ if(ep->e_hline == ep->e_hismax) { if(ep->e_eol<0) return(-1); #if SHOPT_MULTIBYTE ep->e_inbuf[ep->e_eol+1] = 0; ed_external(ep->e_inbuf, (char *)ep->e_inbuf); #endif /* SHOPT_MULTIBYTE */ sfwrite(shgd->hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1); sh_onstate(SH_HISTORY); hist_flush(shgd->hist_ptr); } cp = strcopy((char*)ep->e_inbuf,e_runvi); cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0)); ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); return(0); }
int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) { struct comnod *comptr; struct argnod *ap; register char *out; char *av[2], *begin , *dir=0; int addstar=0, rval=0, var=0, strip=1; int nomarkdirs = !sh_isoption(SH_MARKDIRS); sh_onstate(SH_FCOMPLETE); if(ep->e_nlist) { if(mode=='=' && count>0) { if(count> ep->e_nlist) return(-1); mode = '?'; av[0] = ep->e_clist[count-1]; av[1] = 0; } else { stakset(ep->e_stkptr,ep->e_stkoff); ep->e_nlist = 0; } } comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); ap = (struct argnod*)stakseek(ARGVAL); #if SHOPT_MULTIBYTE { register int c = *cur; register genchar *cp; /* adjust cur */ cp = (genchar *)outbuff + *cur; c = *cp; *cp = 0; *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); *cp = c; *eol = ed_external((genchar*)outbuff,outbuff); } #endif /* SHOPT_MULTIBYTE */ out = outbuff + *cur + (sh_isoption(SH_VI)!=0); if(out[-1]=='"' || out[-1]=='\'') { rval = -(sh_isoption(SH_VI)!=0); goto done; } comptr->comtyp = COMSCAN; comptr->comarg = ap; ap->argflag = (ARG_MAC|ARG_EXP); ap->argnxt.ap = 0; ap->argchn.cp = 0; { register int c; char *last = out; c = *(unsigned char*)out; var = mode; begin = out = find_begin(outbuff,last,0,&var); /* addstar set to zero if * should not be added */ if(var=='$') { stakputs("${!"); stakwrite(out,last-out); stakputs("@}"); out = last; } else { addstar = '*'; while(out < last) { c = *(unsigned char*)out; if(isexp(c)) addstar = 0; if (c == '/') { if(addstar == 0) strip = 0; dir = out+1; } stakputc(c); out++; } } if(mode=='?') mode = '*'; if(var!='$' && mode=='\\' && out[-1]!='*') addstar = '*'; if(*begin=='~' && !strchr(begin,'/')) addstar = 0; stakputc(addstar); ap = (struct argnod*)stakfreeze(1); } if(mode!='*') sh_onoption(SH_MARKDIRS); { register char **com; char *cp=begin, *left=0, *saveout="."; int nocase=0,narg,cmd_completion=0; register int size='x'; while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) cp--; if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) { cmd_completion=1; sh_onstate(SH_COMPLETE); } if(ep->e_nlist) { narg = 1; com = av; if(dir) begin += (dir-begin); } else { com = sh_argbuild(ep->sh,&narg,comptr,0); /* special handling for leading quotes */ if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) begin--; } sh_offstate(SH_COMPLETE); /* allow a search to be aborted */ if(ep->sh->trapnote&SH_SIGSET) { rval = -1; goto done; } /* match? */ if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) { rval = -1; goto done; } if(mode=='\\' && out[-1]=='/' && narg>1) mode = '='; if(mode=='=') { if (strip && !cmd_completion) { register char **ptrcom; for(ptrcom=com;*ptrcom;ptrcom++) /* trim directory prefix */ *ptrcom = path_basename(*ptrcom); } sfputc(sfstderr,'\n'); sh_menu(sfstderr,narg,com); sfsync(sfstderr); ep->e_nlist = narg; ep->e_clist = com; goto done; } /* see if there is enough room */ size = *eol - (out-begin); if(mode=='\\') { int c; if(dir) { c = *dir; *dir = 0; saveout = begin; } if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) nocase = (strchr(saveout,'c')!=0); if(dir) *dir = c; /* just expand until name is unique */ size += strlen(*com); } else { size += narg; { char **savcom = com; while (*com) size += strlen(cp=fmtx(*com++)); com = savcom; } } /* see if room for expansion */ if(outbuff+size >= &outbuff[MAXLINE]) { com[0] = ap->argval; com[1] = 0; } /* save remainder of the buffer */ if(*out) left=stakcopy(out); if(cmd_completion && mode=='\\') out = strcopy(begin,path_basename(cp= *com++)); else if(mode=='*') { if(ep->e_nlist && dir && var) { if(*cp==var) cp++; else *begin++ = var; out = strcopy(begin,cp); var = 0; } else out = strcopy(begin,fmtx(*com)); com++; } else out = strcopy(begin,*com++); if(mode=='\\') { saveout= ++out; while (*com && *begin) { if(cmd_completion) out = overlaid(begin,path_basename(*com++),nocase); else out = overlaid(begin,*com++,nocase); } mode = (out==saveout); if(out[-1]==0) out--; if(mode && out[-1]!='/') { if(cmd_completion) { Namval_t *np; /* add as tracked alias */ Pathcomp_t *pp; if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD))) path_alias(np,pp); out = strcopy(begin,cp); } /* add quotes if necessary */ if((cp=fmtx(begin))!=begin) out = strcopy(begin,cp); if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; *++out = 0; } else if((cp=fmtx(begin))!=begin) { out = strcopy(begin,cp); if(out[-1] =='"' || out[-1]=='\'') *--out = 0; } if(*begin==0) ed_ringbell(); } else { while (*com) { *out++ = ' '; out = strcopy(out,fmtx(*com++)); } } if(ep->e_nlist) { cp = com[-1]; if(cp[strlen(cp)-1]!='/') { if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; out++; } else if(out[-1] =='"' || out[-1]=='\'') out--; *out = 0; } *cur = (out-outbuff); /* restore rest of buffer */ if(left) out = strcopy(out,left); *eol = (out-outbuff); } done: sh_offstate(SH_FCOMPLETE); if(!ep->e_nlist) stakset(ep->e_stkptr,ep->e_stkoff); if(nomarkdirs) sh_offoption(SH_MARKDIRS); #if SHOPT_MULTIBYTE { register int c,n=0; /* first re-adjust cur */ c = outbuff[*cur]; outbuff[*cur] = 0; for(out=outbuff; *out;n++) mbchar(out); outbuff[*cur] = c; *cur = n; outbuff[*eol+1] = 0; *eol = ed_internal(outbuff,(genchar*)outbuff); } #endif /* SHOPT_MULTIBYTE */ return(rval); }
// This routine turns options on and off. // The options "PDicr" are illegal from set command. // The -o option is used to set option by name. // This routine returns the number of non-option arguments. int sh_argopts(int argc, char *argv[], void *context) { Shell_t *shp = context; int n, o; Arg_t *ap = shp->arg_context; Lex_t *lp = shp->lex_context; Shopt_t newflags; int setflag = 0, action = 0, trace = (int)sh_isoption(shp, SH_XTRACE); Namval_t *np = NULL; const char *sp; char *keylist = NULL; int verbose, f, unsetnp = 0; Optdisc_t disc; newflags = shp->options; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; disc.infof = infof; opt_info.disc = &disc; if (argc > 0) { setflag = 4; } else { argc = -argc; } while ((n = optget(argv, setflag ? sh_optset : sh_optksh))) { o = 0; f = *opt_info.option == '-' && (opt_info.num || opt_info.arg); switch (n) { case 'A': { np = nv_open(opt_info.arg, shp->var_tree, NV_ARRAY | NV_VARNAME); if (f) unsetnp = 1; continue; } case 'K': { keylist = opt_info.arg; continue; } #if SHOPT_BASH case 'O': { // shopt options, only in bash mode if (!sh_isoption(shp, SH_BASH)) { errormsg(SH_DICT, ERROR_exit(1), e_option, opt_info.name); __builtin_unreachable(); } } #endif // FALLTHRU case 'o': { // set options byname: if (!opt_info.arg || !*opt_info.arg || *opt_info.arg == '-') { action = PRINT; // print style: -O => shopt options. // bash => print unset options also, no heading. verbose = (f ? PRINT_VERBOSE : PRINT_NO_HEADER) | (n == 'O' ? PRINT_SHOPT : 0) | (sh_isoption(shp, SH_BASH) ? PRINT_ALL | PRINT_NO_HEADER : 0) | ((opt_info.arg && (!*opt_info.arg || *opt_info.arg == '-')) ? (PRINT_TABLE | PRINT_NO_HEADER) : 0); continue; } o = sh_lookopt(opt_info.arg, &f); if (o <= 0 || (!sh_isoption(shp, SH_BASH) && (o & SH_BASHEXTRA)) || ((!sh_isoption(shp, SH_BASH) || n == 'o') && (o & SH_BASHOPT)) || (setflag && (o & SH_COMMANDLINE))) { errormsg(SH_DICT, 2, e_option, opt_info.arg); error_info.errors++; } o &= 0xff; if (sh_isoption(shp, SH_RESTRICTED) && !f && o == SH_RESTRICTED) { errormsg(SH_DICT, ERROR_exit(1), e_restricted, opt_info.arg); __builtin_unreachable(); } break; } #if SHOPT_BASH case -1: { // --rcfile shp->gd->rcfile = opt_info.arg; continue; } case -2: { // --noediting if (!f) { off_option(&newflags, SH_VI); off_option(&newflags, SH_EMACS); off_option(&newflags, SH_GMACS); } continue; } case -3: { // --profile n = 'l'; sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case -4: { // --posix // Mask lower 8 bits to find char in optksh string. n &= 0xff; sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case -5: { // --version sfputr(sfstdout, "ksh bash emulation, version ", -1); np = nv_open("BASH_VERSION", shp->var_tree, 0); sfputr(sfstdout, nv_getval(np), -1); np = nv_open("MACHTYPE", shp->var_tree, 0); sfprintf(sfstdout, " (%s)\n", nv_getval(np)); sh_exit(shp, 0); } #endif case -6: { // --default const Shtable_t *tp; for (tp = shtab_options; (o = tp->sh_number); tp++) { if (!(o & SH_COMMANDLINE) && is_option(&newflags, o & 0xff)) { off_option(&newflags, o & 0xff); } } continue; } case -7: { f = 0; goto byname; } case 'D': { on_option(&newflags, SH_NOEXEC); // Cppcheck doesn't recognize the "goto" in the preceding case and thus thinks we // might fall through and call strchr() with n == -7. Even though this it // technically a bug in cppcheck it is one reason why `goto` shouldn't be used; at // least inside `switch` blocks. // cppcheck-suppress invalidFunctionArg sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case 'T': { if (opt_info.num) { shp->test |= opt_info.num; } else { shp->test = 0; } continue; } case 's': { if (setflag) { action = SORT; continue; } sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case 'R': { if (setflag) { n = ':'; } else { ap->kiafile = opt_info.arg; n = 'n'; } sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case ':': { if (opt_info.name[0] == '-' && opt_info.name[1] == '-') { opt_info.arg = argv[opt_info.index - 1] + 2; f = 1; goto byname; } errormsg(SH_DICT, 2, "%s", opt_info.arg); continue; } case '?': { errormsg(SH_DICT, ERROR_usage(0), "%s", opt_info.arg); return -1; } default: { sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } } if (f) { if (o == SH_VI || o == SH_EMACS || o == SH_GMACS) { off_option(&newflags, SH_VI); off_option(&newflags, SH_EMACS); off_option(&newflags, SH_GMACS); } on_option(&newflags, o); off_option(&shp->offoptions, o); } else { if (o == SH_RESTRICTED && sh_isoption(shp, SH_RESTRICTED)) { errormsg(SH_DICT, ERROR_exit(1), e_restricted, "r"); __builtin_unreachable(); } if (o == SH_XTRACE) trace = 0; off_option(&newflags, o); if (setflag == 0) on_option(&shp->offoptions, o); } } if (error_info.errors) { errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NULL)); __builtin_unreachable(); } // Check for '-' or '+' argument. sp = argv[opt_info.index]; if (sp && sp[1] == 0 && (*sp == '+' || *sp == '-') && strcmp(argv[opt_info.index - 1], "--")) { opt_info.index++; off_option(&newflags, SH_XTRACE); off_option(&newflags, SH_VERBOSE); trace = 0; } if (trace) sh_trace(shp, argv, 1); argc -= opt_info.index; argv += opt_info.index; if (action == PRINT) sh_printopts(shp, newflags, verbose, 0); if (setflag) { if (action == SORT) { int (*sortfn)(const char *, const char *) = strcoll; Namarr_t *arp; struct Value *args; unsigned char *bits; if (argc > 0) { strsort(argv, argc, sortfn); } else if (np && (arp = nv_arrayptr(np)) && (args = nv_aivec(np, &bits))) { char *cp; int i, c, keys = 0; if (keylist) { for (cp = keylist; (c = *cp); cp++) { if (c == ',') keys++; } keys++; } else { keylist = ""; } arp->nelem = nv_aipack(arp); cp = nv_name(np); c = strlen(cp); // This used to multiply by `(keys - 1)` but `keys` can be zero which means the // nodesize can be less than `sizeof(struct Node)` which is obviously wrong. // Whether multiplying by `keys` is correct is unclear. // See issue #824. size_t nodesize = sizeof(struct Node) + keys * sizeof(Namval_t *); struct Sort *sp = malloc(sizeof(struct Sort) + strlen(keylist) + (sizeof(char *) + 1) * keys + (arp->nelem + 1) * (nodesize + sizeof(void *)) + c + 3); sp->shp = shp; sp->np = np; if (!(sp->root = shp->last_root)) sp->root = shp->var_tree; sp->vp = args; sp->cur = 0; sp->nodes = (struct Node *)&sp->keys[keys + 2]; memset(sp->nodes, 0, arp->nelem * nodesize); sp->nptrs = (struct Node **)((char *)sp->nodes + arp->nelem * nodesize); sp->flags = (char *)&sp->nptrs[arp->nelem + 1]; memset(sp->flags, 0, keys + 1); sp->name = sp->flags + keys + 1; memcpy(sp->name, cp, c + 1); sp->keys[0] = sp->name + c + 1; strcpy(sp->keys[0], keylist); cp = (char *)sp->nodes; for (c = 0; c < arp->nelem; c++) { if (*keylist && *keylist != ':') { struct Namval *np = FETCH_VT(args[c], np); ((struct Node *)cp)->index = strtol(np->nvname, NULL, 10); ((struct Node *)cp)->bits = bits[c]; } else { ((struct Node *)cp)->index = c; } ((struct Node *)cp)->vp = args[c]; sp->nptrs[c] = (struct Node *)cp; cp += nodesize; } if (!(cp = sp->keys[0])) cp = keylist; for (keys = 0; (c = *cp); cp++) { if (c == ',') { *cp++ = 0; sp->keys[++keys] = cp; sp->flags[keys] = 0; } else if (c == ':') { again: *cp++ = 0; c = *cp; if (c == 'r') { sp->flags[keys] |= SORT_reverse; c = cp[1]; } else if (c == 'n') { sp->flags[keys] |= SORT_numeric; c = cp[1]; } if (c == 'n' || c == 'r') goto again; } } sp->keys[++keys] = 0; Sp = sp; if (sp->keys[0] && *sp->keys[0]) { sortfn = arraysort; } else if (sp->flags[0] & SORT_numeric) { sortfn = numsort; } else { sortfn = alphasort; } strsort((char **)sp->nptrs, arp->nelem, sortfn); cp = (char *)sp->nodes; for (c = 0; c < arp->nelem; c++) { i = (char *)sp->nptrs[c] - (char *)&sp->nodes[0]; if (i / nodesize != c) { args[c] = ((struct Node *)(cp + i))->vp; bits[c] = ((struct Node *)(cp + i))->bits; } } free(sp); nv_close(np); np = NULL; } else { strsort(shp->st.dolv + 1, shp->st.dolc, sortfn); } } if (np) { if (unsetnp) nv_unset(np); nv_setvec(np, 0, argc, argv); nv_close(np); } else if (argc > 0 || ((sp = argv[-1]) && strcmp(sp, "--") == 0)) { sh_argset(ap, argv - 1); } } else if (is_option(&newflags, SH_CFLAG)) { if (!(shp->comdiv = *argv++)) { errormsg(SH_DICT, 2, e_cneedsarg); errormsg(SH_DICT, ERROR_usage(2), optusage(NULL)); __builtin_unreachable(); } argc--; } // Handling SH_INTERACTIVE and SH_PRIVILEGED has been moved to // sh_applyopts(), so that the code can be reused from b_shopt(), too. sh_applyopts(shp, newflags); if (!ap->kiafile) return argc; if (!argv[0]) { errormsg(SH_DICT, ERROR_usage(2), "-R requires scriptname"); __builtin_unreachable(); } if (!(lp->kiafile = sfopen(NULL, ap->kiafile, "w+"))) { errormsg(SH_DICT, ERROR_system(3), e_create, ap->kiafile); __builtin_unreachable(); } if (!(lp->kiatmp = sftmp(2 * SF_BUFSIZE))) { errormsg(SH_DICT, ERROR_system(3), e_tmpcreate); __builtin_unreachable(); } sfputr(lp->kiafile, ";vdb;CIAO/ksh", '\n'); lp->kiabegin = sftell(lp->kiafile); lp->entity_tree = dtopen(&_Nvdisc, Dtbag); lp->scriptname = strdup(sh_fmtq(argv[0])); lp->script = kiaentity(lp, lp->scriptname, -1, 'p', -1, 0, 0, 's', 0, ""); lp->fscript = kiaentity(lp, lp->scriptname, -1, 'f', -1, 0, 0, 's', 0, ""); lp->unknown = kiaentity(lp, "<unknown>", -1, 'p', -1, 0, 0, '0', 0, ""); kiaentity(lp, "<unknown>", -1, 'p', 0, 0, lp->unknown, '0', 0, ""); lp->current = lp->script; ap->kiafile = NULL; return argc; }
// // 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); }
/* * This routine turns options on and off * The options "PDicr" are illegal from set command. * The -o option is used to set option by name * This routine returns the number of non-option arguments */ int sh_argopts(int argc,register char *argv[], void *context) { Shell_t *shp = (Shell_t*)context; register int n,o; register Arg_t *ap = (Arg_t*)(shp->arg_context); Lex_t *lp = (Lex_t*)(shp->lex_context); Shopt_t newflags; int setflag=0, action=0, trace=(int)sh_isoption(SH_XTRACE); Namval_t *np = NIL(Namval_t*); const char *cp; int verbose,f; Optdisc_t disc; newflags=ap->sh->options; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; disc.infof = infof; opt_info.disc = &disc; if(argc>0) setflag = 4; else argc = -argc; while((n = optget(argv,setflag?sh_optset:sh_optksh))) { o=0; f=*opt_info.option=='-' && (opt_info.num || opt_info.arg); switch(n) { case 'A': np = nv_open(opt_info.arg,ap->sh->var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME); if(f) nv_unset(np); continue; #if SHOPT_BASH case 'O': /* shopt options, only in bash mode */ if(!sh_isoption(SH_BASH)) errormsg(SH_DICT,ERROR_exit(1), e_option, opt_info.name); #endif case 'o': /* set options */ byname: if(!opt_info.arg||!*opt_info.arg||*opt_info.arg=='-') { action = PRINT; /* print style: -O => shopt options * bash => print unset options also, no heading */ verbose = (f?PRINT_VERBOSE:PRINT_NO_HEADER)| (n=='O'?PRINT_SHOPT:0)| (sh_isoption(SH_BASH)?PRINT_ALL|PRINT_NO_HEADER:0)| ((opt_info.arg&&(!*opt_info.arg||*opt_info.arg=='-'))?(PRINT_TABLE|PRINT_NO_HEADER):0); continue; } o = sh_lookopt(opt_info.arg,&f); if(o<=0 || (!sh_isoption(SH_BASH) && (o&SH_BASHEXTRA)) || ((!sh_isoption(SH_BASH) || n=='o') && (o&SH_BASHOPT)) || (setflag && (o&SH_COMMANDLINE))) { errormsg(SH_DICT,2, e_option, opt_info.arg); error_info.errors++; } o &= 0xff; if(sh_isoption(SH_RESTRICTED) && !f && o==SH_RESTRICTED) errormsg(SH_DICT,ERROR_exit(1), e_restricted, opt_info.arg); break; #if SHOPT_BASH case -1: /* --rcfile */ ap->sh->gd->rcfile = opt_info.arg; continue; case -2: /* --noediting */ if (!f) { off_option(&newflags,SH_VI); off_option(&newflags,SH_EMACS); off_option(&newflags,SH_GMACS); } continue; case -3: /* --profile */ n = 'l'; goto skip; case -4: /* --posix */ /* mask lower 8 bits to find char in optksh string */ n&=0xff; goto skip; case -5: /* --version */ sfputr(sfstdout, "ksh bash emulation, version ",-1); np = nv_open("BASH_VERSION",ap->sh->var_tree,0); sfputr(sfstdout, nv_getval(np),-1); np = nv_open("MACHTYPE",ap->sh->var_tree,0); sfprintf(sfstdout, " (%s)\n", nv_getval(np)); sh_exit(0); #endif case -6: /* --default */ { register const Shtable_t *tp; for(tp=shtab_options; o = tp->sh_number; tp++) if(!(o&SH_COMMANDLINE) && is_option(&newflags,o&0xff)) off_option(&newflags,o&0xff); } continue; case -7: f = 0; goto byname; case 'D': on_option(&newflags,SH_NOEXEC); goto skip; case 'T': if (opt_info.num) ap->sh->test |= opt_info.num; else ap->sh->test = 0; continue; case 's': if(setflag) { action = SORT; continue; } #if SHOPT_KIA goto skip; case 'R': if(setflag) n = ':'; else { ap->kiafile = opt_info.arg; n = 'n'; } /*FALLTHROUGH*/ #endif /* SHOPT_KIA */ #if SHOPT_REGRESS goto skip; case 'I': continue; #endif /* SHOPT_REGRESS */ skip: default: if(cp=strchr(optksh,n)) o = flagval[cp-optksh]; break; case ':': if(opt_info.name[0]=='-'&&opt_info.name[1]=='-') { opt_info.arg = argv[opt_info.index-1] + 2; f = 1; goto byname; } errormsg(SH_DICT,2, "%s", opt_info.arg); continue; case '?': errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); return(-1); } if(f) { if(o==SH_VI || o==SH_EMACS || o==SH_GMACS) { off_option(&newflags,SH_VI); off_option(&newflags,SH_EMACS); off_option(&newflags,SH_GMACS); } on_option(&newflags,o); off_option(&ap->sh->offoptions,o); } else { if(o==SH_XTRACE) trace = 0; off_option(&newflags,o); if(setflag==0) on_option(&ap->sh->offoptions,o); } } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); /* check for '-' or '+' argument */ if((cp=argv[opt_info.index]) && cp[1]==0 && (*cp=='+' || *cp=='-') && strcmp(argv[opt_info.index-1],"--")) { opt_info.index++; off_option(&newflags,SH_XTRACE); off_option(&newflags,SH_VERBOSE); trace = 0; } if(trace) sh_trace(shp,argv,1); argc -= opt_info.index; argv += opt_info.index; if(action==PRINT) sh_printopts(newflags,verbose,0); if(setflag) { if(action==SORT) { if(argc>0) strsort(argv,argc,strcoll); else strsort(ap->sh->st.dolv+1,ap->sh->st.dolc,strcoll); } if(np) { nv_setvec(np,0,argc,argv); nv_close(np); } else if(argc>0 || ((cp=argv[-1]) && strcmp(cp,"--")==0)) sh_argset(ap,argv-1); } else if(is_option(&newflags,SH_CFLAG)) { if(!(ap->sh->comdiv = *argv++)) { errormsg(SH_DICT,2,e_cneedsarg); errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*))); } argc--; }
/* * This routine turns options on and off * The options "PDicr" are illegal from set command. * The -o option is used to set option by name * This routine returns the number of non-option arguments */ int sh_argopts(int argc,register char *argv[], void *context) { Shell_t *shp = (Shell_t*)context; register int n,o; register Arg_t *ap = (Arg_t*)(shp->arg_context); Lex_t *lp = (Lex_t*)(shp->lex_context); Shopt_t newflags; int setflag=0, action=0, trace=(int)sh_isoption(shp,SH_XTRACE); Namval_t *np = NIL(Namval_t*); const char *sp; char *keylist=0; int verbose,f,unsetnp=0; Optdisc_t disc; newflags=shp->options; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; disc.infof = infof; opt_info.disc = &disc; if(argc>0) setflag = 4; else argc = -argc; while((n = optget(argv,setflag?sh_optset:sh_optksh))) { o=0; f=*opt_info.option=='-' && (opt_info.num || opt_info.arg); switch(n) { case 'A': np = nv_open(opt_info.arg,shp->var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME); if(f) unsetnp=1; continue; case 'K': keylist = opt_info.arg; continue; #if SHOPT_BASH case 'O': /* shopt options, only in bash mode */ if(!sh_isoption(shp,SH_BASH)) errormsg(SH_DICT,ERROR_exit(1), e_option, opt_info.name); #endif case 'o': /* set options */ byname: if(!opt_info.arg||!*opt_info.arg||*opt_info.arg=='-') { action = PRINT; /* print style: -O => shopt options * bash => print unset options also, no heading */ verbose = (f?PRINT_VERBOSE:PRINT_NO_HEADER)| (n=='O'?PRINT_SHOPT:0)| (sh_isoption(shp,SH_BASH)?PRINT_ALL|PRINT_NO_HEADER:0)| ((opt_info.arg&&(!*opt_info.arg||*opt_info.arg=='-'))?(PRINT_TABLE|PRINT_NO_HEADER):0); continue; } o = sh_lookopt(opt_info.arg,&f); if(o<=0 || (!sh_isoption(shp,SH_BASH) && (o&SH_BASHEXTRA)) || ((!sh_isoption(shp,SH_BASH) || n=='o') && (o&SH_BASHOPT)) || (setflag && (o&SH_COMMANDLINE))) { errormsg(SH_DICT,2, e_option, opt_info.arg); error_info.errors++; } o &= 0xff; if(sh_isoption(shp,SH_RESTRICTED) && !f && o==SH_RESTRICTED) errormsg(SH_DICT,ERROR_exit(1), e_restricted, opt_info.arg); break; #if SHOPT_BASH case -1: /* --rcfile */ shp->gd->rcfile = opt_info.arg; continue; case -2: /* --noediting */ if (!f) { off_option(&newflags,SH_VI); off_option(&newflags,SH_EMACS); off_option(&newflags,SH_GMACS); } continue; case -3: /* --profile */ n = 'l'; goto skip; case -4: /* --posix */ /* mask lower 8 bits to find char in optksh string */ n&=0xff; goto skip; case -5: /* --version */ sfputr(sfstdout, "ksh bash emulation, version ",-1); np = nv_open("BASH_VERSION",shp->var_tree,0); sfputr(sfstdout, nv_getval(np),-1); np = nv_open("MACHTYPE",shp->var_tree,0); sfprintf(sfstdout, " (%s)\n", nv_getval(np)); sh_exit(shp,0); #endif case -6: /* --default */ { register const Shtable_t *tp; for(tp=shtab_options; o = tp->sh_number; tp++) if(!(o&SH_COMMANDLINE) && is_option(&newflags,o&0xff)) off_option(&newflags,o&0xff); } continue; case -7: f = 0; goto byname; case 'D': on_option(&newflags,SH_NOEXEC); goto skip; case 'T': if (opt_info.num) shp->test |= opt_info.num; else shp->test = 0; continue; case 's': if(setflag) { action = SORT; continue; } #if SHOPT_KIA goto skip; case 'R': if(setflag) n = ':'; else { ap->kiafile = opt_info.arg; n = 'n'; } /*FALLTHROUGH*/ #endif /* SHOPT_KIA */ #if SHOPT_REGRESS goto skip; case 'I': continue; #endif /* SHOPT_REGRESS */ skip: default: if(sp=strchr(optksh,n)) o = flagval[sp-optksh]; break; case ':': if(opt_info.name[0]=='-'&&opt_info.name[1]=='-') { opt_info.arg = argv[opt_info.index-1] + 2; f = 1; goto byname; } errormsg(SH_DICT,2, "%s", opt_info.arg); continue; case '?': errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); return(-1); } if(f) { if(o==SH_VI || o==SH_EMACS || o==SH_GMACS) { off_option(&newflags,SH_VI); off_option(&newflags,SH_EMACS); off_option(&newflags,SH_GMACS); } on_option(&newflags,o); off_option(&shp->offoptions,o); } else { if(o==SH_XTRACE) trace = 0; off_option(&newflags,o); if(setflag==0) on_option(&shp->offoptions,o); } } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); /* check for '-' or '+' argument */ if((sp=argv[opt_info.index]) && sp[1]==0 && (*sp=='+' || *sp=='-') && strcmp(argv[opt_info.index-1],"--")) { opt_info.index++; off_option(&newflags,SH_XTRACE); off_option(&newflags,SH_VERBOSE); trace = 0; } if(trace) sh_trace(shp,argv,1); argc -= opt_info.index; argv += opt_info.index; if(action==PRINT) sh_printopts(shp,newflags,verbose,0); if(setflag) { if(action==SORT) { int (*sortfn)(const char*,const char*) = strcoll; Namarr_t *arp; union Value *args; unsigned char *bits; if(argc>0) strsort(argv,argc,sortfn); else if(np && (arp=nv_arrayptr(np)) && (args = nv_aivec(np,&bits))) { struct Sort *sp; char *cp; int i, c, keys=0; size_t nodesize; if(keylist) { for(cp=keylist;c= *cp; cp++) { if(c==',') keys++; } keys++; } else keylist = Empty; arp->nelem = nv_aipack(arp);; cp = nv_name(np); c = strlen(cp); nodesize = sizeof(struct Node)+(keys-1)*sizeof(Namval_t*); sp = (struct Sort*)malloc(sizeof(struct Sort)+strlen(keylist)+(sizeof(char*)+1)*keys+(arp->nelem+1)*(nodesize+sizeof(void*))+c+3); sp->shp = shp; sp->np = np; if(!(sp->root = shp->last_root)) sp->root = shp->var_tree; sp->vp = args; sp->cur = 0; sp->nodes = (struct Node*)&sp->keys[keys+2]; memset(sp->nodes, 0, arp->nelem*nodesize); sp->nptrs = (struct Node**)((char*)sp->nodes+arp->nelem*nodesize); sp->flags = (char*)&sp->nptrs[arp->nelem+1]; memset(sp->flags,0,keys+1); sp->name = sp->flags + keys+1; memcpy(sp->name,cp,c+1); sp->keys[0] = sp->name+c+1; strcpy(sp->keys[0],keylist); cp = (char*)sp->nodes; for(c=0; c < arp->nelem; c++) { if(keylist!=Empty && *keylist!=':') { ((struct Node*)cp)->index = strtol(args[c].np->nvname,NULL,10); ((struct Node*)cp)->bits = bits[c]; } else ((struct Node*)cp)->index = c; ((struct Node*)cp)->vp = args[c]; sp->nptrs[c] = (struct Node*)cp; cp += nodesize; } if(!(cp = sp->keys[0])) cp = keylist; for(keys=0;c= *cp; cp++) { if(c==',') { *cp++ = 0; sp->keys[++keys] = cp; sp->flags[keys] = 0; } else if(c==':') { again: *cp++ = 0; if((c= *cp) == 'r') { sp->flags[keys] |= SORT_reverse; c = cp[1]; } else if(c=='n') { sp->flags[keys] |= SORT_numeric; c = cp[1]; } if(c=='n' || c=='r') goto again; } } sp->keys[++keys] = 0; Sp = sp; if(sp->keys[0] && *sp->keys[0]) sortfn = arraysort; else if(sp->flags[0]&SORT_numeric) sortfn = numsort; else sortfn = alphasort; strsort((char**)sp->nptrs,arp->nelem,sortfn); cp = (char*)sp->nodes; for(c=0; c < arp->nelem; c++) { i = (char*)sp->nptrs[c]-(char*)&sp->nodes[0]; if(i/nodesize !=c) { args[c] = ((struct Node*)(cp+i))->vp; bits[c] = ((struct Node*)(cp+i))->bits; } } free(sp); nv_close(np); np = 0; } else strsort(shp->st.dolv+1,shp->st.dolc,sortfn); } if(np) { if(unsetnp) nv_unset(np); nv_setvec(np,0,argc,argv); nv_close(np); } else if(argc>0 || ((sp=argv[-1]) && strcmp(sp,"--")==0)) sh_argset(ap,argv-1); } else if(is_option(&newflags,SH_CFLAG)) { if(!(shp->comdiv = *argv++)) { errormsg(SH_DICT,2,e_cneedsarg); errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*))); } argc--; }
/* * 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); }
void ed_setup(register Edit_t *ep, int fd, int reedit) { Shell_t *shp = ep->sh; register char *pp; register char *last, *prev; char *ppmax; int myquote = 0, n; register int qlen = 1, qwid; char inquote = 0; ep->e_fd = fd; ep->e_multiline = sh_isoption(SH_MULTILINE)!=0; #ifdef SIGWINCH if(!(shp->sigflag[SIGWINCH]&SH_SIGFAULT)) { signal(SIGWINCH,sh_fault); shp->sigflag[SIGWINCH] |= SH_SIGFAULT; } pp = shp->st.trapcom[SIGWINCH]; shp->st.trapcom[SIGWINCH] = 0; sh_fault(SIGWINCH); shp->st.trapcom[SIGWINCH] = pp; ep->sh->winch = 0; #endif #if SHOPT_EDPREDICT ep->hlist = 0; ep->nhlist = 0; ep->hoff = 0; #endif /* SHOPT_EDPREDICT */ #if KSHELL ep->e_stkptr = stakptr(0); ep->e_stkoff = staktell(); if(!(last = shp->prompt)) last = ""; shp->prompt = 0; #else last = ep->e_prbuff; #endif /* KSHELL */ if(shp->gd->hist_ptr) { register History_t *hp = shp->gd->hist_ptr; ep->e_hismax = hist_max(hp); ep->e_hismin = hist_min(hp); } else { ep->e_hismax = ep->e_hismin = ep->e_hloff = 0; } ep->e_hline = ep->e_hismax; if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS)) ep->e_wsize = MAXLINE; else ep->e_wsize = ed_window()-2; ep->e_winsz = ep->e_wsize+2; ep->e_crlf = 1; ep->e_plen = 0; pp = ep->e_prompt; ppmax = pp+PRSIZE-1; *pp++ = '\r'; { register int c; while(prev = last, c = mbchar(last)) switch(c) { case ESC: { int skip=0; ep->e_crlf = 0; *pp++ = c; for(n=1; c = *last++; n++) { if(pp < ppmax) *pp++ = c; if(c=='\a' || c==ESC || c=='\r') break; if(skip || (c>='0' && c<='9')) { skip = 0; continue; } if(n>1 && c==';') skip = 1; else if(n>2 || (c!= '[' && c!= ']')) break; } if(c==0 || c==ESC || c=='\r') last--; qlen += (n+1); break; } case '\b': if(pp>ep->e_prompt+1) pp--; break; case '\r': if(pp == (ep->e_prompt+2)) /* quote char */ myquote = *(pp-1); /*FALLTHROUGH*/ case '\n': /* start again */ ep->e_crlf = 1; qlen = 1; inquote = 0; pp = ep->e_prompt+1; break; case '\t': /* expand tabs */ while((pp-ep->e_prompt)%TABSIZE) { if(pp >= ppmax) break; *pp++ = ' '; } break; case '\a': /* cut out bells */ break; default: if(c==myquote) { qlen += inquote; inquote ^= 1; } if(pp < ppmax) { if(inquote) qlen++; else if(!is_print(c)) ep->e_crlf = 0; if((qwid = last - prev) > 1) qlen += qwid - mbwidth(c); while(prev < last && pp < ppmax) *pp++ = *prev++; } break; } } if(pp-ep->e_prompt > qlen) ep->e_plen = pp - ep->e_prompt - qlen; *pp = 0; if(!ep->e_multiline && (ep->e_wsize -= ep->e_plen) < 7) { register int shift = 7-ep->e_wsize; ep->e_wsize = 7; pp = ep->e_prompt+1; strcpy(pp,pp+shift); ep->e_plen -= shift; last[-ep->e_plen-2] = '\r'; } sfsync(sfstderr); if(fd == sffileno(sfstderr)) { /* can't use output buffer when reading from stderr */ static char *buff; if(!buff) buff = (char*)malloc(MAXLINE); ep->e_outbase = ep->e_outptr = buff; ep->e_outlast = ep->e_outptr + MAXLINE; return; } qlen = sfset(sfstderr,SF_READ,0); /* make sure SF_READ not on */ ep->e_outbase = ep->e_outptr = (char*)sfreserve(sfstderr,SF_UNBOUND,SF_LOCKR); ep->e_outlast = ep->e_outptr + sfvalue(sfstderr); if(qlen) sfset(sfstderr,SF_READ,1); sfwrite(sfstderr,ep->e_outptr,0); ep->e_eol = reedit; if(ep->e_multiline) { #ifdef _cmd_tput char *term; if(!ep->e_term) ep->e_term = nv_search("TERM",shp->var_tree,0); if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname)) { sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0); if(pp=nv_getval(SH_SUBSCRNOD)) strncpy(CURSOR_UP,pp,sizeof(CURSOR_UP)-1); nv_unset(SH_SUBSCRNOD); strcpy(ep->e_termname,term); } #endif ep->e_wsize = MAXLINE - (ep->e_plen+1); } if(ep->e_default && (pp = nv_getval(ep->e_default))) { n = strlen(pp); if(n > LOOKAHEAD) n = LOOKAHEAD; ep->e_lookahead = n; while(n-- > 0) ep->e_lbuf[n] = *pp++; ep->e_default = 0; } }
int test_unop(Shell_t *shp,register int op,register const char *arg) { struct stat statb; int f; switch(op) { case 'r': return(permission(arg, R_OK)); case 'w': return(permission(arg, W_OK)); case 'x': return(permission(arg, X_OK)); case 'V': #if SHOPT_FS_3D { register int offset = staktell(); if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode)) return(0); /* add trailing / */ stakputs(arg); stakputc('/'); stakputc(0); arg = (const char*)stakptr(offset); stakseek(offset); /* FALL THRU */ } #else return(0); #endif /* SHOPT_FS_3D */ case 'd': return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode)); case 'c': return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode)); case 'b': return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode)); case 'f': return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode)); case 'u': return(test_mode(arg)&S_ISUID); case 'g': return(test_mode(arg)&S_ISGID); case 'k': #ifdef S_ISVTX return(test_mode(arg)&S_ISVTX); #else return(0); #endif /* S_ISVTX */ #if SHOPT_TEST_L case 'l': #endif case 'L': case 'h': /* undocumented, and hopefully will disappear */ if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0) return(0); return(S_ISLNK(statb.st_mode)); case 'C': #ifdef S_ISCTG return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode)); #else return(0); #endif /* S_ISCTG */ case 'H': #ifdef S_ISCDF { register int offset = staktell(); if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)) return(1); stakputs(arg); stakputc('+'); stakputc(0); arg = (const char*)stakptr(offset); stakseek(offset); return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)); } #else return(0); #endif /* S_ISCDF */ case 'S': return(isasock(arg,&statb)); case 'N': return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb)); case 'p': return(isapipe(arg,&statb)); case 'n': return(*arg != 0); case 'z': return(*arg == 0); case 's': sfsync(sfstdout); case 'O': case 'G': if(*arg==0 || test_stat(arg,&statb)<0) return(0); if(op=='s') return(statb.st_size>0); else if(op=='O') return(statb.st_uid==shp->gd->userid); return(statb.st_gid==shp->gd->groupid); case 'a': case 'e': if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK)) return(1); return(permission(arg, F_OK)); case 'o': f=1; if(*arg=='?') return(sh_lookopt(arg+1,&f)>0); op = sh_lookopt(arg,&f); return(op && (f==(sh_isoption(op)!=0))); case 't': { char *last; op = strtol(arg,&last, 10); return(*last?0:tty_check(op)); } case 'v': case 'R': { Namval_t *np; Namarr_t *ap; int isref; if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF))) return(0); isref = nv_isref(np); if(op=='R') return(isref); if(isref) { if(np->nvalue.cp) np = nv_refnode(np); else return(0); } if(ap = nv_arrayptr(np)) return(nv_arrayisset(np,ap)); return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER)); } default: { static char a[3] = "-?"; a[1]= op; errormsg(SH_DICT,ERROR_exit(2),e_badop,a); /* NOTREACHED */ return(0); } } }
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; }
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); 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); }
// // File name generation for edit modes. // Non-zero exit for error, <0 ring bell. // Don't search back past beginning of the buffer. // Mode is '*' for inline expansion. // Mode is '\' for filename completion. // Mode is '=' cause files to be listed in select format. // int ed_expand(Edit_t *ep, char outbuff[], int *cur, int *eol, int mode, int count) { struct comnod *comptr; struct argnod *ap; char *out; char *av[2], *begin; char *dir = NULL; int addstar = 0, rval = 0, var = 0, strip = 1, narg = 0; int nomarkdirs = !sh_isoption(ep->sh, SH_MARKDIRS); Shell_t *shp = ep->sh; char **com = NULL; sh_onstate(shp, SH_FCOMPLETE); if (ep->e_nlist) { if (mode == '=' && count > 0) { if (count > ep->e_nlist) return -1; mode = '?'; av[0] = ep->e_clist[count - 1]; av[1] = 0; } else { stkset(shp->stk, ep->e_stkptr, ep->e_stkoff); ep->e_nlist = 0; } } comptr = stkalloc(shp->stk, sizeof(struct comnod)); ap = (struct argnod *)stkseek(shp->stk, ARGVAL); { // Adjust cur. int c; genchar *cp; cp = (genchar *)outbuff + *cur; c = *cp; *cp = 0; *cur = ed_external((genchar *)outbuff, (char *)stkptr(shp->stk, 0)); *cp = c; *eol = ed_external((genchar *)outbuff, outbuff); } out = outbuff + *cur + (sh_isoption(shp, SH_VI) != 0); #if 0 if(out[-1]=='"' || out[-1]=='\'') { rval = -(sh_isoption(shp,SH_VI)!=0); goto done; } #endif comptr->comtyp = COMSCAN; comptr->comarg = ap; ap->argflag = (ARG_MAC | ARG_EXP); ap->argnxt.ap = NULL; ap->argchn.cp = NULL; { char *last = out; Namval_t *np = nv_search("COMP_KEY", shp->var_tree, 0); if (np) STORE_VT(np->nvalue, i16, '\t'); np = nv_search("COMP_TYPE", shp->var_tree, 0); if (np) STORE_VT(np->nvalue, i16, mode == '\\' ? '\t' : '?'); var = mode; begin = out = find_begin(outbuff, last, 0, &var); if (ep->compdict && mode != '?' && (com = prog_complete(ep->compdict, outbuff, out, *cur))) { char **av; for (av = com; *av; av++) { ; // empty loop } narg = av - com; } // Addstar set to zero if * should not be added. if (var == '$') { sfwrite(shp->stk, "${!", 3); sfwrite(shp->stk, out, last - out); sfwrite(shp->stk, "$@}", 2); out = last; } else { addstar = '*'; while (out < last) { char c = *out; if (c == 0) break; if (isexp(c)) addstar = 0; if (c == '/') { if (addstar == 0) strip = 0; dir = out + 1; } sfputc(shp->stk, c); out++; } } if (mode == '?') mode = '*'; if (var != '$' && mode == '\\' && out[-1] != '*') addstar = '*'; if (*begin == '~' && !strchr(begin, '/')) addstar = 0; sfputc(shp->stk, addstar); ap = (struct argnod *)stkfreeze(shp->stk, 1); } if (mode != '*') sh_onoption(shp, SH_MARKDIRS); { char *cp = begin, *left = NULL; int cmd_completion = 0; int size = 'x'; while (cp > outbuff && ((size = cp[-1]) == ' ' || size == '\t')) cp--; if (!var && !strchr(ap->argval, '/') && ((cp == outbuff && shp->nextprompt == 1) || (strchr(";&|(", size) && (cp == outbuff + 1 || size == '(' || cp[-2] != '>') && *begin != '~'))) { cmd_completion = 1; sh_onstate(shp, SH_COMPLETE); } if (ep->e_nlist) { narg = 1; com = av; if (dir) begin += (dir - begin); } else { if (!com) com = sh_argbuild(shp, &narg, comptr, 0); // Special handling for leading quotes. if (begin > outbuff && (begin[-1] == '"' || begin[-1] == '\'')) begin--; } sh_offstate(shp, SH_COMPLETE); // Allow a search to be aborted. if (shp->trapnote & SH_SIGSET) { rval = -1; goto done; } // Match? if (*com == 0 || ((narg <= 1 && (strcmp(ap->argval, *com) == 0)) || (addstar && com[0][strlen(*com) - 1] == '*'))) { rval = -1; goto done; } if (mode == '\\' && out[-1] == '/' && narg > 1) mode = '='; if (mode == '=') { if (strip && !cmd_completion) { char **ptrcom; for (ptrcom = com; *ptrcom; ptrcom++) { // trim directory prefix *ptrcom = path_basename(*ptrcom); } } sfputc(sfstderr, '\n'); sh_menu(shp, sfstderr, narg, com); sfsync(sfstderr); ep->e_nlist = narg; ep->e_clist = com; goto done; } // See if there is enough room. size = *eol - (out - begin); if (mode == '\\') { int c; if (dir) { c = *dir; *dir = 0; } if (dir) *dir = c; // Just expand until name is unique. size += strlen(*com); } else { char **tmpcom = com; size += narg; while (*tmpcom) { cp = fmtx(shp, *tmpcom++); size += strlen(cp); } } // See if room for expansion. if (outbuff + size >= &outbuff[MAXLINE]) { com[0] = ap->argval; com[1] = 0; } // Save remainder of the buffer. if (*out) left = stkcopy(shp->stk, out); if (cmd_completion && mode == '\\') { cp = *com++; out = stpcpy(begin, path_basename(cp)); } else if (mode == '*') { if (ep->e_nlist && dir && var) { if (*cp == var) { cp++; } else { *begin++ = var; } out = stpcpy(begin, cp); var = 0; } else { out = stpcpy(begin, fmtx(shp, *com)); } com++; } else { out = stpcpy(begin, *com++); } if (mode == '\\') { char *saveout = ++out; while (*com && *begin) { if (cmd_completion) { out = overlaid(begin, path_basename(*com++), false); } else { out = overlaid(begin, *com++, false); } } mode = (out == saveout); if (out[-1] == 0) out--; if (mode && out[-1] != '/') { if (cmd_completion) { Namval_t *np; // Add as tracked alias. Pathcomp_t *pp; if (*cp == '/' && (pp = path_dirfind(shp->pathlist, cp, '/')) && (np = nv_search(begin, shp->track_tree, NV_ADD))) { path_alias(np, pp); } out = stpcpy(begin, cp); } // Add quotes if necessary. if ((cp = fmtx(shp, begin)) != begin) out = stpcpy(begin, cp); if (var == '$' && begin[-1] == '{') { *out = '}'; } else { *out = ' '; } *++out = 0; } else if ((cp = fmtx(shp, begin)) != begin) { out = stpcpy(begin, cp); if (out[-1] == '"' || out[-1] == '\'') *--out = 0; } if (*begin == 0 && begin[-1] != ' ') ed_ringbell(); } else { while (*com) { *out++ = ' '; out = stpcpy(out, fmtx(shp, *com++)); } } if (ep->e_nlist) { cp = com[-1]; if (cp[strlen(cp) - 1] != '/') { if (var == '$' && begin[-1] == '{') { *out = '}'; } else { *out = ' '; } out++; } else if (out[-1] == '"' || out[-1] == '\'') { out--; } *out = 0; } *cur = (out - outbuff); // Restore rest of buffer. if (left) out = stpcpy(out, left); *eol = (out - outbuff); } done: sh_offstate(shp, SH_FCOMPLETE); if (!ep->e_nlist) stkset(shp->stk, ep->e_stkptr, ep->e_stkoff); if (nomarkdirs) sh_offoption(shp, SH_MARKDIRS); { // First re-adjust cur. int c, n = 0; c = outbuff[*cur]; outbuff[*cur] = 0; for (out = outbuff; *out; n++) mb1char(&out); outbuff[*cur] = c; *cur = n; outbuff[*eol + 1] = 0; *eol = ed_internal(outbuff, (genchar *)outbuff); } return rval; }
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_shopt(int argc,register char *argv[],void *extra) { Shell_t *shp = (Shell_t*)extra; int n, f, ret=0; Shopt_t newflags=shp->options, opt; int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE; int setflag=0, quietflag=0, oflag=0; memset(&opt,0,sizeof(opt)); #if SHOPT_RAWONLY on_option(&newflags,SH_VIRAW); #endif while((n = optget(argv,sh_optshopt))) { switch(n) { case 'p': verbose&=~PRINT_VERBOSE; break; case 's': case 'u': setflag|=n=='s'?SET_SET:SET_UNSET; if(setflag==(SET_SET|SET_UNSET)) { errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously"); error_info.errors++; } break; case 'q': quietflag=1; break; case 'o': oflag=1; verbose&=~PRINT_SHOPT; break; case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); continue; case '?': errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); return(-1); } } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); argc -= opt_info.index; if(argc==0) { /* no args, -s => mask=current options, -u mask=~(current options) else mask=all bits */ if(setflag&SET_SET) opt=newflags; else if(setflag&SET_UNSET) for(n=0;n<4;n++) opt.v[n]=~newflags.v[n]; else memset(&opt,0xff,sizeof(opt)); setflag=SET_NOARGS; } while(argc>0) { f=1; n=sh_lookopt(argv[opt_info.index],&f); if(n<=0||(setflag && (is_option(&opt,SH_INTERACTIVE) || is_option(&opt,SH_RESTRICTED) || is_option(&opt,SH_RESTRICTED2) || is_option(&opt,SH_BASH) || is_option(&opt,SH_LOGIN_SHELL))) ||(oflag&&(n&SH_BASHOPT))) { errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]); error_info.errors++; ret=1; } else if(f) on_option(&opt,n&0xff); else off_option(&opt,n&0xff); opt_info.index++; argc--; } if(setflag&(SET_SET|SET_UNSET)) { if(setflag&SET_SET) { if(sh_isoption(SH_INTERACTIVE)) off_option(&opt,SH_NOEXEC); if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS)) { off_option(&newflags,SH_VI); off_option(&newflags,SH_EMACS); off_option(&newflags,SH_GMACS); } for(n=0;n<4;n++) newflags.v[n] |= opt.v[n]; } else if(setflag&SET_UNSET) for(n=0;n<4;n++) newflags.v[n] &= ~opt.v[n]; sh_applyopts(shp,newflags); shp->options = newflags; if(is_option(&newflags,SH_XTRACE)) sh_trace(argv,1); } else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */ { for(n=0;n<4;n++) ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]); } if(!quietflag&&!(setflag&(SET_SET|SET_UNSET))) sh_printopts(newflags,verbose,&opt); return(ret); }
// Print option settings on standard output. // If mode is inclusive or of PRINT_*. // If <mask> is set, only options with this mask value are displayed. void sh_printopts(Shell_t *shp, Shopt_t oflags, int mode, Shopt_t *mask) { const Shtable_t *tp; const char *name; int on; int value; if (!(mode & PRINT_NO_HEADER)) sfputr(sfstdout, sh_translate(e_heading), '\n'); if (mode & PRINT_TABLE) { size_t w; int c; int r; int i; c = 0; for (tp = shtab_options; (value = tp->sh_number); tp++) { if (mask && !is_option(mask, value & 0xff)) continue; name = tp->sh_name; if (name[0] == 'n' && name[1] == 'o' && name[2] != 't') name += 2; if (c < (w = strlen(name))) c = w; } c += 4; w = ed_window(); if (w < 2 * c) w = 2 * c; r = w / c; i = 0; for (tp = shtab_options; (value = tp->sh_number); tp++) { if (mask && !is_option(mask, value & 0xff)) continue; on = is_option(&oflags, value); name = tp->sh_name; if (name[0] == 'n' && name[1] == 'o' && name[2] != 't') { name += 2; on = !on; } if (++i >= r) { i = 0; sfprintf(sfstdout, "%s%s\n", on ? "" : "no", name); } else { sfprintf(sfstdout, "%s%-*s", on ? "" : "no", on ? c : (c - 2), name); } } if (i) sfputc(sfstdout, '\n'); return; } if (!(mode & (PRINT_ALL | PRINT_VERBOSE))) { // only print set options if (mode & PRINT_SHOPT) { sfwrite(sfstdout, "shopt -s", 3); } else { sfwrite(sfstdout, "set --default", 13); } } for (tp = shtab_options; (value = tp->sh_number); tp++) { if (mask && !is_option(mask, value & 0xff)) continue; if (sh_isoption(shp, SH_BASH)) { if (!(mode & PRINT_SHOPT) != !(value & SH_BASHOPT)) continue; } else if (value & (SH_BASHEXTRA | SH_BASHOPT)) { continue; } on = is_option(&oflags, value); name = tp->sh_name; if (name[0] == 'n' && name[1] == 'o' && name[2] != 't') { name += 2; on = !on; } if (mode & PRINT_VERBOSE) { sfputr(sfstdout, name, ' '); sfnputc(sfstdout, ' ', 24 - strlen(name)); sfputr(sfstdout, on ? sh_translate(e_on) : sh_translate(e_off), '\n'); } else if (mode & PRINT_ALL) { // print unset options also if (mode & PRINT_SHOPT) { sfprintf(sfstdout, "shopt -%c %s\n", on ? 's' : 'u', name); } else { sfprintf(sfstdout, "set %co %s\n", on ? '-' : '+', name); } } else if (!(value & SH_COMMANDLINE) && is_option(&oflags, value & 0xff)) { sfprintf(sfstdout, " %s%s%s", (mode & PRINT_SHOPT) ? "" : "--", on ? "" : "no", name); } } if (!(mode & (PRINT_VERBOSE | PRINT_ALL))) sfputc(sfstdout, '\n'); }
/* * used with command -x to run the command in multiple passes * spawn is non-zero when invoked via spawn * the exitval is set to the maximum for each execution */ static pid_t path_xargs(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn) { register char *cp, **av, **xv; char **avlast= &argv[shp->xargmax], **saveargs=0; char *const *ev; long size, left; int nlast=1,n,exitval=0; pid_t pid; if(shp->xargmin < 0) return((pid_t)-1); size = shp->gd->lim.arg_max-1024; for(ev=envp; cp= *ev; ev++) size -= strlen(cp)-1; for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++) size -= strlen(cp)-1; for(av=avlast; cp= *av; av++,nlast++) size -= strlen(cp)-1; av = &argv[shp->xargmin]; if(!spawn) job_clear(); shp->exitval = 0; while(av<avlast) { for(xv=av,left=size; left>0 && av<avlast;) left -= strlen(*av++)+1; /* leave at least two for last */ if(left<0 && (avlast-av)<2) av--; if(xv==&argv[shp->xargmin]) { n = nlast*sizeof(char*); saveargs = (char**)malloc(n); memcpy((void*)saveargs, (void*)av, n); memcpy((void*)av,(void*)avlast,n); } else { for(n=shp->xargmin; xv < av; xv++) argv[n++] = *xv; for(xv=avlast; cp= *xv; xv++) argv[n++] = cp; argv[n] = 0; } if(saveargs || av<avlast || (exitval && !spawn)) { if((pid=_spawnveg(shp,path,argv,envp,0)) < 0) return(-1); job_post(shp,pid,0); job_wait(pid); if(shp->exitval>exitval) exitval = shp->exitval; if(saveargs) { memcpy((void*)av,saveargs,n); free((void*)saveargs); saveargs = 0; } } else if(spawn && !sh_isoption(SH_PFSH)) { shp->xargexit = exitval; if(saveargs) free((void*)saveargs); return(_spawnveg(shp,path,argv,envp,spawn>>1)); } else { if(saveargs)
static int hist_write(Sfio_t *iop, const void *buff, int insize, Sfdisc_t *handle) #endif { History_t *hp = (History_t *)handle; char *bufptr = ((char *)buff) + insize; int c, size = insize; off_t cur; Shell_t *shp = hp->histshell; int saved = 0; char saveptr[HIST_MARKSZ]; if (!hp->histflush) return write(sffileno(iop), (char *)buff, size); if ((cur = lseek(sffileno(iop), (off_t)0, SEEK_END)) < 0) { errormsg(SH_DICT, 2, "hist_flush: EOF seek failed errno=%d", errno); return -1; } hp->histcnt = cur; // Remove whitespace from end of commands. while (--bufptr >= (char *)buff) { c = *bufptr; if (!isspace(c)) { if (c == '\\' && *(bufptr + 1) != '\n') bufptr++; break; } } // Don't count empty lines. if (++bufptr <= (char *)buff) return insize; *bufptr++ = '\n'; *bufptr++ = 0; size = bufptr - (char *)buff; if (hp->auditfp) { time_t t = time(NULL); sfprintf(hp->auditfp, "%u;%u;%s;%*s%c", sh_isoption(shp, SH_PRIVILEGED) ? shgd->euserid : shgd->userid, t, hp->tty, size, buff, 0); sfsync(hp->auditfp); } if (size & 01) { size++; *bufptr++ = 0; } hp->histcnt += size; c = hist_ind(hp, ++hp->histind); hp->histcmds[c] = hp->histcnt; if (hp->histflush > HIST_MARKSZ && hp->histcnt > hp->histmarker + HIST_BSIZE / 2) { memcpy(saveptr, bufptr, HIST_MARKSZ); saved = 1; hp->histcnt += HIST_MARKSZ; hist_marker(bufptr, hp->histind); hp->histmarker = hp->histcmds[hist_ind(hp, c)] = hp->histcnt; size += HIST_MARKSZ; } errno = 0; size = write(sffileno(iop), (char *)buff, size); if (saved) memcpy(bufptr, saveptr, HIST_MARKSZ); if (size >= 0) { hp->histwfail = 0; return insize; } return -1; }
int path_generate(Shell_t *shp, struct argnod *todo, struct argnod **arghead) { char *cp; int brace; struct argnod *ap; struct argnod *top = NULL; struct argnod *apin; char *pat, *rescan; char *format = NULL; char comma, range = 0; int first, last, incr, count = 0; char end_char; char tmp[32]; if (!sh_isoption(shp, SH_BRACEEXPAND)) return path_expand(shp, todo->argval, arghead); todo->argchn.ap = NULL; again: apin = ap = todo; todo = ap->argchn.ap; cp = ap->argval; range = comma = brace = 0; // First search for {...,...}. while (1) { switch (*cp++) { case '{': { if (brace++ == 0) pat = cp; break; } case '}': { if (--brace > 0) break; if (brace == 0 && comma && *cp != '(') goto endloop1; comma = brace = 0; break; } case '.': { if (brace != 1) break; if (*cp != '.') break; char *endc; incr = 1; if (isdigit(*pat) || *pat == '+' || *pat == '-') { first = strtol(pat, &endc, 0); if (endc == (cp - 1)) { last = strtol(cp + 1, &endc, 0); if (*endc == '.' && endc[1] == '.') { incr = strtol(endc + 2, &endc, 0); } else if (last < first) { incr = -1; } if (incr) { if (*endc == '%') { Sffmt_t fmt; memset(&fmt, 0, sizeof(fmt)); fmt.version = SFIO_VERSION; fmt.form = endc; fmt.extf = checkfmt; sfprintf(sfstdout, "%!", &fmt); if (!(fmt.flags & (SFFMT_LLONG | SFFMT_LDOUBLE))) { switch (fmt.fmt) { case 'c': case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { format = endc; endc = fmt.form; break; } default: { break; } } } } else { format = "%d"; } if (*endc == '}') { cp = endc + 1; range = 2; goto endloop1; } } } } else if ((cp[2] == '}' || (cp[2] == '.' && cp[3] == '.')) && ((*pat >= 'a' && *pat <= 'z' && cp[1] >= 'a' && cp[1] <= 'z') || (*pat >= 'A' && *pat <= 'Z' && cp[1] >= 'A' && cp[1] <= 'Z'))) { first = *pat; last = cp[1]; cp += 2; if (*cp == '.') { incr = strtol(cp + 2, &endc, 0); cp = endc; } else if (first > last) { incr = -1; } if (incr && *cp == '}') { cp++; range = 1; goto endloop1; } } cp++; break; } case ',': { if (brace == 1) comma = 1; break; } case '\\': { cp++; break; } case 0: { // insert on stack ap->argchn.ap = top; top = ap; if (todo) goto again; for (; ap; ap = apin) { apin = ap->argchn.ap; if (!sh_isoption(shp, SH_NOGLOB)) { brace = path_expand(shp, ap->argval, arghead); } else { ap->argchn.ap = *arghead; *arghead = ap; brace = 1; } if (brace) { count += brace; (*arghead)->argflag |= ARG_MAKE; } } return count; } default: { break; } } } endloop1: rescan = cp; cp = pat - 1; *cp = 0; while (1) { brace = 0; if (range) { if (range == 1) { pat[0] = first; cp = &pat[1]; } else { *(rescan - 1) = 0; pat = tmp; assert(format); sfsprintf(pat, sizeof(tmp), format, first); *(rescan - 1) = '}'; cp = &end_char; *cp = 0; } if (incr * (first + incr) > last * incr) { *cp = '}'; } else { first += incr; } } else { // generate each pattern and put on the todo list while (1) { switch (*++cp) { case '\\': { cp++; break; } case '{': { brace++; break; } case ',': { if (brace == 0) goto endloop2; break; } case '}': { if (--brace < 0) goto endloop2; } default: { break; } } } } endloop2: brace = *cp; *cp = 0; sh_sigcheck(shp); ap = (struct argnod *)stkseek(shp->stk, ARGVAL); ap->argflag = ARG_RAW; ap->argchn.ap = todo; sfputr(shp->stk, apin->argval, -1); sfputr(shp->stk, pat, -1); sfputr(shp->stk, rescan, -1); todo = ap = (struct argnod *)stkfreeze(shp->stk, 1); if (brace == '}') break; if (!range) pat = cp + 1; } goto again; }