10 F_ALLEXPORT
, // -a or -o allexport
11 F_ERREXIT
, // -e or -o errexit
12 F_IGNOREEOF
, // -o ignoreeof
13 F_MONITOR
, // -m or -o monitor
14 F_NOCLOBBER
, // -C or -o noclobb
15 F_NOGLOB
, // -f or -o noglob
17 F_NOEXEC
, // -n or -o noexec
19 F_NOTIFY
, // -b or -o notify
20 F_NOUNSET
, // -u or -o nounset
21 F_VERBOSE
, // -v or -o verbose
23 F_XTRACE
, // -x or -o xtrace
25 F_D_ALLEXPORT
, // +a or +o allexport
26 F_D_ERREXIT
, // +e or +o errexit
27 F_D_IGNOREEOF
, // +o ignoreeof
28 F_D_MONITOR
, // +m or +o monitor
29 F_D_NOCLOBBER
, // +C or +o noclobber
30 F_D_NOGLOB
, // +f or +o noglob
32 F_D_NOEXEC
, // +n or +o noexec
33 F_D_NOLOG
, // +o nolog
34 F_D_NOTIFY
, // +b or +o notify
35 F_D_NOUNSET
, // +u or +o nounset
36 F_D_VERBOSE
, // +v or +o verbose
38 F_D_XTRACE
, // +x or +o xtrace
47 /* we use this enum in the global config so we can know
48 if a particular option has been initialized */
49 typedef enum {Ctrue
= 1, Cfalse
= 0, Cunset
= -1} Cbool
;
72 /* not pretty, but i think it's the less ugly way to do this.
73 we could require Cunset to be 0, but then Cfalse wouldnt be
75 static Config gConfig
= {Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,
76 Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,
77 Cunset
,Cunset
,Cunset
,Cunset
,Cunset
};
81 struct FlagList_
*next
;
84 typedef struct FlagList_ FlagList
;
86 const char *progname
= "sh";
88 static bool contains_flag(Flag f
, FlagList
*l
);
89 static FlagList
*cons_flag(Flag f
, FlagList
*l
);
90 static FlagList
*parse_argv();
91 static void dump_config(void);
92 static void init_config(FlagList
*l
, bool operands
);
93 static void usage(void);
95 bool contains_flag(Flag f
, FlagList
*l
)
106 /* used to build flag list */
107 FlagList
*cons_flag(Flag f
, FlagList
*l
)
109 FlagList
*node
= malloc(sizeof(FlagList
));
117 /* mostly for debugging at this point */
118 void print_flaglist(FlagList
*l
)
123 printf("allexport\n");
129 printf("ignoreeof\n");
135 printf("noclobber\n");
141 printf("remember\n");
165 printf("d_allexport\n");
168 printf("d_errexit\n");
171 printf("d_ignoreeof\n");
174 printf("d_monitor\n");
177 printf("d_noclobber\n");
180 printf("d_noglob\n");
183 printf("d_remember\n");
186 printf("d_noexec\n");
192 printf("d_notify\n");
195 printf("d_nounset\n");
198 printf("d_verbose\n");
204 printf("d_xtrace\n");
210 printf("cmdstdin\n");
213 printf("interactive\n");
220 /* this function modifies the argc and argv so that when
221 * it returns, argv points to the 1st non-option and
222 * argc is the _actual_ count. We also assume that the
223 * caller decremented the argc and pointed argv to its
224 * second field before calling */
225 FlagList
*parse_argv(int *argc
, char *argv
[])
231 FlagList
*list
= NULL
;
238 /* if not a flag, stop parsing flags */
239 if(str
[0] != '-' && str
[0] != '+')
242 /* if its a single '-', discard and stop parsing */
243 if(str
[0] == '-' && len
== 1) {
249 /* if we get to this point, we can start analyzing
250 the string. First, we want to know if we enable
252 enable
= str
[0] == '-';
254 /* the -o <option> case is a little more complicated,
255 so let's treat it first */
257 /* we are unforgiving on bad input format */
259 fprintf(stderr
,"ERROR: unrecognized flag: %s\n", str
);
264 /* we need an option string */
266 fprintf(stderr
,"ERROR: option string needed for %s\n", str
);
270 /* now we are sure we have one, let's jump to it */
275 /* let's try to identify it. hold tight, it's gonna be ugly */
276 if(strcmp(str
, "allexport") == 0) {
277 flag
= enable
? F_ALLEXPORT
: F_D_ALLEXPORT
;
278 } else if(strcmp(str
, "errexit") == 0) {
279 flag
= enable
? F_ERREXIT
: F_D_ERREXIT
;
280 } else if(strcmp(str
, "ignoreeof") == 0) {
281 flag
= enable
? F_IGNOREEOF
: F_D_IGNOREEOF
;
282 } else if(strcmp(str
, "monitor") == 0) {
283 flag
= enable
? F_MONITOR
: F_D_MONITOR
;
284 } else if(strcmp(str
, "noclobber") == 0) {
285 flag
= enable
? F_NOCLOBBER
: F_D_NOCLOBBER
;
286 } else if(strcmp(str
, "noglob") == 0) {
287 flag
= enable
? F_NOGLOB
: F_D_NOGLOB
;
288 } else if(strcmp(str
, "noexec") == 0) {
289 flag
= enable
? F_NOEXEC
: F_D_NOEXEC
;
290 } else if(strcmp(str
, "nolog") == 0) {
291 flag
= enable
? F_NOLOG
: F_D_NOLOG
;
292 } else if(strcmp(str
, "notify") == 0) {
293 flag
= enable
? F_NOTIFY
: F_D_NOTIFY
;
294 } else if(strcmp(str
, "nounset") == 0) {
295 flag
= enable
? F_NOUNSET
: F_D_NOUNSET
;
296 } else if(strcmp(str
, "verbose") == 0) {
297 flag
= enable
? F_VERBOSE
: F_D_VERBOSE
;
298 } else if(strcmp(str
, "vi") == 0) {
299 flag
= enable
? F_VI
: F_D_VI
;
300 } else if(strcmp(str
, "xtrace") == 0) {
301 flag
= enable
? F_XTRACE
: F_D_XTRACE
;
303 /* ohoh, bad input! */
304 fprintf(stderr
, "ERROR: invalid option string: %s\n", str
);
308 /* well, that wasn't fun. lets add the flag to list of known flag
310 list
= cons_flag(flag
,list
);
316 /* if we get here, we are looking for single character options.
317 we loop over all characters in the string. */
318 for(j
= 1; j
< len
; j
++)
320 /* try to identify char at position */
323 flag
= enable
? F_ALLEXPORT
: F_D_ALLEXPORT
;
326 flag
= enable
? F_ERREXIT
: F_D_ERREXIT
;
329 flag
= enable
? F_MONITOR
: F_D_MONITOR
;
332 flag
= enable
? F_NOCLOBBER
: F_D_NOCLOBBER
;
335 flag
= enable
? F_NOGLOB
: F_D_NOGLOB
;
338 flag
= enable
? F_REMEMBER
: F_D_REMEMBER
;
341 flag
= enable
? F_NOEXEC
: F_D_NOEXEC
;
344 flag
= enable
? F_NOTIFY
: F_D_NOTIFY
;
347 flag
= enable
? F_NOUNSET
: F_D_NOUNSET
;
350 flag
= enable
? F_VERBOSE
: F_D_VERBOSE
;
353 flag
= enable
? F_XTRACE
: F_D_XTRACE
;
362 flag
= F_INTERACTIVE
;
365 /* oops, bad input! */
366 fprintf(stderr
,"ERROR: unrecognized flag: %c\n", str
[j
]);
371 /* add it to the list of known flags */
372 list
= cons_flag(flag
,list
);
375 /* we successfuly parsed the whole string! lets
376 decrement the argc, point the argv to the next
377 string and try again. */
382 /* at last, we are done! */
387 void dump_config(void)
389 if(gConfig
.allexport
== Cunset
)
390 printf("allexport : unset\n");
391 else if(gConfig
.allexport
)
392 printf("allexport : true\n");
394 printf("allexport : false\n");
396 if(gConfig
.errexit
== Cunset
)
397 printf("errexit : unset\n");
398 else if(gConfig
.errexit
)
399 printf("errexit : true\n");
401 printf("errexit : false\n");
403 if(gConfig
.ignoreeof
== Cunset
)
404 printf("ignoreeof : unset\n");
405 else if(gConfig
.ignoreeof
)
406 printf("ignoreeof : true\n");
408 printf("ignoreeof : false\n");
410 if(gConfig
.monitor
== Cunset
)
411 printf("monitor : unset\n");
412 else if(gConfig
.monitor
)
413 printf("monitor : true\n");
415 printf("monitor : false\n");
417 if(gConfig
.noclobber
== Cunset
)
418 printf("noclobber : unset\n");
419 else if(gConfig
.noclobber
)
420 printf("noclobber : true\n");
422 printf("noclobber : false\n");
424 if(gConfig
.noglob
== Cunset
)
425 printf("noglob : unset\n");
426 else if(gConfig
.noglob
)
427 printf("noglob : true\n");
429 printf("noglob : false\n");
431 if(gConfig
.remember
== Cunset
)
432 printf("remember : unset\n");
433 else if(gConfig
.remember
)
434 printf("remember : true\n");
436 printf("remember : false\n");
438 if(gConfig
.noexec
== Cunset
)
439 printf("noexec : unset\n");
440 else if(gConfig
.noexec
)
441 printf("noexec : true\n");
443 printf("noexec : false\n");
445 if(gConfig
.nolog
== Cunset
)
446 printf("nolog : unset\n");
447 else if(gConfig
.nolog
)
448 printf("nolog : true\n");
450 printf("nolog : false\n");
452 if(gConfig
.notify
== Cunset
)
453 printf("notify : unset\n");
454 else if(gConfig
.notify
)
455 printf("notify : true\n");
457 printf("notify : false\n");
459 if(gConfig
.nounset
== Cunset
)
460 printf("nounset : unset\n");
461 else if(gConfig
.nounset
)
462 printf("nounset : true\n");
464 printf("nounset : false\n");
466 if(gConfig
.verbose
== Cunset
)
467 printf("verbose : unset\n");
468 else if(gConfig
.verbose
)
469 printf("verbose : true\n");
471 printf("verbose : false\n");
473 if(gConfig
.vi
== Cunset
)
474 printf("vi : unset\n");
476 printf("vi : true\n");
478 printf("vi : false\n");
480 if(gConfig
.xtrace
== Cunset
)
481 printf("xtrace : unset\n");
482 else if(gConfig
.xtrace
)
483 printf("xtrace : true\n");
485 printf("xtrace : false\n");
487 if(gConfig
.cmdstr
== Cunset
)
488 printf("cmdstr : unset\n");
489 else if(gConfig
.cmdstr
)
490 printf("cmdstr : true\n");
492 printf("cmdstr : false\n");
494 if(gConfig
.cmdstdin
== Cunset
)
495 printf("cmdstdin : unset\n");
496 else if(gConfig
.cmdstdin
)
497 printf("cmdstdin : true\n");
499 printf("cmdstdin : false\n");
501 if(gConfig
.interactive
== Cunset
)
502 printf("interactive : unset\n");
503 else if(gConfig
.interactive
)
504 printf("interactive : true\n");
506 printf("interactive : false\n");
509 void init_config(FlagList
*l
, bool operands
)
515 /* by convention, 'disable' flags have precedence over
516 'enable flags, so we only set an opt to Ctrue if
517 it is _not_ Cfalse. We can make a simple 'if' test
518 because Cunset is true as far as bool is concered */
521 if(gConfig
.allexport
) gConfig
.allexport
= Ctrue
;
524 if(gConfig
.errexit
) gConfig
.errexit
= Ctrue
;
527 if(gConfig
.ignoreeof
) gConfig
.ignoreeof
= Ctrue
;
530 if(gConfig
.monitor
) gConfig
.monitor
= Ctrue
;
533 if(gConfig
.noclobber
) gConfig
.noclobber
= Ctrue
;
536 if(gConfig
.noglob
) gConfig
.noglob
= Ctrue
;
539 if(gConfig
.remember
) gConfig
.remember
= Ctrue
;
542 if(gConfig
.noexec
) gConfig
.noexec
= Ctrue
;
545 if(gConfig
.nolog
) gConfig
.nolog
= Ctrue
;
548 if(gConfig
.notify
) gConfig
.notify
= Ctrue
;
551 if(gConfig
.nounset
) gConfig
.nounset
= Ctrue
;
554 if(gConfig
.verbose
) gConfig
.verbose
= Ctrue
;
557 if(gConfig
.vi
) gConfig
.vi
= Ctrue
;
560 if(gConfig
.xtrace
) gConfig
.xtrace
= Ctrue
;
562 /* as said before, 'disable' opts have precendence */
564 gConfig
.allexport
= Cfalse
;
567 gConfig
.errexit
= Cfalse
;
570 gConfig
.ignoreeof
= Cfalse
;
573 gConfig
.monitor
= Cfalse
;
576 gConfig
.noclobber
= Cfalse
;
579 gConfig
.noglob
= Cfalse
;
582 gConfig
.remember
= Cfalse
;
585 gConfig
.noexec
= Cfalse
;
588 gConfig
.nolog
= Cfalse
;
591 gConfig
.notify
= Cfalse
;
594 gConfig
.nounset
= Cfalse
;
597 gConfig
.verbose
= Cfalse
;
603 gConfig
.xtrace
= Cfalse
;
605 /* these options have no 'disable' form */
607 gConfig
.cmdstr
= Ctrue
;
610 gConfig
.cmdstdin
= Ctrue
;
613 gConfig
.interactive
= Ctrue
;
616 /* we continue to the next flag */
620 /* lets make some adjustment. */
622 /* -c has precedence over -s */
624 gConfig
.cmdstdin
= Cfalse
;
626 /* if -c not specified and there are no operands, -s is assumed */
627 if(!operands
&& !gConfig
.cmdstr
)
628 gConfig
.cmdstdin
= Ctrue
;
630 /* if there are no operands and stdin and stderr are connected to
631 a terminal, -i is assumed */
632 if(!operands
&& isatty(fileno(stdin
)) && isatty(fileno(stderr
)))
633 gConfig
.interactive
= Ctrue
;
635 /* if -c, fail if there are no operands */
636 if(gConfig
.cmdstr
== Ctrue
&& !operands
) {
637 fprintf(stderr
, "ERROR: -c: missing command string\n");
641 /* if -i, -m is enabled by default */
642 if(gConfig
.interactive
&& gConfig
.monitor
)
643 gConfig
.monitor
= Ctrue
;
645 /* at this point, we should be good to go. We only need to
646 set anything that is still Cunset to Cfalse */
647 if(gConfig
.allexport
== Cunset
) gConfig
.allexport
= Cfalse
;
648 if(gConfig
.errexit
== Cunset
) gConfig
.errexit
= Cfalse
;
649 if(gConfig
.ignoreeof
== Cunset
) gConfig
.ignoreeof
= Cfalse
;
650 if(gConfig
.monitor
== Cunset
) gConfig
.monitor
= Cfalse
;
651 if(gConfig
.noclobber
== Cunset
) gConfig
.noclobber
= Cfalse
;
652 if(gConfig
.noglob
== Cunset
) gConfig
.noglob
= Cfalse
;
653 if(gConfig
.remember
== Cunset
) gConfig
.remember
= Cfalse
;
654 if(gConfig
.noexec
== Cunset
) gConfig
.noexec
= Cfalse
;
655 if(gConfig
.nolog
== Cunset
) gConfig
.nolog
= Cfalse
;
656 if(gConfig
.notify
== Cunset
) gConfig
.notify
= Cfalse
;
657 if(gConfig
.nounset
== Cunset
) gConfig
.nounset
= Cfalse
;
658 if(gConfig
.verbose
== Cunset
) gConfig
.verbose
= Cfalse
;
659 if(gConfig
.vi
== Cunset
) gConfig
.vi
= Cfalse
;
660 if(gConfig
.xtrace
== Cunset
) gConfig
.xtrace
= Cfalse
;
661 if(gConfig
.cmdstr
== Cunset
) gConfig
.cmdstr
= Cfalse
;
662 if(gConfig
.cmdstdin
== Cunset
) gConfig
.cmdstdin
= Cfalse
;
663 if(gConfig
.interactive
== Cunset
) gConfig
.interactive
= Cfalse
;
668 /* print usage text */
671 printf("usage: %s [-/+ abCefhimnuvz][-/+ o <option>] [<non-options>]\n"
672 "where <non-options> can be:\n"
673 " <command file> [<argument>...]\n"
674 " -c <command string> [<command name> [argument...]]\n"
675 " -s [argument]\n", progname
);
678 int main(int argc
, char *argv
[])
680 /* Welcome to main! */
683 /* our first task is parsing the argv.
684 the function parse_argv() requires us
685 to decrement argc and point argv to
686 its second field, so lets do that */
689 flags
= parse_argv(&argc
, argv
);
690 print_flaglist(flags
);
693 /* now that we have a flag list, we can
694 try to get our initial config */
695 init_config(flags
, argc
> 0);