options.c revision 287296
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/bin/sh/options.c 287296 2015-08-29 19:41:47Z jilles $");
40
41#include <signal.h>
42#include <unistd.h>
43#include <stdlib.h>
44
45#include "shell.h"
46#define DEFINE_OPTIONS
47#include "options.h"
48#undef DEFINE_OPTIONS
49#include "nodes.h"	/* for other header files */
50#include "eval.h"
51#include "jobs.h"
52#include "input.h"
53#include "output.h"
54#include "trap.h"
55#include "var.h"
56#include "memalloc.h"
57#include "error.h"
58#include "mystring.h"
59#include "builtins.h"
60#ifndef NO_HISTORY
61#include "myhistedit.h"
62#endif
63
64char *arg0;			/* value of $0 */
65struct shparam shellparam;	/* current positional parameters */
66char **argptr;			/* argument list for builtin commands */
67char *shoptarg;			/* set by nextopt (like getopt) */
68char *nextopt_optptr;		/* used by nextopt */
69
70char *minusc;			/* argument to -c option */
71
72
73static void options(int);
74static void minus_o(char *, int);
75static void setoption(int, int);
76static void setoptionbyindex(int, int);
77static int getopts(char *, char *, char **, char ***, char **);
78
79
80/*
81 * Process the shell command line arguments.
82 */
83
84void
85procargs(int argc, char **argv)
86{
87	int i;
88	char *scriptname;
89
90	argptr = argv;
91	if (argc > 0)
92		argptr++;
93	for (i = 0; i < NOPTS; i++)
94		optlist[i].val = 2;
95	privileged = (getuid() != geteuid() || getgid() != getegid());
96	options(1);
97	if (*argptr == NULL && minusc == NULL)
98		sflag = 1;
99	if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
100		iflag = 1;
101		if (Eflag == 2)
102			Eflag = 1;
103	}
104	if (mflag == 2)
105		mflag = iflag;
106	for (i = 0; i < NOPTS; i++)
107		if (optlist[i].val == 2)
108			optlist[i].val = 0;
109	arg0 = argv[0];
110	if (sflag == 0 && minusc == NULL) {
111		scriptname = *argptr++;
112		setinputfile(scriptname, 0);
113		commandname = arg0 = scriptname;
114	}
115	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
116	if (argptr && minusc && *argptr)
117		arg0 = *argptr++;
118
119	shellparam.p = argptr;
120	shellparam.reset = 1;
121	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
122	while (*argptr) {
123		shellparam.nparam++;
124		argptr++;
125	}
126	optschanged();
127}
128
129
130void
131optschanged(void)
132{
133	setinteractive(iflag);
134#ifndef NO_HISTORY
135	histedit();
136#endif
137	setjobctl(mflag);
138}
139
140/*
141 * Process shell options.  The global variable argptr contains a pointer
142 * to the argument list; we advance it past the options.
143 */
144
145static void
146options(int cmdline)
147{
148	char *kp, *p;
149	int val;
150	int c;
151
152	if (cmdline)
153		minusc = NULL;
154	while ((p = *argptr) != NULL) {
155		argptr++;
156		if ((c = *p++) == '-') {
157			val = 1;
158			/* A "-" or  "--" terminates options */
159			if (p[0] == '\0')
160				goto end_options1;
161			if (p[0] == '-' && p[1] == '\0')
162				goto end_options2;
163			/**
164			 * For the benefit of `#!' lines in shell scripts,
165			 * treat a string of '-- *#.*' the same as '--'.
166			 * This is needed so that a script starting with:
167			 *	#!/bin/sh -- # -*- perl -*-
168			 * will continue to work after a change is made to
169			 * kern/imgact_shell.c to NOT token-ize the options
170			 * specified on a '#!' line.  A bit of a kludge,
171			 * but that trick is recommended in documentation
172			 * for some scripting languages, and we might as
173			 * well continue to support it.
174			 */
175			if (p[0] == '-') {
176				kp = p + 1;
177				while (*kp == ' ' || *kp == '\t')
178					kp++;
179				if (*kp == '#' || *kp == '\0')
180					goto end_options2;
181			}
182		} else if (c == '+') {
183			val = 0;
184		} else {
185			argptr--;
186			break;
187		}
188		while ((c = *p++) != '\0') {
189			if (c == 'c' && cmdline) {
190				char *q;
191#ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
192				if (*p == '\0')
193#endif
194					q = *argptr++;
195				if (q == NULL || minusc != NULL)
196					error("Bad -c option");
197				minusc = q;
198#ifdef NOHACK
199				break;
200#endif
201			} else if (c == 'o') {
202				minus_o(*argptr, val);
203				if (*argptr)
204					argptr++;
205			} else
206				setoption(c, val);
207		}
208	}
209	return;
210
211	/* When processing `set', a single "-" means turn off -x and -v */
212end_options1:
213	if (!cmdline) {
214		xflag = vflag = 0;
215		return;
216	}
217
218	/*
219	 * When processing `set', a "--" means the remaining arguments
220	 * replace the positional parameters in the active shell.  If
221	 * there are no remaining options, then all the positional
222	 * parameters are cleared (equivalent to doing ``shift $#'').
223	 */
224end_options2:
225	if (!cmdline) {
226		if (*argptr == NULL)
227			setparam(argptr);
228		return;
229	}
230
231	/*
232	 * At this point we are processing options given to 'sh' on a command
233	 * line.  If an end-of-options marker ("-" or "--") is followed by an
234	 * arg of "#", then skip over all remaining arguments.  Some scripting
235	 * languages (e.g.: perl) document that /bin/sh will implement this
236	 * behavior, and they recommend that users take advantage of it to
237	 * solve certain issues that can come up when writing a perl script.
238	 * Yes, this feature is in /bin/sh to help users write perl scripts.
239	 */
240	p = *argptr;
241	if (p != NULL && p[0] == '#' && p[1] == '\0') {
242		while (*argptr != NULL)
243			argptr++;
244		/* We need to keep the final argument */
245		argptr--;
246	}
247}
248
249static void
250minus_o(char *name, int val)
251{
252	int i;
253
254	if (name == NULL) {
255		if (val) {
256			/* "Pretty" output. */
257			out1str("Current option settings\n");
258			for (i = 0; i < NOPTS; i++)
259				out1fmt("%-16s%s\n", optlist[i].name,
260					optlist[i].val ? "on" : "off");
261		} else {
262			/* Output suitable for re-input to shell. */
263			for (i = 0; i < NOPTS; i++)
264				out1fmt("%s %co %s%s",
265				    i % 6 == 0 ? "set" : "",
266				    optlist[i].val ? '-' : '+',
267				    optlist[i].name,
268				    i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
269		}
270	} else {
271		for (i = 0; i < NOPTS; i++)
272			if (equal(name, optlist[i].name)) {
273				setoptionbyindex(i, val);
274				return;
275			}
276		error("Illegal option -o %s", name);
277	}
278}
279
280
281static void
282setoptionbyindex(int idx, int val)
283{
284	if (optlist[idx].letter == 'p' && !val && privileged) {
285		if (setgid(getgid()) == -1)
286			error("setgid");
287		if (setuid(getuid()) == -1)
288			error("setuid");
289	}
290	optlist[idx].val = val;
291	if (val) {
292		/* #%$ hack for ksh semantics */
293		if (optlist[idx].letter == 'V')
294			Eflag = 0;
295		else if (optlist[idx].letter == 'E')
296			Vflag = 0;
297	}
298}
299
300static void
301setoption(int flag, int val)
302{
303	int i;
304
305	for (i = 0; i < NSHORTOPTS; i++)
306		if (optlist[i].letter == flag) {
307			setoptionbyindex(i, val);
308			return;
309		}
310	error("Illegal option -%c", flag);
311}
312
313
314/*
315 * Set the shell parameters.
316 */
317
318void
319setparam(char **argv)
320{
321	char **newparam;
322	char **ap;
323	int nparam;
324
325	for (nparam = 0 ; argv[nparam] ; nparam++);
326	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
327	while (*argv) {
328		*ap++ = savestr(*argv++);
329	}
330	*ap = NULL;
331	freeparam(&shellparam);
332	shellparam.malloc = 1;
333	shellparam.nparam = nparam;
334	shellparam.p = newparam;
335	shellparam.optp = NULL;
336	shellparam.reset = 1;
337	shellparam.optnext = NULL;
338}
339
340
341/*
342 * Free the list of positional parameters.
343 */
344
345void
346freeparam(struct shparam *param)
347{
348	char **ap;
349
350	if (param->malloc) {
351		for (ap = param->p ; *ap ; ap++)
352			ckfree(*ap);
353		ckfree(param->p);
354	}
355	if (param->optp) {
356		for (ap = param->optp ; *ap ; ap++)
357			ckfree(*ap);
358		ckfree(param->optp);
359	}
360}
361
362
363
364/*
365 * The shift builtin command.
366 */
367
368int
369shiftcmd(int argc, char **argv)
370{
371	int n;
372	char **ap1, **ap2;
373
374	n = 1;
375	if (argc > 1)
376		n = number(argv[1]);
377	if (n > shellparam.nparam)
378		return 1;
379	INTOFF;
380	shellparam.nparam -= n;
381	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
382		if (shellparam.malloc)
383			ckfree(*ap1);
384	}
385	ap2 = shellparam.p;
386	while ((*ap2++ = *ap1++) != NULL);
387	shellparam.reset = 1;
388	INTON;
389	return 0;
390}
391
392
393
394/*
395 * The set command builtin.
396 */
397
398int
399setcmd(int argc, char **argv)
400{
401	if (argc == 1)
402		return showvarscmd(argc, argv);
403	INTOFF;
404	options(0);
405	optschanged();
406	if (*argptr != NULL) {
407		setparam(argptr);
408	}
409	INTON;
410	return 0;
411}
412
413
414void
415getoptsreset(const char *value)
416{
417	while (*value == '0')
418		value++;
419	if (strcmp(value, "1") == 0)
420		shellparam.reset = 1;
421}
422
423/*
424 * The getopts builtin.  Shellparam.optnext points to the next argument
425 * to be processed.  Shellparam.optptr points to the next character to
426 * be processed in the current argument.  If shellparam.optnext is NULL,
427 * then it's the first time getopts has been called.
428 */
429
430int
431getoptscmd(int argc, char **argv)
432{
433	char **optbase = NULL, **ap;
434	int i;
435
436	if (argc < 3)
437		error("usage: getopts optstring var [arg]");
438
439	if (shellparam.reset == 1) {
440		INTOFF;
441		if (shellparam.optp) {
442			for (ap = shellparam.optp ; *ap ; ap++)
443				ckfree(*ap);
444			ckfree(shellparam.optp);
445			shellparam.optp = NULL;
446		}
447		if (argc > 3) {
448			shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
449			memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
450			for (i = 0; i < argc - 3; i++)
451				shellparam.optp[i] = savestr(argv[i + 3]);
452		}
453		INTON;
454		optbase = argc == 3 ? shellparam.p : shellparam.optp;
455		shellparam.optnext = optbase;
456		shellparam.optptr = NULL;
457		shellparam.reset = 0;
458	} else
459		optbase = shellparam.optp ? shellparam.optp : shellparam.p;
460
461	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
462		       &shellparam.optptr);
463}
464
465static int
466getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
467    char **optptr)
468{
469	char *p, *q;
470	char c = '?';
471	int done = 0;
472	int ind = 0;
473	int err = 0;
474	char s[10];
475	const char *newoptarg = NULL;
476
477	if ((p = *optptr) == NULL || *p == '\0') {
478		/* Current word is done, advance */
479		if (*optnext == NULL)
480			return 1;
481		p = **optnext;
482		if (p == NULL || *p != '-' || *++p == '\0') {
483atend:
484			ind = *optnext - optfirst + 1;
485			*optnext = NULL;
486			p = NULL;
487			done = 1;
488			goto out;
489		}
490		(*optnext)++;
491		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
492			goto atend;
493	}
494
495	c = *p++;
496	for (q = optstr; *q != c; ) {
497		if (*q == '\0') {
498			if (optstr[0] == ':') {
499				s[0] = c;
500				s[1] = '\0';
501				newoptarg = s;
502			}
503			else
504				out2fmt_flush("Illegal option -%c\n", c);
505			c = '?';
506			goto out;
507		}
508		if (*++q == ':')
509			q++;
510	}
511
512	if (*++q == ':') {
513		if (*p == '\0' && (p = **optnext) == NULL) {
514			if (optstr[0] == ':') {
515				s[0] = c;
516				s[1] = '\0';
517				newoptarg = s;
518				c = ':';
519			}
520			else {
521				out2fmt_flush("No arg for -%c option\n", c);
522				c = '?';
523			}
524			goto out;
525		}
526
527		if (p == **optnext)
528			(*optnext)++;
529		newoptarg = p;
530		p = NULL;
531	}
532
533out:
534	if (*optnext != NULL)
535		ind = *optnext - optfirst + 1;
536	*optptr = p;
537	if (newoptarg != NULL)
538		err |= setvarsafe("OPTARG", newoptarg, 0);
539	else {
540		INTOFF;
541		err |= unsetvar("OPTARG");
542		INTON;
543	}
544	fmtstr(s, sizeof(s), "%d", ind);
545	err |= setvarsafe("OPTIND", s, VNOFUNC);
546	s[0] = c;
547	s[1] = '\0';
548	err |= setvarsafe(optvar, s, 0);
549	if (err) {
550		*optnext = NULL;
551		*optptr = NULL;
552		flushall();
553		exraise(EXERROR);
554	}
555	return done;
556}
557
558/*
559 * Standard option processing (a la getopt) for builtin routines.  The
560 * only argument that is passed to nextopt is the option string; the
561 * other arguments are unnecessary.  It return the character, or '\0' on
562 * end of input.
563 */
564
565int
566nextopt(const char *optstring)
567{
568	char *p;
569	const char *q;
570	char c;
571
572	if ((p = nextopt_optptr) == NULL || *p == '\0') {
573		p = *argptr;
574		if (p == NULL || *p != '-' || *++p == '\0')
575			return '\0';
576		argptr++;
577		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
578			return '\0';
579	}
580	c = *p++;
581	for (q = optstring ; *q != c ; ) {
582		if (*q == '\0')
583			error("Illegal option -%c", c);
584		if (*++q == ':')
585			q++;
586	}
587	if (*++q == ':') {
588		if (*p == '\0' && (p = *argptr++) == NULL)
589			error("No arg for -%c option", c);
590		shoptarg = p;
591		p = NULL;
592	}
593	nextopt_optptr = p;
594	return c;
595}
596