diff options
Diffstat (limited to '')
-rw-r--r-- | src/kash/options.c | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/src/kash/options.c b/src/kash/options.c new file mode 100644 index 0000000..ed99af5 --- /dev/null +++ b/src/kash/options.c @@ -0,0 +1,635 @@ +/* $NetBSD: options.c,v 1.38 2005/03/20 21:38:17 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: options.c,v 1.38 2005/03/20 21:38:17 dsl Exp $"); +#endif /* not lint */ +#endif + +#include <stdlib.h> + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#ifndef SMALL +# include "myhistedit.h" +#endif +#include "show.h" +#include "shinstance.h" + +//char *arg0; /* value of $0 */ +//struct shparam shellparam; /* current positional parameters */ +//char **argptr; /* argument list for builtin commands */ +//char *optionarg; /* set by nextopt (like getopt) */ +//char *optptr; /* used by nextopt */ + +//char *minusc; /* argument to -c option */ + + +STATIC void options(shinstance *, int); +STATIC void minus_o(shinstance *, char *, int); +STATIC void setoption(shinstance *, int, int); +STATIC int getopts(shinstance *, char *, char *, char **, char ***, char **); + +#ifndef SH_FORKED_MODE +void +subshellinitoptions(shinstance *psh, shinstance *inherit) +{ + unsigned i; + int left; + const char *arg; + memcpy(&psh->optlist[0], &inherit->optlist[0], sizeof(psh->optlist)); + + /** @todo opimize: skip this when executing builtins. */ + /* Whether the subshell uses argptr/shellparam/arg0 or replaces them depends + on whether the shell will execute a builtin command or not. + + orgargv is already set by the shinstance.c core code, scan the original + again and update arg0, shellparm, argptr and optptr. */ + + /* arg0 is either something from orgargv, or in the EXSHELLPROC case a + separate allocation that we need to dupe here. The (new) arg0malloc + flag indicates which. */ + i = 0; + psh->arg0malloc = inherit->arg0malloc; + if (inherit->arg0malloc) { + psh->arg0 = savestr(psh, inherit->arg0); + } else { + while ((arg = inherit->orgargv[i]) != NULL) { + if (inherit->arg0 == arg) { + psh->arg0 = psh->orgargv[i]; + break; + } + i++; + } + kHlpAssert(psh->arg0 != NULL); + } + + /* eval.h's commandname is same as arg0 when set unless we're doing a dot-include. */ + if (inherit->commandname) { + if (inherit->commandname == inherit->arg0) { + psh->commandname = psh->arg0; + } else { + psh->commandname = savestr(psh, inherit->commandname); + psh->commandnamemalloc = 1; + } + } + + /* shellparam is either pointing right after arg0 in orgargv, though it may + also be a separately allocated thing (see setparam), or pointing to the + arguments of a shell function we're executing (see eval.c). All in all, + it's simpler if we just copy the whole darn thing, ASSUMING no + modifications will be made that are needed to be visible elsewhere. + */ + psh->shellparam.malloc = 1; + psh->shellparam.reset = inherit->shellparam.reset; + psh->shellparam.nparam = left = inherit->shellparam.nparam; + kHlpAssert(left >= 0); + psh->shellparam.p = (char **)ckmalloc(psh, (left + 1) * sizeof(psh->shellparam.p[0])); + psh->shellparam.p[left] = NULL; + while (left-- > 0) { + arg = inherit->shellparam.p[left]; + psh->shellparam.p[left] = savestr(psh, arg); + } + + /* The shellparam.optnext member is either NULL or points to a 'p' entry. */ + if (inherit->shellparam.optnext) { + size_t idx = (size_t)(inherit->shellparam.optnext - inherit->shellparam.p); + kHlpAssert(idx <= inherit->shellparam.nparam); + if (idx <= inherit->shellparam.nparam) + psh->shellparam.optnext = &psh->shellparam.p[idx]; + } + + /* The shellparam.optptr member is either NULL or points within argument + prior to shellparam.optnext. We can leave it as NULL if at the EOS. */ + if (inherit->shellparam.optptr && *inherit->shellparam.optptr != '\0') { + intptr_t idx; + if (!inherit->shellparam.optnext || inherit->shellparam.optnext == inherit->shellparam.p) + idx = (intptr_t)(inherit->shellparam.nparam - 1); + else { + idx = (intptr_t)(inherit->shellparam.optnext - inherit->shellparam.p - 1); + if (idx > inherit->shellparam.nparam) + idx = inherit->shellparam.nparam - 1; + } + while (idx >= 0) { + size_t arglen, off; + arg = inherit->shellparam.p[idx]; + arglen = strlen(arg); + off = (size_t)(inherit->shellparam.optptr - arg); + if (off < arglen) { + psh->shellparam.optptr = psh->shellparam.p[idx] + off; + break; + } + off--; + } + kHlpAssert(psh->shellparam.optptr != NULL); + } + + /* minusc: only used in main.c, so not applicable to subshells. */ + /* optionarg: only used by callers of nextopt, so not relevant when forking subhells. */ +} +#endif /* SH_FORKED_MODE */ + + +/* + * Process the shell command line arguments. + */ + +void +procargs(shinstance *psh, int argc, char **argv) +{ + int i; + + psh->argptr = argv; + if (argc > 0) + psh->argptr++; + for (i = 0; i < NOPTS; i++) + psh->optlist[i].val = 2; + options(psh, 1); + if (*psh->argptr == NULL && psh->minusc == NULL) + sflag(psh) = 1; + if (iflag(psh) == 2 && sflag(psh) == 1 && shfile_isatty(&psh->fdtab, 0) && shfile_isatty(&psh->fdtab, 1)) + iflag(psh) = 1; + if (mflag(psh) == 2) + mflag(psh) = iflag(psh); + for (i = 0; i < NOPTS; i++) + if (psh->optlist[i].val == 2) + psh->optlist[i].val = 0; +#if DEBUG == 2 + debug(psh) = 1; +#endif + psh->commandnamemalloc = 0; + psh->arg0malloc = 0; + psh->arg0 = argv[0]; + if (sflag(psh) == 0 && psh->minusc == NULL) { + psh->commandname = argv[0]; + psh->arg0 = *psh->argptr++; + setinputfile(psh, psh->arg0, 0); + psh->commandname = psh->arg0; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (psh->minusc != NULL) { + if (psh->argptr == NULL || *psh->argptr == NULL) + error(psh, "Bad -c option"); + psh->minusc = *psh->argptr++; + if (*psh->argptr != 0) + psh->arg0 = *psh->argptr++; + } + + psh->shellparam.p = psh->argptr; + psh->shellparam.reset = 1; + /* kHlpAssert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*psh->argptr) { + psh->shellparam.nparam++; + psh->argptr++; + } + optschanged(psh); +} + + +void +optschanged(shinstance *psh) +{ + setinteractive(psh, iflag(psh)); +#ifndef SMALL + histedit(psh); +#endif + setjobctl(psh, mflag(psh)); +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC void +options(shinstance *psh, int cmdline) +{ + static char empty[] = ""; + char *p; + int val; + int c; + + if (cmdline) + psh->minusc = NULL; + while ((p = *psh->argptr) != NULL) { + psh->argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag(psh) = vflag(psh) = 0; + /* "--" means reset params */ + else if (*psh->argptr == NULL) + setparam(psh, psh->argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + psh->argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + /* command is after shell args*/ + psh->minusc = empty; + } else if (c == 'o') { + minus_o(psh, *psh->argptr, val); + if (*psh->argptr) + psh->argptr++; + } else { + setoption(psh, c, val); + } + } + } +} + +static void +set_opt_val(shinstance *psh, int i, int val) +{ + int j; + int flag; + + if (val && (flag = psh->optlist[i].opt_set)) { + /* some options (eg vi/emacs) are mutually exclusive */ + for (j = 0; j < NOPTS; j++) + if (psh->optlist[j].opt_set == flag) + psh->optlist[j].val = 0; + } + psh->optlist[i].val = val; +#ifdef DEBUG + if (&psh->optlist[i].val == &debug(psh)) + opentrace(psh); +#endif +} + +STATIC void +minus_o(shinstance *psh, char *name, int val) +{ + int i; + + if (name == NULL) { + out1str(psh, "Current option settings\n"); + for (i = 0; i < NOPTS; i++) + out1fmt(psh, "%-16s%s\n", psh->optlist[i].name, + psh->optlist[i].val ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, psh->optlist[i].name)) { + set_opt_val(psh, i, val); + return; + } + error(psh, "Illegal option -o %s", name); + } +} + + +STATIC void +setoption(shinstance *psh, int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (psh->optlist[i].letter == flag) { + set_opt_val(psh, i, val); + return; + } + error(psh, "Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +#ifdef mkinit +INCLUDE "options.h" + +INIT { + memcpy(&psh->optlist[0], &ro_optlist[0], sizeof(psh->optlist)); +} + +SHELLPROC { + int i; + + for (i = 0; psh->optlist[i].name; i++) + psh->optlist[i].val = 0; +# if DEBUG == 2 + debug(psh) = 1; +# endif + optschanged(psh); +} +#endif + + +/* + * Set the shell parameters. + */ + +void +setparam(shinstance *psh, char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++) + continue; + ap = newparam = ckmalloc(psh, (nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(psh, *argv++); + } + *ap = NULL; + freeparam(psh, &psh->shellparam); + psh->shellparam.malloc = 1; + psh->shellparam.nparam = nparam; + psh->shellparam.p = newparam; + psh->shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(shinstance *psh, volatile struct shparam *param) +{ + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(psh, *ap); + ckfree(psh, param->p); + } +} + + + +/* + * The shift builtin command. + */ + +int +shiftcmd(shinstance *psh, int argc, char **argv) +{ + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(psh, argv[1]); + if (n > psh->shellparam.nparam) + error(psh, "can't shift that many"); + INTOFF; + psh->shellparam.nparam -= n; + for (ap1 = psh->shellparam.p ; --n >= 0 ; ap1++) { + if (psh->shellparam.malloc) + ckfree(psh, *ap1); + } + ap2 = psh->shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + psh->shellparam.optnext = NULL; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +int +setcmd(shinstance *psh, int argc, char **argv) +{ + if (argc == 1) + return showvars(psh, 0, 0, 1); + INTOFF; + options(psh, 0); + optschanged(psh); + if (*psh->argptr != NULL) { + setparam(psh, psh->argptr); + } + INTON; + return 0; +} + + +void +getoptsreset(shinstance *psh, const char *value) +{ + if (number(psh, value) == 1) { + psh->shellparam.optnext = NULL; + psh->shellparam.reset = 1; + } +} + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +int +getoptscmd(shinstance *psh, int argc, char **argv) +{ + char **optbase; + + if (argc < 3) + error(psh, "usage: getopts optstring var [arg]"); + else if (argc == 3) + optbase = psh->shellparam.p; + else + optbase = &argv[3]; + + if (psh->shellparam.reset == 1) { + psh->shellparam.optnext = optbase; + psh->shellparam.optptr = NULL; + psh->shellparam.reset = 0; + } + + return getopts(psh, argv[1], argv[2], optbase, &psh->shellparam.optnext, + &psh->shellparam.optptr); +} + +STATIC int +getopts(shinstance *psh, char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) +{ + char *p, *q; + char c = '?'; + int done = 0; + int ind = 0; + int err = 0; + char s[12]; + + if ((p = *optpptr) == NULL || *p == '\0') { + /* Current word is done, advance */ + if (*optnext == NULL) + return 1; + p = **optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + ind = (int)(*optnext - optfirst + 1); + *optnext = NULL; + p = NULL; + done = 1; + goto out; + } + (*optnext)++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(psh, "OPTARG", s, 0); + } else { + outfmt(&psh->errout, "Illegal option -%c\n", c); + (void) unsetvar(psh, "OPTARG", 0); + } + c = '?'; + goto bad; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = **optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(psh, "OPTARG", s, 0); + c = ':'; + } else { + outfmt(&psh->errout, "No arg for -%c option\n", c); + (void) unsetvar(psh, "OPTARG", 0); + c = '?'; + } + goto bad; + } + + if (p == **optnext) + (*optnext)++; + err |= setvarsafe(psh, "OPTARG", p, 0); + p = NULL; + } else + err |= setvarsafe(psh, "OPTARG", "", 0); + ind = (int)(*optnext - optfirst + 1); + goto out; + +bad: + ind = 1; + *optnext = NULL; + p = NULL; +out: + *optpptr = p; + fmtstr(s, sizeof(s), "%d", ind); + err |= setvarsafe(psh, "OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(psh, optvar, s, 0); + if (err) { + *optnext = NULL; + *optpptr = NULL; + output_flushall(psh); + exraise(psh, EXERROR); + } + return done; +} + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(shinstance *psh, const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = psh->optptr) == NULL || *p == '\0') { + p = *psh->argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + psh->argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error(psh, "Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *psh->argptr++) == NULL) + error(psh, "No arg for -%c option", c); + psh->optionarg = p; + p = NULL; + } + psh->optptr = p; + return c; +} |