xref: /netbsd-src/bin/sh/options.c (revision 1580a27b92f58fcdcb23fdfbc04a7c2b54a0b7c8)
1 /*	$NetBSD: options.c,v 1.50 2017/07/24 12:35:37 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.50 2017/07/24 12:35:37 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 	optlist[i].val = val;
256 #ifdef DEBUG
257 	if (&optlist[i].val == &debug)
258 		opentrace();
259 #endif
260 }
261 
262 STATIC void
263 minus_o(char *name, int val)
264 {
265 	size_t i;
266 	const char *sep = ": ";
267 
268 	if (name == NULL) {
269 		if (val) {
270 			out1str("Current option settings");
271 			for (i = 0; i < NOPTS; i++) {
272 				if (optlist[i].name == NULL)  {
273 					out1fmt("%s%c%c", sep,
274 					    "+-"[optlist[i].val],
275 					    optlist[i].letter);
276 					sep = ", ";
277 				}
278 			}
279 			out1c('\n');
280 			for (i = 0; i < NOPTS; i++) {
281 				if (optlist[i].name)
282 				    out1fmt("%-19s %s\n", optlist[i].name,
283 					optlist[i].val ? "on" : "off");
284 			}
285 		} else {
286 			out1str("set -o default");
287 			for (i = 0; i < NOPTS; i++) {
288 				if (optlist[i].val == optlist[i].dflt)
289 					continue;
290 				if (optlist[i].name)
291 				    out1fmt(" %co %s",
292 					"+-"[optlist[i].val], optlist[i].name);
293 				else
294 				    out1fmt(" %c%c", "+-"[optlist[i].val],
295 					optlist[i].letter);
296 			}
297 			out1c('\n');
298 		}
299 	} else {
300 		if (val == 1 && equal(name, "default")) { /* special case */
301 			for (i = 0; i < NOPTS; i++)
302 				set_opt_val(i, optlist[i].dflt);
303 			return;
304 		}
305 		if (val)
306 			val = 1;
307 		for (i = 0; i < NOPTS; i++)
308 			if (optlist[i].name && equal(name, optlist[i].name)) {
309 				set_opt_val(i, val);
310 				return;
311 			}
312 		error("Illegal option %co %s", "+-"[val], name);
313 	}
314 }
315 
316 
317 STATIC void
318 setoption(int flag, int val)
319 {
320 	size_t i;
321 
322 	for (i = 0; i < NOPTS; i++)
323 		if (optlist[i].letter == flag) {
324 			set_opt_val( i, val );
325 			return;
326 		}
327 	error("Illegal option %c%c", "+-"[val], flag);
328 	/* NOTREACHED */
329 }
330 
331 
332 
333 #ifdef mkinit
334 INCLUDE "options.h"
335 
336 SHELLPROC {
337 	int i;
338 
339 	for (i = 0; optlist[i].name; i++)
340 		optlist[i].val = 0;
341 	optschanged();
342 
343 }
344 #endif
345 
346 
347 /*
348  * Set the shell parameters.
349  */
350 
351 void
352 setparam(char **argv)
353 {
354 	char **newparam;
355 	char **ap;
356 	int nparam;
357 
358 	for (nparam = 0 ; argv[nparam] ; nparam++)
359 		continue;
360 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
361 	while (*argv) {
362 		*ap++ = savestr(*argv++);
363 	}
364 	*ap = NULL;
365 	freeparam(&shellparam);
366 	shellparam.malloc = 1;
367 	shellparam.nparam = nparam;
368 	shellparam.p = newparam;
369 	shellparam.optnext = NULL;
370 }
371 
372 
373 /*
374  * Free the list of positional parameters.
375  */
376 
377 void
378 freeparam(volatile struct shparam *param)
379 {
380 	char **ap;
381 
382 	if (param->malloc) {
383 		for (ap = param->p ; *ap ; ap++)
384 			ckfree(*ap);
385 		ckfree(param->p);
386 	}
387 }
388 
389 
390 
391 /*
392  * The shift builtin command.
393  */
394 
395 int
396 shiftcmd(int argc, char **argv)
397 {
398 	int n;
399 	char **ap1, **ap2;
400 
401 	if (argc > 2)
402 		error("Usage: shift [n]");
403 	n = 1;
404 	if (argc > 1)
405 		n = number(argv[1]);
406 	if (n > shellparam.nparam)
407 		error("can't shift that many");
408 	INTOFF;
409 	shellparam.nparam -= n;
410 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
411 		if (shellparam.malloc)
412 			ckfree(*ap1);
413 	}
414 	ap2 = shellparam.p;
415 	while ((*ap2++ = *ap1++) != NULL)
416 		continue;
417 	shellparam.optnext = NULL;
418 	INTON;
419 	return 0;
420 }
421 
422 
423 
424 /*
425  * The set command builtin.
426  */
427 
428 int
429 setcmd(int argc, char **argv)
430 {
431 	if (argc == 1)
432 		return showvars(0, 0, 1, 0);
433 	INTOFF;
434 	options(0);
435 	optschanged();
436 	if (*argptr != NULL) {
437 		setparam(argptr);
438 	}
439 	INTON;
440 	return 0;
441 }
442 
443 
444 void
445 getoptsreset(const char *value)
446 {
447 	if (number(value) == 1) {
448 		shellparam.optnext = NULL;
449 		shellparam.reset = 1;
450 	}
451 }
452 
453 /*
454  * The getopts builtin.  Shellparam.optnext points to the next argument
455  * to be processed.  Shellparam.optptr points to the next character to
456  * be processed in the current argument.  If shellparam.optnext is NULL,
457  * then it's the first time getopts has been called.
458  */
459 
460 int
461 getoptscmd(int argc, char **argv)
462 {
463 	char **optbase;
464 
465 	if (argc < 3)
466 		error("usage: getopts optstring var [arg]");
467 	else if (argc == 3)
468 		optbase = shellparam.p;
469 	else
470 		optbase = &argv[3];
471 
472 	if (shellparam.reset == 1) {
473 		shellparam.optnext = optbase;
474 		shellparam.optptr = NULL;
475 		shellparam.reset = 0;
476 	}
477 
478 	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
479 		       &shellparam.optptr);
480 }
481 
482 STATIC int
483 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
484 {
485 	char *p, *q;
486 	char c = '?';
487 	int done = 0;
488 	int ind = 0;
489 	int err = 0;
490 	char s[12];
491 
492 	if ((p = *optpptr) == NULL || *p == '\0') {
493 		/* Current word is done, advance */
494 		if (*optnext == NULL)
495 			return 1;
496 		p = **optnext;
497 		if (p == NULL || *p != '-' || *++p == '\0') {
498 atend:
499 			ind = *optnext - optfirst + 1;
500 			*optnext = NULL;
501 			p = NULL;
502 			done = 1;
503 			goto out;
504 		}
505 		(*optnext)++;
506 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
507 			goto atend;
508 	}
509 
510 	c = *p++;
511 	for (q = optstr; *q != c; ) {
512 		if (*q == '\0') {
513 			if (optstr[0] == ':') {
514 				s[0] = c;
515 				s[1] = '\0';
516 				err |= setvarsafe("OPTARG", s, 0);
517 			} else {
518 				outfmt(&errout, "Illegal option -%c\n", c);
519 				(void) unsetvar("OPTARG", 0);
520 			}
521 			c = '?';
522 			goto bad;
523 		}
524 		if (*++q == ':')
525 			q++;
526 	}
527 
528 	if (*++q == ':') {
529 		if (*p == '\0' && (p = **optnext) == NULL) {
530 			if (optstr[0] == ':') {
531 				s[0] = c;
532 				s[1] = '\0';
533 				err |= setvarsafe("OPTARG", s, 0);
534 				c = ':';
535 			} else {
536 				outfmt(&errout, "No arg for -%c option\n", c);
537 				(void) unsetvar("OPTARG", 0);
538 				c = '?';
539 			}
540 			goto bad;
541 		}
542 
543 		if (p == **optnext)
544 			(*optnext)++;
545 		err |= setvarsafe("OPTARG", p, 0);
546 		p = NULL;
547 	} else
548 		err |= setvarsafe("OPTARG", "", 0);
549 	ind = *optnext - optfirst + 1;
550 	goto out;
551 
552 bad:
553 	ind = 1;
554 	*optnext = NULL;
555 	p = NULL;
556 out:
557 	*optpptr = p;
558 	fmtstr(s, sizeof(s), "%d", ind);
559 	err |= setvarsafe("OPTIND", s, VNOFUNC);
560 	s[0] = c;
561 	s[1] = '\0';
562 	err |= setvarsafe(optvar, s, 0);
563 	if (err) {
564 		*optnext = NULL;
565 		*optpptr = NULL;
566 		flushall();
567 		exraise(EXERROR);
568 	}
569 	return done;
570 }
571 
572 /*
573  * XXX - should get rid of.  have all builtins use getopt(3).  the
574  * library getopt must have the BSD extension static variable "optreset"
575  * otherwise it can't be used within the shell safely.
576  *
577  * Standard option processing (a la getopt) for builtin routines.  The
578  * only argument that is passed to nextopt is the option string; the
579  * other arguments are unnecessary.  It return the character, or '\0' on
580  * end of input.
581  */
582 
583 int
584 nextopt(const char *optstring)
585 {
586 	char *p;
587 	const char *q;
588 	char c;
589 
590 	if ((p = optptr) == NULL || *p == '\0') {
591 		p = *argptr;
592 		if (p == NULL || *p != '-' || *++p == '\0')
593 			return '\0';
594 		argptr++;
595 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
596 			return '\0';
597 	}
598 	c = *p++;
599 	for (q = optstring ; *q != c ; ) {
600 		if (*q == '\0')
601 			error("Illegal option -%c", c);
602 		if (*++q == ':')
603 			q++;
604 	}
605 	if (*++q == ':') {
606 		if (*p == '\0' && (p = *argptr++) == NULL)
607 			error("No arg for -%c option", c);
608 		optionarg = p;
609 		p = NULL;
610 	}
611 	optptr = p;
612 	return c;
613 }
614