xref: /freebsd/bin/sh/options.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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
39 static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
40 #endif
41 static const char rcsid[] =
42   "$FreeBSD$";
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 
67 char *arg0;			/* value of $0 */
68 struct shparam shellparam;	/* current positional parameters */
69 char **argptr;			/* argument list for builtin commands */
70 char *shoptarg;			/* set by nextopt (like getopt) */
71 char *optptr;			/* used by nextopt */
72 
73 char *minusc;			/* argument to -c option */
74 
75 
76 STATIC void options __P((int));
77 STATIC void minus_o __P((char *, int));
78 STATIC void setoption __P((int, int));
79 STATIC int getopts __P((char *, char *, char **, char ***, char **));
80 
81 
82 /*
83  * Process the shell command line arguments.
84  */
85 
86 void
87 procargs(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 
129 void
130 optschanged()
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 
144 STATIC void
145 options(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 
203 STATIC void
204 minus_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 
230 STATIC void
231 setoption(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
255 INCLUDE "options.h"
256 
257 SHELLPROC {
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 
272 void
273 setparam(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 
298 void
299 freeparam(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 
317 int
318 shiftcmd(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 
349 int
350 setcmd(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 
367 void
368 getoptsreset(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 
384 int
385 getoptscmd(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 
408 STATIC int
409 getopts(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') {
429 atend:
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 
486 bad:
487 	ind = 1;
488 	*optnext = NULL;
489 	p = NULL;
490 out:
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 
517 int
518 nextopt(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 		shoptarg = p;
543 		p = NULL;
544 	}
545 	optptr = p;
546 	return c;
547 }
548