options.c revision 90111
1119482Sobrien/*-
2199855Ssobomax * Copyright (c) 1991, 1993
366133Sarchie *	The Regents of the University of California.  All rights reserved.
4199855Ssobomax *
5199855Ssobomax * This code is derived from software contributed to Berkeley by
666133Sarchie * Kenneth Almquist.
766133Sarchie *
866133Sarchie * Redistribution and use in source and binary forms, with or without
966133Sarchie * modification, are permitted provided that the following conditions
1066133Sarchie * are met:
1166133Sarchie * 1. Redistributions of source code must retain the above copyright
1266133Sarchie *    notice, this list of conditions and the following disclaimer.
1366133Sarchie * 2. Redistributions in binary form must reproduce the above copyright
1466133Sarchie *    notice, this list of conditions and the following disclaimer in the
1566133Sarchie *    documentation and/or other materials provided with the distribution.
1666133Sarchie * 3. All advertising materials mentioning features or use of this software
1766133Sarchie *    must display the following acknowledgement:
1866133Sarchie *	This product includes software developed by the University of
1966133Sarchie *	California, Berkeley and its contributors.
2066133Sarchie * 4. Neither the name of the University nor the names of its contributors
2166133Sarchie *    may be used to endorse or promote products derived from this software
2266133Sarchie *    without specific prior written permission.
2366133Sarchie *
2466133Sarchie * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2566133Sarchie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2666133Sarchie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2766133Sarchie * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2866133Sarchie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2966133Sarchie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3066133Sarchie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3166133Sarchie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3266133Sarchie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3366133Sarchie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3466133Sarchie * SUCH DAMAGE.
3566133Sarchie */
3666133Sarchie
3766133Sarchie#ifndef lint
38119482Sobrien#if 0
39119482Sobrienstatic char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
40119482Sobrien#endif
4166133Sarchiestatic const char rcsid[] =
4266133Sarchie  "$FreeBSD: head/bin/sh/options.c 90111 2002-02-02 06:50:57Z imp $";
4366133Sarchie#endif /* not lint */
44199855Ssobomax
45199855Ssobomax#include <signal.h>
46199855Ssobomax#include <unistd.h>
4766133Sarchie#include <stdlib.h>
48199855Ssobomax
49199855Ssobomax#include "shell.h"
50199855Ssobomax#define DEFINE_OPTIONS
51199855Ssobomax#include "options.h"
52199855Ssobomax#undef DEFINE_OPTIONS
53199855Ssobomax#include "nodes.h"	/* for other header files */
54199855Ssobomax#include "eval.h"
55199855Ssobomax#include "jobs.h"
56199855Ssobomax#include "input.h"
5766133Sarchie#include "output.h"
58199855Ssobomax#include "trap.h"
59199855Ssobomax#include "var.h"
60199855Ssobomax#include "memalloc.h"
61199855Ssobomax#include "error.h"
62199855Ssobomax#include "mystring.h"
6366133Sarchie#ifndef NO_HISTORY
6466133Sarchie#include "myhistedit.h"
6566133Sarchie#endif
66199855Ssobomax
6766133Sarchiechar *arg0;			/* value of $0 */
6866133Sarchiestruct shparam shellparam;	/* current positional parameters */
6966133Sarchiechar **argptr;			/* argument list for builtin commands */
7066133Sarchiechar *shoptarg;			/* set by nextopt (like getopt) */
7166133Sarchiechar *optptr;			/* used by nextopt */
72199855Ssobomax
7366133Sarchiechar *minusc;			/* argument to -c option */
7466133Sarchie
7566133Sarchie
7666133SarchieSTATIC void options(int);
7766133SarchieSTATIC void minus_o(char *, int);
78199855SsobomaxSTATIC void setoption(int, int);
7966133SarchieSTATIC int getopts(char *, char *, char **, char ***, char **);
80199855Ssobomax
81199855Ssobomax
82199857Ssobomax/*
83199855Ssobomax * Process the shell command line arguments.
84199855Ssobomax */
85199856Ssobomax
86199856Ssobomaxvoid
87199856Ssobomaxprocargs(int argc, char **argv)
88199856Ssobomax{
89199855Ssobomax	int i;
90199855Ssobomax
91199855Ssobomax	argptr = argv;
92199855Ssobomax	if (argc > 0)
93199855Ssobomax		argptr++;
9466133Sarchie	for (i = 0; i < NOPTS; i++)
9566133Sarchie		optlist[i].val = 2;
9666133Sarchie	privileged = (getuid() != geteuid() || getgid() != getegid());
97199855Ssobomax	options(1);
9866133Sarchie	if (*argptr == NULL && minusc == NULL)
9966133Sarchie		sflag = 1;
10066133Sarchie	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
10166133Sarchie		iflag = 1;
10266133Sarchie	if (mflag == 2)
103199855Ssobomax		mflag = iflag;
10466133Sarchie	for (i = 0; i < NOPTS; i++)
10566133Sarchie		if (optlist[i].val == 2)
10666133Sarchie			optlist[i].val = 0;
107	arg0 = argv[0];
108	if (sflag == 0 && minusc == NULL) {
109		commandname = arg0 = *argptr++;
110		setinputfile(commandname, 0);
111	}
112	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
113	if (argptr && minusc && *argptr)
114		arg0 = *argptr++;
115
116	shellparam.p = argptr;
117	shellparam.reset = 1;
118	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
119	while (*argptr) {
120		shellparam.nparam++;
121		argptr++;
122	}
123	optschanged();
124}
125
126
127void
128optschanged(void)
129{
130	setinteractive(iflag);
131#ifndef NO_HISTORY
132	histedit();
133#endif
134	setjobctl(mflag);
135}
136
137/*
138 * Process shell options.  The global variable argptr contains a pointer
139 * to the argument list; we advance it past the options.
140 */
141
142STATIC void
143options(int cmdline)
144{
145	char *p;
146	int val;
147	int c;
148
149	if (cmdline)
150		minusc = NULL;
151	while ((p = *argptr) != NULL) {
152		argptr++;
153		if ((c = *p++) == '-') {
154			val = 1;
155                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
156                                if (!cmdline) {
157                                        /* "-" means turn off -x and -v */
158                                        if (p[0] == '\0')
159                                                xflag = vflag = 0;
160                                        /* "--" means reset params */
161                                        else if (*argptr == NULL)
162						setparam(argptr);
163                                }
164				break;	  /* "-" or  "--" terminates options */
165			}
166		} else if (c == '+') {
167			val = 0;
168		} else {
169			argptr--;
170			break;
171		}
172		while ((c = *p++) != '\0') {
173			if (c == 'c' && cmdline) {
174				char *q;
175#ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
176				if (*p == '\0')
177#endif
178					q = *argptr++;
179				if (q == NULL || minusc != NULL)
180					error("Bad -c option");
181				minusc = q;
182#ifdef NOHACK
183				break;
184#endif
185			} else if (c == 'o') {
186				minus_o(*argptr, val);
187				if (*argptr)
188					argptr++;
189			} else {
190				if (c == 'p' && !val && privileged) {
191					(void) setuid(getuid());
192					(void) setgid(getgid());
193				}
194				setoption(c, val);
195			}
196		}
197	}
198}
199
200STATIC void
201minus_o(char *name, int val)
202{
203	int i;
204
205	if (name == NULL) {
206		out1str("Current option settings\n");
207		for (i = 0; i < NOPTS; i++)
208			out1fmt("%-16s%s\n", optlist[i].name,
209				optlist[i].val ? "on" : "off");
210	} else {
211		for (i = 0; i < NOPTS; i++)
212			if (equal(name, optlist[i].name)) {
213				if (!val && privileged && equal(name, "privileged")) {
214					(void) setuid(getuid());
215					(void) setgid(getgid());
216				}
217				setoption(optlist[i].letter, val);
218				return;
219			}
220		error("Illegal option -o %s", name);
221	}
222}
223
224
225STATIC void
226setoption(int flag, int val)
227{
228	int i;
229
230	for (i = 0; i < NOPTS; i++)
231		if (optlist[i].letter == flag) {
232			optlist[i].val = val;
233			if (val) {
234				/* #%$ hack for ksh semantics */
235				if (flag == 'V')
236					Eflag = 0;
237				else if (flag == 'E')
238					Vflag = 0;
239			}
240			return;
241		}
242	error("Illegal option -%c", flag);
243}
244
245
246
247#ifdef mkinit
248INCLUDE "options.h"
249
250SHELLPROC {
251	int i;
252
253	for (i = 0; i < NOPTS; i++)
254		optlist[i].val = 0;
255	optschanged();
256
257}
258#endif
259
260
261/*
262 * Set the shell parameters.
263 */
264
265void
266setparam(char **argv)
267{
268	char **newparam;
269	char **ap;
270	int nparam;
271
272	for (nparam = 0 ; argv[nparam] ; nparam++);
273	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
274	while (*argv) {
275		*ap++ = savestr(*argv++);
276	}
277	*ap = NULL;
278	freeparam(&shellparam);
279	shellparam.malloc = 1;
280	shellparam.nparam = nparam;
281	shellparam.p = newparam;
282	shellparam.optnext = NULL;
283}
284
285
286/*
287 * Free the list of positional parameters.
288 */
289
290void
291freeparam(struct shparam *param)
292{
293	char **ap;
294
295	if (param->malloc) {
296		for (ap = param->p ; *ap ; ap++)
297			ckfree(*ap);
298		ckfree(param->p);
299	}
300}
301
302
303
304/*
305 * The shift builtin command.
306 */
307
308int
309shiftcmd(int argc, char **argv)
310{
311	int n;
312	char **ap1, **ap2;
313
314	n = 1;
315	if (argc > 1)
316		n = number(argv[1]);
317	if (n > shellparam.nparam)
318		error("can't shift that many");
319	INTOFF;
320	shellparam.nparam -= n;
321	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
322		if (shellparam.malloc)
323			ckfree(*ap1);
324	}
325	ap2 = shellparam.p;
326	while ((*ap2++ = *ap1++) != NULL);
327	shellparam.optnext = NULL;
328	INTON;
329	return 0;
330}
331
332
333
334/*
335 * The set command builtin.
336 */
337
338int
339setcmd(int argc, char **argv)
340{
341	if (argc == 1)
342		return showvarscmd(argc, argv);
343	INTOFF;
344	options(0);
345	optschanged();
346	if (*argptr != NULL) {
347		setparam(argptr);
348	}
349	INTON;
350	return 0;
351}
352
353
354void
355getoptsreset(const char *value)
356{
357	if (number(value) == 1) {
358		shellparam.optnext = NULL;
359		shellparam.reset = 1;
360	}
361}
362
363/*
364 * The getopts builtin.  Shellparam.optnext points to the next argument
365 * to be processed.  Shellparam.optptr points to the next character to
366 * be processed in the current argument.  If shellparam.optnext is NULL,
367 * then it's the first time getopts has been called.
368 */
369
370int
371getoptscmd(int argc, char **argv)
372{
373	char **optbase = NULL;
374
375	if (argc < 3)
376		error("Usage: getopts optstring var [arg]");
377	else if (argc == 3)
378		optbase = shellparam.p;
379	else
380		optbase = &argv[3];
381
382	if (shellparam.reset == 1) {
383		shellparam.optnext = optbase;
384		shellparam.optptr = NULL;
385		shellparam.reset = 0;
386	}
387
388	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
389		       &shellparam.optptr);
390}
391
392STATIC int
393getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
394    char **optptr)
395{
396	char *p, *q;
397	char c = '?';
398	int done = 0;
399	int ind = 0;
400	int err = 0;
401	char s[10];
402
403	if ((p = *optptr) == NULL || *p == '\0') {
404		/* Current word is done, advance */
405		if (*optnext == NULL)
406			return 1;
407		p = **optnext;
408		if (p == NULL || *p != '-' || *++p == '\0') {
409atend:
410			ind = *optnext - optfirst + 1;
411			*optnext = NULL;
412			p = NULL;
413			done = 1;
414			goto out;
415		}
416		(*optnext)++;
417		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
418			goto atend;
419	}
420
421	c = *p++;
422	for (q = optstr; *q != c; ) {
423		if (*q == '\0') {
424			if (optstr[0] == ':') {
425				s[0] = c;
426				s[1] = '\0';
427				err |= setvarsafe("OPTARG", s, 0);
428			}
429			else {
430				out1fmt("Illegal option -%c\n", c);
431				(void) unsetvar("OPTARG");
432			}
433			c = '?';
434			goto bad;
435		}
436		if (*++q == ':')
437			q++;
438	}
439
440	if (*++q == ':') {
441		if (*p == '\0' && (p = **optnext) == NULL) {
442			if (optstr[0] == ':') {
443				s[0] = c;
444				s[1] = '\0';
445				err |= setvarsafe("OPTARG", s, 0);
446				c = ':';
447			}
448			else {
449				out1fmt("No arg for -%c option\n", c);
450				(void) unsetvar("OPTARG");
451				c = '?';
452			}
453			goto bad;
454		}
455
456		if (p == **optnext)
457			(*optnext)++;
458		setvarsafe("OPTARG", p, 0);
459		p = NULL;
460	}
461	else
462		setvarsafe("OPTARG", "", 0);
463	ind = *optnext - optfirst + 1;
464	goto out;
465
466bad:
467	ind = 1;
468	*optnext = NULL;
469	p = NULL;
470out:
471	*optptr = p;
472	fmtstr(s, sizeof(s), "%d", ind);
473	err |= setvarsafe("OPTIND", s, VNOFUNC);
474	s[0] = c;
475	s[1] = '\0';
476	err |= setvarsafe(optvar, s, 0);
477	if (err) {
478		*optnext = NULL;
479		*optptr = NULL;
480		flushall();
481		exraise(EXERROR);
482	}
483	return done;
484}
485
486/*
487 * XXX - should get rid of.  have all builtins use getopt(3).  the
488 * library getopt must have the BSD extension static variable "optreset"
489 * otherwise it can't be used within the shell safely.
490 *
491 * Standard option processing (a la getopt) for builtin routines.  The
492 * only argument that is passed to nextopt is the option string; the
493 * other arguments are unnecessary.  It return the character, or '\0' on
494 * end of input.
495 */
496
497int
498nextopt(char *optstring)
499{
500	char *p, *q;
501	char c;
502
503	if ((p = optptr) == NULL || *p == '\0') {
504		p = *argptr;
505		if (p == NULL || *p != '-' || *++p == '\0')
506			return '\0';
507		argptr++;
508		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
509			return '\0';
510	}
511	c = *p++;
512	for (q = optstring ; *q != c ; ) {
513		if (*q == '\0')
514			error("Illegal option -%c", c);
515		if (*++q == ':')
516			q++;
517	}
518	if (*++q == ':') {
519		if (*p == '\0' && (p = *argptr++) == NULL)
520			error("No arg for -%c option", c);
521		shoptarg = p;
522		p = NULL;
523	}
524	optptr = p;
525	return c;
526}
527