main: parse argv and init config in 1 pass
[glg-sh.git] / main.c
blob834b186baf647182efb6c82d27cfc492c7afe230
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 #ifdef DEBUG
10 #define DEBUG_CMD(cmd) {puts("+ DEBUG"); cmd; puts("- DEBUG");}
11 #else
12 #define DEBUG_CMD(cmd) {}
13 #endif
15 const char *progname = "sh";
17 static char *callname;
19 /* we use this enum in the global config so we can know
20 if a particular option has been initialized */
21 typedef enum {Ctrue = 1, Cfalse = 0, Cunset = -1} Cbool;
23 typedef struct {
24 Cbool allexport; // -a or -o allexport
25 Cbool errexit; // -e or -o errexit
26 Cbool ignoreeof; // -o ignoreeof
27 Cbool monitor; // -m or -o monitor
28 Cbool noclobber; // -C or -o noclobber
29 Cbool noglob; // -f or -o noglob
30 Cbool remember; // -h
31 Cbool noexec; // -n or -o noexec
32 Cbool nolog; // -o nolog
33 Cbool notify; // -b or -o notify
34 Cbool nounset; // -u or -o nounset
35 Cbool verbose; // -v or -o verbose
36 Cbool vi; // -o vi
37 Cbool xtrace; // -x or -o xtrace
38 Cbool cmdstr; // -c
39 Cbool cmdstdin; // -s;
40 Cbool interactive; // -i
41 } Config;
43 /* not pretty, but i think it's the less ugly way to do this.
44 we could require Cunset to be 0, but then Cfalse wouldnt be
45 false anymore... */
46 static Config gConfig = {Cunset,Cunset,Cunset,Cunset,Cunset,Cunset,
47 Cunset,Cunset,Cunset,Cunset,Cunset,Cunset,
48 Cunset,Cunset,Cunset,Cunset,Cunset};
50 static void parse_argv(int *argc, char *argv[]);
51 static void dump_config(void);
52 static void usage(void);
54 /* parse the argv, init global config
55 * *** this function modifies the argc and argv so that when
56 * it returns, argv points to the 1st non-option and
57 * argc is the _actual_ count. We also assume that the
58 * caller decremented the argc and pointed argv to its
59 * second field before calling */
60 void parse_argv(int *argc, char *argv[])
62 int j,len;
63 char *str;
64 Cbool enable;
65 bool operands;
67 while(*argc != 0)
69 str = argv[0];
70 len = strlen(str);
72 /* if not a flag, stop parsing flags */
73 if(str[0] != '-' && str[0] != '+')
74 break;
76 /* if its a single '-', discard and stop parsing */
77 if(str[0] == '-' && len == 1) {
78 argv++;
79 (*argc)--;
80 break;
83 /* if we get to this point, we can start analyzing
84 the string. First, we want to know if we enable
85 or disable. Also, keep in mind that disable opts
86 have precedence over enable opts */
87 enable = (Cbool) str[0] == '-';
89 /* the -o <option> case is a little more complicated,
90 so let's treat it first */
91 if(str[1] == 'o') {
92 /* we are unforgiving on bad input format */
93 if(len != 2) {
94 fprintf(stderr,"ERROR: unrecognized flag: %s\n", str);
95 usage();
96 exit(1);
99 /* we need an option string */
100 if(*argc == 1) {
101 fprintf(stderr,"ERROR: option string needed for %s\n", str);
102 exit(1);
105 /* now we are sure we have one, let's jump to it */
106 (*argc)--;
107 str = *(++argv);
109 /* let's try to identify it. */
110 if(strcmp(str, "allexport") == 0 && gConfig.allexport) {
111 gConfig.allexport = enable;
112 } else if(strcmp(str, "errexit") == 0 && gConfig.errexit) {
113 gConfig.errexit = enable;
114 } else if(strcmp(str, "ignoreeof") == 0 && gConfig.ignoreeof) {
115 gConfig.ignoreeof = enable;
116 } else if(strcmp(str, "monitor") == 0 && gConfig.monitor) {
117 gConfig.monitor = enable;
118 } else if(strcmp(str, "noclobber") == 0 && gConfig.noclobber) {
119 gConfig.noclobber = enable;
120 } else if(strcmp(str, "noglob") == 0 && gConfig.noglob) {
121 gConfig.noglob = enable;
122 } else if(strcmp(str, "noexec") == 0 && gConfig.noexec) {
123 gConfig.noexec = enable;
124 } else if(strcmp(str, "nolog") == 0 && gConfig.nolog) {
125 gConfig.nolog = enable;
126 } else if(strcmp(str, "notify") == 0 && gConfig.notify) {
127 gConfig.notify = enable;
128 } else if(strcmp(str, "nounset") == 0 && gConfig.nounset) {
129 gConfig.nounset = enable;
130 } else if(strcmp(str, "verbose") == 0 && gConfig.verbose) {
131 gConfig.verbose = enable;
132 } else if(strcmp(str, "vi") == 0 && gConfig.vi) {
133 gConfig.vi = enable;
134 } else if(strcmp(str, "xtrace") == 0 && gConfig.xtrace) {
135 gConfig.xtrace = enable;
136 } else {
137 /* ohoh, bad input! */
138 fprintf(stderr, "ERROR: invalid option string: %s\n", str);
139 exit(1);
141 /* success! continue with next string */
142 (*argc)--;
143 argv++;
144 continue;
147 /* if we get here, we are looking for single character options.
148 we loop over all characters in the string. */
149 for(j = 1; j < len; j++)
151 /* try to identify char at position */
152 switch(str[j]) {
153 case 'a':
154 if(gConfig.allexport) gConfig.allexport = enable;
155 break;
156 case 'e':
157 if(gConfig.errexit) gConfig.errexit = enable;
158 break;
159 case 'm':
160 if(gConfig.monitor) gConfig.monitor = enable;
161 break;
162 case 'C':
163 if(gConfig.noclobber) gConfig.noclobber = enable;
164 break;
165 case 'f':
166 if(gConfig.noglob) gConfig.noglob = enable;
167 break;
168 case 'h':
169 if(gConfig.remember) gConfig.remember = enable;
170 break;
171 case 'n':
172 if(gConfig.noexec) gConfig.noexec = enable;
173 break;
174 case 'b':
175 if(gConfig.notify) gConfig.notify = enable;
176 break;
177 case 'u':
178 if(gConfig.nounset) gConfig.nounset = enable;
179 break;
180 case 'v':
181 if(gConfig.verbose) gConfig.verbose = enable;
182 break;
183 case 'x':
184 if(gConfig.xtrace) gConfig.xtrace = enable;
185 break;
186 case 'c':
187 gConfig.cmdstr = Ctrue;
188 break;
189 case 's':
190 gConfig.cmdstdin = Ctrue;
191 break;
192 case 'i':
193 gConfig.interactive = Ctrue;
194 break;
195 default:
196 /* oops, bad input! */
197 fprintf(stderr,"ERROR: unrecognized flag: %c\n", str[j]);
198 usage();
199 exit(1);
203 /* we successfuly parsed the whole string! lets
204 decrement the argc, point the argv to the next
205 string and try again. */
206 (*argc)--;
207 argv++;
210 /* lets make some adjustment. */
212 operands = *argc > 0;
214 /* -c has precedence over -s */ // FIXME not sure if valid
215 if(gConfig.cmdstr == Ctrue)
216 gConfig.cmdstdin = Cfalse;
218 /* if -c not specified and there are no operands, -s is assumed */
219 if(!operands && gConfig.cmdstr != Ctrue)
220 gConfig.cmdstdin = Ctrue;
222 /* if there are no operands and stdin and stderr are connected to
223 a terminal, -i is assumed */
224 if(!operands && isatty(fileno(stdin)) && isatty(fileno(stderr)))
225 gConfig.interactive = Ctrue;
227 /* if -c, fail if there are no operands */
228 if(gConfig.cmdstr == Ctrue && !operands) {
229 fprintf(stderr, "ERROR: -c: missing command string\n");
230 exit(1);
233 /* if -i, -m is enabled by default */
234 if(gConfig.interactive == Ctrue && gConfig.monitor)
235 gConfig.monitor = Ctrue;
237 /* at this point, we should be good to go. We only need to
238 set anything that is still Cunset to Cfalse */
239 if(gConfig.allexport == Cunset) gConfig.allexport = Cfalse;
240 if(gConfig.errexit == Cunset) gConfig.errexit = Cfalse;
241 if(gConfig.ignoreeof == Cunset) gConfig.ignoreeof = Cfalse;
242 if(gConfig.monitor == Cunset) gConfig.monitor = Cfalse;
243 if(gConfig.noclobber == Cunset) gConfig.noclobber = Cfalse;
244 if(gConfig.noglob == Cunset) gConfig.noglob = Cfalse;
245 if(gConfig.remember == Cunset) gConfig.remember = Cfalse;
246 if(gConfig.noexec == Cunset) gConfig.noexec = Cfalse;
247 if(gConfig.nolog == Cunset) gConfig.nolog = Cfalse;
248 if(gConfig.notify == Cunset) gConfig.notify = Cfalse;
249 if(gConfig.nounset == Cunset) gConfig.nounset = Cfalse;
250 if(gConfig.verbose == Cunset) gConfig.verbose = Cfalse;
251 if(gConfig.vi == Cunset) gConfig.vi = Cfalse;
252 if(gConfig.xtrace == Cunset) gConfig.xtrace = Cfalse;
253 if(gConfig.cmdstr == Cunset) gConfig.cmdstr = Cfalse;
254 if(gConfig.cmdstdin == Cunset) gConfig.cmdstdin = Cfalse;
255 if(gConfig.interactive == Cunset) gConfig.interactive = Cfalse;
257 /* all done :) */
260 void dump_config(void)
262 if(gConfig.allexport == Cunset)
263 printf("allexport : unset\n");
264 else if(gConfig.allexport)
265 printf("allexport : true\n");
266 else
267 printf("allexport : false\n");
269 if(gConfig.errexit == Cunset)
270 printf("errexit : unset\n");
271 else if(gConfig.errexit)
272 printf("errexit : true\n");
273 else
274 printf("errexit : false\n");
276 if(gConfig.ignoreeof == Cunset)
277 printf("ignoreeof : unset\n");
278 else if(gConfig.ignoreeof)
279 printf("ignoreeof : true\n");
280 else
281 printf("ignoreeof : false\n");
283 if(gConfig.monitor == Cunset)
284 printf("monitor : unset\n");
285 else if(gConfig.monitor)
286 printf("monitor : true\n");
287 else
288 printf("monitor : false\n");
290 if(gConfig.noclobber == Cunset)
291 printf("noclobber : unset\n");
292 else if(gConfig.noclobber)
293 printf("noclobber : true\n");
294 else
295 printf("noclobber : false\n");
297 if(gConfig.noglob == Cunset)
298 printf("noglob : unset\n");
299 else if(gConfig.noglob)
300 printf("noglob : true\n");
301 else
302 printf("noglob : false\n");
304 if(gConfig.remember == Cunset)
305 printf("remember : unset\n");
306 else if(gConfig.remember)
307 printf("remember : true\n");
308 else
309 printf("remember : false\n");
311 if(gConfig.noexec == Cunset)
312 printf("noexec : unset\n");
313 else if(gConfig.noexec)
314 printf("noexec : true\n");
315 else
316 printf("noexec : false\n");
318 if(gConfig.nolog == Cunset)
319 printf("nolog : unset\n");
320 else if(gConfig.nolog)
321 printf("nolog : true\n");
322 else
323 printf("nolog : false\n");
325 if(gConfig.notify == Cunset)
326 printf("notify : unset\n");
327 else if(gConfig.notify)
328 printf("notify : true\n");
329 else
330 printf("notify : false\n");
332 if(gConfig.nounset == Cunset)
333 printf("nounset : unset\n");
334 else if(gConfig.nounset)
335 printf("nounset : true\n");
336 else
337 printf("nounset : false\n");
339 if(gConfig.verbose == Cunset)
340 printf("verbose : unset\n");
341 else if(gConfig.verbose)
342 printf("verbose : true\n");
343 else
344 printf("verbose : false\n");
346 if(gConfig.vi == Cunset)
347 printf("vi : unset\n");
348 else if(gConfig.vi)
349 printf("vi : true\n");
350 else
351 printf("vi : false\n");
353 if(gConfig.xtrace == Cunset)
354 printf("xtrace : unset\n");
355 else if(gConfig.xtrace)
356 printf("xtrace : true\n");
357 else
358 printf("xtrace : false\n");
360 if(gConfig.cmdstr == Cunset)
361 printf("cmdstr : unset\n");
362 else if(gConfig.cmdstr)
363 printf("cmdstr : true\n");
364 else
365 printf("cmdstr : false\n");
367 if(gConfig.cmdstdin == Cunset)
368 printf("cmdstdin : unset\n");
369 else if(gConfig.cmdstdin)
370 printf("cmdstdin : true\n");
371 else
372 printf("cmdstdin : false\n");
374 if(gConfig.interactive == Cunset)
375 printf("interactive : unset\n");
376 else if(gConfig.interactive)
377 printf("interactive : true\n");
378 else
379 printf("interactive : false\n");
382 /* print usage text */
383 void usage(void)
385 printf("usage: %s [-/+ abCefhimnuvz][-/+ o <option>] [<non-options>]\n"
386 "where <non-options> can be:\n"
387 " <command file> [<argument>...]\n"
388 " -c <command string> [<command name> [argument...]]\n"
389 " -s [argument]\n", progname);
392 int main(int argc, char *argv[])
394 /* Welcome to main! */
396 /* our first task is parsing the argv.
397 the function parse_argv() requires us
398 to decrement argc and point argv to
399 its second field, so lets do that */
400 callname = argv[0];
401 argc--;
402 argv++;
403 parse_argv(&argc, argv);
405 DEBUG_CMD(dump_config());
407 return 0;