/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn * * * ***********************************************************************/ /* * bash specific extensions * originally provided by Karsten Fleischer */ #include "defs.h" #include "path.h" #include "io.h" #include "builtins.h" #include "name.h" #ifndef BASH_MAJOR # define BASH_MAJOR "1" # define BASH_MINOR "0" # define BASH_PATCH "0" # define BASH_BUILD "0" # define BASH_RELEASE "experimental" #endif #define BASH_VERSION BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE extern const char bash_pre_rc[]; static char *login_files[4]; const char sh_bash1[] = "[B?Enable brace group expansion. This option is only availabe in bash " "compatibility mode. In ksh mode, brace group expansion is always on.]" "[P?Do not follow symbolic links, use physical directory structure " "instead. Only available in bash compatibility mode.]"; const char sh_bash2[] = "[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by " "the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets " "the value of that option; \b+O\b unsets it. If \ashopt_option\a is " "not supplied, the names and values of the shell options accepted by " "\bshopt\b are printed on the standard output. If the invocation " "option is \b+O\b, the output is displayed in a format that may be " "reused as input. Only available if invoked as \bbash\b.]" "[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the " "standard personal initialization file ~/.bashrc if the shell is " "interactive. Only available if invoked as \bbash\b.]" "[02:editing?For option compatibility with \bbash\b only. Ignored.]" "[03:profile?Read either the system-wide startup file or any of the " "personal initialization files. On by default for interactive " "shells. Only available if invoked as \bbash\b.]" "[04:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in " "POSIX mode is not the same as \bksh\b.]" "[05:version?Print version number and exit.]"; const char sh_optshopt[] = "+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]" "[-author?Karsten Fleischer ]" USAGE_LICENSE "[+NAME?shopt - set/unset variables controlling optional shell behavior]" "[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell " "behavior. With no options, or with the \b-p\b option, a list of all " "settable options is displayed, with an indication of whether or not " "each is set.]" "[p?Causes output to be displayed in a form that may be reused as input.]" "[s?Set each \aoptname\a.]" "[u?Unset each \aoptname\a.]" "[q?Suppress output (quiet mode). The return status indicates whether the " "\aoptname\a is set or unset. If multiple \aoptname\a arguments are " "given with \b-q\b, the return status is zero if all \aoptname\as are " "enabled; non-zero otherwise.]" "[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b " "option to the set builtin.]" "[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the " "display is limited to those options which are set or unset.]" "[+?\bshopt\b supports all bash options. Some settings do not have any effect " "or are are always on and cannot be changed.]" "[+?The value of \aoptname\a must be one of the following:]{" "[+cdable_vars?If set, arguments to the \bcd\b command are " "assumed to be names of variables whose values are to " "be used if the usual \bcd\b proceeding fails.]" "[+cdspell?Currently ignored.]" "[+checkhash?Always on.]" "[+checkwinsize?Currently ignored.]" "[+cmdhist?Always on.]" "[+dotglob?If set, include filenames beginning with a \b.\b " "in the results of pathname expansion.]" "[+execfail?Always on.]" "[+expand_aliases?Always on.]" "[+extglob?Enable extended pattern matching features.]" "[+histappend?Always on.]" "[+histreedit?If set and an edit mode is selected, the user " "is given the opportunity to re-edit a failed history " "substitution.]" "[+histverify?If set and an edit mode is selected, the result " "of a history substitution will not be executed " "immediately but be placed in the edit buffer for " "further modifications.]" "[+hostcomplete?Currently ignored.]" "[+huponexit?Currently ignored.]" "[+interactive_comments?Always on.]" "[+lithist?Always on.]" "[+login_shell?This option is set if the shell is started as " "a login shell. The value cannot be changed.]" "[+mailwarn?Currently ignored.]" "[+no_empty_cmd_completion?Always on.]" "[+nocaseglob?Match filenames in a case-insensitive fashion " "when performing filename expansion.]" "[+nullglob?Allows filename patterns which match no files to " "expand to a null string, rather than themselves.]" "[+progcomp?Currently ignored.]" "[+promptvars?Currently ignored.]" "[+restricted_shell?This option is set if the shell is started " "as a restricted shell. The value cannot be changed. " "It is not reset during execution of startup files, " "allowing the startup files to determine whether the " "shell is restricted.]" "[+shift_verbose?Currently ignored.]" "[+sourcepath?If set, the \b.\b builtin uses the value of PATH " "to find the directory containing the file supplied " "as an argument.]" "[+xpg_echo?If set, the \becho\b and \bprint\b builtins " "expand backslash-escape sequences.]" "}" "\n" "\n[optname ...]\n" "\n" "[+EXIT STATUS?]{" "[+?The return status when listing options is zero if all \aoptnames\a " "are enabled, non-zero otherwise. When setting or unsetting options, " "the return status is zero unless an \aoptname\a is not a valid shell " "option.]" "}" "[+SEE ALSO?\bset\b(1)]" ; /* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */ static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp) { if(val) sh_onoption(SH_DOTGLOB); else sh_offoption(SH_DOTGLOB); nv_putv(np,val,flags,fp); } const Namdisc_t SH_GLOBIGNORE_disc = { sizeof(Namfun_t), put_globignore }; /* FUNCNAME discipline */ struct funcname { Namfun_t hdr; }; static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { /* bash silently returns with an error when FUNCNAME is set, unsetting FUNCNAME is allowed */ if(val && !(flags&NV_RDONLY)) error_info.exit(1); nv_putv(np,val,flags,fp); } const Namdisc_t SH_FUNCNAME_disc = { sizeof(struct funcname), put_funcname }; #define SET_SET 1 #define SET_UNSET 2 #define SET_NOARGS 4 /* shopt builtin */ 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); } /* 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(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); }