Annotation of src/bin/sh/options.c, Revision 1.51
1.51 ! kre 1: /* $NetBSD: options.c,v 1.50 2017/07/24 12:35:37 kre Exp $ */
1.11 cgd 2:
1.1 cgd 3: /*-
1.5 jtc 4: * Copyright (c) 1991, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * This code is derived from software contributed to Berkeley by
8: * Kenneth Almquist.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
1.35 agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
34:
1.25 christos 35: #include <sys/cdefs.h>
1.1 cgd 36: #ifndef lint
1.11 cgd 37: #if 0
1.14 christos 38: static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95";
1.11 cgd 39: #else
1.51 ! kre 40: __RCSID("$NetBSD: options.c,v 1.50 2017/07/24 12:35:37 kre Exp $");
1.11 cgd 41: #endif
1.1 cgd 42: #endif /* not lint */
43:
1.14 christos 44: #include <signal.h>
45: #include <unistd.h>
46: #include <stdlib.h>
47:
1.1 cgd 48: #include "shell.h"
49: #define DEFINE_OPTIONS
50: #include "options.h"
51: #undef DEFINE_OPTIONS
1.42 christos 52: #include "builtins.h"
1.1 cgd 53: #include "nodes.h" /* for other header files */
54: #include "eval.h"
55: #include "jobs.h"
56: #include "input.h"
57: #include "output.h"
58: #include "trap.h"
59: #include "var.h"
60: #include "memalloc.h"
61: #include "error.h"
62: #include "mystring.h"
1.24 christos 63: #ifndef SMALL
1.14 christos 64: #include "myhistedit.h"
65: #endif
1.32 christos 66: #include "show.h"
1.1 cgd 67:
68: char *arg0; /* value of $0 */
69: struct shparam shellparam; /* current positional parameters */
70: char **argptr; /* argument list for builtin commands */
1.30 christos 71: char *optionarg; /* set by nextopt (like getopt) */
1.1 cgd 72: char *optptr; /* used by nextopt */
73:
74: char *minusc; /* argument to -c option */
75:
76:
1.32 christos 77: STATIC void options(int);
78: STATIC void minus_o(char *, int);
79: STATIC void setoption(int, int);
80: STATIC int getopts(char *, char *, char **, char ***, char **);
1.1 cgd 81:
82:
83: /*
84: * Process the shell command line arguments.
85: */
86:
87: void
1.32 christos 88: procargs(int argc, char **argv)
1.10 cgd 89: {
1.41 lukem 90: size_t i;
1.48 kre 91: int psx;
1.1 cgd 92:
93: argptr = argv;
94: if (argc > 0)
95: argptr++;
1.48 kre 96:
97: psx = posix; /* save what we set it to earlier */
98: /*
99: * option values are mostly boolean 0:off 1:on
100: * we use 2 (just in this routine) to mean "unknown yet"
101: */
1.5 jtc 102: for (i = 0; i < NOPTS; i++)
103: optlist[i].val = 2;
1.48 kre 104: posix = psx; /* restore before processing -o ... */
105:
1.1 cgd 106: options(1);
1.48 kre 107:
1.1 cgd 108: if (*argptr == NULL && minusc == NULL)
109: sflag = 1;
1.50 kre 110: if (iflag == 2 && sflag == 1 && isatty(0) && isatty(2))
1.1 cgd 111: iflag = 1;
1.39 christos 112: if (iflag == 1 && sflag == 2)
113: iflag = 2;
1.5 jtc 114: if (mflag == 2)
115: mflag = iflag;
1.44 christos 116: #ifndef DO_SHAREDVFORK
117: if (usefork == 2)
118: usefork = 1;
119: #endif
1.49 kre 120: #if DEBUG >= 2
1.48 kre 121: if (debug == 2)
122: debug = 1;
1.32 christos 123: #endif
1.48 kre 124: /*
125: * Any options not dealt with as special cases just above,
126: * and which were not set on the command line, are set to
127: * their expected default values (mostly "off")
128: *
129: * then as each option is initialised, save its setting now
130: * as its "default" value for future use ("set -o default").
131: */
132: for (i = 0; i < NOPTS; i++) {
133: if (optlist[i].val == 2)
134: optlist[i].val = optlist[i].dflt;
135: optlist[i].dflt = optlist[i].val;
136: }
137:
1.1 cgd 138: arg0 = argv[0];
139: if (sflag == 0 && minusc == NULL) {
1.31 wiz 140: commandname = argv[0];
141: arg0 = *argptr++;
142: setinputfile(arg0, 0);
143: commandname = arg0;
1.1 cgd 144: }
1.17 christos 145: /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
1.33 dsl 146: if (minusc != NULL) {
147: if (argptr == NULL || *argptr == NULL)
148: error("Bad -c option");
149: minusc = *argptr++;
150: if (*argptr != 0)
151: arg0 = *argptr++;
152: }
1.17 christos 153:
1.1 cgd 154: shellparam.p = argptr;
1.19 christos 155: shellparam.reset = 1;
1.1 cgd 156: /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
157: while (*argptr) {
158: shellparam.nparam++;
159: argptr++;
160: }
1.5 jtc 161: optschanged();
1.1 cgd 162: }
163:
164:
1.9 cgd 165: void
1.32 christos 166: optschanged(void)
1.9 cgd 167: {
1.5 jtc 168: setinteractive(iflag);
1.24 christos 169: #ifndef SMALL
1.5 jtc 170: histedit();
1.7 cgd 171: #endif
1.5 jtc 172: setjobctl(mflag);
173: }
1.1 cgd 174:
175: /*
176: * Process shell options. The global variable argptr contains a pointer
177: * to the argument list; we advance it past the options.
178: */
179:
180: STATIC void
1.32 christos 181: options(int cmdline)
1.10 cgd 182: {
1.37 christos 183: static char empty[] = "";
1.22 tls 184: char *p;
1.1 cgd 185: int val;
186: int c;
187:
188: if (cmdline)
189: minusc = NULL;
190: while ((p = *argptr) != NULL) {
191: argptr++;
192: if ((c = *p++) == '-') {
193: val = 1;
1.14 christos 194: if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
1.1 cgd 195: if (!cmdline) {
196: /* "-" means turn off -x and -v */
197: if (p[0] == '\0')
198: xflag = vflag = 0;
199: /* "--" means reset params */
1.13 christos 200: else if (*argptr == NULL)
201: setparam(argptr);
1.1 cgd 202: }
203: break; /* "-" or "--" terminates options */
204: }
205: } else if (c == '+') {
206: val = 0;
207: } else {
208: argptr--;
209: break;
210: }
211: while ((c = *p++) != '\0') {
1.48 kre 212: if (val == 1 && c == 'c' && cmdline) {
1.37 christos 213: /* command is after shell args*/
214: minusc = empty;
1.5 jtc 215: } else if (c == 'o') {
1.48 kre 216: if (*p != '\0')
217: minus_o(p, val + (cmdline ? val : 0));
218: else if (*argptr)
219: minus_o(*argptr++,
220: val + (cmdline ? val : 0));
221: else if (!cmdline)
222: minus_o(NULL, val);
223: else
224: error("arg for %co missing", "+-"[val]);
225: break;
1.47 kre 226: #ifdef DEBUG
227: } else if (c == 'D') {
228: if (*p) {
229: set_debug(p, val);
230: break;
231: } else if (*argptr)
232: set_debug(*argptr++, val);
233: else
234: set_debug("*$", val);
235: #endif
1.1 cgd 236: } else {
237: setoption(c, val);
238: }
239: }
240: }
241: }
242:
1.32 christos 243: static void
1.41 lukem 244: set_opt_val(size_t i, int val)
1.32 christos 245: {
1.41 lukem 246: size_t j;
1.32 christos 247: int flag;
248:
249: if (val && (flag = optlist[i].opt_set)) {
250: /* some options (eg vi/emacs) are mutually exclusive */
251: for (j = 0; j < NOPTS; j++)
252: if (optlist[j].opt_set == flag)
253: optlist[j].val = 0;
254: }
1.51 ! kre 255: if (i == _SH_OPT_Xflag)
! 256: xtracefdsetup(val);
1.32 christos 257: optlist[i].val = val;
258: #ifdef DEBUG
259: if (&optlist[i].val == &debug)
1.51 ! kre 260: opentrace(); /* different "trace" than the -x one... */
1.32 christos 261: #endif
262: }
263:
1.5 jtc 264: STATIC void
1.32 christos 265: minus_o(char *name, int val)
1.5 jtc 266: {
1.41 lukem 267: size_t i;
1.46 christos 268: const char *sep = ": ";
1.5 jtc 269:
270: if (name == NULL) {
1.40 dsl 271: if (val) {
1.46 christos 272: out1str("Current option settings");
1.40 dsl 273: for (i = 0; i < NOPTS; i++) {
1.46 christos 274: if (optlist[i].name == NULL) {
275: out1fmt("%s%c%c", sep,
276: "+-"[optlist[i].val],
277: optlist[i].letter);
278: sep = ", ";
279: }
280: }
281: out1c('\n');
282: for (i = 0; i < NOPTS; i++) {
283: if (optlist[i].name)
1.48 kre 284: out1fmt("%-19s %s\n", optlist[i].name,
1.40 dsl 285: optlist[i].val ? "on" : "off");
286: }
287: } else {
1.48 kre 288: out1str("set -o default");
1.40 dsl 289: for (i = 0; i < NOPTS; i++) {
1.48 kre 290: if (optlist[i].val == optlist[i].dflt)
291: continue;
1.46 christos 292: if (optlist[i].name)
293: out1fmt(" %co %s",
1.40 dsl 294: "+-"[optlist[i].val], optlist[i].name);
1.46 christos 295: else
296: out1fmt(" %c%c", "+-"[optlist[i].val],
297: optlist[i].letter);
1.40 dsl 298: }
1.46 christos 299: out1c('\n');
1.40 dsl 300: }
1.5 jtc 301: } else {
1.48 kre 302: if (val == 1 && equal(name, "default")) { /* special case */
303: for (i = 0; i < NOPTS; i++)
304: set_opt_val(i, optlist[i].dflt);
305: return;
306: }
307: if (val)
308: val = 1;
1.5 jtc 309: for (i = 0; i < NOPTS; i++)
1.46 christos 310: if (optlist[i].name && equal(name, optlist[i].name)) {
1.32 christos 311: set_opt_val(i, val);
1.51 ! kre 312: if (i == _SH_OPT_Xflag)
! 313: set_opt_val(_SH_OPT_xflag, val);
1.5 jtc 314: return;
315: }
1.48 kre 316: error("Illegal option %co %s", "+-"[val], name);
1.5 jtc 317: }
318: }
1.17 christos 319:
1.1 cgd 320:
321: STATIC void
1.32 christos 322: setoption(int flag, int val)
323: {
1.41 lukem 324: size_t i;
1.1 cgd 325:
1.5 jtc 326: for (i = 0; i < NOPTS; i++)
327: if (optlist[i].letter == flag) {
1.51 ! kre 328: set_opt_val(i, val);
! 329: if (i == _SH_OPT_Xflag)
! 330: set_opt_val(_SH_OPT_xflag, val);
1.5 jtc 331: return;
332: }
1.48 kre 333: error("Illegal option %c%c", "+-"[val], flag);
1.28 mycroft 334: /* NOTREACHED */
1.1 cgd 335: }
336:
337:
338:
339: #ifdef mkinit
340: INCLUDE "options.h"
341:
342: SHELLPROC {
1.5 jtc 343: int i;
344:
1.32 christos 345: for (i = 0; optlist[i].name; i++)
1.5 jtc 346: optlist[i].val = 0;
347: optschanged();
1.1 cgd 348:
349: }
350: #endif
351:
352:
353: /*
354: * Set the shell parameters.
355: */
356:
357: void
1.32 christos 358: setparam(char **argv)
359: {
1.1 cgd 360: char **newparam;
361: char **ap;
362: int nparam;
363:
1.38 dsl 364: for (nparam = 0 ; argv[nparam] ; nparam++)
365: continue;
1.1 cgd 366: ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
367: while (*argv) {
368: *ap++ = savestr(*argv++);
369: }
370: *ap = NULL;
371: freeparam(&shellparam);
372: shellparam.malloc = 1;
373: shellparam.nparam = nparam;
374: shellparam.p = newparam;
375: shellparam.optnext = NULL;
376: }
377:
378:
379: /*
380: * Free the list of positional parameters.
381: */
382:
383: void
1.32 christos 384: freeparam(volatile struct shparam *param)
385: {
1.1 cgd 386: char **ap;
387:
388: if (param->malloc) {
389: for (ap = param->p ; *ap ; ap++)
390: ckfree(*ap);
391: ckfree(param->p);
392: }
393: }
394:
395:
396:
397: /*
398: * The shift builtin command.
399: */
400:
1.10 cgd 401: int
1.32 christos 402: shiftcmd(int argc, char **argv)
1.10 cgd 403: {
1.1 cgd 404: int n;
405: char **ap1, **ap2;
406:
1.45 christos 407: if (argc > 2)
408: error("Usage: shift [n]");
1.1 cgd 409: n = 1;
410: if (argc > 1)
411: n = number(argv[1]);
412: if (n > shellparam.nparam)
1.5 jtc 413: error("can't shift that many");
1.1 cgd 414: INTOFF;
415: shellparam.nparam -= n;
416: for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
417: if (shellparam.malloc)
418: ckfree(*ap1);
419: }
420: ap2 = shellparam.p;
1.45 christos 421: while ((*ap2++ = *ap1++) != NULL)
422: continue;
1.1 cgd 423: shellparam.optnext = NULL;
424: INTON;
425: return 0;
426: }
427:
428:
429:
430: /*
431: * The set command builtin.
432: */
433:
1.10 cgd 434: int
1.32 christos 435: setcmd(int argc, char **argv)
1.10 cgd 436: {
1.1 cgd 437: if (argc == 1)
1.46 christos 438: return showvars(0, 0, 1, 0);
1.1 cgd 439: INTOFF;
440: options(0);
1.5 jtc 441: optschanged();
1.1 cgd 442: if (*argptr != NULL) {
443: setparam(argptr);
444: }
445: INTON;
446: return 0;
447: }
448:
449:
1.16 christos 450: void
1.43 matt 451: getoptsreset(const char *value)
1.16 christos 452: {
1.19 christos 453: if (number(value) == 1) {
1.18 christos 454: shellparam.optnext = NULL;
1.19 christos 455: shellparam.reset = 1;
456: }
1.16 christos 457: }
458:
1.1 cgd 459: /*
460: * The getopts builtin. Shellparam.optnext points to the next argument
461: * to be processed. Shellparam.optptr points to the next character to
462: * be processed in the current argument. If shellparam.optnext is NULL,
463: * then it's the first time getopts has been called.
464: */
465:
1.10 cgd 466: int
1.32 christos 467: getoptscmd(int argc, char **argv)
1.10 cgd 468: {
1.15 christos 469: char **optbase;
470:
471: if (argc < 3)
1.36 jmmv 472: error("usage: getopts optstring var [arg]");
1.15 christos 473: else if (argc == 3)
474: optbase = shellparam.p;
1.17 christos 475: else
1.15 christos 476: optbase = &argv[3];
1.1 cgd 477:
1.19 christos 478: if (shellparam.reset == 1) {
1.15 christos 479: shellparam.optnext = optbase;
1.1 cgd 480: shellparam.optptr = NULL;
1.19 christos 481: shellparam.reset = 0;
1.1 cgd 482: }
1.15 christos 483:
484: return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
485: &shellparam.optptr);
486: }
487:
488: STATIC int
1.32 christos 489: getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
1.15 christos 490: {
1.22 tls 491: char *p, *q;
1.15 christos 492: char c = '?';
493: int done = 0;
494: int ind = 0;
1.16 christos 495: int err = 0;
1.34 itojun 496: char s[12];
1.15 christos 497:
1.29 christos 498: if ((p = *optpptr) == NULL || *p == '\0') {
1.15 christos 499: /* Current word is done, advance */
1.19 christos 500: if (*optnext == NULL)
501: return 1;
1.15 christos 502: p = **optnext;
1.1 cgd 503: if (p == NULL || *p != '-' || *++p == '\0') {
504: atend:
1.21 christos 505: ind = *optnext - optfirst + 1;
1.20 christos 506: *optnext = NULL;
1.21 christos 507: p = NULL;
1.15 christos 508: done = 1;
509: goto out;
1.1 cgd 510: }
1.15 christos 511: (*optnext)++;
1.1 cgd 512: if (p[0] == '-' && p[1] == '\0') /* check for "--" */
513: goto atend;
514: }
1.15 christos 515:
1.1 cgd 516: c = *p++;
1.15 christos 517: for (q = optstr; *q != c; ) {
1.1 cgd 518: if (*q == '\0') {
1.15 christos 519: if (optstr[0] == ':') {
520: s[0] = c;
521: s[1] = '\0';
1.16 christos 522: err |= setvarsafe("OPTARG", s, 0);
1.32 christos 523: } else {
1.26 christos 524: outfmt(&errout, "Illegal option -%c\n", c);
1.32 christos 525: (void) unsetvar("OPTARG", 0);
1.15 christos 526: }
1.1 cgd 527: c = '?';
1.16 christos 528: goto bad;
1.1 cgd 529: }
530: if (*++q == ':')
531: q++;
532: }
1.15 christos 533:
1.1 cgd 534: if (*++q == ':') {
1.15 christos 535: if (*p == '\0' && (p = **optnext) == NULL) {
536: if (optstr[0] == ':') {
537: s[0] = c;
538: s[1] = '\0';
1.16 christos 539: err |= setvarsafe("OPTARG", s, 0);
1.15 christos 540: c = ':';
1.32 christos 541: } else {
1.26 christos 542: outfmt(&errout, "No arg for -%c option\n", c);
1.32 christos 543: (void) unsetvar("OPTARG", 0);
1.15 christos 544: c = '?';
545: }
1.16 christos 546: goto bad;
1.1 cgd 547: }
1.15 christos 548:
549: if (p == **optnext)
550: (*optnext)++;
1.32 christos 551: err |= setvarsafe("OPTARG", p, 0);
1.1 cgd 552: p = NULL;
1.32 christos 553: } else
554: err |= setvarsafe("OPTARG", "", 0);
1.19 christos 555: ind = *optnext - optfirst + 1;
556: goto out;
557:
1.16 christos 558: bad:
559: ind = 1;
560: *optnext = NULL;
561: p = NULL;
1.1 cgd 562: out:
1.29 christos 563: *optpptr = p;
1.15 christos 564: fmtstr(s, sizeof(s), "%d", ind);
1.16 christos 565: err |= setvarsafe("OPTIND", s, VNOFUNC);
1.1 cgd 566: s[0] = c;
567: s[1] = '\0';
1.16 christos 568: err |= setvarsafe(optvar, s, 0);
569: if (err) {
570: *optnext = NULL;
1.29 christos 571: *optpptr = NULL;
1.16 christos 572: flushall();
573: exraise(EXERROR);
574: }
1.15 christos 575: return done;
1.1 cgd 576: }
577:
578: /*
1.5 jtc 579: * XXX - should get rid of. have all builtins use getopt(3). the
580: * library getopt must have the BSD extension static variable "optreset"
581: * otherwise it can't be used within the shell safely.
582: *
1.1 cgd 583: * Standard option processing (a la getopt) for builtin routines. The
584: * only argument that is passed to nextopt is the option string; the
585: * other arguments are unnecessary. It return the character, or '\0' on
586: * end of input.
587: */
588:
589: int
1.32 christos 590: nextopt(const char *optstring)
591: {
1.29 christos 592: char *p;
593: const char *q;
1.1 cgd 594: char c;
595:
596: if ((p = optptr) == NULL || *p == '\0') {
597: p = *argptr;
598: if (p == NULL || *p != '-' || *++p == '\0')
599: return '\0';
600: argptr++;
601: if (p[0] == '-' && p[1] == '\0') /* check for "--" */
602: return '\0';
603: }
604: c = *p++;
605: for (q = optstring ; *q != c ; ) {
606: if (*q == '\0')
607: error("Illegal option -%c", c);
608: if (*++q == ':')
609: q++;
610: }
611: if (*++q == ':') {
612: if (*p == '\0' && (p = *argptr++) == NULL)
613: error("No arg for -%c option", c);
1.30 christos 614: optionarg = p;
1.1 cgd 615: p = NULL;
616: }
617: optptr = p;
618: return c;
619: }
CVSweb <webmaster@jp.NetBSD.org>