10 #define DEBUG_CMD(cmd) {puts("+ DEBUG"); cmd; puts("- DEBUG");}
12 #define DEBUG_CMD(cmd) {}
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
;
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
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
37 Cbool xtrace
; // -x or -o xtrace
39 Cbool cmdstdin
; // -s;
40 Cbool interactive
; // -i
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
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
[])
72 /* if not a flag, stop parsing flags */
73 if(str
[0] != '-' && str
[0] != '+')
76 /* if its a single '-', discard and stop parsing */
77 if(str
[0] == '-' && len
== 1) {
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 */
92 /* we are unforgiving on bad input format */
94 fprintf(stderr
,"ERROR: unrecognized flag: %s\n", str
);
99 /* we need an option string */
101 fprintf(stderr
,"ERROR: option string needed for %s\n", str
);
105 /* now we are sure we have one, let's jump to it */
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
) {
134 } else if(strcmp(str
, "xtrace") == 0 && gConfig
.xtrace
) {
135 gConfig
.xtrace
= enable
;
137 /* ohoh, bad input! */
138 fprintf(stderr
, "ERROR: invalid option string: %s\n", str
);
141 /* success! continue with next string */
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 */
154 if(gConfig
.allexport
) gConfig
.allexport
= enable
;
157 if(gConfig
.errexit
) gConfig
.errexit
= enable
;
160 if(gConfig
.monitor
) gConfig
.monitor
= enable
;
163 if(gConfig
.noclobber
) gConfig
.noclobber
= enable
;
166 if(gConfig
.noglob
) gConfig
.noglob
= enable
;
169 if(gConfig
.remember
) gConfig
.remember
= enable
;
172 if(gConfig
.noexec
) gConfig
.noexec
= enable
;
175 if(gConfig
.notify
) gConfig
.notify
= enable
;
178 if(gConfig
.nounset
) gConfig
.nounset
= enable
;
181 if(gConfig
.verbose
) gConfig
.verbose
= enable
;
184 if(gConfig
.xtrace
) gConfig
.xtrace
= enable
;
187 gConfig
.cmdstr
= Ctrue
;
190 gConfig
.cmdstdin
= Ctrue
;
193 gConfig
.interactive
= Ctrue
;
196 /* oops, bad input! */
197 fprintf(stderr
,"ERROR: unrecognized flag: %c\n", str
[j
]);
203 /* we successfuly parsed the whole string! lets
204 decrement the argc, point the argv to the next
205 string and try again. */
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");
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
;
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
344 printf("verbose : false\n");
346 if(gConfig
.vi
== Cunset
)
347 printf("vi : unset\n");
349 printf("vi : true\n");
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");
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");
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");
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");
379 printf("interactive : false\n");
382 /* print usage text */
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 */
403 parse_argv(&argc
, argv
);
405 DEBUG_CMD(dump_config());