xref: /netbsd-src/bin/sh/options.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /*	$NetBSD: options.c,v 1.52 2017/11/21 03:42:39 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
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.
18  * 3. Neither the name of the University nor the names of its contributors
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 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: options.c,v 1.52 2017/11/21 03:42:39 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <signal.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 
48 #include "shell.h"
49 #define DEFINE_OPTIONS
50 #include "options.h"
51 #undef DEFINE_OPTIONS
52 #include "builtins.h"
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"
63 #ifndef SMALL
64 #include "myhistedit.h"
65 #endif
66 #include "show.h"
67 
68 char *arg0;			/* value of $0 */
69 struct shparam shellparam;	/* current positional parameters */
70 char **argptr;			/* argument list for builtin commands */
71 char *optionarg;		/* set by nextopt (like getopt) */
72 char *optptr;			/* used by nextopt */
73 
74 char *minusc;			/* argument to -c option */
75 
76 
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 **);
81 
82 
83 /*
84  * Process the shell command line arguments.
85  */
86 
87 void
88 procargs(int argc, char **argv)
89 {
90 	size_t i;
91 	int psx;
92 
93 	argptr = argv;
94 	if (argc > 0)
95 		argptr++;
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 	 */
102 	for (i = 0; i < NOPTS; i++)
103 		optlist[i].val = 2;
104 	posix = psx;		/* restore before processing -o ... */
105 
106 	options(1);
107 
108 	if (*argptr == NULL && minusc == NULL)
109 		sflag = 1;
110 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(2))
111 		iflag = 1;
112 	if (iflag == 1 && sflag == 2)
113 		iflag = 2;
114 	if (mflag == 2)
115 		mflag = iflag;
116 #ifndef DO_SHAREDVFORK
117 	if (usefork == 2)
118 		usefork = 1;
119 #endif
120 #if DEBUG >= 2
121 	if (debug == 2)
122 		debug = 1;
123 #endif
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 
138 	arg0 = argv[0];
139 	if (sflag == 0 && minusc == NULL) {
140 		commandname = argv[0];
141 		arg0 = *argptr++;
142 		setinputfile(arg0, 0);
143 		commandname = arg0;
144 	}
145 	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
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 	}
153 
154 	shellparam.p = argptr;
155 	shellparam.reset = 1;
156 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
157 	while (*argptr) {
158 		shellparam.nparam++;
159 		argptr++;
160 	}
161 	optschanged();
162 }
163 
164 
165 void
166 optschanged(void)
167 {
168 	setinteractive(iflag);
169 #ifndef SMALL
170 	histedit();
171 #endif
172 	setjobctl(mflag);
173 }
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
181 options(int cmdline)
182 {
183 	static char empty[] = "";
184 	char *p;
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;
194                         if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
195                                 if (!cmdline) {
196                                         /* "-" means turn off -x and -v */
197                                         if (p[0] == '\0')
198                                                 xflag = vflag = 0;
199                                         /* "--" means reset params */
200                                         else if (*argptr == NULL)
201 						setparam(argptr);
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') {
212 			if (val == 1 && c == 'c' && cmdline) {
213 				/* command is after shell args*/
214 				minusc = empty;
215 			} else if (c == 'o') {
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;
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
236 			} else {
237 				setoption(c, val);
238 			}
239 		}
240 	}
241 }
242 
243 static void
244 set_opt_val(size_t i, int val)
245 {
246 	size_t j;
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 	}
255 #ifndef SMALL
256 	if (i == _SH_OPT_Xflag)
257 		xtracefdsetup(val);
258 #endif
259 	optlist[i].val = val;
260 #ifdef DEBUG
261 	if (&optlist[i].val == &debug)
262 		opentrace();	/* different "trace" than the -x one... */
263 #endif
264 }
265 
266 STATIC void
267 minus_o(char *name, int val)
268 {
269 	size_t i;
270 	const char *sep = ": ";
271 
272 	if (name == NULL) {
273 		if (val) {
274 			out1str("Current option settings");
275 			for (i = 0; i < NOPTS; i++) {
276 				if (optlist[i].name == NULL)  {
277 					out1fmt("%s%c%c", sep,
278 					    "+-"[optlist[i].val],
279 					    optlist[i].letter);
280 					sep = ", ";
281 				}
282 			}
283 			out1c('\n');
284 			for (i = 0; i < NOPTS; i++) {
285 				if (optlist[i].name)
286 				    out1fmt("%-19s %s\n", optlist[i].name,
287 					optlist[i].val ? "on" : "off");
288 			}
289 		} else {
290 			out1str("set -o default");
291 			for (i = 0; i < NOPTS; i++) {
292 				if (optlist[i].val == optlist[i].dflt)
293 					continue;
294 				if (optlist[i].name)
295 				    out1fmt(" %co %s",
296 					"+-"[optlist[i].val], optlist[i].name);
297 				else
298 				    out1fmt(" %c%c", "+-"[optlist[i].val],
299 					optlist[i].letter);
300 			}
301 			out1c('\n');
302 		}
303 	} else {
304 		if (val == 1 && equal(name, "default")) { /* special case */
305 			for (i = 0; i < NOPTS; i++)
306 				set_opt_val(i, optlist[i].dflt);
307 			return;
308 		}
309 		if (val)
310 			val = 1;
311 		for (i = 0; i < NOPTS; i++)
312 			if (optlist[i].name && equal(name, optlist[i].name)) {
313 				set_opt_val(i, val);
314 #ifndef SMALL
315 				if (i == _SH_OPT_Xflag)
316 					set_opt_val(_SH_OPT_xflag, val);
317 #endif
318 				return;
319 			}
320 		error("Illegal option %co %s", "+-"[val], name);
321 	}
322 }
323 
324 
325 STATIC void
326 setoption(int flag, int val)
327 {
328 	size_t i;
329 
330 	for (i = 0; i < NOPTS; i++)
331 		if (optlist[i].letter == flag) {
332 			set_opt_val(i, val);
333 #ifndef SMALL
334 			if (i == _SH_OPT_Xflag)
335 				set_opt_val(_SH_OPT_xflag, val);
336 #endif
337 			return;
338 		}
339 	error("Illegal option %c%c", "+-"[val], flag);
340 	/* NOTREACHED */
341 }
342 
343 
344 
345 #ifdef mkinit
346 INCLUDE "options.h"
347 
348 SHELLPROC {
349 	int i;
350 
351 	for (i = 0; optlist[i].name; i++)
352 		optlist[i].val = 0;
353 	optschanged();
354 
355 }
356 #endif
357 
358 
359 /*
360  * Set the shell parameters.
361  */
362 
363 void
364 setparam(char **argv)
365 {
366 	char **newparam;
367 	char **ap;
368 	int nparam;
369 
370 	for (nparam = 0 ; argv[nparam] ; nparam++)
371 		continue;
372 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
373 	while (*argv) {
374 		*ap++ = savestr(*argv++);
375 	}
376 	*ap = NULL;
377 	freeparam(&shellparam);
378 	shellparam.malloc = 1;
379 	shellparam.nparam = nparam;
380 	shellparam.p = newparam;
381 	shellparam.optnext = NULL;
382 }
383 
384 
385 /*
386  * Free the list of positional parameters.
387  */
388 
389 void
390 freeparam(volatile struct shparam *param)
391 {
392 	char **ap;
393 
394 	if (param->malloc) {
395 		for (ap = param->p ; *ap ; ap++)
396 			ckfree(*ap);
397 		ckfree(param->p);
398 	}
399 }
400 
401 
402 
403 /*
404  * The shift builtin command.
405  */
406 
407 int
408 shiftcmd(int argc, char **argv)
409 {
410 	int n;
411 	char **ap1, **ap2;
412 
413 	if (argc > 2)
414 		error("Usage: shift [n]");
415 	n = 1;
416 	if (argc > 1)
417 		n = number(argv[1]);
418 	if (n > shellparam.nparam)
419 		error("can't shift that many");
420 	INTOFF;
421 	shellparam.nparam -= n;
422 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
423 		if (shellparam.malloc)
424 			ckfree(*ap1);
425 	}
426 	ap2 = shellparam.p;
427 	while ((*ap2++ = *ap1++) != NULL)
428 		continue;
429 	shellparam.optnext = NULL;
430 	INTON;
431 	return 0;
432 }
433 
434 
435 
436 /*
437  * The set command builtin.
438  */
439 
440 int
441 setcmd(int argc, char **argv)
442 {
443 	if (argc == 1)
444 		return showvars(0, 0, 1, 0);
445 	INTOFF;
446 	options(0);
447 	optschanged();
448 	if (*argptr != NULL) {
449 		setparam(argptr);
450 	}
451 	INTON;
452 	return 0;
453 }
454 
455 
456 void
457 getoptsreset(const char *value)
458 {
459 	if (number(value) == 1) {
460 		shellparam.optnext = NULL;
461 		shellparam.reset = 1;
462 	}
463 }
464 
465 /*
466  * The getopts builtin.  Shellparam.optnext points to the next argument
467  * to be processed.  Shellparam.optptr points to the next character to
468  * be processed in the current argument.  If shellparam.optnext is NULL,
469  * then it's the first time getopts has been called.
470  */
471 
472 int
473 getoptscmd(int argc, char **argv)
474 {
475 	char **optbase;
476 
477 	if (argc < 3)
478 		error("usage: getopts optstring var [arg]");
479 	else if (argc == 3)
480 		optbase = shellparam.p;
481 	else
482 		optbase = &argv[3];
483 
484 	if (shellparam.reset == 1) {
485 		shellparam.optnext = optbase;
486 		shellparam.optptr = NULL;
487 		shellparam.reset = 0;
488 	}
489 
490 	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
491 		       &shellparam.optptr);
492 }
493 
494 STATIC int
495 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
496 {
497 	char *p, *q;
498 	char c = '?';
499 	int done = 0;
500 	int ind = 0;
501 	int err = 0;
502 	char s[12];
503 
504 	if ((p = *optpptr) == NULL || *p == '\0') {
505 		/* Current word is done, advance */
506 		if (*optnext == NULL)
507 			return 1;
508 		p = **optnext;
509 		if (p == NULL || *p != '-' || *++p == '\0') {
510 atend:
511 			ind = *optnext - optfirst + 1;
512 			*optnext = NULL;
513 			p = NULL;
514 			done = 1;
515 			goto out;
516 		}
517 		(*optnext)++;
518 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
519 			goto atend;
520 	}
521 
522 	c = *p++;
523 	for (q = optstr; *q != c; ) {
524 		if (*q == '\0') {
525 			if (optstr[0] == ':') {
526 				s[0] = c;
527 				s[1] = '\0';
528 				err |= setvarsafe("OPTARG", s, 0);
529 			} else {
530 				outfmt(&errout, "Illegal option -%c\n", c);
531 				(void) unsetvar("OPTARG", 0);
532 			}
533 			c = '?';
534 			goto bad;
535 		}
536 		if (*++q == ':')
537 			q++;
538 	}
539 
540 	if (*++q == ':') {
541 		if (*p == '\0' && (p = **optnext) == NULL) {
542 			if (optstr[0] == ':') {
543 				s[0] = c;
544 				s[1] = '\0';
545 				err |= setvarsafe("OPTARG", s, 0);
546 				c = ':';
547 			} else {
548 				outfmt(&errout, "No arg for -%c option\n", c);
549 				(void) unsetvar("OPTARG", 0);
550 				c = '?';
551 			}
552 			goto bad;
553 		}
554 
555 		if (p == **optnext)
556 			(*optnext)++;
557 		err |= setvarsafe("OPTARG", p, 0);
558 		p = NULL;
559 	} else
560 		err |= setvarsafe("OPTARG", "", 0);
561 	ind = *optnext - optfirst + 1;
562 	goto out;
563 
564 bad:
565 	ind = 1;
566 	*optnext = NULL;
567 	p = NULL;
568 out:
569 	*optpptr = p;
570 	fmtstr(s, sizeof(s), "%d", ind);
571 	err |= setvarsafe("OPTIND", s, VNOFUNC);
572 	s[0] = c;
573 	s[1] = '\0';
574 	err |= setvarsafe(optvar, s, 0);
575 	if (err) {
576 		*optnext = NULL;
577 		*optpptr = NULL;
578 		flushall();
579 		exraise(EXERROR);
580 	}
581 	return done;
582 }
583 
584 /*
585  * XXX - should get rid of.  have all builtins use getopt(3).  the
586  * library getopt must have the BSD extension static variable "optreset"
587  * otherwise it can't be used within the shell safely.
588  *
589  * Standard option processing (a la getopt) for builtin routines.  The
590  * only argument that is passed to nextopt is the option string; the
591  * other arguments are unnecessary.  It return the character, or '\0' on
592  * end of input.
593  */
594 
595 int
596 nextopt(const char *optstring)
597 {
598 	char *p;
599 	const char *q;
600 	char c;
601 
602 	if ((p = optptr) == NULL || *p == '\0') {
603 		p = *argptr;
604 		if (p == NULL || *p != '-' || *++p == '\0')
605 			return '\0';
606 		argptr++;
607 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
608 			return '\0';
609 	}
610 	c = *p++;
611 	for (q = optstring ; *q != c ; ) {
612 		if (*q == '\0')
613 			error("Illegal option -%c", c);
614 		if (*++q == ':')
615 			q++;
616 	}
617 	if (*++q == ':') {
618 		if (*p == '\0' && (p = *argptr++) == NULL)
619 			error("No arg for -%c option", c);
620 		optionarg = p;
621 		p = NULL;
622 	}
623 	optptr = p;
624 	return c;
625 }
626