Initial import from FreeBSD RELENG_4:
[dragonfly.git] / bin / sh / options.c
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 #endif /* not lint */
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD: src/bin/sh/options.c,v 1.15.2.2 2002/07/19 04:38:52 tjr Exp $");
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(int);
77 STATIC void minus_o(char *, int);
78 STATIC void setoption(int, int);
79 STATIC int getopts(char *, char *, char **, char ***, char **);
80
81
82 /*
83  * Process the shell command line arguments.
84  */
85
86 void
87 procargs(int argc, char **argv)
88 {
89         int i;
90
91         argptr = argv;
92         if (argc > 0)
93                 argptr++;
94         for (i = 0; i < NOPTS; i++)
95                 optlist[i].val = 2;
96         privileged = (getuid() != geteuid() || getgid() != getegid());
97         options(1);
98         if (*argptr == NULL && minusc == NULL)
99                 sflag = 1;
100         if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
101                 iflag = 1;
102         if (mflag == 2)
103                 mflag = iflag;
104         for (i = 0; i < NOPTS; i++)
105                 if (optlist[i].val == 2)
106                         optlist[i].val = 0;
107         arg0 = argv[0];
108         if (sflag == 0 && minusc == NULL) {
109                 commandname = arg0 = *argptr++;
110                 setinputfile(commandname, 0);
111         }
112         /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
113         if (argptr && minusc && *argptr)
114                 arg0 = *argptr++;
115
116         shellparam.p = argptr;
117         shellparam.reset = 1;
118         /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
119         while (*argptr) {
120                 shellparam.nparam++;
121                 argptr++;
122         }
123         optschanged();
124 }
125
126
127 void
128 optschanged(void)
129 {
130         setinteractive(iflag);
131 #ifndef NO_HISTORY
132         histedit();
133 #endif
134         setjobctl(mflag);
135 }
136
137 /*
138  * Process shell options.  The global variable argptr contains a pointer
139  * to the argument list; we advance it past the options.
140  */
141
142 STATIC void
143 options(int cmdline)
144 {
145         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                                 if (c == 'p' && !val && privileged) {
191                                         (void) setuid(getuid());
192                                         (void) setgid(getgid());
193                                 }
194                                 setoption(c, val);
195                         }
196                 }
197         }
198 }
199
200 STATIC void
201 minus_o(char *name, int val)
202 {
203         int doneset, i;
204
205         if (name == NULL) {
206                 if (val) {
207                         /* "Pretty" output. */
208                         out1str("Current option settings\n");
209                         for (i = 0; i < NOPTS; i++)
210                                 out1fmt("%-16s%s\n", optlist[i].name,
211                                         optlist[i].val ? "on" : "off");
212                 } else {
213                         /* Output suitable for re-input to shell. */
214                         for (doneset = i = 0; i < NOPTS; i++)
215                                 if (optlist[i].val) {
216                                         if (!doneset) {
217                                                 out1str("set");
218                                                 doneset = 1;
219                                         }
220                                         out1fmt(" -o %s", optlist[i].name);
221                                 }
222                         if (doneset)
223                                 out1c('\n');
224                 }
225         } else {
226                 for (i = 0; i < NOPTS; i++)
227                         if (equal(name, optlist[i].name)) {
228                                 if (!val && privileged && equal(name, "privileged")) {
229                                         (void) setuid(getuid());
230                                         (void) setgid(getgid());
231                                 }
232                                 setoption(optlist[i].letter, val);
233                                 return;
234                         }
235                 error("Illegal option -o %s", name);
236         }
237 }
238
239
240 STATIC void
241 setoption(int flag, int val)
242 {
243         int i;
244
245         for (i = 0; i < NOPTS; i++)
246                 if (optlist[i].letter == flag) {
247                         optlist[i].val = val;
248                         if (val) {
249                                 /* #%$ hack for ksh semantics */
250                                 if (flag == 'V')
251                                         Eflag = 0;
252                                 else if (flag == 'E')
253                                         Vflag = 0;
254                         }
255                         return;
256                 }
257         error("Illegal option -%c", flag);
258 }
259
260
261
262 #ifdef mkinit
263 INCLUDE "options.h"
264
265 SHELLPROC {
266         int i;
267
268         for (i = 0; i < NOPTS; i++)
269                 optlist[i].val = 0;
270         optschanged();
271
272 }
273 #endif
274
275
276 /*
277  * Set the shell parameters.
278  */
279
280 void
281 setparam(char **argv)
282 {
283         char **newparam;
284         char **ap;
285         int nparam;
286
287         for (nparam = 0 ; argv[nparam] ; nparam++);
288         ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
289         while (*argv) {
290                 *ap++ = savestr(*argv++);
291         }
292         *ap = NULL;
293         freeparam(&shellparam);
294         shellparam.malloc = 1;
295         shellparam.nparam = nparam;
296         shellparam.p = newparam;
297         shellparam.optnext = NULL;
298 }
299
300
301 /*
302  * Free the list of positional parameters.
303  */
304
305 void
306 freeparam(struct shparam *param)
307 {
308         char **ap;
309
310         if (param->malloc) {
311                 for (ap = param->p ; *ap ; ap++)
312                         ckfree(*ap);
313                 ckfree(param->p);
314         }
315 }
316
317
318
319 /*
320  * The shift builtin command.
321  */
322
323 int
324 shiftcmd(int argc, char **argv)
325 {
326         int n;
327         char **ap1, **ap2;
328
329         n = 1;
330         if (argc > 1)
331                 n = number(argv[1]);
332         if (n > shellparam.nparam)
333                 error("can't shift that many");
334         INTOFF;
335         shellparam.nparam -= n;
336         for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
337                 if (shellparam.malloc)
338                         ckfree(*ap1);
339         }
340         ap2 = shellparam.p;
341         while ((*ap2++ = *ap1++) != NULL);
342         shellparam.optnext = NULL;
343         INTON;
344         return 0;
345 }
346
347
348
349 /*
350  * The set command builtin.
351  */
352
353 int
354 setcmd(int argc, char **argv)
355 {
356         if (argc == 1)
357                 return showvarscmd(argc, argv);
358         INTOFF;
359         options(0);
360         optschanged();
361         if (*argptr != NULL) {
362                 setparam(argptr);
363         }
364         INTON;
365         return 0;
366 }
367
368
369 void
370 getoptsreset(const char *value)
371 {
372         if (number(value) == 1) {
373                 shellparam.optnext = NULL;
374                 shellparam.reset = 1;
375         }
376 }
377
378 /*
379  * The getopts builtin.  Shellparam.optnext points to the next argument
380  * to be processed.  Shellparam.optptr points to the next character to
381  * be processed in the current argument.  If shellparam.optnext is NULL,
382  * then it's the first time getopts has been called.
383  */
384
385 int
386 getoptscmd(int argc, char **argv)
387 {
388         char **optbase = NULL;
389
390         if (argc < 3)
391                 error("usage: getopts optstring var [arg]");
392         else if (argc == 3)
393                 optbase = shellparam.p;
394         else
395                 optbase = &argv[3];
396
397         if (shellparam.reset == 1) {
398                 shellparam.optnext = optbase;
399                 shellparam.optptr = NULL;
400                 shellparam.reset = 0;
401         }
402
403         return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
404                        &shellparam.optptr);
405 }
406
407 STATIC int
408 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
409     char **optptr)
410 {
411         char *p, *q;
412         char c = '?';
413         int done = 0;
414         int ind = 0;
415         int err = 0;
416         char s[10];
417
418         if ((p = *optptr) == NULL || *p == '\0') {
419                 /* Current word is done, advance */
420                 if (*optnext == NULL)
421                         return 1;
422                 p = **optnext;
423                 if (p == NULL || *p != '-' || *++p == '\0') {
424 atend:
425                         ind = *optnext - optfirst + 1;
426                         *optnext = NULL;
427                         p = NULL;
428                         done = 1;
429                         goto out;
430                 }
431                 (*optnext)++;
432                 if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
433                         goto atend;
434         }
435
436         c = *p++;
437         for (q = optstr; *q != c; ) {
438                 if (*q == '\0') {
439                         if (optstr[0] == ':') {
440                                 s[0] = c;
441                                 s[1] = '\0';
442                                 err |= setvarsafe("OPTARG", s, 0);
443                         }
444                         else {
445                                 out1fmt("Illegal option -%c\n", c);
446                                 (void) unsetvar("OPTARG");
447                         }
448                         c = '?';
449                         goto bad;
450                 }
451                 if (*++q == ':')
452                         q++;
453         }
454
455         if (*++q == ':') {
456                 if (*p == '\0' && (p = **optnext) == NULL) {
457                         if (optstr[0] == ':') {
458                                 s[0] = c;
459                                 s[1] = '\0';
460                                 err |= setvarsafe("OPTARG", s, 0);
461                                 c = ':';
462                         }
463                         else {
464                                 out1fmt("No arg for -%c option\n", c);
465                                 (void) unsetvar("OPTARG");
466                                 c = '?';
467                         }
468                         goto bad;
469                 }
470
471                 if (p == **optnext)
472                         (*optnext)++;
473                 setvarsafe("OPTARG", p, 0);
474                 p = NULL;
475         }
476         else
477                 setvarsafe("OPTARG", "", 0);
478         ind = *optnext - optfirst + 1;
479         goto out;
480
481 bad:
482         ind = 1;
483         *optnext = NULL;
484         p = NULL;
485 out:
486         *optptr = p;
487         fmtstr(s, sizeof(s), "%d", ind);
488         err |= setvarsafe("OPTIND", s, VNOFUNC);
489         s[0] = c;
490         s[1] = '\0';
491         err |= setvarsafe(optvar, s, 0);
492         if (err) {
493                 *optnext = NULL;
494                 *optptr = NULL;
495                 flushall();
496                 exraise(EXERROR);
497         }
498         return done;
499 }
500
501 /*
502  * XXX - should get rid of.  have all builtins use getopt(3).  the
503  * library getopt must have the BSD extension static variable "optreset"
504  * otherwise it can't be used within the shell safely.
505  *
506  * Standard option processing (a la getopt) for builtin routines.  The
507  * only argument that is passed to nextopt is the option string; the
508  * other arguments are unnecessary.  It return the character, or '\0' on
509  * end of input.
510  */
511
512 int
513 nextopt(char *optstring)
514 {
515         char *p, *q;
516         char c;
517
518         if ((p = optptr) == NULL || *p == '\0') {
519                 p = *argptr;
520                 if (p == NULL || *p != '-' || *++p == '\0')
521                         return '\0';
522                 argptr++;
523                 if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
524                         return '\0';
525         }
526         c = *p++;
527         for (q = optstring ; *q != c ; ) {
528                 if (*q == '\0')
529                         error("Illegal option -%c", c);
530                 if (*++q == ':')
531                         q++;
532         }
533         if (*++q == ':') {
534                 if (*p == '\0' && (p = *argptr++) == NULL)
535                         error("No arg for -%c option", c);
536                 shoptarg = p;
537                 p = NULL;
538         }
539         optptr = p;
540         return c;
541 }