options.c revision 20425
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. 353044Sdg * 3620425Ssteve * $Id: options.c,v 1.8 1996/10/29 03:12:48 steve Exp $ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 4020425Sstevestatic char const sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 4317987Speter#include <signal.h> 4417987Speter#include <unistd.h> 4517987Speter#include <stdlib.h> 4617987Speter 471556Srgrimes#include "shell.h" 481556Srgrimes#define DEFINE_OPTIONS 491556Srgrimes#include "options.h" 501556Srgrimes#undef DEFINE_OPTIONS 511556Srgrimes#include "nodes.h" /* for other header files */ 521556Srgrimes#include "eval.h" 531556Srgrimes#include "jobs.h" 541556Srgrimes#include "input.h" 551556Srgrimes#include "output.h" 561556Srgrimes#include "trap.h" 571556Srgrimes#include "var.h" 581556Srgrimes#include "memalloc.h" 591556Srgrimes#include "error.h" 601556Srgrimes#include "mystring.h" 6117987Speter#ifndef NO_HISTORY 6217987Speter#include "myhistedit.h" 6317987Speter#endif 641556Srgrimes 651556Srgrimeschar *arg0; /* value of $0 */ 661556Srgrimesstruct shparam shellparam; /* current positional parameters */ 671556Srgrimeschar **argptr; /* argument list for builtin commands */ 681556Srgrimeschar *optarg; /* set by nextopt (like getopt) */ 691556Srgrimeschar *optptr; /* used by nextopt */ 701556Srgrimes 711556Srgrimeschar *minusc; /* argument to -c option */ 721556Srgrimes 731556Srgrimes 7417987SpeterSTATIC void options __P((int)); 7517987SpeterSTATIC void minus_o __P((char *, int)); 7617987SpeterSTATIC void setoption __P((int, int)); 7720425SsteveSTATIC int getopts __P((char *, char *, char **, char ***, char **)); 781556Srgrimes 791556Srgrimes 801556Srgrimes/* 811556Srgrimes * Process the shell command line arguments. 821556Srgrimes */ 831556Srgrimes 841556Srgrimesvoid 851556Srgrimesprocargs(argc, argv) 8617987Speter int argc; 871556Srgrimes char **argv; 8817987Speter{ 891556Srgrimes int i; 901556Srgrimes 911556Srgrimes argptr = argv; 921556Srgrimes if (argc > 0) 931556Srgrimes argptr++; 941556Srgrimes for (i = 0; i < NOPTS; i++) 951556Srgrimes optlist[i].val = 2; 9619240Ssteve privileged = (getuid() != geteuid() || getgid() != getegid()); 971556Srgrimes options(1); 981556Srgrimes if (*argptr == NULL && minusc == NULL) 991556Srgrimes sflag = 1; 1001556Srgrimes if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 1011556Srgrimes iflag = 1; 1021556Srgrimes if (mflag == 2) 1031556Srgrimes mflag = iflag; 1041556Srgrimes for (i = 0; i < NOPTS; i++) 1051556Srgrimes if (optlist[i].val == 2) 1061556Srgrimes optlist[i].val = 0; 1071556Srgrimes arg0 = argv[0]; 1081556Srgrimes if (sflag == 0 && minusc == NULL) { 1091556Srgrimes commandname = arg0 = *argptr++; 1101556Srgrimes setinputfile(commandname, 0); 1111556Srgrimes } 11220425Ssteve /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 11311377Sjoerg if (*argptr && minusc) 11411111Sjoerg arg0 = *argptr++; 1151556Srgrimes shellparam.p = argptr; 1161556Srgrimes /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 1171556Srgrimes while (*argptr) { 1181556Srgrimes shellparam.nparam++; 1191556Srgrimes argptr++; 1201556Srgrimes } 1211556Srgrimes optschanged(); 1221556Srgrimes} 1231556Srgrimes 1241556Srgrimes 12517987Spetervoid 12617987Speteroptschanged() 12717987Speter{ 1281556Srgrimes setinteractive(iflag); 12917987Speter#ifndef NO_HISTORY 1301556Srgrimes histedit(); 13117987Speter#endif 1321556Srgrimes setjobctl(mflag); 1331556Srgrimes} 1341556Srgrimes 1351556Srgrimes/* 1361556Srgrimes * Process shell options. The global variable argptr contains a pointer 1371556Srgrimes * to the argument list; we advance it past the options. 1381556Srgrimes */ 1391556Srgrimes 1401556SrgrimesSTATIC void 14120425Ssteveoptions(cmdline) 14217987Speter int cmdline; 14317987Speter{ 1441556Srgrimes register char *p; 1451556Srgrimes int val; 1461556Srgrimes int c; 1471556Srgrimes 1481556Srgrimes if (cmdline) 1491556Srgrimes minusc = NULL; 1501556Srgrimes while ((p = *argptr) != NULL) { 1511556Srgrimes argptr++; 1521556Srgrimes if ((c = *p++) == '-') { 1531556Srgrimes val = 1; 15417987Speter if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 1551556Srgrimes if (!cmdline) { 1561556Srgrimes /* "-" means turn off -x and -v */ 1571556Srgrimes if (p[0] == '\0') 1581556Srgrimes xflag = vflag = 0; 1591556Srgrimes /* "--" means reset params */ 1601556Srgrimes else if (*argptr == NULL) 16117987Speter setparam(argptr); 1621556Srgrimes } 1631556Srgrimes break; /* "-" or "--" terminates options */ 1641556Srgrimes } 1651556Srgrimes } else if (c == '+') { 1661556Srgrimes val = 0; 1671556Srgrimes } else { 1681556Srgrimes argptr--; 1691556Srgrimes break; 1701556Srgrimes } 1711556Srgrimes while ((c = *p++) != '\0') { 1721556Srgrimes if (c == 'c' && cmdline) { 1731556Srgrimes char *q; 1741556Srgrimes#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 1751556Srgrimes if (*p == '\0') 1761556Srgrimes#endif 1771556Srgrimes q = *argptr++; 1781556Srgrimes if (q == NULL || minusc != NULL) 1791556Srgrimes error("Bad -c option"); 1801556Srgrimes minusc = q; 1811556Srgrimes#ifdef NOHACK 1821556Srgrimes break; 1831556Srgrimes#endif 1841556Srgrimes } else if (c == 'o') { 1851556Srgrimes minus_o(*argptr, val); 1861556Srgrimes if (*argptr) 1871556Srgrimes argptr++; 1881556Srgrimes } else { 18919240Ssteve if (c == 'p' && !val && privileged) { 19019240Ssteve (void) setuid(getuid()); 19119240Ssteve (void) setgid(getgid()); 19219240Ssteve } 1931556Srgrimes setoption(c, val); 1941556Srgrimes } 1951556Srgrimes } 1961556Srgrimes } 1971556Srgrimes} 1981556Srgrimes 1991556SrgrimesSTATIC void 2001556Srgrimesminus_o(name, val) 2011556Srgrimes char *name; 2021556Srgrimes int val; 2031556Srgrimes{ 2041556Srgrimes int i; 2051556Srgrimes 2061556Srgrimes if (name == NULL) { 2071556Srgrimes out1str("Current option settings\n"); 2081556Srgrimes for (i = 0; i < NOPTS; i++) 2091556Srgrimes out1fmt("%-16s%s\n", optlist[i].name, 2101556Srgrimes optlist[i].val ? "on" : "off"); 2111556Srgrimes } else { 2121556Srgrimes for (i = 0; i < NOPTS; i++) 2131556Srgrimes if (equal(name, optlist[i].name)) { 21419240Ssteve if (!val && privileged && equal(name, "privileged")) { 21519240Ssteve (void) setuid(getuid()); 21619240Ssteve (void) setgid(getgid()); 21719240Ssteve } 2181556Srgrimes setoption(optlist[i].letter, val); 2191556Srgrimes return; 2201556Srgrimes } 2211556Srgrimes error("Illegal option -o %s", name); 2221556Srgrimes } 2231556Srgrimes} 2241556Srgrimes 2258855Srgrimes 2261556SrgrimesSTATIC void 2271556Srgrimessetoption(flag, val) 2281556Srgrimes char flag; 2291556Srgrimes int val; 2301556Srgrimes { 2311556Srgrimes int i; 2321556Srgrimes 2331556Srgrimes for (i = 0; i < NOPTS; i++) 2341556Srgrimes if (optlist[i].letter == flag) { 2351556Srgrimes optlist[i].val = val; 2361556Srgrimes if (val) { 2371556Srgrimes /* #%$ hack for ksh semantics */ 2381556Srgrimes if (flag == 'V') 2391556Srgrimes Eflag = 0; 2401556Srgrimes else if (flag == 'E') 2411556Srgrimes Vflag = 0; 2421556Srgrimes } 2431556Srgrimes return; 2441556Srgrimes } 2451556Srgrimes error("Illegal option -%c", flag); 2461556Srgrimes} 2471556Srgrimes 2481556Srgrimes 2491556Srgrimes 2501556Srgrimes#ifdef mkinit 2511556SrgrimesINCLUDE "options.h" 2521556Srgrimes 2531556SrgrimesSHELLPROC { 2541556Srgrimes int i; 2551556Srgrimes 2561556Srgrimes for (i = 0; i < NOPTS; i++) 2571556Srgrimes optlist[i].val = 0; 2581556Srgrimes optschanged(); 2591556Srgrimes 2601556Srgrimes} 2611556Srgrimes#endif 2621556Srgrimes 2631556Srgrimes 2641556Srgrimes/* 2651556Srgrimes * Set the shell parameters. 2661556Srgrimes */ 2671556Srgrimes 2681556Srgrimesvoid 2691556Srgrimessetparam(argv) 2701556Srgrimes char **argv; 2711556Srgrimes { 2721556Srgrimes char **newparam; 2731556Srgrimes char **ap; 2741556Srgrimes int nparam; 2751556Srgrimes 2761556Srgrimes for (nparam = 0 ; argv[nparam] ; nparam++); 2771556Srgrimes ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 2781556Srgrimes while (*argv) { 2791556Srgrimes *ap++ = savestr(*argv++); 2801556Srgrimes } 2811556Srgrimes *ap = NULL; 2821556Srgrimes freeparam(&shellparam); 2831556Srgrimes shellparam.malloc = 1; 2841556Srgrimes shellparam.nparam = nparam; 2851556Srgrimes shellparam.p = newparam; 2861556Srgrimes shellparam.optnext = NULL; 2871556Srgrimes} 2881556Srgrimes 2891556Srgrimes 2901556Srgrimes/* 2911556Srgrimes * Free the list of positional parameters. 2921556Srgrimes */ 2931556Srgrimes 2941556Srgrimesvoid 2951556Srgrimesfreeparam(param) 2961556Srgrimes struct shparam *param; 2971556Srgrimes { 2981556Srgrimes char **ap; 2991556Srgrimes 3001556Srgrimes if (param->malloc) { 3011556Srgrimes for (ap = param->p ; *ap ; ap++) 3021556Srgrimes ckfree(*ap); 3031556Srgrimes ckfree(param->p); 3041556Srgrimes } 3051556Srgrimes} 3061556Srgrimes 3071556Srgrimes 3081556Srgrimes 3091556Srgrimes/* 3101556Srgrimes * The shift builtin command. 3111556Srgrimes */ 3121556Srgrimes 31317987Speterint 31417987Spetershiftcmd(argc, argv) 31517987Speter int argc; 31620425Ssteve char **argv; 31717987Speter{ 3181556Srgrimes int n; 3191556Srgrimes char **ap1, **ap2; 3201556Srgrimes 3211556Srgrimes n = 1; 3221556Srgrimes if (argc > 1) 3231556Srgrimes n = number(argv[1]); 3241556Srgrimes if (n > shellparam.nparam) 3251556Srgrimes error("can't shift that many"); 3261556Srgrimes INTOFF; 3271556Srgrimes shellparam.nparam -= n; 3281556Srgrimes for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 3291556Srgrimes if (shellparam.malloc) 3301556Srgrimes ckfree(*ap1); 3311556Srgrimes } 3321556Srgrimes ap2 = shellparam.p; 3331556Srgrimes while ((*ap2++ = *ap1++) != NULL); 3341556Srgrimes shellparam.optnext = NULL; 3351556Srgrimes INTON; 3361556Srgrimes return 0; 3371556Srgrimes} 3381556Srgrimes 3391556Srgrimes 3401556Srgrimes 3411556Srgrimes/* 3421556Srgrimes * The set command builtin. 3431556Srgrimes */ 3441556Srgrimes 34517987Speterint 34617987Spetersetcmd(argc, argv) 34717987Speter int argc; 34820425Ssteve char **argv; 34917987Speter{ 3501556Srgrimes if (argc == 1) 3511556Srgrimes return showvarscmd(argc, argv); 3521556Srgrimes INTOFF; 3531556Srgrimes options(0); 3541556Srgrimes optschanged(); 3551556Srgrimes if (*argptr != NULL) { 3561556Srgrimes setparam(argptr); 3571556Srgrimes } 3581556Srgrimes INTON; 3591556Srgrimes return 0; 3601556Srgrimes} 3611556Srgrimes 3621556Srgrimes 36320425Sstevevoid 36420425Sstevegetoptsreset(value) 36520425Ssteve const char *value; 36620425Ssteve{ 36720425Ssteve if (number(value) == 1) { 36820425Ssteve shellparam.optnext = NULL; 36920425Ssteve shellparam.reset = 1; 37020425Ssteve } 37120425Ssteve} 37220425Ssteve 3731556Srgrimes/* 3741556Srgrimes * The getopts builtin. Shellparam.optnext points to the next argument 3751556Srgrimes * to be processed. Shellparam.optptr points to the next character to 3761556Srgrimes * be processed in the current argument. If shellparam.optnext is NULL, 3771556Srgrimes * then it's the first time getopts has been called. 3781556Srgrimes */ 3791556Srgrimes 38017987Speterint 38117987Spetergetoptscmd(argc, argv) 38217987Speter int argc; 38320425Ssteve char **argv; 38417987Speter{ 38520425Ssteve char **optbase = NULL; 38620425Ssteve 38720425Ssteve if (argc < 3) 38820425Ssteve error("Usage: getopts optstring var [arg]"); 38920425Ssteve else if (argc == 3) 39020425Ssteve optbase = shellparam.p; 39120425Ssteve else 39220425Ssteve optbase = &argv[3]; 39320425Ssteve 39420425Ssteve if (shellparam.reset == 1) { 39520425Ssteve shellparam.optnext = optbase; 39620425Ssteve shellparam.optptr = NULL; 39720425Ssteve shellparam.reset = 0; 39820425Ssteve } 39920425Ssteve 40020425Ssteve return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 40120425Ssteve &shellparam.optptr); 40220425Ssteve} 40320425Ssteve 40420425SsteveSTATIC int 40520425Sstevegetopts(optstr, optvar, optfirst, optnext, optptr) 40620425Ssteve char *optstr; 40720425Ssteve char *optvar; 40820425Ssteve char **optfirst; 40920425Ssteve char ***optnext; 41020425Ssteve char **optptr; 41120425Ssteve{ 4121556Srgrimes register char *p, *q; 41320425Ssteve char c = '?'; 41420425Ssteve int done = 0; 41520425Ssteve int ind = 0; 41620425Ssteve int err = 0; 4171556Srgrimes char s[10]; 4181556Srgrimes 41920425Ssteve if ((p = *optptr) == NULL || *p == '\0') { 42020425Ssteve /* Current word is done, advance */ 42120425Ssteve if (*optnext == NULL) 42220425Ssteve return 1; 42320425Ssteve p = **optnext; 4241556Srgrimes if (p == NULL || *p != '-' || *++p == '\0') { 4251556Srgrimesatend: 42620425Ssteve *optnext = NULL; 42720425Ssteve ind = *optnext - optfirst + 1; 42820425Ssteve done = 1; 42920425Ssteve goto out; 4301556Srgrimes } 43120425Ssteve (*optnext)++; 4321556Srgrimes if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 4331556Srgrimes goto atend; 4341556Srgrimes } 43520425Ssteve 4361556Srgrimes c = *p++; 43720425Ssteve for (q = optstr; *q != c; ) { 4381556Srgrimes if (*q == '\0') { 43920425Ssteve if (optstr[0] == ':') { 44020425Ssteve s[0] = c; 44120425Ssteve s[1] = '\0'; 44220425Ssteve err |= setvarsafe("OPTARG", s, 0); 44320425Ssteve } 44420425Ssteve else { 44520425Ssteve out1fmt("Illegal option -%c\n", c); 44620425Ssteve (void) unsetvar("OPTARG"); 44720425Ssteve } 4481556Srgrimes c = '?'; 44920425Ssteve goto bad; 4501556Srgrimes } 4511556Srgrimes if (*++q == ':') 4521556Srgrimes q++; 4531556Srgrimes } 45420425Ssteve 4551556Srgrimes if (*++q == ':') { 45620425Ssteve if (*p == '\0' && (p = **optnext) == NULL) { 45720425Ssteve if (optstr[0] == ':') { 45820425Ssteve s[0] = c; 45920425Ssteve s[1] = '\0'; 46020425Ssteve err |= setvarsafe("OPTARG", s, 0); 46120425Ssteve c = ':'; 46220425Ssteve } 46320425Ssteve else { 46420425Ssteve out1fmt("No arg for -%c option\n", c); 46520425Ssteve (void) unsetvar("OPTARG"); 46620425Ssteve c = '?'; 46720425Ssteve } 46820425Ssteve goto bad; 4691556Srgrimes } 47020425Ssteve 47120425Ssteve if (p == **optnext) 47220425Ssteve (*optnext)++; 47320425Ssteve setvarsafe("OPTARG", p, 0); 4741556Srgrimes p = NULL; 4751556Srgrimes } 47620425Ssteve else 47720425Ssteve setvarsafe("OPTARG", "", 0); 47820425Ssteve ind = *optnext - optfirst + 1; 47920425Ssteve goto out; 48020425Ssteve 48120425Sstevebad: 48220425Ssteve ind = 1; 48320425Ssteve *optnext = NULL; 48420425Ssteve p = NULL; 4851556Srgrimesout: 48620425Ssteve *optptr = p; 48720425Ssteve fmtstr(s, sizeof(s), "%d", ind); 48820425Ssteve err |= setvarsafe("OPTIND", s, VNOFUNC); 4891556Srgrimes s[0] = c; 4901556Srgrimes s[1] = '\0'; 49120425Ssteve err |= setvarsafe(optvar, s, 0); 49220425Ssteve if (err) { 49320425Ssteve *optnext = NULL; 49420425Ssteve *optptr = NULL; 49520425Ssteve flushall(); 49620425Ssteve exraise(EXERROR); 49720425Ssteve } 49820425Ssteve return done; 4991556Srgrimes} 5001556Srgrimes 5011556Srgrimes/* 5021556Srgrimes * XXX - should get rid of. have all builtins use getopt(3). the 5031556Srgrimes * library getopt must have the BSD extension static variable "optreset" 5041556Srgrimes * otherwise it can't be used within the shell safely. 5051556Srgrimes * 5061556Srgrimes * Standard option processing (a la getopt) for builtin routines. The 5071556Srgrimes * only argument that is passed to nextopt is the option string; the 5081556Srgrimes * other arguments are unnecessary. It return the character, or '\0' on 5091556Srgrimes * end of input. 5101556Srgrimes */ 5111556Srgrimes 5121556Srgrimesint 5131556Srgrimesnextopt(optstring) 5141556Srgrimes char *optstring; 5151556Srgrimes { 5161556Srgrimes register char *p, *q; 5171556Srgrimes char c; 5181556Srgrimes 5191556Srgrimes if ((p = optptr) == NULL || *p == '\0') { 5201556Srgrimes p = *argptr; 5211556Srgrimes if (p == NULL || *p != '-' || *++p == '\0') 5221556Srgrimes return '\0'; 5231556Srgrimes argptr++; 5241556Srgrimes if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 5251556Srgrimes return '\0'; 5261556Srgrimes } 5271556Srgrimes c = *p++; 5281556Srgrimes for (q = optstring ; *q != c ; ) { 5291556Srgrimes if (*q == '\0') 5301556Srgrimes error("Illegal option -%c", c); 5311556Srgrimes if (*++q == ':') 5321556Srgrimes q++; 5331556Srgrimes } 5341556Srgrimes if (*++q == ':') { 5351556Srgrimes if (*p == '\0' && (p = *argptr++) == NULL) 5361556Srgrimes error("No arg for -%c option", c); 5371556Srgrimes optarg = p; 5381556Srgrimes p = NULL; 5391556Srgrimes } 5401556Srgrimes optptr = p; 5411556Srgrimes return c; 5421556Srgrimes} 543