12 #define DEBUG_CMD(cmd) {puts("+ DEBUG"); cmd; puts("- DEBUG");}
14 #define DEBUG_CMD(cmd) {}
17 const char *progname
= "sh";
19 static char *callname
;
21 /* we use this enum in the global config so we can know
22 if a particular option has been initialized */
23 typedef enum {Ctrue
= 1, Cfalse
= 0, Cunset
= -1} Cbool
;
26 Cbool allexport
; // -a or -o allexport
27 Cbool errexit
; // -e or -o errexit
28 Cbool ignoreeof
; // -o ignoreeof
29 Cbool monitor
; // -m or -o monitor
30 Cbool noclobber
; // -C or -o noclobber
31 Cbool noglob
; // -f or -o noglob
33 Cbool noexec
; // -n or -o noexec
34 Cbool nolog
; // -o nolog
35 Cbool notify
; // -b or -o notify
36 Cbool nounset
; // -u or -o nounset
37 Cbool verbose
; // -v or -o verbose
39 Cbool xtrace
; // -x or -o xtrace
41 Cbool cmdstdin
; // -s;
42 Cbool interactive
; // -i
45 /* not pretty, but i think it's the less ugly way to do this.
46 we could require Cunset to be 0, but then Cfalse wouldnt be
48 static Config gConfig
= {Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,
49 Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,Cunset
,
50 Cunset
,Cunset
,Cunset
,Cunset
,Cunset
};
52 static void parse_argv(int *argc
, char **argv
[]);
53 static void dump_config(void);
54 static void usage(void);
56 /* parse the argv, init global config
57 * *** this function modifies the argc and argv so that when
58 * it returns, argv points to the 1st non-option and
59 * argc is the _actual_ count. We also assume that the
60 * caller decremented the argc and pointed argv to its
61 * second field before calling */
62 void parse_argv(int *argc
, char **argv
[])
74 /* if not a flag, stop parsing flags */
75 if(str
[0] != '-' && str
[0] != '+')
78 /* if its a single '-', discard and stop parsing */
79 if(str
[0] == '-' && len
== 1) {
85 /* if we get to this point, we can start analyzing
86 the string. First, we want to know if we enable
87 or disable. Also, keep in mind that disable opts
88 have precedence over enable opts */
89 enable
= (Cbool
) str
[0] == '-';
91 /* the -o <option> case is a little more complicated,
92 so let's treat it first */
94 /* we are unforgiving on bad input format */
96 fprintf(stderr
,"ERROR: unrecognized flag: %s\n", str
);
101 /* we need an option string */
103 fprintf(stderr
,"ERROR: option string needed for %s\n", str
);
107 /* now we are sure we have one, let's jump to it */
111 /* let's try to identify it. */
112 if(strcmp(str
, "allexport") == 0 && gConfig
.allexport
) {
113 gConfig
.allexport
= enable
;
114 } else if(strcmp(str
, "errexit") == 0 && gConfig
.errexit
) {
115 gConfig
.errexit
= enable
;
116 } else if(strcmp(str
, "ignoreeof") == 0 && gConfig
.ignoreeof
) {
117 gConfig
.ignoreeof
= enable
;
118 } else if(strcmp(str
, "monitor") == 0 && gConfig
.monitor
) {
119 gConfig
.monitor
= enable
;
120 } else if(strcmp(str
, "noclobber") == 0 && gConfig
.noclobber
) {
121 gConfig
.noclobber
= enable
;
122 } else if(strcmp(str
, "noglob") == 0 && gConfig
.noglob
) {
123 gConfig
.noglob
= enable
;
124 } else if(strcmp(str
, "noexec") == 0 && gConfig
.noexec
) {
125 gConfig
.noexec
= enable
;
126 } else if(strcmp(str
, "nolog") == 0 && gConfig
.nolog
) {
127 gConfig
.nolog
= enable
;
128 } else if(strcmp(str
, "notify") == 0 && gConfig
.notify
) {
129 gConfig
.notify
= enable
;
130 } else if(strcmp(str
, "nounset") == 0 && gConfig
.nounset
) {
131 gConfig
.nounset
= enable
;
132 } else if(strcmp(str
, "verbose") == 0 && gConfig
.verbose
) {
133 gConfig
.verbose
= enable
;
134 } else if(strcmp(str
, "vi") == 0 && gConfig
.vi
) {
136 } else if(strcmp(str
, "xtrace") == 0 && gConfig
.xtrace
) {
137 gConfig
.xtrace
= enable
;
139 /* ohoh, bad input! */
140 fprintf(stderr
, "ERROR: invalid option string: %s\n", str
);
143 /* success! continue with next string */
149 /* if we get here, we are looking for single character options.
150 we loop over all characters in the string. */
151 for(j
= 1; j
< len
; j
++)
153 /* try to identify char at position */
156 if(gConfig
.allexport
) gConfig
.allexport
= enable
;
159 if(gConfig
.errexit
) gConfig
.errexit
= enable
;
162 if(gConfig
.monitor
) gConfig
.monitor
= enable
;
165 if(gConfig
.noclobber
) gConfig
.noclobber
= enable
;
168 if(gConfig
.noglob
) gConfig
.noglob
= enable
;
171 if(gConfig
.remember
) gConfig
.remember
= enable
;
174 if(gConfig
.noexec
) gConfig
.noexec
= enable
;
177 if(gConfig
.notify
) gConfig
.notify
= enable
;
180 if(gConfig
.nounset
) gConfig
.nounset
= enable
;
183 if(gConfig
.verbose
) gConfig
.verbose
= enable
;
186 if(gConfig
.xtrace
) gConfig
.xtrace
= enable
;
189 gConfig
.cmdstr
= Ctrue
;
192 gConfig
.cmdstdin
= Ctrue
;
195 gConfig
.interactive
= Ctrue
;
198 /* oops, bad input! */
199 fprintf(stderr
,"ERROR: unrecognized flag: %c\n", str
[j
]);
205 /* we successfuly parsed the whole string! lets
206 decrement the argc, point the argv to the next
207 string and try again. */
212 /* lets make some adjustment. */
214 operands
= *argc
> 0;
216 /* -c has precedence over -s */ // FIXME not sure if valid
217 if(gConfig
.cmdstr
== Ctrue
)
218 gConfig
.cmdstdin
= Cfalse
;
220 /* if -c not specified and there are no operands, -s is assumed */
221 if(!operands
&& gConfig
.cmdstr
!= Ctrue
)
222 gConfig
.cmdstdin
= Ctrue
;
224 /* if there are no operands and stdin and stderr are connected to
225 a terminal, -i is assumed */
226 if(!operands
&& isatty(fileno(stdin
)) && isatty(fileno(stderr
)))
227 gConfig
.interactive
= Ctrue
;
229 /* if -i, -m is enabled by default */
230 if(gConfig
.interactive
== Ctrue
&& gConfig
.monitor
)
231 gConfig
.monitor
= Ctrue
;
233 /* at this point, we should be good to go. We only need to
234 set anything that is still Cunset to Cfalse */
235 if(gConfig
.allexport
== Cunset
) gConfig
.allexport
= Cfalse
;
236 if(gConfig
.errexit
== Cunset
) gConfig
.errexit
= Cfalse
;
237 if(gConfig
.ignoreeof
== Cunset
) gConfig
.ignoreeof
= Cfalse
;
238 if(gConfig
.monitor
== Cunset
) gConfig
.monitor
= Cfalse
;
239 if(gConfig
.noclobber
== Cunset
) gConfig
.noclobber
= Cfalse
;
240 if(gConfig
.noglob
== Cunset
) gConfig
.noglob
= Cfalse
;
241 if(gConfig
.remember
== Cunset
) gConfig
.remember
= Cfalse
;
242 if(gConfig
.noexec
== Cunset
) gConfig
.noexec
= Cfalse
;
243 if(gConfig
.nolog
== Cunset
) gConfig
.nolog
= Cfalse
;
244 if(gConfig
.notify
== Cunset
) gConfig
.notify
= Cfalse
;
245 if(gConfig
.nounset
== Cunset
) gConfig
.nounset
= Cfalse
;
246 if(gConfig
.verbose
== Cunset
) gConfig
.verbose
= Cfalse
;
247 if(gConfig
.vi
== Cunset
) gConfig
.vi
= Cfalse
;
248 if(gConfig
.xtrace
== Cunset
) gConfig
.xtrace
= Cfalse
;
249 if(gConfig
.cmdstr
== Cunset
) gConfig
.cmdstr
= Cfalse
;
250 if(gConfig
.cmdstdin
== Cunset
) gConfig
.cmdstdin
= Cfalse
;
251 if(gConfig
.interactive
== Cunset
) gConfig
.interactive
= Cfalse
;
256 void dump_config(void)
258 if(gConfig
.allexport
== Cunset
)
259 printf("allexport : unset\n");
260 else if(gConfig
.allexport
)
261 printf("allexport : true\n");
263 printf("allexport : false\n");
265 if(gConfig
.errexit
== Cunset
)
266 printf("errexit : unset\n");
267 else if(gConfig
.errexit
)
268 printf("errexit : true\n");
270 printf("errexit : false\n");
272 if(gConfig
.ignoreeof
== Cunset
)
273 printf("ignoreeof : unset\n");
274 else if(gConfig
.ignoreeof
)
275 printf("ignoreeof : true\n");
277 printf("ignoreeof : false\n");
279 if(gConfig
.monitor
== Cunset
)
280 printf("monitor : unset\n");
281 else if(gConfig
.monitor
)
282 printf("monitor : true\n");
284 printf("monitor : false\n");
286 if(gConfig
.noclobber
== Cunset
)
287 printf("noclobber : unset\n");
288 else if(gConfig
.noclobber
)
289 printf("noclobber : true\n");
291 printf("noclobber : false\n");
293 if(gConfig
.noglob
== Cunset
)
294 printf("noglob : unset\n");
295 else if(gConfig
.noglob
)
296 printf("noglob : true\n");
298 printf("noglob : false\n");
300 if(gConfig
.remember
== Cunset
)
301 printf("remember : unset\n");
302 else if(gConfig
.remember
)
303 printf("remember : true\n");
305 printf("remember : false\n");
307 if(gConfig
.noexec
== Cunset
)
308 printf("noexec : unset\n");
309 else if(gConfig
.noexec
)
310 printf("noexec : true\n");
312 printf("noexec : false\n");
314 if(gConfig
.nolog
== Cunset
)
315 printf("nolog : unset\n");
316 else if(gConfig
.nolog
)
317 printf("nolog : true\n");
319 printf("nolog : false\n");
321 if(gConfig
.notify
== Cunset
)
322 printf("notify : unset\n");
323 else if(gConfig
.notify
)
324 printf("notify : true\n");
326 printf("notify : false\n");
328 if(gConfig
.nounset
== Cunset
)
329 printf("nounset : unset\n");
330 else if(gConfig
.nounset
)
331 printf("nounset : true\n");
333 printf("nounset : false\n");
335 if(gConfig
.verbose
== Cunset
)
336 printf("verbose : unset\n");
337 else if(gConfig
.verbose
)
338 printf("verbose : true\n");
340 printf("verbose : false\n");
342 if(gConfig
.vi
== Cunset
)
343 printf("vi : unset\n");
345 printf("vi : true\n");
347 printf("vi : false\n");
349 if(gConfig
.xtrace
== Cunset
)
350 printf("xtrace : unset\n");
351 else if(gConfig
.xtrace
)
352 printf("xtrace : true\n");
354 printf("xtrace : false\n");
356 if(gConfig
.cmdstr
== Cunset
)
357 printf("cmdstr : unset\n");
358 else if(gConfig
.cmdstr
)
359 printf("cmdstr : true\n");
361 printf("cmdstr : false\n");
363 if(gConfig
.cmdstdin
== Cunset
)
364 printf("cmdstdin : unset\n");
365 else if(gConfig
.cmdstdin
)
366 printf("cmdstdin : true\n");
368 printf("cmdstdin : false\n");
370 if(gConfig
.interactive
== Cunset
)
371 printf("interactive : unset\n");
372 else if(gConfig
.interactive
)
373 printf("interactive : true\n");
375 printf("interactive : false\n");
378 /* print usage text */
381 printf("usage: %s [-/+ abCefhimnuvz][-/+ o <option>] [<non-options>]\n"
382 "where <non-options> can be:\n"
383 " <command file> [<argument>...]\n"
384 " -c <command string> [<command name> [argument...]]\n"
385 " -s [argument]\n", progname
);
388 int main(int argc
, char *argv
[])
390 /* Welcome to main! */
392 /* our first task is parsing the argv.
393 the function parse_argv() requires us
394 to decrement argc and point argv to
395 its second field, so lets do that */
399 parse_argv(&argc
, &argv
);
401 /* if reading from cmd string, fail if there are no operands */
402 if(gConfig
.cmdstr
== Ctrue
) {
404 fprintf(stderr
, "ERROR: -c: missing command string\n");
407 input_set_cmdstr(argv
[0]);
411 DEBUG_CMD(dump_config());