options.c revision 50471
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 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
40#endif
41static const char rcsid[] =
42  "$FreeBSD: head/bin/sh/options.c 50471 1999-08-27 23:15:48Z peter $";
43#endif /* not lint */
44
45#include <signal.h>
46#include <unistd.h>
47#include <stdlib.h>
48
49#include "shell.h"
50#define DEFINE_OPTIONS
51#include "options.h"
52#undef DEFINE_OPTIONS
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 NO_HISTORY
64#include "myhistedit.h"
65#endif
66
67char *arg0;			/* value of $0 */
68struct shparam shellparam;	/* current positional parameters */
69char **argptr;			/* argument list for builtin commands */
70char *optarg;			/* set by nextopt (like getopt) */
71char *optptr;			/* used by nextopt */
72
73char *minusc;			/* argument to -c option */
74
75
76STATIC void options __P((int));
77STATIC void minus_o __P((char *, int));
78STATIC void setoption __P((int, int));
79STATIC int getopts __P((char *, char *, char **, char ***, char **));
80
81
82/*
83 * Process the shell command line arguments.
84 */
85
86void
87procargs(argc, argv)
88	int argc;
89	char **argv;
90{
91	int i;
92
93	argptr = argv;
94	if (argc > 0)
95		argptr++;
96	for (i = 0; i < NOPTS; i++)
97		optlist[i].val = 2;
98	privileged = (getuid() != geteuid() || getgid() != getegid());
99	options(1);
100	if (*argptr == NULL && minusc == NULL)
101		sflag = 1;
102	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
103		iflag = 1;
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		commandname = arg0 = *argptr++;
112		setinputfile(commandname, 0);
113	}
114	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
115	if (argptr && minusc && *argptr)
116		arg0 = *argptr++;
117
118	shellparam.p = argptr;
119	shellparam.reset = 1;
120	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
121	while (*argptr) {
122		shellparam.nparam++;
123		argptr++;
124	}
125	optschanged();
126}
127
128
129void
130optschanged()
131{
132	setinteractive(iflag);
133#ifndef NO_HISTORY
134	histedit();
135#endif
136	setjobctl(mflag);
137}
138
139/*
140 * Process shell options.  The global variable argptr contains a pointer
141 * to the argument list; we advance it past the options.
142 */
143
144STATIC void
145options(cmdline)
146	int cmdline;
147{
148	char *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                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
159                                if (!cmdline) {
160                                        /* "-" means turn off -x and -v */
161                                        if (p[0] == '\0')
162                                                xflag = vflag = 0;
163                                        /* "--" means reset params */
164                                        else if (*argptr == NULL)
165						setparam(argptr);
166                                }
167				break;	  /* "-" or  "--" terminates options */
168			}
169		} else if (c == '+') {
170			val = 0;
171		} else {
172			argptr--;
173			break;
174		}
175		while ((c = *p++) != '\0') {
176			if (c == 'c' && cmdline) {
177				char *q;
178#ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
179				if (*p == '\0')
180#endif
181					q = *argptr++;
182				if (q == NULL || minusc != NULL)
183					error("Bad -c option");
184				minusc = q;
185#ifdef NOHACK
186				break;
187#endif
188			} else if (c == 'o') {
189				minus_o(*argptr, val);
190				if (*argptr)
191					argptr++;
192			} else {
193				if (c == 'p' && !val && privileged) {
194					(void) setuid(getuid());
195					(void) setgid(getgid());
196				}
197				setoption(c, val);
198			}
199		}
200	}
201}
202
203STATIC void
204minus_o(name, val)
205	char *name;
206	int val;
207{
208	int i;
209
210	if (name == NULL) {
211		out1str("Current option settings\n");
212		for (i = 0; i < NOPTS; i++)
213			out1fmt("%-16s%s\n", optlist[i].name,
214				optlist[i].val ? "on" : "off");
215	} else {
216		for (i = 0; i < NOPTS; i++)
217			if (equal(name, optlist[i].name)) {
218				if (!val && privileged && equal(name, "privileged")) {
219					(void) setuid(getuid());
220					(void) setgid(getgid());
221				}
222				setoption(optlist[i].letter, val);
223				return;
224			}
225		error("Illegal option -o %s", name);
226	}
227}
228
229
230STATIC void
231setoption(flag, val)
232	char flag;
233	int val;
234	{
235	int i;
236
237	for (i = 0; i < NOPTS; i++)
238		if (optlist[i].letter == flag) {
239			optlist[i].val = val;
240			if (val) {
241				/* #%$ hack for ksh semantics */
242				if (flag == 'V')
243					Eflag = 0;
244				else if (flag == 'E')
245					Vflag = 0;
246			}
247			return;
248		}
249	error("Illegal option -%c", flag);
250}
251
252
253
254#ifdef mkinit
255INCLUDE "options.h"
256
257SHELLPROC {
258	int i;
259
260	for (i = 0; i < NOPTS; i++)
261		optlist[i].val = 0;
262	optschanged();
263
264}
265#endif
266
267
268/*
269 * Set the shell parameters.
270 */
271
272void
273setparam(argv)
274	char **argv;
275	{
276	char **newparam;
277	char **ap;
278	int nparam;
279
280	for (nparam = 0 ; argv[nparam] ; nparam++);
281	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
282	while (*argv) {
283		*ap++ = savestr(*argv++);
284	}
285	*ap = NULL;
286	freeparam(&shellparam);
287	shellparam.malloc = 1;
288	shellparam.nparam = nparam;
289	shellparam.p = newparam;
290	shellparam.optnext = NULL;
291}
292
293
294/*
295 * Free the list of positional parameters.
296 */
297
298void
299freeparam(param)
300	struct shparam *param;
301	{
302	char **ap;
303
304	if (param->malloc) {
305		for (ap = param->p ; *ap ; ap++)
306			ckfree(*ap);
307		ckfree(param->p);
308	}
309}
310
311
312
313/*
314 * The shift builtin command.
315 */
316
317int
318shiftcmd(argc, argv)
319	int argc;
320	char **argv;
321{
322	int n;
323	char **ap1, **ap2;
324
325	n = 1;
326	if (argc > 1)
327		n = number(argv[1]);
328	if (n > shellparam.nparam)
329		error("can't shift that many");
330	INTOFF;
331	shellparam.nparam -= n;
332	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
333		if (shellparam.malloc)
334			ckfree(*ap1);
335	}
336	ap2 = shellparam.p;
337	while ((*ap2++ = *ap1++) != NULL);
338	shellparam.optnext = NULL;
339	INTON;
340	return 0;
341}
342
343
344
345/*
346 * The set command builtin.
347 */
348
349int
350setcmd(argc, argv)
351	int argc;
352	char **argv;
353{
354	if (argc == 1)
355		return showvarscmd(argc, argv);
356	INTOFF;
357	options(0);
358	optschanged();
359	if (*argptr != NULL) {
360		setparam(argptr);
361	}
362	INTON;
363	return 0;
364}
365
366
367void
368getoptsreset(value)
369	const char *value;
370{
371	if (number(value) == 1) {
372		shellparam.optnext = NULL;
373		shellparam.reset = 1;
374	}
375}
376
377/*
378 * The getopts builtin.  Shellparam.optnext points to the next argument
379 * to be processed.  Shellparam.optptr points to the next character to
380 * be processed in the current argument.  If shellparam.optnext is NULL,
381 * then it's the first time getopts has been called.
382 */
383
384int
385getoptscmd(argc, argv)
386	int argc;
387	char **argv;
388{
389	char **optbase = NULL;
390
391	if (argc < 3)
392		error("Usage: getopts optstring var [arg]");
393	else if (argc == 3)
394		optbase = shellparam.p;
395	else
396		optbase = &argv[3];
397
398	if (shellparam.reset == 1) {
399		shellparam.optnext = optbase;
400		shellparam.optptr = NULL;
401		shellparam.reset = 0;
402	}
403
404	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
405		       &shellparam.optptr);
406}
407
408STATIC int
409getopts(optstr, optvar, optfirst, optnext, optptr)
410	char *optstr;
411	char *optvar;
412	char **optfirst;
413	char ***optnext;
414	char **optptr;
415{
416	char *p, *q;
417	char c = '?';
418	int done = 0;
419	int ind = 0;
420	int err = 0;
421	char s[10];
422
423	if ((p = *optptr) == NULL || *p == '\0') {
424		/* Current word is done, advance */
425		if (*optnext == NULL)
426			return 1;
427		p = **optnext;
428		if (p == NULL || *p != '-' || *++p == '\0') {
429atend:
430			ind = *optnext - optfirst + 1;
431			*optnext = NULL;
432			p = NULL;
433			done = 1;
434			goto out;
435		}
436		(*optnext)++;
437		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
438			goto atend;
439	}
440
441	c = *p++;
442	for (q = optstr; *q != c; ) {
443		if (*q == '\0') {
444			if (optstr[0] == ':') {
445				s[0] = c;
446				s[1] = '\0';
447				err |= setvarsafe("OPTARG", s, 0);
448			}
449			else {
450				out1fmt("Illegal option -%c\n", c);
451				(void) unsetvar("OPTARG");
452			}
453			c = '?';
454			goto bad;
455		}
456		if (*++q == ':')
457			q++;
458	}
459
460	if (*++q == ':') {
461		if (*p == '\0' && (p = **optnext) == NULL) {
462			if (optstr[0] == ':') {
463				s[0] = c;
464				s[1] = '\0';
465				err |= setvarsafe("OPTARG", s, 0);
466				c = ':';
467			}
468			else {
469				out1fmt("No arg for -%c option\n", c);
470				(void) unsetvar("OPTARG");
471				c = '?';
472			}
473			goto bad;
474		}
475
476		if (p == **optnext)
477			(*optnext)++;
478		setvarsafe("OPTARG", p, 0);
479		p = NULL;
480	}
481	else
482		setvarsafe("OPTARG", "", 0);
483	ind = *optnext - optfirst + 1;
484	goto out;
485
486bad:
487	ind = 1;
488	*optnext = NULL;
489	p = NULL;
490out:
491	*optptr = p;
492	fmtstr(s, sizeof(s), "%d", ind);
493	err |= setvarsafe("OPTIND", s, VNOFUNC);
494	s[0] = c;
495	s[1] = '\0';
496	err |= setvarsafe(optvar, s, 0);
497	if (err) {
498		*optnext = NULL;
499		*optptr = NULL;
500		flushall();
501		exraise(EXERROR);
502	}
503	return done;
504}
505
506/*
507 * XXX - should get rid of.  have all builtins use getopt(3).  the
508 * library getopt must have the BSD extension static variable "optreset"
509 * otherwise it can't be used within the shell safely.
510 *
511 * Standard option processing (a la getopt) for builtin routines.  The
512 * only argument that is passed to nextopt is the option string; the
513 * other arguments are unnecessary.  It return the character, or '\0' on
514 * end of input.
515 */
516
517int
518nextopt(optstring)
519	char *optstring;
520	{
521	char *p, *q;
522	char c;
523
524	if ((p = optptr) == NULL || *p == '\0') {
525		p = *argptr;
526		if (p == NULL || *p != '-' || *++p == '\0')
527			return '\0';
528		argptr++;
529		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
530			return '\0';
531	}
532	c = *p++;
533	for (q = optstring ; *q != c ; ) {
534		if (*q == '\0')
535			error("Illegal option -%c", c);
536		if (*++q == ':')
537			q++;
538	}
539	if (*++q == ':') {
540		if (*p == '\0' && (p = *argptr++) == NULL)
541			error("No arg for -%c option", c);
542		optarg = p;
543		p = NULL;
544	}
545	optptr = p;
546	return c;
547}
548