1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * David Korn <dgk@research.att.com> * 18 * * 19 ***********************************************************************/ 20 /* 21 * bash specific extensions 22 * originally provided by Karsten Fleischer 23 */ 24 25 #include "defs.h" 26 #include "path.h" 27 #include "io.h" 28 #include "builtins.h" 29 #include "name.h" 30 31 #ifndef BASH_MAJOR 32 # define BASH_MAJOR "1" 33 # define BASH_MINOR "0" 34 # define BASH_PATCH "0" 35 # define BASH_BUILD "0" 36 # define BASH_RELEASE "experimental" 37 #endif 38 #define BASH_VERSION BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE 39 40 41 void sh_applyopts(Shopt_t); 42 43 extern const char bash_pre_rc[]; 44 45 static char *login_files[4]; 46 47 const char sh_bash1[] = 48 "[B?Enable brace group expansion. This option is only availabe in bash " 49 "compatibility mode. In ksh mode, brace group expansion is always on.]" 50 "[P?Do not follow symbolic links, use physical directory structure " 51 "instead. Only available in bash compatibility mode.]"; 52 const char sh_bash2[] = 53 "[l:login?Make the shell act as if it had been invoked as a login shell. " 54 "Only available if invoked as \bbash\b.]" 55 "[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by " 56 "the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets " 57 "the value of that option; \b+O\b unsets it. If \ashopt_option\a is " 58 "not supplied, the names and values of the shell options accepted by " 59 "\bshopt\b are printed on the standard output. If the invocation " 60 "option is \b+O\b, the output is displayed in a format that may be " 61 "reused as input. Only available if invoked as \bbash\b.]" 62 "[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the " 63 "standard personal initialization file ~/.bashrc if the shell is " 64 "interactive. Only available if invoked as \bbash\b.]" 65 "[02:editing?For option compatibility with \bbash\b only. Ignored.]" 66 "[03:profile?Read either the system-wide startup file or any of the " 67 "personal initialization files. On by default for interactive " 68 "shells. Only available if invoked as \bbash\b.]" 69 "[04:rc?Read and execute the personal initialization file " 70 "\b$HOME/.bashrc\b. On by default for interactive shells. Only " 71 "available if invoked as \bbash\b.]" 72 "[05:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in " 73 "POSIX mode is not the same as \bksh\b.]" 74 "[06:version?Print version number and exit.]"; 75 76 const char sh_optshopt[] = 77 "+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]" 78 "[-author?Karsten Fleischer <K.Fleischer@omnium.de>]" 79 USAGE_LICENSE 80 "[+NAME?shopt - set/unset variables controlling optional shell behavior]" 81 "[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell " 82 "behavior. With no options, or with the \b-p\b option, a list of all " 83 "settable options is displayed, with an indication of whether or not " 84 "each is set.]" 85 "[p?Causes output to be displayed in a form that may be reused as input.]" 86 "[s?Set each \aoptname\a.]" 87 "[u?Unset each \aoptname\a.]" 88 "[q?Suppress output (quiet mode). The return status indicates whether the " 89 "\aoptname\a is set or unset. If multiple \aoptname\a arguments are " 90 "given with \b-q\b, the return status is zero if all \aoptname\as are " 91 "enabled; non-zero otherwise.]" 92 "[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b " 93 "option to the set builtin.]" 94 "[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the " 95 "display is limited to those options which are set or unset.]" 96 "[+?\bshopt\b supports all bash options. Some settings do not have any effect " 97 "or are are always on and cannot be changed.]" 98 "[+?The value of \aoptname\a must be one of the following:]{" 99 "[+cdable_vars?If set, arguments to the \bcd\b command are " 100 "assumed to be names of variables whose values are to " 101 "be used if the usual \bcd\b proceeding fails.]" 102 "[+cdspell?Currently ignored.]" 103 "[+checkhash?Always on.]" 104 "[+checkwinsize?Currently ignored.]" 105 "[+cmdhist?Always on.]" 106 "[+dotglob?If set, include filenames beginning with a \b.\b " 107 "in the results of pathname expansion.]" 108 "[+execfail?Always on.]" 109 "[+expand_aliases?Always on.]" 110 "[+extglob?Enable extended pattern matching features.]" 111 "[+histappend?Always on.]" 112 "[+histreedit?If set and an edit mode is selected, the user " 113 "is given the opportunity to re-edit a failed history " 114 "substitution.]" 115 "[+histverify?If set and an edit mode is selected, the result " 116 "of a history substitution will not be executed " 117 "immediately but be placed in the edit buffer for " 118 "further modifications.]" 119 "[+hostcomplete?Currently ignored.]" 120 "[+huponexit?Currently ignored.]" 121 "[+interactive_comments?Always on.]" 122 "[+lithist?Always on.]" 123 "[+login_shell?This option is set if the shell is started as " 124 "a login shell. The value cannot be changed.]" 125 "[+mailwarn?Currently ignored.]" 126 "[+no_empty_cmd_completion?Always on.]" 127 "[+nocaseglob?Match filenames in a case-insensitive fashion " 128 "when performing filename expansion.]" 129 "[+nullglob?Allows filename patterns which match no files to " 130 "expand to a null string, rather than themselves.]" 131 "[+progcomp?Currently ignored.]" 132 "[+promptvars?Currently ignored.]" 133 "[+restricted_shell?This option is set if the shell is started " 134 "as a restricted shell. The value cannot be changed. " 135 "It is not reset during execution of startup files, " 136 "allowing the startup files to determine whether the " 137 "shell is restricted.]" 138 "[+shift_verbose?Currently ignored.]" 139 "[+sourcepath?If set, the \b.\b builtin uses the value of PATH " 140 "to find the directory containing the file supplied " 141 "as an argument.]" 142 "[+xpg_echo?If set, the \becho\b and \bprint\b builtins " 143 "expand backslash-escape sequences.]" 144 "}" 145 "\n" 146 "\n[optname ...]\n" 147 "\n" 148 "[+EXIT STATUS?]{" 149 "[+?The return status when listing options is zero if all \aoptnames\a " 150 "are enabled, non-zero otherwise. When setting or unsetting options, " 151 "the return status is zero unless an \aoptname\a is not a valid shell " 152 "option.]" 153 "}" 154 155 "[+SEE ALSO?\bset\b(1)]" 156 ; 157 158 /* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */ 159 160 static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp) 161 { 162 if(val) 163 sh_onoption(SH_DOTGLOB); 164 else 165 sh_offoption(SH_DOTGLOB); 166 167 nv_putv(np,val,flags,fp); 168 } 169 170 const Namdisc_t SH_GLOBIGNORE_disc = { sizeof(Namfun_t), put_globignore }; 171 172 /* FUNCNAME discipline */ 173 174 struct funcname 175 { 176 Namfun_t hdr; 177 }; 178 179 static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp) 180 { 181 /* bash silently returns with an error when FUNCNAME is set, 182 unsetting FUNCNAME is allowed */ 183 if(val && !(flags&NV_RDONLY)) 184 error_info.exit(1); 185 186 nv_putv(np,val,flags,fp); 187 } 188 189 const Namdisc_t SH_FUNCNAME_disc = { sizeof(struct funcname), put_funcname }; 190 191 #define SET_SET 1 192 #define SET_UNSET 2 193 #define SET_NOARGS 4 194 195 /* shopt builtin */ 196 197 int b_shopt(int argc,register char *argv[],void *extra) 198 { 199 Shell_t *shp = (Shell_t*)extra; 200 int n, f, ret=0; 201 Shopt_t newflags=shp->options, opt; 202 int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE; 203 int setflag=0, quietflag=0, oflag=0; 204 memset(&opt,0,sizeof(opt)); 205 #if SHOPT_RAWONLY 206 on_option(&newflags,SH_VIRAW); 207 #endif 208 while((n = optget(argv,sh_optshopt))) 209 { 210 switch(n) 211 { 212 case 'p': 213 verbose&=~PRINT_VERBOSE; 214 break; 215 case 's': 216 case 'u': 217 setflag|=n=='s'?SET_SET:SET_UNSET; 218 if(setflag==(SET_SET|SET_UNSET)) 219 { 220 errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously"); 221 error_info.errors++; 222 } 223 break; 224 case 'q': 225 quietflag=1; 226 break; 227 case 'o': 228 oflag=1; 229 verbose&=~PRINT_SHOPT; 230 break; 231 case ':': 232 errormsg(SH_DICT,2, "%s", opt_info.arg); 233 continue; 234 case '?': 235 errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); 236 return(-1); 237 } 238 } 239 if(error_info.errors) 240 errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); 241 argc -= opt_info.index; 242 if(argc==0) 243 { 244 /* no args, -s => mask=current options, -u mask=~(current options) 245 else mask=all bits */ 246 if(setflag&SET_SET) 247 opt=newflags; 248 else if(setflag&SET_UNSET) 249 for(n=0;n<4;n++) 250 opt.v[n]=~newflags.v[n]; 251 else 252 memset(&opt,0xff,sizeof(opt)); 253 setflag=SET_NOARGS; 254 } 255 while(argc>0) 256 { 257 f=1; 258 n=sh_lookopt(argv[opt_info.index],&f); 259 if(n<=0||(setflag 260 && (is_option(&opt,SH_INTERACTIVE) 261 || is_option(&opt,SH_RESTRICTED) 262 || is_option(&opt,SH_RESTRICTED2) 263 || is_option(&opt,SH_BASH) 264 || is_option(&opt,SH_LOGIN_SHELL))) 265 ||(oflag&&(n&SH_BASHOPT))) 266 { 267 errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]); 268 error_info.errors++; 269 ret=1; 270 } 271 else if(f) 272 on_option(&opt,n&0xff); 273 else 274 off_option(&opt,n&0xff); 275 opt_info.index++; 276 argc--; 277 } 278 if(setflag&(SET_SET|SET_UNSET)) 279 { 280 if(setflag&SET_SET) 281 { 282 if(sh_isoption(SH_INTERACTIVE)) 283 off_option(&opt,SH_NOEXEC); 284 if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS)) 285 { 286 off_option(&newflags,SH_VI); 287 off_option(&newflags,SH_EMACS); 288 off_option(&newflags,SH_GMACS); 289 } 290 for(n=0;n<4;n++) 291 newflags.v[n] |= opt.v[n]; 292 } 293 else if(setflag&SET_UNSET) 294 for(n=0;n<4;n++) 295 newflags.v[n] &= ~opt.v[n]; 296 sh_applyopts(newflags); 297 shp->options = newflags; 298 if(is_option(&newflags,SH_XTRACE)) 299 sh_trace(argv,1); 300 } 301 else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */ 302 { 303 for(n=0;n<4;n++) 304 ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]); 305 } 306 if(!quietflag&&!(setflag&(SET_SET|SET_UNSET))) 307 sh_printopts(newflags,verbose,&opt); 308 return(ret); 309 } 310 311 /* mode = 0: init, called two times 312 before parsing shell args with SH_PREINIT state turned on 313 second time after sh_init() is through and with SH_PREINIT state turned off 314 mode > 1: re-init 315 mode < 0: shutdown 316 */ 317 318 void bash_init(int mode) 319 { 320 Sfio_t *iop; 321 Namval_t *np; 322 int n=0,xtrace,verbose; 323 if(mode>0) 324 goto reinit; 325 if(mode < 0) 326 { 327 /* termination code */ 328 if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX)) 329 sh_source(&sh, NiL, sh_mactry((char*)e_bash_logout)); 330 return; 331 } 332 333 if(sh_isstate(SH_PREINIT)) 334 { /* pre-init stage */ 335 if(sh_isoption(SH_RESTRICTED)) 336 sh_onoption(SH_RESTRICTED2); 337 sh_onoption(SH_HISTORY2); 338 sh_onoption(SH_INTERACTIVE_COMM); 339 sh_onoption(SH_SOURCEPATH); 340 sh_onoption(SH_HISTAPPEND); 341 sh_onoption(SH_CMDHIST); 342 sh_onoption(SH_LITHIST); 343 sh_onoption(SH_NOEMPTYCMDCOMPL); 344 if(sh.login_sh==2) 345 sh_onoption(SH_LOGIN_SHELL); 346 if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0) 347 sh_onoption(SH_POSIX); 348 if(strcmp(astconf("UNIVERSE",0,0),"att")==0) 349 sh_onoption(SH_XPG_ECHO); 350 else 351 sh_offoption(SH_XPG_ECHO); 352 if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0) 353 sh_onoption(SH_PHYSICAL); 354 else 355 sh_offoption(SH_PHYSICAL); 356 357 /* add builtins */ 358 sh_addbuiltin("shopt", b_shopt, &sh); 359 360 /* set up some variables needed for --version 361 * needs to go here because --version option is parsed before the init script. 362 */ 363 if(np=nv_open("HOSTTYPE",sh.var_tree,0)) 364 nv_putval(np, BASH_HOSTTYPE, NV_NOFREE); 365 if(np=nv_open("MACHTYPE",sh.var_tree,0)) 366 nv_putval(np, BASH_MACHTYPE, NV_NOFREE); 367 if(np=nv_open("BASH_VERSION",sh.var_tree,0)) 368 nv_putval(np, BASH_VERSION, NV_NOFREE); 369 if(np=nv_open("BASH_VERSINFO",sh.var_tree,0)) 370 { 371 char *argv[7]; 372 argv[0] = BASH_MAJOR; 373 argv[1] = BASH_MINOR; 374 argv[2] = BASH_PATCH; 375 argv[3] = BASH_BUILD; 376 argv[4] = BASH_RELEASE; 377 argv[5] = BASH_MACHTYPE; 378 argv[6] = 0; 379 nv_setvec(np, 0, 6, argv); 380 nv_onattr(np,NV_RDONLY); 381 } 382 return; 383 } 384 385 /* rest of init stage */ 386 387 /* restrict BASH_ENV */ 388 if(np=nv_open("BASH_ENV",sh.var_tree,0)) 389 { 390 const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT); 391 Namfun_t *fp = calloc(dp->dsize,1); 392 fp->disc = dp; 393 nv_disc(np, fp, 0); 394 } 395 396 /* open GLOBIGNORE node */ 397 if(np=nv_open("GLOBIGNORE",sh.var_tree,0)) 398 { 399 const Namdisc_t *dp = &SH_GLOBIGNORE_disc; 400 Namfun_t *fp = calloc(dp->dsize,1); 401 fp->disc = dp; 402 nv_disc(np, fp, 0); 403 } 404 405 /* set startup files */ 406 n=0; 407 if(!sh_isoption(SH_NOPROFILE)) 408 { 409 if(!sh_isoption(SH_POSIX)) 410 { 411 login_files[n++] = (char*)e_bash_profile; 412 login_files[n++] = (char*)e_bash_login; 413 } 414 login_files[n++] = (char*)e_profile; 415 } 416 sh.login_files = login_files; 417 reinit: 418 xtrace = sh_isoption(SH_XTRACE); 419 sh_offoption(SH_XTRACE); 420 verbose = sh_isoption(SH_VERBOSE); 421 sh_offoption(SH_VERBOSE); 422 if(np = nv_open("SHELLOPTS", sh.var_tree, NV_NOADD)) 423 nv_offattr(np,NV_RDONLY); 424 iop = sfopen(NULL, bash_pre_rc, "s"); 425 sh_eval(iop,0); 426 if(xtrace) 427 sh_offoption(SH_XTRACE); 428 if(verbose) 429 sh_offoption(SH_VERBOSE); 430 } 431