xref: /original-bsd/bin/sh/options.c (revision 96c6443c)
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  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)options.c	8.1 (Berkeley) 05/31/93";
13 #endif /* not lint */
14 
15 #include "shell.h"
16 #define DEFINE_OPTIONS
17 #include "options.h"
18 #undef DEFINE_OPTIONS
19 #include "nodes.h"	/* for other header files */
20 #include "eval.h"
21 #include "jobs.h"
22 #include "input.h"
23 #include "output.h"
24 #include "trap.h"
25 #include "var.h"
26 #include "memalloc.h"
27 #include "error.h"
28 #include "mystring.h"
29 
30 char *arg0;			/* value of $0 */
31 struct shparam shellparam;	/* current positional parameters */
32 char **argptr;			/* argument list for builtin commands */
33 char *optarg;			/* set by nextopt (like getopt) */
34 char *optptr;			/* used by nextopt */
35 
36 char *minusc;			/* argument to -c option */
37 
38 
39 #ifdef __STDC__
40 STATIC void options(int);
41 STATIC void setoption(int, int);
42 STATIC void minus_o(char *, int);
43 #else
44 STATIC void options();
45 STATIC void setoption();
46 STATIC void minus_o();
47 #endif
48 
49 
50 
51 /*
52  * Process the shell command line arguments.
53  */
54 
55 void
56 procargs(argc, argv)
57 	char **argv;
58 	{
59 	int i;
60 
61 	argptr = argv;
62 	if (argc > 0)
63 		argptr++;
64 	for (i = 0; i < NOPTS; i++)
65 		optlist[i].val = 2;
66 	options(1);
67 	if (*argptr == NULL && minusc == NULL)
68 		sflag = 1;
69 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
70 		iflag = 1;
71 	if (mflag == 2)
72 		mflag = iflag;
73 	for (i = 0; i < NOPTS; i++)
74 		if (optlist[i].val == 2)
75 			optlist[i].val = 0;
76 	arg0 = argv[0];
77 	if (sflag == 0 && minusc == NULL) {
78 		commandname = arg0 = *argptr++;
79 		setinputfile(commandname, 0);
80 	}
81 	shellparam.p = argptr;
82 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
83 	while (*argptr) {
84 		shellparam.nparam++;
85 		argptr++;
86 	}
87 	optschanged();
88 }
89 
90 
91 optschanged() {
92 	setinteractive(iflag);
93 	histedit();
94 	setjobctl(mflag);
95 }
96 
97 /*
98  * Process shell options.  The global variable argptr contains a pointer
99  * to the argument list; we advance it past the options.
100  */
101 
102 STATIC void
103 options(cmdline) {
104 	register char *p;
105 	int val;
106 	int c;
107 
108 	if (cmdline)
109 		minusc = NULL;
110 	while ((p = *argptr) != NULL) {
111 		argptr++;
112 		if ((c = *p++) == '-') {
113 			val = 1;
114                         if (p[0] == '\0' || p[0] == '-' && p[1] == '\0') {
115                                 if (!cmdline) {
116                                         /* "-" means turn off -x and -v */
117                                         if (p[0] == '\0')
118                                                 xflag = vflag = 0;
119                                         /* "--" means reset params */
120                                         else if (*argptr == NULL)
121                                                 setparam(argptr);
122                                 }
123 				break;	  /* "-" or  "--" terminates options */
124 			}
125 		} else if (c == '+') {
126 			val = 0;
127 		} else {
128 			argptr--;
129 			break;
130 		}
131 		while ((c = *p++) != '\0') {
132 			if (c == 'c' && cmdline) {
133 				char *q;
134 #ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
135 				if (*p == '\0')
136 #endif
137 					q = *argptr++;
138 				if (q == NULL || minusc != NULL)
139 					error("Bad -c option");
140 				minusc = q;
141 #ifdef NOHACK
142 				break;
143 #endif
144 			} else if (c == 'o') {
145 				minus_o(*argptr, val);
146 				if (*argptr)
147 					argptr++;
148 			} else {
149 				setoption(c, val);
150 			}
151 		}
152 	}
153 }
154 
155 STATIC void
156 minus_o(name, val)
157 	char *name;
158 	int val;
159 {
160 	int i;
161 
162 	if (name == NULL) {
163 		out1str("Current option settings\n");
164 		for (i = 0; i < NOPTS; i++)
165 			out1fmt("%-16s%s\n", optlist[i].name,
166 				optlist[i].val ? "on" : "off");
167 	} else {
168 		for (i = 0; i < NOPTS; i++)
169 			if (equal(name, optlist[i].name)) {
170 				setoption(optlist[i].letter, val);
171 				return;
172 			}
173 		error("Illegal option -o %s", name);
174 	}
175 }
176 
177 
178 STATIC void
179 setoption(flag, val)
180 	char flag;
181 	int val;
182 	{
183 	int i;
184 
185 	for (i = 0; i < NOPTS; i++)
186 		if (optlist[i].letter == flag) {
187 			optlist[i].val = val;
188 			if (val) {
189 				/* #%$ hack for ksh semantics */
190 				if (flag == 'V')
191 					Eflag = 0;
192 				else if (flag == 'E')
193 					Vflag = 0;
194 			}
195 			return;
196 		}
197 	error("Illegal option -%c", flag);
198 }
199 
200 
201 
202 #ifdef mkinit
203 INCLUDE "options.h"
204 
205 SHELLPROC {
206 	int i;
207 
208 	for (i = 0; i < NOPTS; i++)
209 		optlist[i].val = 0;
210 	optschanged();
211 
212 }
213 #endif
214 
215 
216 /*
217  * Set the shell parameters.
218  */
219 
220 void
221 setparam(argv)
222 	char **argv;
223 	{
224 	char **newparam;
225 	char **ap;
226 	int nparam;
227 
228 	for (nparam = 0 ; argv[nparam] ; nparam++);
229 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
230 	while (*argv) {
231 		*ap++ = savestr(*argv++);
232 	}
233 	*ap = NULL;
234 	freeparam(&shellparam);
235 	shellparam.malloc = 1;
236 	shellparam.nparam = nparam;
237 	shellparam.p = newparam;
238 	shellparam.optnext = NULL;
239 }
240 
241 
242 /*
243  * Free the list of positional parameters.
244  */
245 
246 void
247 freeparam(param)
248 	struct shparam *param;
249 	{
250 	char **ap;
251 
252 	if (param->malloc) {
253 		for (ap = param->p ; *ap ; ap++)
254 			ckfree(*ap);
255 		ckfree(param->p);
256 	}
257 }
258 
259 
260 
261 /*
262  * The shift builtin command.
263  */
264 
265 shiftcmd(argc, argv)  char **argv; {
266 	int n;
267 	char **ap1, **ap2;
268 
269 	n = 1;
270 	if (argc > 1)
271 		n = number(argv[1]);
272 	if (n > shellparam.nparam)
273 		error("can't shift that many");
274 	INTOFF;
275 	shellparam.nparam -= n;
276 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
277 		if (shellparam.malloc)
278 			ckfree(*ap1);
279 	}
280 	ap2 = shellparam.p;
281 	while ((*ap2++ = *ap1++) != NULL);
282 	shellparam.optnext = NULL;
283 	INTON;
284 	return 0;
285 }
286 
287 
288 
289 /*
290  * The set command builtin.
291  */
292 
293 setcmd(argc, argv)  char **argv; {
294 	if (argc == 1)
295 		return showvarscmd(argc, argv);
296 	INTOFF;
297 	options(0);
298 	optschanged();
299 	if (*argptr != NULL) {
300 		setparam(argptr);
301 	}
302 	INTON;
303 	return 0;
304 }
305 
306 
307 /*
308  * The getopts builtin.  Shellparam.optnext points to the next argument
309  * to be processed.  Shellparam.optptr points to the next character to
310  * be processed in the current argument.  If shellparam.optnext is NULL,
311  * then it's the first time getopts has been called.
312  */
313 
314 getoptscmd(argc, argv)  char **argv; {
315 	register char *p, *q;
316 	char c;
317 	char s[10];
318 
319 	if (argc != 3)
320 		error("Usage: getopts optstring var");
321 	if (shellparam.optnext == NULL) {
322 		shellparam.optnext = shellparam.p;
323 		shellparam.optptr = NULL;
324 	}
325 	if ((p = shellparam.optptr) == NULL || *p == '\0') {
326 		p = *shellparam.optnext;
327 		if (p == NULL || *p != '-' || *++p == '\0') {
328 atend:
329 			fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
330 			setvar("OPTIND", s, 0);
331 			shellparam.optnext = NULL;
332 			return 1;
333 		}
334 		shellparam.optnext++;
335 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
336 			goto atend;
337 	}
338 	c = *p++;
339 	for (q = argv[1] ; *q != c ; ) {
340 		if (*q == '\0') {
341 			out1fmt("Illegal option -%c\n", c);
342 			c = '?';
343 			goto out;
344 		}
345 		if (*++q == ':')
346 			q++;
347 	}
348 	if (*++q == ':') {
349 		if (*p == '\0' && (p = *shellparam.optnext) == NULL) {
350 			out1fmt("No arg for -%c option\n", c);
351 			c = '?';
352 			goto out;
353 		}
354 		shellparam.optnext++;
355 		setvar("OPTARG", p, 0);
356 		p = NULL;
357 	}
358 out:
359 	shellparam.optptr = p;
360 	s[0] = c;
361 	s[1] = '\0';
362 	setvar(argv[2], s, 0);
363 	return 0;
364 }
365 
366 /*
367  * XXX - should get rid of.  have all builtins use getopt(3).  the
368  * library getopt must have the BSD extension static variable "optreset"
369  * otherwise it can't be used within the shell safely.
370  *
371  * Standard option processing (a la getopt) for builtin routines.  The
372  * only argument that is passed to nextopt is the option string; the
373  * other arguments are unnecessary.  It return the character, or '\0' on
374  * end of input.
375  */
376 
377 int
378 nextopt(optstring)
379 	char *optstring;
380 	{
381 	register char *p, *q;
382 	char c;
383 
384 	if ((p = optptr) == NULL || *p == '\0') {
385 		p = *argptr;
386 		if (p == NULL || *p != '-' || *++p == '\0')
387 			return '\0';
388 		argptr++;
389 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
390 			return '\0';
391 	}
392 	c = *p++;
393 	for (q = optstring ; *q != c ; ) {
394 		if (*q == '\0')
395 			error("Illegal option -%c", c);
396 		if (*++q == ':')
397 			q++;
398 	}
399 	if (*++q == ':') {
400 		if (*p == '\0' && (p = *argptr++) == NULL)
401 			error("No arg for -%c option", c);
402 		optarg = p;
403 		p = NULL;
404 	}
405 	optptr = p;
406 	return c;
407 }
408