xref: /netbsd-src/bin/sh/options.c (revision da7ff22a4ae89b32ca9e90ca20e7d0d434e8cdd2)
1*da7ff22aSkre /*	$NetBSD: options.c,v 1.62 2024/10/14 09:10:35 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*da7ff22aSkre __RCSID("$NetBSD: options.c,v 1.62 2024/10/14 09:10:35 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4407bae7edSchristos #include <signal.h>
4507bae7edSchristos #include <unistd.h>
4607bae7edSchristos #include <stdlib.h>
4707bae7edSchristos 
4861f28255Scgd #include "shell.h"
4961f28255Scgd #define DEFINE_OPTIONS
5061f28255Scgd #include "options.h"
5161f28255Scgd #undef DEFINE_OPTIONS
524fc4fe2eSchristos #include "builtins.h"
5361f28255Scgd #include "nodes.h"	/* for other header files */
5461f28255Scgd #include "eval.h"
5561f28255Scgd #include "jobs.h"
5661f28255Scgd #include "input.h"
5761f28255Scgd #include "output.h"
5861f28255Scgd #include "trap.h"
5961f28255Scgd #include "var.h"
6061f28255Scgd #include "memalloc.h"
6161f28255Scgd #include "error.h"
6261f28255Scgd #include "mystring.h"
63c6c29888Skre #include "syntax.h"
647accaec4Schristos #ifndef SMALL
6507bae7edSchristos #include "myhistedit.h"
6607bae7edSchristos #endif
67c02b3bbdSchristos #include "show.h"
6861f28255Scgd 
6961f28255Scgd char *arg0;			/* value of $0 */
7061f28255Scgd struct shparam shellparam;	/* current positional parameters */
7161f28255Scgd char **argptr;			/* argument list for builtin commands */
7233809804Schristos char *optionarg;		/* set by nextopt (like getopt) */
7361f28255Scgd char *optptr;			/* used by nextopt */
7461f28255Scgd 
7561f28255Scgd char *minusc;			/* argument to -c option */
7661f28255Scgd 
7761f28255Scgd 
78c02b3bbdSchristos STATIC void options(int);
79c02b3bbdSchristos STATIC void minus_o(char *, int);
80c02b3bbdSchristos STATIC void setoption(int, int);
81c02b3bbdSchristos STATIC int getopts(char *, char *, char **, char ***, char **);
8261f28255Scgd 
8361f28255Scgd 
8461f28255Scgd /*
8561f28255Scgd  * Process the shell command line arguments.
8661f28255Scgd  */
8761f28255Scgd 
8861f28255Scgd void
89c02b3bbdSchristos procargs(int argc, char **argv)
9061f28255Scgd {
91c6144e48Slukem 	size_t i;
92d98a8b3cSkre 	int psx;
9361f28255Scgd 
9461f28255Scgd 	argptr = argv;
9561f28255Scgd 	if (argc > 0)
9661f28255Scgd 		argptr++;
97d98a8b3cSkre 
98d98a8b3cSkre 	psx = posix;		/* save what we set it to earlier */
99d98a8b3cSkre 	/*
100d98a8b3cSkre 	 * option values are mostly boolean 0:off 1:on
101d98a8b3cSkre 	 * we use 2 (just in this routine) to mean "unknown yet"
102d98a8b3cSkre 	 */
10337ed7877Sjtc 	for (i = 0; i < NOPTS; i++)
10437ed7877Sjtc 		optlist[i].val = 2;
105d98a8b3cSkre 	posix = psx;		/* restore before processing -o ... */
106d98a8b3cSkre 
10761f28255Scgd 	options(1);
108d98a8b3cSkre 
10961f28255Scgd 	if (*argptr == NULL && minusc == NULL)
11061f28255Scgd 		sflag = 1;
111a373e69aSkre 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(2))
11261f28255Scgd 		iflag = 1;
113b0d51313Schristos 	if (iflag == 1 && sflag == 2)
114b0d51313Schristos 		iflag = 2;
11537ed7877Sjtc 	if (mflag == 2)
11637ed7877Sjtc 		mflag = iflag;
117750aa922Schristos #ifndef DO_SHAREDVFORK
118750aa922Schristos 	if (usefork == 2)
119750aa922Schristos 		usefork = 1;
120750aa922Schristos #endif
1218ffd1099Skre #if DEBUG >= 2
122d98a8b3cSkre 	if (debug == 2)
123c02b3bbdSchristos 		debug = 1;
124c02b3bbdSchristos #endif
125e7b0505eSkre 	arg0 = argv[0];
126e7b0505eSkre 	if (loginsh == 2 && arg0 != NULL && arg0[0] == '-')
127e7b0505eSkre 		loginsh = 1;
128e7b0505eSkre 
129d98a8b3cSkre 	/*
130d98a8b3cSkre 	 * Any options not dealt with as special cases just above,
131d98a8b3cSkre 	 * and which were not set on the command line, are set to
132d98a8b3cSkre 	 * their expected default values (mostly "off")
133d98a8b3cSkre 	 *
134d98a8b3cSkre 	 * then as each option is initialised, save its setting now
135d98a8b3cSkre 	 * as its "default" value for future use ("set -o default").
136d98a8b3cSkre 	 */
137d98a8b3cSkre 	for (i = 0; i < NOPTS; i++) {
138d98a8b3cSkre 		if (optlist[i].val == 2)
139d98a8b3cSkre 			optlist[i].val = optlist[i].dflt;
140d98a8b3cSkre 		optlist[i].dflt = optlist[i].val;
141d98a8b3cSkre 	}
142d98a8b3cSkre 
14361f28255Scgd 	if (sflag == 0 && minusc == NULL) {
1448d35854bSwiz 		commandname = argv[0];
1458d35854bSwiz 		arg0 = *argptr++;
1468d35854bSwiz 		setinputfile(arg0, 0);
1478d35854bSwiz 		commandname = arg0;
14861f28255Scgd 	}
149fd8c9943Schristos 	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
150e314f958Sdsl 	if (minusc != NULL) {
151e314f958Sdsl 		if (argptr == NULL || *argptr == NULL)
152e314f958Sdsl 			error("Bad -c option");
153e314f958Sdsl 		minusc = *argptr++;
154e314f958Sdsl 		if (*argptr != 0)
155fd8c9943Schristos 			arg0 = *argptr++;
156e314f958Sdsl 	}
157fd8c9943Schristos 
15861f28255Scgd 	shellparam.p = argptr;
159ccce082dSchristos 	shellparam.reset = 1;
16061f28255Scgd 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
16161f28255Scgd 	while (*argptr) {
16261f28255Scgd 		shellparam.nparam++;
16361f28255Scgd 		argptr++;
16461f28255Scgd 	}
16537ed7877Sjtc 	optschanged();
16661f28255Scgd }
16761f28255Scgd 
16861f28255Scgd 
1695dad1439Scgd void
170c02b3bbdSchristos optschanged(void)
1715dad1439Scgd {
17237ed7877Sjtc 	setinteractive(iflag);
1737accaec4Schristos #ifndef SMALL
17437ed7877Sjtc 	histedit();
175e3c63ad9Scgd #endif
17637ed7877Sjtc 	setjobctl(mflag);
1774ca0efa9Skre 
1784ca0efa9Skre 	if (privileged && !pflag) {
1794ca0efa9Skre 		setuid(getuid());
1804ca0efa9Skre 		setgid(getgid());
1814ca0efa9Skre 		privileged = 0;
1824ca0efa9Skre 		setvarsafe("PSc", (getuid() == 0 ? "#" : "$"), 0);
1834ca0efa9Skre 	}
18437ed7877Sjtc }
18561f28255Scgd 
18661f28255Scgd /*
18761f28255Scgd  * Process shell options.  The global variable argptr contains a pointer
18861f28255Scgd  * to the argument list; we advance it past the options.
18961f28255Scgd  */
19061f28255Scgd 
19161f28255Scgd STATIC void
192c02b3bbdSchristos options(int cmdline)
1934ce0d34aScgd {
194027df12dSchristos 	static char empty[] = "";
19548250187Stls 	char *p;
19661f28255Scgd 	int val;
19761f28255Scgd 	int c;
19861f28255Scgd 
19961f28255Scgd 	if (cmdline)
20061f28255Scgd 		minusc = NULL;
20161f28255Scgd 	while ((p = *argptr) != NULL) {
20261f28255Scgd 		argptr++;
20361f28255Scgd 		if ((c = *p++) == '-') {
20461f28255Scgd 			val = 1;
20507bae7edSchristos                         if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
20661f28255Scgd                                 if (!cmdline) {
20761f28255Scgd                                         /* "-" means turn off -x and -v */
20861f28255Scgd                                         if (p[0] == '\0')
20961f28255Scgd                                                 xflag = vflag = 0;
21061f28255Scgd                                         /* "--" means reset params */
2116ee2193dSchristos                                         else if (*argptr == NULL)
21261f28255Scgd 						setparam(argptr);
21361f28255Scgd                                 }
21461f28255Scgd 				break;	  /* "-" or  "--" terminates options */
21561f28255Scgd 			}
21661f28255Scgd 		} else if (c == '+') {
21761f28255Scgd 			val = 0;
21861f28255Scgd 		} else {
21961f28255Scgd 			argptr--;
22061f28255Scgd 			break;
22161f28255Scgd 		}
22261f28255Scgd 		while ((c = *p++) != '\0') {
223d98a8b3cSkre 			if (val == 1 && c == 'c' && cmdline) {
224027df12dSchristos 				/* command is after shell args*/
225027df12dSchristos 				minusc = empty;
22637ed7877Sjtc 			} else if (c == 'o') {
227d98a8b3cSkre 				if (*p != '\0')
228d98a8b3cSkre 					minus_o(p, val + (cmdline ? val : 0));
229d98a8b3cSkre 				else if (*argptr)
230d98a8b3cSkre 					minus_o(*argptr++,
231d98a8b3cSkre 					    val + (cmdline ? val : 0));
232d98a8b3cSkre 				else if (!cmdline)
233d98a8b3cSkre 					minus_o(NULL, val);
234d98a8b3cSkre 				else
235d98a8b3cSkre 					error("arg for %co missing", "+-"[val]);
236d98a8b3cSkre 				break;
237ab36694aSkre #ifdef DEBUG
238ab36694aSkre 			} else if (c == 'D') {
239ab36694aSkre 				if (*p) {
240ab36694aSkre 					set_debug(p, val);
241ab36694aSkre 					break;
242ab36694aSkre 				} else if (*argptr)
243ab36694aSkre 					set_debug(*argptr++, val);
244ab36694aSkre 				else
245ab36694aSkre 					set_debug("*$", val);
246ab36694aSkre #endif
2472ecc8e99Skre 			} else if (cmdline && c == 'r') {
248*da7ff22aSkre 				out1fmt("NetBSD shell: %s\n",
249*da7ff22aSkre 				    lookupvar("NETBSD_SHELL"));
2502ecc8e99Skre 				sh_exit(0);
25161f28255Scgd 			} else {
25261f28255Scgd 				setoption(c, val);
25361f28255Scgd 			}
25461f28255Scgd 		}
25537ed7877Sjtc 	}
25637ed7877Sjtc }
25737ed7877Sjtc 
258c02b3bbdSchristos static void
259c6144e48Slukem set_opt_val(size_t i, int val)
260c02b3bbdSchristos {
261c6144e48Slukem 	size_t j;
262c02b3bbdSchristos 	int flag;
263c02b3bbdSchristos 
264c02b3bbdSchristos 	if (val && (flag = optlist[i].opt_set)) {
265c02b3bbdSchristos 		/* some options (eg vi/emacs) are mutually exclusive */
266c02b3bbdSchristos 		for (j = 0; j < NOPTS; j++)
267c02b3bbdSchristos 		    if (optlist[j].opt_set == flag)
268c02b3bbdSchristos 			optlist[j].val = 0;
269c02b3bbdSchristos 	}
270ae40879bSkre #ifndef SMALL
271ffc64c63Skre 	if (i == _SH_OPT_Xflag)
272ffc64c63Skre 		xtracefdsetup(val);
273ae40879bSkre #endif
274c02b3bbdSchristos 	optlist[i].val = val;
275c02b3bbdSchristos #ifdef DEBUG
276c02b3bbdSchristos 	if (&optlist[i].val == &debug)
277ffc64c63Skre 		opentrace();	/* different "trace" than the -x one... */
278c02b3bbdSchristos #endif
279c02b3bbdSchristos }
280c02b3bbdSchristos 
28137ed7877Sjtc STATIC void
282c02b3bbdSchristos minus_o(char *name, int val)
28337ed7877Sjtc {
284c6144e48Slukem 	size_t i;
2859302f8efSchristos 	const char *sep = ": ";
28637ed7877Sjtc 
28737ed7877Sjtc 	if (name == NULL) {
288ea207881Sdsl 		if (val) {
2899302f8efSchristos 			out1str("Current option settings");
290ea207881Sdsl 			for (i = 0; i < NOPTS; i++) {
2919302f8efSchristos 				if (optlist[i].name == NULL)  {
2929302f8efSchristos 					out1fmt("%s%c%c", sep,
2939302f8efSchristos 					    "+-"[optlist[i].val],
2949302f8efSchristos 					    optlist[i].letter);
2959302f8efSchristos 					sep = ", ";
2969302f8efSchristos 				}
2979302f8efSchristos 			}
2989302f8efSchristos 			out1c('\n');
2999302f8efSchristos 			for (i = 0; i < NOPTS; i++) {
3009302f8efSchristos 				if (optlist[i].name)
301d98a8b3cSkre 				    out1fmt("%-19s %s\n", optlist[i].name,
30237ed7877Sjtc 					optlist[i].val ? "on" : "off");
303ea207881Sdsl 			}
304ea207881Sdsl 		} else {
305d98a8b3cSkre 			out1str("set -o default");
306ea207881Sdsl 			for (i = 0; i < NOPTS; i++) {
307d98a8b3cSkre 				if (optlist[i].val == optlist[i].dflt)
308d98a8b3cSkre 					continue;
3099302f8efSchristos 				if (optlist[i].name)
310ea207881Sdsl 				    out1fmt(" %co %s",
311ea207881Sdsl 					"+-"[optlist[i].val], optlist[i].name);
3129302f8efSchristos 				else
3139302f8efSchristos 				    out1fmt(" %c%c", "+-"[optlist[i].val],
3149302f8efSchristos 					optlist[i].letter);
315ea207881Sdsl 			}
3169302f8efSchristos 			out1c('\n');
317ea207881Sdsl 		}
31837ed7877Sjtc 	} else {
319d98a8b3cSkre 		if (val == 1 && equal(name, "default")) { /* special case */
320d98a8b3cSkre 			for (i = 0; i < NOPTS; i++)
321d98a8b3cSkre 				set_opt_val(i, optlist[i].dflt);
322d98a8b3cSkre 			return;
323d98a8b3cSkre 		}
324d98a8b3cSkre 		if (val)
325d98a8b3cSkre 			val = 1;
32637ed7877Sjtc 		for (i = 0; i < NOPTS; i++)
3279302f8efSchristos 			if (optlist[i].name && equal(name, optlist[i].name)) {
328c02b3bbdSchristos 				set_opt_val(i, val);
329ae40879bSkre #ifndef SMALL
330ffc64c63Skre 				if (i == _SH_OPT_Xflag)
331ffc64c63Skre 					set_opt_val(_SH_OPT_xflag, val);
332ae40879bSkre #endif
33337ed7877Sjtc 				return;
33437ed7877Sjtc 			}
33594d6cf1cSkre 		error("Unknown option %co %s", "+-"[val], name);
33661f28255Scgd 	}
33761f28255Scgd }
33861f28255Scgd 
33961f28255Scgd 
34061f28255Scgd STATIC void
341c02b3bbdSchristos setoption(int flag, int val)
34261f28255Scgd {
343c6144e48Slukem 	size_t i;
34461f28255Scgd 
34537ed7877Sjtc 	for (i = 0; i < NOPTS; i++)
34637ed7877Sjtc 		if (optlist[i].letter == flag) {
347c02b3bbdSchristos 			set_opt_val(i, val);
348ae40879bSkre #ifndef SMALL
349ffc64c63Skre 			if (i == _SH_OPT_Xflag)
350ffc64c63Skre 				set_opt_val(_SH_OPT_xflag, val);
351ae40879bSkre #endif
35237ed7877Sjtc 			return;
35337ed7877Sjtc 		}
35494d6cf1cSkre 	error("Unknown option %c%c", "+-"[val], flag);
355ee9e50eaSmycroft 	/* NOTREACHED */
35661f28255Scgd }
35761f28255Scgd 
35861f28255Scgd 
35961f28255Scgd 
36061f28255Scgd #ifdef mkinit
36161f28255Scgd INCLUDE "options.h"
36261f28255Scgd 
36361f28255Scgd SHELLPROC {
36437ed7877Sjtc 	int i;
36561f28255Scgd 
366c02b3bbdSchristos 	for (i = 0; optlist[i].name; i++)
36737ed7877Sjtc 		optlist[i].val = 0;
36837ed7877Sjtc 	optschanged();
36937ed7877Sjtc 
37061f28255Scgd }
37161f28255Scgd #endif
37261f28255Scgd 
37361f28255Scgd 
37461f28255Scgd /*
37561f28255Scgd  * Set the shell parameters.
37661f28255Scgd  */
37761f28255Scgd 
37861f28255Scgd void
379c02b3bbdSchristos setparam(char **argv)
38061f28255Scgd {
38161f28255Scgd 	char **newparam;
38261f28255Scgd 	char **ap;
38361f28255Scgd 	int nparam;
38461f28255Scgd 
385aecb1ce4Sdsl 	for (nparam = 0 ; argv[nparam] ; nparam++)
386aecb1ce4Sdsl 		continue;
38761f28255Scgd 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
38861f28255Scgd 	while (*argv) {
38961f28255Scgd 		*ap++ = savestr(*argv++);
39061f28255Scgd 	}
39161f28255Scgd 	*ap = NULL;
39261f28255Scgd 	freeparam(&shellparam);
39361f28255Scgd 	shellparam.malloc = 1;
39461f28255Scgd 	shellparam.nparam = nparam;
39561f28255Scgd 	shellparam.p = newparam;
39661f28255Scgd 	shellparam.optnext = NULL;
39761f28255Scgd }
39861f28255Scgd 
39961f28255Scgd 
40061f28255Scgd /*
40161f28255Scgd  * Free the list of positional parameters.
40261f28255Scgd  */
40361f28255Scgd 
40461f28255Scgd void
405c02b3bbdSchristos freeparam(volatile struct shparam *param)
40661f28255Scgd {
40761f28255Scgd 	char **ap;
40861f28255Scgd 
40961f28255Scgd 	if (param->malloc) {
41061f28255Scgd 		for (ap = param->p ; *ap ; ap++)
41161f28255Scgd 			ckfree(*ap);
41261f28255Scgd 		ckfree(param->p);
41361f28255Scgd 	}
41461f28255Scgd }
41561f28255Scgd 
41661f28255Scgd 
41761f28255Scgd 
41861f28255Scgd /*
41961f28255Scgd  * The shift builtin command.
42061f28255Scgd  */
42161f28255Scgd 
4222f76de8bSkre #ifndef TINY
4232f76de8bSkre /* first the rotate variant */
4242f76de8bSkre static inline int
4252f76de8bSkre rotatecmd(int argc, char **argv)
4262f76de8bSkre {
4272f76de8bSkre 	int n;
4282f76de8bSkre 	char **ap1, **ap2, **ss;
4292f76de8bSkre 
4302f76de8bSkre 	(void) nextopt(NULL);	/* ignore '--' as leading option */
4312f76de8bSkre 
4322f76de8bSkre 	/*
4332f76de8bSkre 	 * half this is just in case it ever becomes
4342f76de8bSkre 	 * a separate named command, while it remains
4352f76de8bSkre 	 * puerly an inline inside shift, the compiler
4362f76de8bSkre 	 * should optimise most of it to nothingness
4372f76de8bSkre 	 */
4382f76de8bSkre 	if (argptr[0] && argptr[1])
4392f76de8bSkre 		error("Usage: rotate [n]");
4402f76de8bSkre 	n = 1;
4412f76de8bSkre 	if (*argptr) {
4422f76de8bSkre 		if (**argptr == '-')
4432f76de8bSkre 			n = number(*argptr + 1);
4442f76de8bSkre 		else		/* anti-clockwise n == clockwise $# - n */
4452f76de8bSkre 			n = shellparam.nparam - number(*argptr);
4462f76de8bSkre 	}
4472f76de8bSkre 
4482f76de8bSkre 	if (n == 0 || n == shellparam.nparam)		/* nothing to do */
4492f76de8bSkre 		return 0;
4502f76de8bSkre 
4512f76de8bSkre 	if (n < 0 || n > shellparam.nparam)
4522f76de8bSkre 		error("can't rotate that many");
4532f76de8bSkre 
4542f76de8bSkre 	ap2 = ss = (char **)stalloc(n * sizeof(char *));
4552f76de8bSkre 	INTOFF;
4562f76de8bSkre 	for (ap1 = shellparam.p + shellparam.nparam - n;
4572f76de8bSkre 	     ap1 < shellparam.p + shellparam.nparam; )
4582f76de8bSkre 		*ap2++ = *ap1++;
4592f76de8bSkre 	for (ap2 = shellparam.p + shellparam.nparam, ap1 = ap2 - n;
4602f76de8bSkre 	     ap1 > shellparam.p; )
4612f76de8bSkre 		*--ap2 = *--ap1;
4622f76de8bSkre 	for (ap1 = ss + n; ap1 > ss; )
4632f76de8bSkre 		*--ap2 = *--ap1;
4642f76de8bSkre 	shellparam.optnext = NULL;
4652f76de8bSkre 	INTON;
4662f76de8bSkre 	stunalloc(ss);
4672f76de8bSkre 
4682f76de8bSkre 	return 0;
4692f76de8bSkre }
4702f76de8bSkre #endif
4712f76de8bSkre 
4724ce0d34aScgd int
473c02b3bbdSchristos shiftcmd(int argc, char **argv)
4744ce0d34aScgd {
47561f28255Scgd 	int n;
47661f28255Scgd 	char **ap1, **ap2;
47761f28255Scgd 
4782f76de8bSkre 	(void) nextopt(NULL);	/* ignore '--' as leading option */
4792f76de8bSkre 
4802f76de8bSkre 	if (argptr[0] && argptr[1])
481bff14f8dSchristos 		error("Usage: shift [n]");
4822f76de8bSkre 
4832f76de8bSkre #ifndef TINY
4842f76de8bSkre 	if (*argptr && **argptr == '-') {
4852f76de8bSkre 		argptr = argv + 1;	/* reinit nextopt() */
4862f76de8bSkre 		optptr = NULL;
4872f76de8bSkre 		return rotatecmd(argc, argv);
4882f76de8bSkre 	}
4892f76de8bSkre #endif
4902f76de8bSkre 
49161f28255Scgd 	n = 1;
4922f76de8bSkre 	if (*argptr)
4932f76de8bSkre 		n = number(*argptr);
49461f28255Scgd 	if (n > shellparam.nparam)
49537ed7877Sjtc 		error("can't shift that many");
49661f28255Scgd 	INTOFF;
49761f28255Scgd 	shellparam.nparam -= n;
49861f28255Scgd 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
49961f28255Scgd 		if (shellparam.malloc)
50061f28255Scgd 			ckfree(*ap1);
50161f28255Scgd 	}
50261f28255Scgd 	ap2 = shellparam.p;
503bff14f8dSchristos 	while ((*ap2++ = *ap1++) != NULL)
504bff14f8dSchristos 		continue;
50561f28255Scgd 	shellparam.optnext = NULL;
50661f28255Scgd 	INTON;
50761f28255Scgd 	return 0;
50861f28255Scgd }
50961f28255Scgd 
51061f28255Scgd 
51161f28255Scgd 
51261f28255Scgd /*
51361f28255Scgd  * The set command builtin.
51461f28255Scgd  */
51561f28255Scgd 
5164ce0d34aScgd int
517c02b3bbdSchristos setcmd(int argc, char **argv)
5184ce0d34aScgd {
51961f28255Scgd 	if (argc == 1)
5209302f8efSchristos 		return showvars(0, 0, 1, 0);
52161f28255Scgd 	INTOFF;
52261f28255Scgd 	options(0);
52337ed7877Sjtc 	optschanged();
52461f28255Scgd 	if (*argptr != NULL) {
52561f28255Scgd 		setparam(argptr);
52661f28255Scgd 	}
52761f28255Scgd 	INTON;
52861f28255Scgd 	return 0;
52961f28255Scgd }
53061f28255Scgd 
53161f28255Scgd 
532cc31700aSchristos void
533d73cf48cSkre getoptsreset(char *value, int flags __unused)
534cc31700aSchristos {
535c6c29888Skre 	/*
536c6c29888Skre 	 * This is just to detect the case where OPTIND=1
537c6c29888Skre 	 * is executed.   Any other string assigned to OPTIND
538c6c29888Skre 	 * is OK, but is not a reset.   No errors, so cannot use number()
539c6c29888Skre 	 */
540c6c29888Skre 	if (is_digit(*value) && strtol(value, NULL, 10) == 1) {
541846dce0eSchristos 		shellparam.optnext = NULL;
542ccce082dSchristos 		shellparam.reset = 1;
543ccce082dSchristos 	}
544cc31700aSchristos }
545cc31700aSchristos 
54661f28255Scgd /*
54761f28255Scgd  * The getopts builtin.  Shellparam.optnext points to the next argument
54861f28255Scgd  * to be processed.  Shellparam.optptr points to the next character to
54961f28255Scgd  * be processed in the current argument.  If shellparam.optnext is NULL,
55061f28255Scgd  * then it's the first time getopts has been called.
55161f28255Scgd  */
55261f28255Scgd 
5534ce0d34aScgd int
554c02b3bbdSchristos getoptscmd(int argc, char **argv)
5554ce0d34aScgd {
5560bc88b24Schristos 	char **optbase;
55761f28255Scgd 
5580bc88b24Schristos 	if (argc < 3)
559b635f565Sjmmv 		error("usage: getopts optstring var [arg]");
5600bc88b24Schristos 	else if (argc == 3)
5610bc88b24Schristos 		optbase = shellparam.p;
5620bc88b24Schristos 	else
5630bc88b24Schristos 		optbase = &argv[3];
5640bc88b24Schristos 
565ccce082dSchristos 	if (shellparam.reset == 1) {
5660bc88b24Schristos 		shellparam.optnext = optbase;
56761f28255Scgd 		shellparam.optptr = NULL;
568ccce082dSchristos 		shellparam.reset = 0;
56961f28255Scgd 	}
5700bc88b24Schristos 
5710bc88b24Schristos 	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
5720bc88b24Schristos 		       &shellparam.optptr);
5730bc88b24Schristos }
5740bc88b24Schristos 
5750bc88b24Schristos STATIC int
576c02b3bbdSchristos getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
5770bc88b24Schristos {
57848250187Stls 	char *p, *q;
5790bc88b24Schristos 	char c = '?';
5800bc88b24Schristos 	int done = 0;
5810bc88b24Schristos 	int ind = 0;
582cc31700aSchristos 	int err = 0;
583e6a384fcSitojun 	char s[12];
5840bc88b24Schristos 
5853d424690Schristos 	if ((p = *optpptr) == NULL || *p == '\0') {
5860bc88b24Schristos 		/* Current word is done, advance */
587ccce082dSchristos 		if (*optnext == NULL)
588ccce082dSchristos 			return 1;
5890bc88b24Schristos 		p = **optnext;
59061f28255Scgd 		if (p == NULL || *p != '-' || *++p == '\0') {
59161f28255Scgd atend:
5920bc88b24Schristos 			ind = *optnext - optfirst + 1;
593a00e49c2Schristos 			*optnext = NULL;
594a00e49c2Schristos 			p = NULL;
5950bc88b24Schristos 			done = 1;
5960bc88b24Schristos 			goto out;
59761f28255Scgd 		}
5980bc88b24Schristos 		(*optnext)++;
59961f28255Scgd 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
60061f28255Scgd 			goto atend;
60161f28255Scgd 	}
6020bc88b24Schristos 
60361f28255Scgd 	c = *p++;
6040bc88b24Schristos 	for (q = optstr; *q != c; ) {
60561f28255Scgd 		if (*q == '\0') {
6060bc88b24Schristos 			if (optstr[0] == ':') {
6070bc88b24Schristos 				s[0] = c;
6080bc88b24Schristos 				s[1] = '\0';
609cc31700aSchristos 				err |= setvarsafe("OPTARG", s, 0);
610c02b3bbdSchristos 			} else {
61194d6cf1cSkre 				outfmt(&errout, "Unknown option -%c\n", c);
612c02b3bbdSchristos 				(void) unsetvar("OPTARG", 0);
6130bc88b24Schristos 			}
61461f28255Scgd 			c = '?';
615cc31700aSchristos 			goto bad;
61661f28255Scgd 		}
61761f28255Scgd 		if (*++q == ':')
61861f28255Scgd 			q++;
61961f28255Scgd 	}
6200bc88b24Schristos 
62161f28255Scgd 	if (*++q == ':') {
6220bc88b24Schristos 		if (*p == '\0' && (p = **optnext) == NULL) {
6230bc88b24Schristos 			if (optstr[0] == ':') {
6240bc88b24Schristos 				s[0] = c;
6250bc88b24Schristos 				s[1] = '\0';
626cc31700aSchristos 				err |= setvarsafe("OPTARG", s, 0);
6270bc88b24Schristos 				c = ':';
628c02b3bbdSchristos 			} else {
629d2ded939Schristos 				outfmt(&errout, "No arg for -%c option\n", c);
630c02b3bbdSchristos 				(void) unsetvar("OPTARG", 0);
63161f28255Scgd 				c = '?';
6320bc88b24Schristos 			}
633cc31700aSchristos 			goto bad;
63461f28255Scgd 		}
6350bc88b24Schristos 
6360bc88b24Schristos 		if (p == **optnext)
6370bc88b24Schristos 			(*optnext)++;
638c02b3bbdSchristos 		err |= setvarsafe("OPTARG", p, 0);
63961f28255Scgd 		p = NULL;
640c02b3bbdSchristos 	} else
641c02b3bbdSchristos 		err |= setvarsafe("OPTARG", "", 0);
642ccce082dSchristos 	ind = *optnext - optfirst + 1;
643ccce082dSchristos 	goto out;
644ccce082dSchristos 
645cc31700aSchristos bad:
646cc31700aSchristos 	ind = 1;
647cc31700aSchristos 	*optnext = NULL;
648cc31700aSchristos 	p = NULL;
64961f28255Scgd out:
6503d424690Schristos 	*optpptr = p;
6510bc88b24Schristos 	fmtstr(s, sizeof(s), "%d", ind);
652cc31700aSchristos 	err |= setvarsafe("OPTIND", s, VNOFUNC);
65361f28255Scgd 	s[0] = c;
65461f28255Scgd 	s[1] = '\0';
655cc31700aSchristos 	err |= setvarsafe(optvar, s, 0);
656cc31700aSchristos 	if (err) {
657cc31700aSchristos 		*optnext = NULL;
6583d424690Schristos 		*optpptr = NULL;
659cc31700aSchristos 		flushall();
660cc31700aSchristos 		exraise(EXERROR);
661cc31700aSchristos 	}
6620bc88b24Schristos 	return done;
66361f28255Scgd }
66461f28255Scgd 
66561f28255Scgd /*
66637ed7877Sjtc  * XXX - should get rid of.  have all builtins use getopt(3).  the
66737ed7877Sjtc  * library getopt must have the BSD extension static variable "optreset"
66837ed7877Sjtc  * otherwise it can't be used within the shell safely.
66937ed7877Sjtc  *
67061f28255Scgd  * Standard option processing (a la getopt) for builtin routines.  The
67161f28255Scgd  * only argument that is passed to nextopt is the option string; the
67261f28255Scgd  * other arguments are unnecessary.  It return the character, or '\0' on
673ebc4cf1cSkre  * end of input.  If optstring is NULL, then there are no options, and
674ebc4cf1cSkre  * args are allowed to begin with '-', but a single leading "--" will be
675ebc4cf1cSkre  * discarded.   This is for some POSIX special builtins that require
676ebc4cf1cSkre  * -- processing, have no args, and we never did opt processing before
677ebc4cf1cSkre  * and need to retain backwards compat.
67861f28255Scgd  */
67961f28255Scgd 
68061f28255Scgd int
681c02b3bbdSchristos nextopt(const char *optstring)
68261f28255Scgd {
6833d424690Schristos 	char *p;
6843d424690Schristos 	const char *q;
68561f28255Scgd 	char c;
68661f28255Scgd 
68761f28255Scgd 	if ((p = optptr) == NULL || *p == '\0') {
68861f28255Scgd 		p = *argptr;
68961f28255Scgd 		if (p == NULL || *p != '-' || *++p == '\0')
69061f28255Scgd 			return '\0';
69161f28255Scgd 		argptr++;
69261f28255Scgd 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
69361f28255Scgd 			return '\0';
69492dfd40cSkre 		if (optstring == NULL)	/* not processing the "option" */
69592dfd40cSkre 			argptr--;	/* so make it be an arg again */
69661f28255Scgd 	}
697ebc4cf1cSkre 	if (optstring == NULL)
698ebc4cf1cSkre 		return '\0';
69961f28255Scgd 	c = *p++;
70061f28255Scgd 	for (q = optstring ; *q != c ; ) {
70161f28255Scgd 		if (*q == '\0')
70294d6cf1cSkre 			error("Unknown option -%c", c);
70361f28255Scgd 		if (*++q == ':')
70461f28255Scgd 			q++;
70561f28255Scgd 	}
70661f28255Scgd 	if (*++q == ':') {
70761f28255Scgd 		if (*p == '\0' && (p = *argptr++) == NULL)
70861f28255Scgd 			error("No arg for -%c option", c);
70933809804Schristos 		optionarg = p;
71061f28255Scgd 		p = NULL;
71161f28255Scgd 	}
71261f28255Scgd 	optptr = p;
71361f28255Scgd 	return c;
71461f28255Scgd }
715