options.c revision 25227
150397Sobrien/*- 290075Sobrien * Copyright (c) 1991, 1993 390075Sobrien * The Regents of the University of California. All rights reserved. 450397Sobrien * 550397Sobrien * This code is derived from software contributed to Berkeley by 650397Sobrien * Kenneth Almquist. 750397Sobrien * 890075Sobrien * Redistribution and use in source and binary forms, with or without 950397Sobrien * modification, are permitted provided that the following conditions 1090075Sobrien * are met: 1190075Sobrien * 1. Redistributions of source code must retain the above copyright 1290075Sobrien * notice, this list of conditions and the following disclaimer. 1390075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1450397Sobrien * notice, this list of conditions and the following disclaimer in the 1590075Sobrien * documentation and/or other materials provided with the distribution. 1690075Sobrien * 3. All advertising materials mentioning features or use of this software 1790075Sobrien * must display the following acknowledgement: 1890075Sobrien * This product includes software developed by the University of 1950397Sobrien * California, Berkeley and its contributors. 2050397Sobrien * 4. Neither the name of the University nor the names of its contributors 2190075Sobrien * may be used to endorse or promote products derived from this software 2290075Sobrien * without specific prior written permission. 2390075Sobrien * 2450397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3090075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3150397Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3250397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3350397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450397Sobrien * SUCH DAMAGE. 3550397Sobrien * 3650397Sobrien * $Id: options.c,v 1.12 1997/02/22 13:58:40 peter Exp $ 3750397Sobrien */ 3850397Sobrien 3950397Sobrien#ifndef lint 4050397Sobrienstatic char const sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 41117395Skan#endif /* not lint */ 4250397Sobrien 4350397Sobrien#include <signal.h> 4450397Sobrien#include <unistd.h> 4550397Sobrien#include <stdlib.h> 4650397Sobrien 4790075Sobrien#include "shell.h" 4850397Sobrien#define DEFINE_OPTIONS 4950397Sobrien#include "options.h" 5090075Sobrien#undef DEFINE_OPTIONS 5150397Sobrien#include "nodes.h" /* for other header files */ 5250397Sobrien#include "eval.h" 5350397Sobrien#include "jobs.h" 5490075Sobrien#include "input.h" 5550397Sobrien#include "output.h" 5690075Sobrien#include "trap.h" 5790075Sobrien#include "var.h" 5890075Sobrien#include "memalloc.h" 5990075Sobrien#include "error.h" 6090075Sobrien#include "mystring.h" 6190075Sobrien#ifndef NO_HISTORY 6290075Sobrien#include "myhistedit.h" 6390075Sobrien#endif 6490075Sobrien 65117395Skanchar *arg0; /* value of $0 */ 6650397Sobrienstruct shparam shellparam; /* current positional parameters */ 6790075Sobrienchar **argptr; /* argument list for builtin commands */ 6890075Sobrienchar *optarg; /* set by nextopt (like getopt) */ 6950397Sobrienchar *optptr; /* used by nextopt */ 7050397Sobrien 7190075Sobrienchar *minusc; /* argument to -c option */ 7290075Sobrien 7390075Sobrien 7490075SobrienSTATIC void options __P((int)); 7590075SobrienSTATIC void minus_o __P((char *, int)); 7690075SobrienSTATIC void setoption __P((int, int)); 7790075SobrienSTATIC int getopts __P((char *, char *, char **, char ***, char **)); 7890075Sobrien 7990075Sobrien 8090075Sobrien/* 8190075Sobrien * Process the shell command line arguments. 8290075Sobrien */ 8390075Sobrien 8490075Sobrienvoid 8590075Sobrienprocargs(argc, argv) 8690075Sobrien int argc; 8790075Sobrien char **argv; 8890075Sobrien{ 8950397Sobrien int i; 9050397Sobrien 9150397Sobrien argptr = argv; 9250397Sobrien if (argc > 0) 9350397Sobrien argptr++; 9450397Sobrien for (i = 0; i < NOPTS; i++) 9550397Sobrien optlist[i].val = 2; 9690075Sobrien privileged = (getuid() != geteuid() || getgid() != getegid()); 9750397Sobrien options(1); 9890075Sobrien if (*argptr == NULL && minusc == NULL) 9950397Sobrien sflag = 1; 10050397Sobrien if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 10190075Sobrien iflag = 1; 10290075Sobrien if (mflag == 2) 10350397Sobrien mflag = iflag; 10450397Sobrien for (i = 0; i < NOPTS; i++) 10550397Sobrien if (optlist[i].val == 2) 10650397Sobrien optlist[i].val = 0; 10790075Sobrien arg0 = argv[0]; 10890075Sobrien if (sflag == 0 && minusc == NULL) { 10990075Sobrien commandname = arg0 = *argptr++; 11090075Sobrien setinputfile(commandname, 0); 11190075Sobrien } 11290075Sobrien /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 11390075Sobrien if (argptr && minusc && *argptr) 11490075Sobrien arg0 = *argptr++; 11590075Sobrien 11690075Sobrien shellparam.p = argptr; 11790075Sobrien shellparam.reset = 1; 11890075Sobrien /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 11990075Sobrien while (*argptr) { 120117395Skan shellparam.nparam++; 121117395Skan argptr++; 122117395Skan } 123117395Skan optschanged(); 124117395Skan} 125117395Skan 126117395Skan 127117395Skanvoid 128117395Skanoptschanged() 129117395Skan{ 130117395Skan setinteractive(iflag); 131117395Skan#ifndef NO_HISTORY 132117395Skan histedit(); 133117395Skan#endif 134117395Skan setjobctl(mflag); 135117395Skan} 13690075Sobrien 137117395Skan/* 13890075Sobrien * Process shell options. The global variable argptr contains a pointer 13990075Sobrien * to the argument list; we advance it past the options. 14090075Sobrien */ 14190075Sobrien 14290075SobrienSTATIC void 143117395Skanoptions(cmdline) 14490075Sobrien int cmdline; 14590075Sobrien{ 14690075Sobrien char *p; 14790075Sobrien int val; 148117395Skan int c; 149117395Skan 150117395Skan if (cmdline) 151117395Skan minusc = NULL; 152117395Skan while ((p = *argptr) != NULL) { 153117395Skan argptr++; 154117395Skan if ((c = *p++) == '-') { 155117395Skan val = 1; 156117395Skan if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 157117395Skan if (!cmdline) { 158117395Skan /* "-" means turn off -x and -v */ 159117395Skan if (p[0] == '\0') 160117395Skan xflag = vflag = 0; 161117395Skan /* "--" means reset params */ 162117395Skan else if (*argptr == NULL) 163117395Skan setparam(argptr); 16450397Sobrien } 16550397Sobrien break; /* "-" or "--" terminates options */ 16650397Sobrien } 16750397Sobrien } else if (c == '+') { 16850397Sobrien val = 0; 16950397Sobrien } else { 17050397Sobrien argptr--; 17150397Sobrien break; 17250397Sobrien } 17350397Sobrien while ((c = *p++) != '\0') { 17450397Sobrien if (c == 'c' && cmdline) { 17550397Sobrien char *q; 17650397Sobrien#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 17750397Sobrien if (*p == '\0') 17850397Sobrien#endif 17950397Sobrien q = *argptr++; 18050397Sobrien if (q == NULL || minusc != NULL) 18150397Sobrien error("Bad -c option"); 18250397Sobrien minusc = q; 18350397Sobrien#ifdef NOHACK 18490075Sobrien break; 18590075Sobrien#endif 18650397Sobrien } else if (c == 'o') { 18750397Sobrien minus_o(*argptr, val); 18850397Sobrien if (*argptr) 18950397Sobrien argptr++; 19050397Sobrien } else { 19150397Sobrien if (c == 'p' && !val && privileged) { 19250397Sobrien (void) setuid(getuid()); 19350397Sobrien (void) setgid(getgid()); 19450397Sobrien } 19550397Sobrien setoption(c, val); 19650397Sobrien } 19750397Sobrien } 19890075Sobrien } 19990075Sobrien} 20090075Sobrien 20190075SobrienSTATIC void 20290075Sobrienminus_o(name, val) 20390075Sobrien char *name; 20490075Sobrien int val; 20590075Sobrien{ 20690075Sobrien int i; 20790075Sobrien 20890075Sobrien if (name == NULL) { 20990075Sobrien out1str("Current option settings\n"); 21090075Sobrien for (i = 0; i < NOPTS; i++) 21150397Sobrien out1fmt("%-16s%s\n", optlist[i].name, 21250397Sobrien optlist[i].val ? "on" : "off"); 21390075Sobrien } else { 21450397Sobrien for (i = 0; i < NOPTS; i++) 21550397Sobrien if (equal(name, optlist[i].name)) { 21650397Sobrien if (!val && privileged && equal(name, "privileged")) { 21750397Sobrien (void) setuid(getuid()); 21850397Sobrien (void) setgid(getgid()); 21990075Sobrien } 22090075Sobrien setoption(optlist[i].letter, val); 22190075Sobrien return; 22250397Sobrien } 22390075Sobrien error("Illegal option -o %s", name); 224117395Skan } 22590075Sobrien} 22690075Sobrien 22750397Sobrien 22850397SobrienSTATIC void 22950397Sobriensetoption(flag, val) 23090075Sobrien char flag; 23150397Sobrien int val; 23250397Sobrien { 23390075Sobrien int i; 23490075Sobrien 23590075Sobrien for (i = 0; i < NOPTS; i++) 23690075Sobrien if (optlist[i].letter == flag) { 23790075Sobrien optlist[i].val = val; 23890075Sobrien if (val) { 23990075Sobrien /* #%$ hack for ksh semantics */ 24050397Sobrien if (flag == 'V') 24150397Sobrien Eflag = 0; 24250397Sobrien else if (flag == 'E') 24390075Sobrien Vflag = 0; 24490075Sobrien } 24590075Sobrien return; 24650397Sobrien } 24750397Sobrien error("Illegal option -%c", flag); 24850397Sobrien} 24950397Sobrien 25050397Sobrien 25150397Sobrien 25250397Sobrien#ifdef mkinit 25350397SobrienINCLUDE "options.h" 25450397Sobrien 25590075SobrienSHELLPROC { 25650397Sobrien int i; 25750397Sobrien 25890075Sobrien for (i = 0; i < NOPTS; i++) 25950397Sobrien optlist[i].val = 0; 26090075Sobrien optschanged(); 26150397Sobrien 26290075Sobrien} 26350397Sobrien#endif 26490075Sobrien 26550397Sobrien 26650397Sobrien/* 26750397Sobrien * Set the shell parameters. 26850397Sobrien */ 26950397Sobrien 27050397Sobrienvoid 27150397Sobriensetparam(argv) 27250397Sobrien char **argv; 27350397Sobrien { 27450397Sobrien char **newparam; 27550397Sobrien char **ap; 27650397Sobrien int nparam; 27750397Sobrien 27850397Sobrien for (nparam = 0 ; argv[nparam] ; nparam++); 27950397Sobrien ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 28050397Sobrien while (*argv) { 28150397Sobrien *ap++ = savestr(*argv++); 28250397Sobrien } 28350397Sobrien *ap = NULL; 28450397Sobrien freeparam(&shellparam); 28590075Sobrien shellparam.malloc = 1; 28650397Sobrien shellparam.nparam = nparam; 28750397Sobrien shellparam.p = newparam; 28850397Sobrien shellparam.optnext = NULL; 28990075Sobrien} 29090075Sobrien 29190075Sobrien 29290075Sobrien/* 29390075Sobrien * Free the list of positional parameters. 29490075Sobrien */ 29590075Sobrien 29690075Sobrienvoid 29790075Sobrienfreeparam(param) 29890075Sobrien struct shparam *param; 29950397Sobrien { 30050397Sobrien char **ap; 30190075Sobrien 30290075Sobrien if (param->malloc) { 30390075Sobrien for (ap = param->p ; *ap ; ap++) 30490075Sobrien ckfree(*ap); 30590075Sobrien ckfree(param->p); 30690075Sobrien } 30790075Sobrien} 30890075Sobrien 30990075Sobrien 31090075Sobrien 31190075Sobrien/* 31290075Sobrien * The shift builtin command. 31390075Sobrien */ 31490075Sobrien 31590075Sobrienint 31690075Sobrienshiftcmd(argc, argv) 31790075Sobrien int argc; 31890075Sobrien char **argv; 31990075Sobrien{ 32050397Sobrien int n; 32190075Sobrien char **ap1, **ap2; 32290075Sobrien 32390075Sobrien n = 1; 32490075Sobrien if (argc > 1) 32590075Sobrien n = number(argv[1]); 32690075Sobrien if (n > shellparam.nparam) 32790075Sobrien error("can't shift that many"); 32890075Sobrien INTOFF; 32950397Sobrien shellparam.nparam -= n; 33090075Sobrien for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 33190075Sobrien if (shellparam.malloc) 33290075Sobrien ckfree(*ap1); 33350397Sobrien } 33450397Sobrien ap2 = shellparam.p; 33550397Sobrien while ((*ap2++ = *ap1++) != NULL); 33650397Sobrien shellparam.optnext = NULL; 33750397Sobrien INTON; 33850397Sobrien return 0; 33990075Sobrien} 34090075Sobrien 34150397Sobrien 34250397Sobrien 34350397Sobrien/* 34450397Sobrien * The set command builtin. 34550397Sobrien */ 34690075Sobrien 34750397Sobrienint 34850397Sobriensetcmd(argc, argv) 34950397Sobrien int argc; 35090075Sobrien char **argv; 35190075Sobrien{ 35250397Sobrien if (argc == 1) 35350397Sobrien return showvarscmd(argc, argv); 35490075Sobrien INTOFF; 35590075Sobrien options(0); 35650397Sobrien optschanged(); 35790075Sobrien if (*argptr != NULL) { 35890075Sobrien setparam(argptr); 35990075Sobrien } 36090075Sobrien INTON; 36190075Sobrien return 0; 36250397Sobrien} 36350397Sobrien 36450397Sobrien 36550397Sobrienvoid 36650397Sobriengetoptsreset(value) 36750397Sobrien const char *value; 36850397Sobrien{ 36950397Sobrien if (number(value) == 1) { 37090075Sobrien shellparam.optnext = NULL; 37150397Sobrien shellparam.reset = 1; 37250397Sobrien } 37350397Sobrien} 37450397Sobrien 37550397Sobrien/* 37650397Sobrien * The getopts builtin. Shellparam.optnext points to the next argument 37750397Sobrien * to be processed. Shellparam.optptr points to the next character to 37850397Sobrien * be processed in the current argument. If shellparam.optnext is NULL, 37950397Sobrien * then it's the first time getopts has been called. 38090075Sobrien */ 38190075Sobrien 38290075Sobrienint 38390075Sobriengetoptscmd(argc, argv) 38490075Sobrien int argc; 38590075Sobrien char **argv; 38650397Sobrien{ 38750397Sobrien char **optbase = NULL; 38850397Sobrien 389117395Skan if (argc < 3) 39050397Sobrien error("Usage: getopts optstring var [arg]"); 391117395Skan else if (argc == 3) 39250397Sobrien optbase = shellparam.p; 39350397Sobrien else 39450397Sobrien optbase = &argv[3]; 39550397Sobrien 39650397Sobrien if (shellparam.reset == 1) { 39750397Sobrien shellparam.optnext = optbase; 39850397Sobrien shellparam.optptr = NULL; 39990075Sobrien shellparam.reset = 0; 40050397Sobrien } 40150397Sobrien 40250397Sobrien return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 40350397Sobrien &shellparam.optptr); 40450397Sobrien} 40550397Sobrien 40650397SobrienSTATIC int 40750397Sobriengetopts(optstr, optvar, optfirst, optnext, optptr) 40850397Sobrien char *optstr; 40950397Sobrien char *optvar; 41050397Sobrien char **optfirst; 41150397Sobrien char ***optnext; 41250397Sobrien char **optptr; 41390075Sobrien{ 41450397Sobrien char *p, *q; 41590075Sobrien char c = '?'; 41690075Sobrien int done = 0; 41790075Sobrien int ind = 0; 41850397Sobrien int err = 0; 41990075Sobrien char s[10]; 42090075Sobrien 42190075Sobrien if ((p = *optptr) == NULL || *p == '\0') { 42290075Sobrien /* Current word is done, advance */ 42350397Sobrien if (*optnext == NULL) 424102780Skan return 1; 425102780Skan p = **optnext; 426102780Skan if (p == NULL || *p != '-' || *++p == '\0') { 427102780Skanatend: 428102780Skan ind = *optnext - optfirst + 1; 42950397Sobrien *optnext = NULL; 430102780Skan p = NULL; 431102780Skan done = 1; 43250397Sobrien goto out; 433102780Skan } 434102780Skan (*optnext)++; 435122180Skan if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 436122180Skan goto atend; 437122180Skan } 438122180Skan 439122180Skan c = *p++; 440122180Skan for (q = optstr; *q != c; ) { 441122180Skan if (*q == '\0') { 442122180Skan if (optstr[0] == ':') { 443122180Skan s[0] = c; 44450397Sobrien s[1] = '\0'; 44550397Sobrien err |= setvarsafe("OPTARG", s, 0); 44650397Sobrien } 44750397Sobrien else { 44890075Sobrien out1fmt("Illegal option -%c\n", c); 44950397Sobrien (void) unsetvar("OPTARG"); 45090075Sobrien } 45150397Sobrien c = '?'; 45250397Sobrien goto bad; 45350397Sobrien } 45450397Sobrien if (*++q == ':') 45550397Sobrien q++; 45650397Sobrien } 45750397Sobrien 45850397Sobrien if (*++q == ':') { 45950397Sobrien if (*p == '\0' && (p = **optnext) == NULL) { 46050397Sobrien if (optstr[0] == ':') { 46150397Sobrien s[0] = c; 46250397Sobrien s[1] = '\0'; 46350397Sobrien err |= setvarsafe("OPTARG", s, 0); 46450397Sobrien c = ':'; 46550397Sobrien } 46650397Sobrien else { 46750397Sobrien out1fmt("No arg for -%c option\n", c); 46850397Sobrien (void) unsetvar("OPTARG"); 46950397Sobrien c = '?'; 47050397Sobrien } 47150397Sobrien goto bad; 47250397Sobrien } 47350397Sobrien 47450397Sobrien if (p == **optnext) 47550397Sobrien (*optnext)++; 47650397Sobrien setvarsafe("OPTARG", p, 0); 47750397Sobrien p = NULL; 47850397Sobrien } 47950397Sobrien else 48050397Sobrien setvarsafe("OPTARG", "", 0); 48150397Sobrien ind = *optnext - optfirst + 1; 48250397Sobrien goto out; 48350397Sobrien 48450397Sobrienbad: 48550397Sobrien ind = 1; 48650397Sobrien *optnext = NULL; 48750397Sobrien p = NULL; 48850397Sobrienout: 48950397Sobrien *optptr = p; 49050397Sobrien fmtstr(s, sizeof(s), "%d", ind); 49190075Sobrien err |= setvarsafe("OPTIND", s, VNOFUNC); 49290075Sobrien s[0] = c; 49390075Sobrien s[1] = '\0'; 49490075Sobrien err |= setvarsafe(optvar, s, 0); 49590075Sobrien if (err) { 49690075Sobrien *optnext = NULL; 49790075Sobrien *optptr = NULL; 49890075Sobrien flushall(); 49990075Sobrien exraise(EXERROR); 50090075Sobrien } 50190075Sobrien return done; 50290075Sobrien} 50350397Sobrien 50450397Sobrien/* 50550397Sobrien * XXX - should get rid of. have all builtins use getopt(3). the 50650397Sobrien * library getopt must have the BSD extension static variable "optreset" 50750397Sobrien * otherwise it can't be used within the shell safely. 50850397Sobrien * 50950397Sobrien * Standard option processing (a la getopt) for builtin routines. The 51050397Sobrien * only argument that is passed to nextopt is the option string; the 51150397Sobrien * other arguments are unnecessary. It return the character, or '\0' on 51270635Sobrien * end of input. 51370635Sobrien */ 51450397Sobrien 51550397Sobrienint 51650397Sobriennextopt(optstring) 51750397Sobrien char *optstring; 51850397Sobrien { 51950397Sobrien char *p, *q; 52050397Sobrien char c; 52150397Sobrien 52250397Sobrien if ((p = optptr) == NULL || *p == '\0') { 52350397Sobrien p = *argptr; 52450397Sobrien if (p == NULL || *p != '-' || *++p == '\0') 52590075Sobrien return '\0'; 52650397Sobrien argptr++; 52750397Sobrien if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 52850397Sobrien return '\0'; 52950397Sobrien } 53050397Sobrien c = *p++; 53150397Sobrien for (q = optstring ; *q != c ; ) { 53250397Sobrien if (*q == '\0') 53350397Sobrien error("Illegal option -%c", c); 53450397Sobrien if (*++q == ':') 53550397Sobrien q++; 53650397Sobrien } 53750397Sobrien if (*++q == ':') { 53890075Sobrien if (*p == '\0' && (p = *argptr++) == NULL) 53990075Sobrien error("No arg for -%c option", c); 54050397Sobrien optarg = p; 54190075Sobrien p = NULL; 54250397Sobrien } 54350397Sobrien optptr = p; 54450397Sobrien return c; 54550397Sobrien} 54650397Sobrien