xref: /netbsd-src/bin/sh/options.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*	$NetBSD: options.c,v 1.13 1995/03/26 17:25:10 christos 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)options.c	8.1 (Berkeley) 5/31/93";
42 #else
43 static char rcsid[] = "$NetBSD: options.c,v 1.13 1995/03/26 17:25:10 christos Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include "shell.h"
48 #define DEFINE_OPTIONS
49 #include "options.h"
50 #undef DEFINE_OPTIONS
51 #include "nodes.h"	/* for other header files */
52 #include "eval.h"
53 #include "jobs.h"
54 #include "input.h"
55 #include "output.h"
56 #include "trap.h"
57 #include "var.h"
58 #include "memalloc.h"
59 #include "error.h"
60 #include "mystring.h"
61 #include "extern.h"
62 #include <unistd.h>
63 
64 char *arg0;			/* value of $0 */
65 struct shparam shellparam;	/* current positional parameters */
66 char **argptr;			/* argument list for builtin commands */
67 char *optarg;			/* set by nextopt (like getopt) */
68 char *optptr;			/* used by nextopt */
69 
70 char *minusc;			/* argument to -c option */
71 
72 
73 #ifdef __STDC__
74 STATIC void options(int);
75 STATIC void setoption(int, int);
76 STATIC void minus_o(char *, int);
77 #else
78 STATIC void options();
79 STATIC void setoption();
80 STATIC void minus_o();
81 #endif
82 
83 
84 
85 /*
86  * Process the shell command line arguments.
87  */
88 
89 void
90 procargs(argc, argv)
91 	int argc;
92 	char **argv;
93 {
94 	int i;
95 
96 	argptr = argv;
97 	if (argc > 0)
98 		argptr++;
99 	for (i = 0; i < NOPTS; i++)
100 		optlist[i].val = 2;
101 	options(1);
102 	if (*argptr == NULL && minusc == NULL)
103 		sflag = 1;
104 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
105 		iflag = 1;
106 	if (mflag == 2)
107 		mflag = iflag;
108 	for (i = 0; i < NOPTS; i++)
109 		if (optlist[i].val == 2)
110 			optlist[i].val = 0;
111 	arg0 = argv[0];
112 	if (sflag == 0 && minusc == NULL) {
113 		commandname = arg0 = *argptr++;
114 		setinputfile(commandname, 0);
115 	}
116 	shellparam.p = argptr;
117 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
118 	while (*argptr) {
119 		shellparam.nparam++;
120 		argptr++;
121 	}
122 	optschanged();
123 }
124 
125 
126 void
127 optschanged()
128 {
129 	setinteractive(iflag);
130 #ifndef NO_HISTORY
131 	histedit();
132 #endif
133 	setjobctl(mflag);
134 }
135 
136 /*
137  * Process shell options.  The global variable argptr contains a pointer
138  * to the argument list; we advance it past the options.
139  */
140 
141 STATIC void
142 options(cmdline)
143 	int cmdline;
144 {
145 	register 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 				setoption(c, val);
191 			}
192 		}
193 	}
194 }
195 
196 STATIC void
197 minus_o(name, val)
198 	char *name;
199 	int val;
200 {
201 	int i;
202 
203 	if (name == NULL) {
204 		out1str("Current option settings\n");
205 		for (i = 0; i < NOPTS; i++)
206 			out1fmt("%-16s%s\n", optlist[i].name,
207 				optlist[i].val ? "on" : "off");
208 	} else {
209 		for (i = 0; i < NOPTS; i++)
210 			if (equal(name, optlist[i].name)) {
211 				setoption(optlist[i].letter, val);
212 				return;
213 			}
214 		error("Illegal option -o %s", name);
215 	}
216 }
217 
218 
219 STATIC void
220 setoption(flag, val)
221 	char flag;
222 	int val;
223 	{
224 	int i;
225 
226 	for (i = 0; i < NOPTS; i++)
227 		if (optlist[i].letter == flag) {
228 			optlist[i].val = val;
229 			if (val) {
230 				/* #%$ hack for ksh semantics */
231 				if (flag == 'V')
232 					Eflag = 0;
233 				else if (flag == 'E')
234 					Vflag = 0;
235 			}
236 			return;
237 		}
238 	error("Illegal option -%c", flag);
239 }
240 
241 
242 
243 #ifdef mkinit
244 INCLUDE "options.h"
245 INCLUDE "extern.h"
246 
247 SHELLPROC {
248 	int i;
249 
250 	for (i = 0; i < NOPTS; i++)
251 		optlist[i].val = 0;
252 	optschanged();
253 
254 }
255 #endif
256 
257 
258 /*
259  * Set the shell parameters.
260  */
261 
262 void
263 setparam(argv)
264 	char **argv;
265 	{
266 	char **newparam;
267 	char **ap;
268 	int nparam;
269 
270 	for (nparam = 0 ; argv[nparam] ; nparam++);
271 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
272 	while (*argv) {
273 		*ap++ = savestr(*argv++);
274 	}
275 	*ap = NULL;
276 	freeparam(&shellparam);
277 	shellparam.malloc = 1;
278 	shellparam.nparam = nparam;
279 	shellparam.p = newparam;
280 	shellparam.optnext = NULL;
281 }
282 
283 
284 /*
285  * Free the list of positional parameters.
286  */
287 
288 void
289 freeparam(param)
290 	struct shparam *param;
291 	{
292 	char **ap;
293 
294 	if (param->malloc) {
295 		for (ap = param->p ; *ap ; ap++)
296 			ckfree(*ap);
297 		ckfree(param->p);
298 	}
299 }
300 
301 
302 
303 /*
304  * The shift builtin command.
305  */
306 
307 int
308 shiftcmd(argc, argv)
309 	int argc;
310 	char **argv;
311 {
312 	int n;
313 	char **ap1, **ap2;
314 
315 	n = 1;
316 	if (argc > 1)
317 		n = number(argv[1]);
318 	if (n > shellparam.nparam)
319 		error("can't shift that many");
320 	INTOFF;
321 	shellparam.nparam -= n;
322 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
323 		if (shellparam.malloc)
324 			ckfree(*ap1);
325 	}
326 	ap2 = shellparam.p;
327 	while ((*ap2++ = *ap1++) != NULL);
328 	shellparam.optnext = NULL;
329 	INTON;
330 	return 0;
331 }
332 
333 
334 
335 /*
336  * The set command builtin.
337  */
338 
339 int
340 setcmd(argc, argv)
341 	int argc;
342 	char **argv;
343 {
344 	if (argc == 1)
345 		return showvarscmd(argc, argv);
346 	INTOFF;
347 	options(0);
348 	optschanged();
349 	if (*argptr != NULL) {
350 		setparam(argptr);
351 	}
352 	INTON;
353 	return 0;
354 }
355 
356 
357 /*
358  * The getopts builtin.  Shellparam.optnext points to the next argument
359  * to be processed.  Shellparam.optptr points to the next character to
360  * be processed in the current argument.  If shellparam.optnext is NULL,
361  * then it's the first time getopts has been called.
362  */
363 
364 int
365 getoptscmd(argc, argv)
366 	int argc;
367 	char **argv;
368 {
369 	register char *p, *q;
370 	char c;
371 	char s[10];
372 
373 	if (argc != 3)
374 		error("Usage: getopts optstring var");
375 	if (shellparam.optnext == NULL) {
376 		shellparam.optnext = shellparam.p;
377 		shellparam.optptr = NULL;
378 	}
379 	if ((p = shellparam.optptr) == NULL || *p == '\0') {
380 		p = *shellparam.optnext;
381 		if (p == NULL || *p != '-' || *++p == '\0') {
382 atend:
383 			fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
384 			setvar("OPTIND", s, 0);
385 			shellparam.optnext = NULL;
386 			return 1;
387 		}
388 		shellparam.optnext++;
389 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
390 			goto atend;
391 	}
392 	c = *p++;
393 	for (q = argv[1] ; *q != c ; ) {
394 		if (*q == '\0') {
395 			out1fmt("Illegal option -%c\n", c);
396 			c = '?';
397 			goto out;
398 		}
399 		if (*++q == ':')
400 			q++;
401 	}
402 	if (*++q == ':') {
403 		if (*p == '\0' && (p = *shellparam.optnext) == NULL) {
404 			out1fmt("No arg for -%c option\n", c);
405 			c = '?';
406 			goto out;
407 		}
408 		shellparam.optnext++;
409 		setvar("OPTARG", p, 0);
410 		p = NULL;
411 	}
412 out:
413 	shellparam.optptr = p;
414 	s[0] = c;
415 	s[1] = '\0';
416 	setvar(argv[2], s, 0);
417 	return 0;
418 }
419 
420 /*
421  * XXX - should get rid of.  have all builtins use getopt(3).  the
422  * library getopt must have the BSD extension static variable "optreset"
423  * otherwise it can't be used within the shell safely.
424  *
425  * Standard option processing (a la getopt) for builtin routines.  The
426  * only argument that is passed to nextopt is the option string; the
427  * other arguments are unnecessary.  It return the character, or '\0' on
428  * end of input.
429  */
430 
431 int
432 nextopt(optstring)
433 	char *optstring;
434 	{
435 	register char *p, *q;
436 	char c;
437 
438 	if ((p = optptr) == NULL || *p == '\0') {
439 		p = *argptr;
440 		if (p == NULL || *p != '-' || *++p == '\0')
441 			return '\0';
442 		argptr++;
443 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
444 			return '\0';
445 	}
446 	c = *p++;
447 	for (q = optstring ; *q != c ; ) {
448 		if (*q == '\0')
449 			error("Illegal option -%c", c);
450 		if (*++q == ':')
451 			q++;
452 	}
453 	if (*++q == ':') {
454 		if (*p == '\0' && (p = *argptr++) == NULL)
455 			error("No arg for -%c option", c);
456 		optarg = p;
457 		p = NULL;
458 	}
459 	optptr = p;
460 	return c;
461 }
462