main: add init_config()
[glg-sh.git] / main.c
blobe256cb42ff738dfd7b294fb45051bc0dd893a6e2
1 #define _POSIX_SOURCE
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
9 typedef enum {
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
16 F_REMEMBER, // -h
17 F_NOEXEC, // -n or -o noexec
18 F_NOLOG, // -o nolog
19 F_NOTIFY, // -b or -o notify
20 F_NOUNSET, // -u or -o nounset
21 F_VERBOSE, // -v or -o verbose
22 F_VI, // -o vi
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
31 F_D_REMEMBER, // +h
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
37 F_D_VI, // +o vi
38 F_D_XTRACE, // +x or +o xtrace
40 F_CMDSTR, // -c
41 F_CMDSTDIN, // -s
43 F_INTERACTIVE // -i
45 } Flag;
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;
51 typedef struct {
52 Cbool allexport;
53 Cbool errexit;
54 Cbool ignoreeof;
55 Cbool monitor;
56 Cbool noclobber;
57 Cbool noglob;
58 Cbool remember;
59 Cbool noexec;
60 Cbool nolog;
61 Cbool notify;
62 Cbool nounset;
63 Cbool verbose;
64 Cbool vi;
65 Cbool xtrace;
66 Cbool cmdstr;
67 Cbool cmdstdin;
68 Cbool interactive;
69 } Config;
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
74 false anymore... */
75 static Config gConfig = {Cunset,Cunset,Cunset,Cunset,Cunset,Cunset,
76 Cunset,Cunset,Cunset,Cunset,Cunset,Cunset,
77 Cunset,Cunset,Cunset,Cunset,Cunset};
79 struct FlagList_ {
80 Flag flag;
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)
97 FlagList *p = l;
98 while(p != NULL) {
99 if(p->flag == f)
100 return true;
101 p = p->next;
103 return false;
106 /* used to build flag list */
107 FlagList *cons_flag(Flag f, FlagList *l)
109 FlagList *node = malloc(sizeof(FlagList));
110 if(node != NULL) {
111 node->flag = f;
112 node->next = l;
114 return node;
117 /* mostly for debugging at this point */
118 void print_flaglist(FlagList *l)
120 while(l != NULL) {
121 switch(l->flag) {
122 case F_ALLEXPORT:
123 printf("allexport\n");
124 break;
125 case F_ERREXIT:
126 printf("errexit\n");
127 break;
128 case F_IGNOREEOF:
129 printf("ignoreeof\n");
130 break;
131 case F_MONITOR:
132 printf("monitor\n");
133 break;
134 case F_NOCLOBBER:
135 printf("noclobber\n");
136 break;
137 case F_NOGLOB:
138 printf("noglob\n");
139 break;
140 case F_REMEMBER:
141 printf("remember\n");
142 break;
143 case F_NOEXEC:
144 printf("noexec\n");
145 break;
146 case F_NOLOG:
147 printf("nolog\n");
148 break;
149 case F_NOTIFY:
150 printf("notify\n");
151 break;
152 case F_NOUNSET:
153 printf("nounset\n");
154 break;
155 case F_VERBOSE:
156 printf("verbose\n");
157 break;
158 case F_VI:
159 printf("vi\n");
160 break;
161 case F_XTRACE:
162 printf("xtrace\n");
163 break;
164 case F_D_ALLEXPORT:
165 printf("d_allexport\n");
166 break;
167 case F_D_ERREXIT:
168 printf("d_errexit\n");
169 break;
170 case F_D_IGNOREEOF:
171 printf("d_ignoreeof\n");
172 break;
173 case F_D_MONITOR:
174 printf("d_monitor\n");
175 break;
176 case F_D_NOCLOBBER:
177 printf("d_noclobber\n");
178 break;
179 case F_D_NOGLOB:
180 printf("d_noglob\n");
181 break;
182 case F_D_REMEMBER:
183 printf("d_remember\n");
184 break;
185 case F_D_NOEXEC:
186 printf("d_noexec\n");
187 break;
188 case F_D_NOLOG:
189 printf("d_nolog\n");
190 break;
191 case F_D_NOTIFY:
192 printf("d_notify\n");
193 break;
194 case F_D_NOUNSET:
195 printf("d_nounset\n");
196 break;
197 case F_D_VERBOSE:
198 printf("d_verbose\n");
199 break;
200 case F_D_VI:
201 printf("d_vi\n");
202 break;
203 case F_D_XTRACE:
204 printf("d_xtrace\n");
205 break;
206 case F_CMDSTR:
207 printf("cmdstr\n");
208 break;
209 case F_CMDSTDIN:
210 printf("cmdstdin\n");
211 break;
212 case F_INTERACTIVE:
213 printf("interactive\n");
214 break;
216 l = l->next;
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[])
227 int j,len;
228 char *str;
229 bool enable;
230 Flag flag;
231 FlagList *list = NULL;
233 while(*argc != 0)
235 str = argv[0];
236 len = strlen(str);
238 /* if not a flag, stop parsing flags */
239 if(str[0] != '-' && str[0] != '+')
240 break;
242 /* if its a single '-', discard and stop parsing */
243 if(str[0] == '-' && len == 1) {
244 argv += 1;
245 (*argc)--;
246 break;
249 /* if we get to this point, we can start analyzing
250 the string. First, we want to know if we enable
251 or disable: */
252 enable = str[0] == '-';
254 /* the -o <option> case is a little more complicated,
255 so let's treat it first */
256 if(str[1] == 'o') {
257 /* we are unforgiving on bad input format */
258 if(len != 2) {
259 fprintf(stderr,"ERROR: unrecognized flag: %s\n", str);
260 usage();
261 exit(1);
264 /* we need an option string */
265 if(*argc == 1) {
266 fprintf(stderr,"ERROR: option string needed for %s\n", str);
267 exit(1);
270 /* now we are sure we have one, let's jump to it */
271 (*argc)--;
272 argv += 1;
273 str = argv[0];
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;
302 } else {
303 /* ohoh, bad input! */
304 fprintf(stderr, "ERROR: invalid option string: %s\n", str);
305 exit(1);
308 /* well, that wasn't fun. lets add the flag to list of known flag
309 and move on */
310 list = cons_flag(flag,list);
311 (*argc)--;
312 argv += 1;
313 continue;
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 */
321 switch(str[j]) {
322 case 'a':
323 flag = enable ? F_ALLEXPORT : F_D_ALLEXPORT;
324 break;
325 case 'e':
326 flag = enable ? F_ERREXIT : F_D_ERREXIT;
327 break;
328 case 'm':
329 flag = enable ? F_MONITOR : F_D_MONITOR;
330 break;
331 case 'C':
332 flag = enable ? F_NOCLOBBER : F_D_NOCLOBBER;
333 break;
334 case 'f':
335 flag = enable ? F_NOGLOB : F_D_NOGLOB;
336 break;
337 case 'h':
338 flag = enable ? F_REMEMBER : F_D_REMEMBER;
339 break;
340 case 'n':
341 flag = enable ? F_NOEXEC : F_D_NOEXEC;
342 break;
343 case 'b':
344 flag = enable ? F_NOTIFY : F_D_NOTIFY;
345 break;
346 case 'u':
347 flag = enable ? F_NOUNSET : F_D_NOUNSET;
348 break;
349 case 'v':
350 flag = enable ? F_VERBOSE : F_D_VERBOSE;
351 break;
352 case 'x':
353 flag = enable ? F_XTRACE : F_D_XTRACE;
354 break;
355 case 'c':
356 flag = F_CMDSTR;
357 break;
358 case 's':
359 flag = F_CMDSTDIN;
360 break;
361 case 'i':
362 flag = F_INTERACTIVE;
363 break;
364 default:
365 /* oops, bad input! */
366 fprintf(stderr,"ERROR: unrecognized flag: %c\n", str[j]);
367 usage();
368 exit(1);
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. */
378 (*argc)--;
379 argv += 1;
382 /* at last, we are done! */
383 return list;
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");
393 else
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");
400 else
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");
407 else
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");
414 else
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");
421 else
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");
428 else
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");
435 else
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");
442 else
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");
449 else
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");
456 else
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");
463 else
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");
470 else
471 printf("verbose : false\n");
473 if(gConfig.vi == Cunset)
474 printf("vi : unset\n");
475 else if(gConfig.vi)
476 printf("vi : true\n");
477 else
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");
484 else
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");
491 else
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");
498 else
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");
505 else
506 printf("interactive : false\n");
509 void init_config(FlagList *l, bool operands)
511 FlagList *p = l;
513 while(p != NULL)
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 */
519 switch(p->flag) {
520 case F_ALLEXPORT:
521 if(gConfig.allexport) gConfig.allexport = Ctrue;
522 break;
523 case F_ERREXIT:
524 if(gConfig.errexit) gConfig.errexit = Ctrue;
525 break;
526 case F_IGNOREEOF:
527 if(gConfig.ignoreeof) gConfig.ignoreeof = Ctrue;
528 break;
529 case F_MONITOR:
530 if(gConfig.monitor) gConfig.monitor = Ctrue;
531 break;
532 case F_NOCLOBBER:
533 if(gConfig.noclobber) gConfig.noclobber = Ctrue;
534 break;
535 case F_NOGLOB:
536 if(gConfig.noglob) gConfig.noglob = Ctrue;
537 break;
538 case F_REMEMBER:
539 if(gConfig.remember) gConfig.remember = Ctrue;
540 break;
541 case F_NOEXEC:
542 if(gConfig.noexec) gConfig.noexec = Ctrue;
543 break;
544 case F_NOLOG:
545 if(gConfig.nolog) gConfig.nolog = Ctrue;
546 break;
547 case F_NOTIFY:
548 if(gConfig.notify) gConfig.notify = Ctrue;
549 break;
550 case F_NOUNSET:
551 if(gConfig.nounset) gConfig.nounset = Ctrue;
552 break;
553 case F_VERBOSE:
554 if(gConfig.verbose) gConfig.verbose = Ctrue;
555 break;
556 case F_VI:
557 if(gConfig.vi) gConfig.vi = Ctrue;
558 break;
559 case F_XTRACE:
560 if(gConfig.xtrace) gConfig.xtrace = Ctrue;
561 break;
562 /* as said before, 'disable' opts have precendence */
563 case F_D_ALLEXPORT:
564 gConfig.allexport = Cfalse;
565 break;
566 case F_D_ERREXIT:
567 gConfig.errexit = Cfalse;
568 break;
569 case F_D_IGNOREEOF:
570 gConfig.ignoreeof = Cfalse;
571 break;
572 case F_D_MONITOR:
573 gConfig.monitor = Cfalse;
574 break;
575 case F_D_NOCLOBBER:
576 gConfig.noclobber = Cfalse;
577 break;
578 case F_D_NOGLOB:
579 gConfig.noglob = Cfalse;
580 break;
581 case F_D_REMEMBER:
582 gConfig.remember = Cfalse;
583 break;
584 case F_D_NOEXEC:
585 gConfig.noexec = Cfalse;
586 break;
587 case F_D_NOLOG:
588 gConfig.nolog = Cfalse;
589 break;
590 case F_D_NOTIFY:
591 gConfig.notify = Cfalse;
592 break;
593 case F_D_NOUNSET:
594 gConfig.nounset = Cfalse;
595 break;
596 case F_D_VERBOSE:
597 gConfig.verbose = Cfalse;
598 break;
599 case F_D_VI:
600 gConfig.vi = Cfalse;
601 break;
602 case F_D_XTRACE:
603 gConfig.xtrace = Cfalse;
604 break;
605 /* these options have no 'disable' form */
606 case F_CMDSTR:
607 gConfig.cmdstr = Ctrue;
608 break;
609 case F_CMDSTDIN:
610 gConfig.cmdstdin = Ctrue;
611 break;
612 case F_INTERACTIVE:
613 gConfig.interactive = Ctrue;
614 break;
616 /* we continue to the next flag */
617 p = p->next;
620 /* lets make some adjustment. */
622 /* -c has precedence over -s */
623 if(gConfig.cmdstr)
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");
638 exit(1);
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;
665 /* all done :) */
668 /* print usage text */
669 void usage(void)
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! */
681 FlagList *flags;
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 */
687 argc--;
688 argv += 1;
689 flags = parse_argv(&argc, argv);
690 print_flaglist(flags);
691 puts("");
693 /* now that we have a flag list, we can
694 try to get our initial config */
695 init_config(flags, argc > 0);
696 dump_config();
698 return 0;