options.c revision 59436
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836150Scharnier#if 0 3936150Scharnierstatic char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 4036150Scharnier#endif 4136150Scharnierstatic const char rcsid[] = 4250471Speter "$FreeBSD: head/bin/sh/options.c 59436 2000-04-20 09:49:16Z cracauer $"; 431556Srgrimes#endif /* not lint */ 441556Srgrimes 4517987Speter#include <signal.h> 4617987Speter#include <unistd.h> 4717987Speter#include <stdlib.h> 4817987Speter 491556Srgrimes#include "shell.h" 501556Srgrimes#define DEFINE_OPTIONS 511556Srgrimes#include "options.h" 521556Srgrimes#undef DEFINE_OPTIONS 531556Srgrimes#include "nodes.h" /* for other header files */ 541556Srgrimes#include "eval.h" 551556Srgrimes#include "jobs.h" 561556Srgrimes#include "input.h" 571556Srgrimes#include "output.h" 581556Srgrimes#include "trap.h" 591556Srgrimes#include "var.h" 601556Srgrimes#include "memalloc.h" 611556Srgrimes#include "error.h" 621556Srgrimes#include "mystring.h" 6317987Speter#ifndef NO_HISTORY 6417987Speter#include "myhistedit.h" 6517987Speter#endif 661556Srgrimes 671556Srgrimeschar *arg0; /* value of $0 */ 681556Srgrimesstruct shparam shellparam; /* current positional parameters */ 691556Srgrimeschar **argptr; /* argument list for builtin commands */ 7059436Scracauerchar *shoptarg; /* set by nextopt (like getopt) */ 711556Srgrimeschar *optptr; /* used by nextopt */ 721556Srgrimes 731556Srgrimeschar *minusc; /* argument to -c option */ 741556Srgrimes 751556Srgrimes 7617987SpeterSTATIC void options __P((int)); 7717987SpeterSTATIC void minus_o __P((char *, int)); 7817987SpeterSTATIC void setoption __P((int, int)); 7920425SsteveSTATIC int getopts __P((char *, char *, char **, char ***, char **)); 801556Srgrimes 811556Srgrimes 821556Srgrimes/* 831556Srgrimes * Process the shell command line arguments. 841556Srgrimes */ 851556Srgrimes 861556Srgrimesvoid 871556Srgrimesprocargs(argc, argv) 8817987Speter int argc; 891556Srgrimes char **argv; 9017987Speter{ 911556Srgrimes int i; 921556Srgrimes 931556Srgrimes argptr = argv; 941556Srgrimes if (argc > 0) 951556Srgrimes argptr++; 961556Srgrimes for (i = 0; i < NOPTS; i++) 971556Srgrimes optlist[i].val = 2; 9819240Ssteve privileged = (getuid() != geteuid() || getgid() != getegid()); 991556Srgrimes options(1); 1001556Srgrimes if (*argptr == NULL && minusc == NULL) 1011556Srgrimes sflag = 1; 1021556Srgrimes if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 1031556Srgrimes iflag = 1; 1041556Srgrimes if (mflag == 2) 1051556Srgrimes mflag = iflag; 1061556Srgrimes for (i = 0; i < NOPTS; i++) 1071556Srgrimes if (optlist[i].val == 2) 1081556Srgrimes optlist[i].val = 0; 1091556Srgrimes arg0 = argv[0]; 1101556Srgrimes if (sflag == 0 && minusc == NULL) { 1111556Srgrimes commandname = arg0 = *argptr++; 1121556Srgrimes setinputfile(commandname, 0); 1131556Srgrimes } 11420425Ssteve /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 11525227Ssteve if (argptr && minusc && *argptr) 11611111Sjoerg arg0 = *argptr++; 11720742Ssteve 1181556Srgrimes shellparam.p = argptr; 11920742Ssteve shellparam.reset = 1; 1201556Srgrimes /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 1211556Srgrimes while (*argptr) { 1221556Srgrimes shellparam.nparam++; 1231556Srgrimes argptr++; 1241556Srgrimes } 1251556Srgrimes optschanged(); 1261556Srgrimes} 1271556Srgrimes 1281556Srgrimes 12917987Spetervoid 13017987Speteroptschanged() 13117987Speter{ 1321556Srgrimes setinteractive(iflag); 13317987Speter#ifndef NO_HISTORY 1341556Srgrimes histedit(); 13517987Speter#endif 1361556Srgrimes setjobctl(mflag); 1371556Srgrimes} 1381556Srgrimes 1391556Srgrimes/* 1401556Srgrimes * Process shell options. The global variable argptr contains a pointer 1411556Srgrimes * to the argument list; we advance it past the options. 1421556Srgrimes */ 1431556Srgrimes 1441556SrgrimesSTATIC void 14520425Ssteveoptions(cmdline) 14617987Speter int cmdline; 14717987Speter{ 14825227Ssteve char *p; 1491556Srgrimes int val; 1501556Srgrimes int c; 1511556Srgrimes 1521556Srgrimes if (cmdline) 1531556Srgrimes minusc = NULL; 1541556Srgrimes while ((p = *argptr) != NULL) { 1551556Srgrimes argptr++; 1561556Srgrimes if ((c = *p++) == '-') { 1571556Srgrimes val = 1; 15817987Speter if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 1591556Srgrimes if (!cmdline) { 1601556Srgrimes /* "-" means turn off -x and -v */ 1611556Srgrimes if (p[0] == '\0') 1621556Srgrimes xflag = vflag = 0; 1631556Srgrimes /* "--" means reset params */ 1641556Srgrimes else if (*argptr == NULL) 16517987Speter setparam(argptr); 1661556Srgrimes } 1671556Srgrimes break; /* "-" or "--" terminates options */ 1681556Srgrimes } 1691556Srgrimes } else if (c == '+') { 1701556Srgrimes val = 0; 1711556Srgrimes } else { 1721556Srgrimes argptr--; 1731556Srgrimes break; 1741556Srgrimes } 1751556Srgrimes while ((c = *p++) != '\0') { 1761556Srgrimes if (c == 'c' && cmdline) { 1771556Srgrimes char *q; 1781556Srgrimes#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 1791556Srgrimes if (*p == '\0') 1801556Srgrimes#endif 1811556Srgrimes q = *argptr++; 1821556Srgrimes if (q == NULL || minusc != NULL) 1831556Srgrimes error("Bad -c option"); 1841556Srgrimes minusc = q; 1851556Srgrimes#ifdef NOHACK 1861556Srgrimes break; 1871556Srgrimes#endif 1881556Srgrimes } else if (c == 'o') { 1891556Srgrimes minus_o(*argptr, val); 1901556Srgrimes if (*argptr) 1911556Srgrimes argptr++; 1921556Srgrimes } else { 19319240Ssteve if (c == 'p' && !val && privileged) { 19419240Ssteve (void) setuid(getuid()); 19519240Ssteve (void) setgid(getgid()); 19619240Ssteve } 1971556Srgrimes setoption(c, val); 1981556Srgrimes } 1991556Srgrimes } 2001556Srgrimes } 2011556Srgrimes} 2021556Srgrimes 2031556SrgrimesSTATIC void 2041556Srgrimesminus_o(name, val) 2051556Srgrimes char *name; 2061556Srgrimes int val; 2071556Srgrimes{ 2081556Srgrimes int i; 2091556Srgrimes 2101556Srgrimes if (name == NULL) { 2111556Srgrimes out1str("Current option settings\n"); 2121556Srgrimes for (i = 0; i < NOPTS; i++) 2131556Srgrimes out1fmt("%-16s%s\n", optlist[i].name, 2141556Srgrimes optlist[i].val ? "on" : "off"); 2151556Srgrimes } else { 2161556Srgrimes for (i = 0; i < NOPTS; i++) 2171556Srgrimes if (equal(name, optlist[i].name)) { 21819240Ssteve if (!val && privileged && equal(name, "privileged")) { 21919240Ssteve (void) setuid(getuid()); 22019240Ssteve (void) setgid(getgid()); 22119240Ssteve } 2221556Srgrimes setoption(optlist[i].letter, val); 2231556Srgrimes return; 2241556Srgrimes } 2251556Srgrimes error("Illegal option -o %s", name); 2261556Srgrimes } 2271556Srgrimes} 2281556Srgrimes 2298855Srgrimes 2301556SrgrimesSTATIC void 2311556Srgrimessetoption(flag, val) 2321556Srgrimes char flag; 2331556Srgrimes int val; 2341556Srgrimes { 2351556Srgrimes int i; 2361556Srgrimes 2371556Srgrimes for (i = 0; i < NOPTS; i++) 2381556Srgrimes if (optlist[i].letter == flag) { 2391556Srgrimes optlist[i].val = val; 2401556Srgrimes if (val) { 2411556Srgrimes /* #%$ hack for ksh semantics */ 2421556Srgrimes if (flag == 'V') 2431556Srgrimes Eflag = 0; 2441556Srgrimes else if (flag == 'E') 2451556Srgrimes Vflag = 0; 2461556Srgrimes } 2471556Srgrimes return; 2481556Srgrimes } 2491556Srgrimes error("Illegal option -%c", flag); 2501556Srgrimes} 2511556Srgrimes 2521556Srgrimes 2531556Srgrimes 2541556Srgrimes#ifdef mkinit 2551556SrgrimesINCLUDE "options.h" 2561556Srgrimes 2571556SrgrimesSHELLPROC { 2581556Srgrimes int i; 2591556Srgrimes 2601556Srgrimes for (i = 0; i < NOPTS; i++) 2611556Srgrimes optlist[i].val = 0; 2621556Srgrimes optschanged(); 2631556Srgrimes 2641556Srgrimes} 2651556Srgrimes#endif 2661556Srgrimes 2671556Srgrimes 2681556Srgrimes/* 2691556Srgrimes * Set the shell parameters. 2701556Srgrimes */ 2711556Srgrimes 2721556Srgrimesvoid 2731556Srgrimessetparam(argv) 2741556Srgrimes char **argv; 2751556Srgrimes { 2761556Srgrimes char **newparam; 2771556Srgrimes char **ap; 2781556Srgrimes int nparam; 2791556Srgrimes 2801556Srgrimes for (nparam = 0 ; argv[nparam] ; nparam++); 2811556Srgrimes ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 2821556Srgrimes while (*argv) { 2831556Srgrimes *ap++ = savestr(*argv++); 2841556Srgrimes } 2851556Srgrimes *ap = NULL; 2861556Srgrimes freeparam(&shellparam); 2871556Srgrimes shellparam.malloc = 1; 2881556Srgrimes shellparam.nparam = nparam; 2891556Srgrimes shellparam.p = newparam; 2901556Srgrimes shellparam.optnext = NULL; 2911556Srgrimes} 2921556Srgrimes 2931556Srgrimes 2941556Srgrimes/* 2951556Srgrimes * Free the list of positional parameters. 2961556Srgrimes */ 2971556Srgrimes 2981556Srgrimesvoid 2991556Srgrimesfreeparam(param) 3001556Srgrimes struct shparam *param; 3011556Srgrimes { 3021556Srgrimes char **ap; 3031556Srgrimes 3041556Srgrimes if (param->malloc) { 3051556Srgrimes for (ap = param->p ; *ap ; ap++) 3061556Srgrimes ckfree(*ap); 3071556Srgrimes ckfree(param->p); 3081556Srgrimes } 3091556Srgrimes} 3101556Srgrimes 3111556Srgrimes 3121556Srgrimes 3131556Srgrimes/* 3141556Srgrimes * The shift builtin command. 3151556Srgrimes */ 3161556Srgrimes 31717987Speterint 31817987Spetershiftcmd(argc, argv) 31917987Speter int argc; 32020425Ssteve char **argv; 32117987Speter{ 3221556Srgrimes int n; 3231556Srgrimes char **ap1, **ap2; 3241556Srgrimes 3251556Srgrimes n = 1; 3261556Srgrimes if (argc > 1) 3271556Srgrimes n = number(argv[1]); 3281556Srgrimes if (n > shellparam.nparam) 3291556Srgrimes error("can't shift that many"); 3301556Srgrimes INTOFF; 3311556Srgrimes shellparam.nparam -= n; 3321556Srgrimes for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 3331556Srgrimes if (shellparam.malloc) 3341556Srgrimes ckfree(*ap1); 3351556Srgrimes } 3361556Srgrimes ap2 = shellparam.p; 3371556Srgrimes while ((*ap2++ = *ap1++) != NULL); 3381556Srgrimes shellparam.optnext = NULL; 3391556Srgrimes INTON; 3401556Srgrimes return 0; 3411556Srgrimes} 3421556Srgrimes 3431556Srgrimes 3441556Srgrimes 3451556Srgrimes/* 3461556Srgrimes * The set command builtin. 3471556Srgrimes */ 3481556Srgrimes 34917987Speterint 35017987Spetersetcmd(argc, argv) 35117987Speter int argc; 35220425Ssteve char **argv; 35317987Speter{ 3541556Srgrimes if (argc == 1) 3551556Srgrimes return showvarscmd(argc, argv); 3561556Srgrimes INTOFF; 3571556Srgrimes options(0); 3581556Srgrimes optschanged(); 3591556Srgrimes if (*argptr != NULL) { 3601556Srgrimes setparam(argptr); 3611556Srgrimes } 3621556Srgrimes INTON; 3631556Srgrimes return 0; 3641556Srgrimes} 3651556Srgrimes 3661556Srgrimes 36720425Sstevevoid 36820425Sstevegetoptsreset(value) 36920425Ssteve const char *value; 37020425Ssteve{ 37120425Ssteve if (number(value) == 1) { 37220425Ssteve shellparam.optnext = NULL; 37320425Ssteve shellparam.reset = 1; 37420425Ssteve } 37520425Ssteve} 37620425Ssteve 3771556Srgrimes/* 3781556Srgrimes * The getopts builtin. Shellparam.optnext points to the next argument 3791556Srgrimes * to be processed. Shellparam.optptr points to the next character to 3801556Srgrimes * be processed in the current argument. If shellparam.optnext is NULL, 3811556Srgrimes * then it's the first time getopts has been called. 3821556Srgrimes */ 3831556Srgrimes 38417987Speterint 38517987Spetergetoptscmd(argc, argv) 38617987Speter int argc; 38720425Ssteve char **argv; 38817987Speter{ 38920425Ssteve char **optbase = NULL; 39020425Ssteve 39120425Ssteve if (argc < 3) 39220425Ssteve error("Usage: getopts optstring var [arg]"); 39320425Ssteve else if (argc == 3) 39420425Ssteve optbase = shellparam.p; 39520425Ssteve else 39620425Ssteve optbase = &argv[3]; 39720425Ssteve 39820425Ssteve if (shellparam.reset == 1) { 39920425Ssteve shellparam.optnext = optbase; 40020425Ssteve shellparam.optptr = NULL; 40120425Ssteve shellparam.reset = 0; 40220425Ssteve } 40320425Ssteve 40420425Ssteve return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 40520425Ssteve &shellparam.optptr); 40620425Ssteve} 40720425Ssteve 40820425SsteveSTATIC int 40920425Sstevegetopts(optstr, optvar, optfirst, optnext, optptr) 41020425Ssteve char *optstr; 41120425Ssteve char *optvar; 41220425Ssteve char **optfirst; 41320425Ssteve char ***optnext; 41420425Ssteve char **optptr; 41520425Ssteve{ 41625227Ssteve char *p, *q; 41720425Ssteve char c = '?'; 41820425Ssteve int done = 0; 41920425Ssteve int ind = 0; 42020425Ssteve int err = 0; 4211556Srgrimes char s[10]; 4221556Srgrimes 42320425Ssteve if ((p = *optptr) == NULL || *p == '\0') { 42420425Ssteve /* Current word is done, advance */ 42520425Ssteve if (*optnext == NULL) 42620425Ssteve return 1; 42720425Ssteve p = **optnext; 4281556Srgrimes if (p == NULL || *p != '-' || *++p == '\0') { 4291556Srgrimesatend: 43020742Ssteve ind = *optnext - optfirst + 1; 43120425Ssteve *optnext = NULL; 43220742Ssteve p = NULL; 43320425Ssteve done = 1; 43420425Ssteve goto out; 4351556Srgrimes } 43620425Ssteve (*optnext)++; 4371556Srgrimes if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 4381556Srgrimes goto atend; 4391556Srgrimes } 44020425Ssteve 4411556Srgrimes c = *p++; 44220425Ssteve for (q = optstr; *q != c; ) { 4431556Srgrimes if (*q == '\0') { 44420425Ssteve if (optstr[0] == ':') { 44520425Ssteve s[0] = c; 44620425Ssteve s[1] = '\0'; 44720425Ssteve err |= setvarsafe("OPTARG", s, 0); 44820425Ssteve } 44920425Ssteve else { 45020425Ssteve out1fmt("Illegal option -%c\n", c); 45120425Ssteve (void) unsetvar("OPTARG"); 45220425Ssteve } 4531556Srgrimes c = '?'; 45420425Ssteve goto bad; 4551556Srgrimes } 4561556Srgrimes if (*++q == ':') 4571556Srgrimes q++; 4581556Srgrimes } 45920425Ssteve 4601556Srgrimes if (*++q == ':') { 46120425Ssteve if (*p == '\0' && (p = **optnext) == NULL) { 46220425Ssteve if (optstr[0] == ':') { 46320425Ssteve s[0] = c; 46420425Ssteve s[1] = '\0'; 46520425Ssteve err |= setvarsafe("OPTARG", s, 0); 46620425Ssteve c = ':'; 46720425Ssteve } 46820425Ssteve else { 46920425Ssteve out1fmt("No arg for -%c option\n", c); 47020425Ssteve (void) unsetvar("OPTARG"); 47120425Ssteve c = '?'; 47220425Ssteve } 47320425Ssteve goto bad; 4741556Srgrimes } 47520425Ssteve 47620425Ssteve if (p == **optnext) 47720425Ssteve (*optnext)++; 47820425Ssteve setvarsafe("OPTARG", p, 0); 4791556Srgrimes p = NULL; 4801556Srgrimes } 48120425Ssteve else 48220425Ssteve setvarsafe("OPTARG", "", 0); 48320425Ssteve ind = *optnext - optfirst + 1; 48420425Ssteve goto out; 48520425Ssteve 48620425Sstevebad: 48720425Ssteve ind = 1; 48820425Ssteve *optnext = NULL; 48920425Ssteve p = NULL; 4901556Srgrimesout: 49120425Ssteve *optptr = p; 49220425Ssteve fmtstr(s, sizeof(s), "%d", ind); 49320425Ssteve err |= setvarsafe("OPTIND", s, VNOFUNC); 4941556Srgrimes s[0] = c; 4951556Srgrimes s[1] = '\0'; 49620425Ssteve err |= setvarsafe(optvar, s, 0); 49720425Ssteve if (err) { 49820425Ssteve *optnext = NULL; 49920425Ssteve *optptr = NULL; 50020425Ssteve flushall(); 50120425Ssteve exraise(EXERROR); 50220425Ssteve } 50320425Ssteve return done; 5041556Srgrimes} 5051556Srgrimes 5061556Srgrimes/* 5071556Srgrimes * XXX - should get rid of. have all builtins use getopt(3). the 5081556Srgrimes * library getopt must have the BSD extension static variable "optreset" 5091556Srgrimes * otherwise it can't be used within the shell safely. 5101556Srgrimes * 5111556Srgrimes * Standard option processing (a la getopt) for builtin routines. The 5121556Srgrimes * only argument that is passed to nextopt is the option string; the 5131556Srgrimes * other arguments are unnecessary. It return the character, or '\0' on 5141556Srgrimes * end of input. 5151556Srgrimes */ 5161556Srgrimes 5171556Srgrimesint 5181556Srgrimesnextopt(optstring) 5191556Srgrimes char *optstring; 5201556Srgrimes { 52125227Ssteve char *p, *q; 5221556Srgrimes char c; 5231556Srgrimes 5241556Srgrimes if ((p = optptr) == NULL || *p == '\0') { 5251556Srgrimes p = *argptr; 5261556Srgrimes if (p == NULL || *p != '-' || *++p == '\0') 5271556Srgrimes return '\0'; 5281556Srgrimes argptr++; 5291556Srgrimes if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 5301556Srgrimes return '\0'; 5311556Srgrimes } 5321556Srgrimes c = *p++; 5331556Srgrimes for (q = optstring ; *q != c ; ) { 5341556Srgrimes if (*q == '\0') 5351556Srgrimes error("Illegal option -%c", c); 5361556Srgrimes if (*++q == ':') 5371556Srgrimes q++; 5381556Srgrimes } 5391556Srgrimes if (*++q == ':') { 5401556Srgrimes if (*p == '\0' && (p = *argptr++) == NULL) 5411556Srgrimes error("No arg for -%c option", c); 54259436Scracauer shoptarg = p; 5431556Srgrimes p = NULL; 5441556Srgrimes } 5451556Srgrimes optptr = p; 5461556Srgrimes return c; 5471556Srgrimes} 548