add support for input command str
[glg-sh.git] / main.c
blob1576ba5c6d2ac5bde336d3e90a70be4b1625bebf
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 #include "input.h"
11 #ifdef DEBUG
12 #define DEBUG_CMD(cmd) {puts("+ DEBUG"); cmd; puts("- DEBUG");}
13 #else
14 #define DEBUG_CMD(cmd) {}
15 #endif
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;
25 typedef struct {
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
32 Cbool remember; // -h
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
38 Cbool vi; // -o vi
39 Cbool xtrace; // -x or -o xtrace
40 Cbool cmdstr; // -c
41 Cbool cmdstdin; // -s;
42 Cbool interactive; // -i
43 } Config;
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
47 false anymore... */
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[])
64 int j,len;
65 char *str;
66 Cbool enable;
67 bool operands;
69 while(*argc != 0)
71 str = *argv[0];
72 len = strlen(str);
74 /* if not a flag, stop parsing flags */
75 if(str[0] != '-' && str[0] != '+')
76 break;
78 /* if its a single '-', discard and stop parsing */
79 if(str[0] == '-' && len == 1) {
80 argv++;
81 (*argc)--;
82 break;
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 */
93 if(str[1] == 'o') {
94 /* we are unforgiving on bad input format */
95 if(len != 2) {
96 fprintf(stderr,"ERROR: unrecognized flag: %s\n", str);
97 usage();
98 exit(1);
101 /* we need an option string */
102 if(*argc == 1) {
103 fprintf(stderr,"ERROR: option string needed for %s\n", str);
104 exit(1);
107 /* now we are sure we have one, let's jump to it */
108 (*argc)--;
109 str = *(++(*argv));
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) {
135 gConfig.vi = enable;
136 } else if(strcmp(str, "xtrace") == 0 && gConfig.xtrace) {
137 gConfig.xtrace = enable;
138 } else {
139 /* ohoh, bad input! */
140 fprintf(stderr, "ERROR: invalid option string: %s\n", str);
141 exit(1);
143 /* success! continue with next string */
144 (*argc)--;
145 (*argv)++;
146 continue;
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 */
154 switch(str[j]) {
155 case 'a':
156 if(gConfig.allexport) gConfig.allexport = enable;
157 break;
158 case 'e':
159 if(gConfig.errexit) gConfig.errexit = enable;
160 break;
161 case 'm':
162 if(gConfig.monitor) gConfig.monitor = enable;
163 break;
164 case 'C':
165 if(gConfig.noclobber) gConfig.noclobber = enable;
166 break;
167 case 'f':
168 if(gConfig.noglob) gConfig.noglob = enable;
169 break;
170 case 'h':
171 if(gConfig.remember) gConfig.remember = enable;
172 break;
173 case 'n':
174 if(gConfig.noexec) gConfig.noexec = enable;
175 break;
176 case 'b':
177 if(gConfig.notify) gConfig.notify = enable;
178 break;
179 case 'u':
180 if(gConfig.nounset) gConfig.nounset = enable;
181 break;
182 case 'v':
183 if(gConfig.verbose) gConfig.verbose = enable;
184 break;
185 case 'x':
186 if(gConfig.xtrace) gConfig.xtrace = enable;
187 break;
188 case 'c':
189 gConfig.cmdstr = Ctrue;
190 break;
191 case 's':
192 gConfig.cmdstdin = Ctrue;
193 break;
194 case 'i':
195 gConfig.interactive = Ctrue;
196 break;
197 default:
198 /* oops, bad input! */
199 fprintf(stderr,"ERROR: unrecognized flag: %c\n", str[j]);
200 usage();
201 exit(1);
205 /* we successfuly parsed the whole string! lets
206 decrement the argc, point the argv to the next
207 string and try again. */
208 (*argc)--;
209 (*argv)++;
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;
253 /* all done :) */
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");
262 else
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");
269 else
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");
276 else
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");
283 else
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");
290 else
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");
297 else
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");
304 else
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");
311 else
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");
318 else
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");
325 else
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");
332 else
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");
339 else
340 printf("verbose : false\n");
342 if(gConfig.vi == Cunset)
343 printf("vi : unset\n");
344 else if(gConfig.vi)
345 printf("vi : true\n");
346 else
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");
353 else
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");
360 else
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");
367 else
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");
374 else
375 printf("interactive : false\n");
378 /* print usage text */
379 void usage(void)
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 */
396 callname = argv[0];
397 argc--;
398 argv++;
399 parse_argv(&argc, &argv);
401 /* if reading from cmd string, fail if there are no operands */
402 if(gConfig.cmdstr == Ctrue) {
403 if( argc == 0) {
404 fprintf(stderr, "ERROR: -c: missing command string\n");
405 exit(1);
406 } else {
407 input_set_cmdstr(argv[0]);
411 DEBUG_CMD(dump_config());
413 return 0;