Small change to getopt stuff
[enscript.git] / states / main.c
1 /*
2  * The main for states.
3  * Copyright (c) 1997-2000 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7
8 /*
9  * This file is part of GNU Enscript.
10  *
11  * Enscript is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Enscript is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include "defs.h"
26 #include "getopt.h"
27
28 /*
29  * Types and definitions.
30  */
31
32 #define STDIN_NAME "(stdin)"
33
34
35 /*
36  * Global variables.
37  */
38
39 char *program;
40
41 /* Namespaces. */
42 StringHashPtr ns_prims = NULL;
43 StringHashPtr ns_vars = NULL;
44 StringHashPtr ns_subs = NULL;
45 StringHashPtr ns_states = NULL;
46
47 /*
48  * Global expressions which are evaluated after config file has been
49  * parsed.
50  */
51 List *global_stmts = NULL;
52
53 /* Statements from the start{} block. */
54 List *start_stmts = NULL;
55
56 /* Start and name rules. */
57 List *startrules = NULL;
58 List *namerules = NULL;
59
60 Node *nvoid = NULL;
61
62 FILE *ifp = NULL;
63 char *inbuf = NULL;
64 unsigned int data_in_buffer;
65 unsigned int bufpos;
66 int eof_seen;
67 char *current_fname;
68 unsigned int current_linenum;
69
70 struct re_registers *current_match = NULL;
71 char *current_match_buf = NULL;
72
73 /* Options. */
74
75 /*
76  * -D VAR=VAL, --define=VAR=VAL
77  *
78  * Define variable VAR to have value VAL.
79  */
80
81 VariableDef *vardefs = NULL;
82 VariableDef *vardefs_tail = NULL;
83
84 /*
85  * -f NAME, --file=NAME
86  *
87  * Read state definitions from file NAME.  The default is "states.st" in
88  * the current working directory.
89  */
90
91 char *defs_file = "states.st";
92 unsigned int linenum = 1;
93 char *yyin_name;
94
95 /*
96  * -o FILE, --output=FILE
97  *
98  * Save output to file FILE, the default is stdout.
99  */
100
101 FILE *ofp = NULL;
102
103 /*
104  * -p PATH, --path=PATH
105  *
106  * Set the load path to PATH.
107  */
108
109 char *path = NULL;
110
111 /*
112  * -s NAME, --state=NAME
113  *
114  * Start from state NAME.  As a default, start date is resolved during
115  * the program startup by using start, namerules and startrules rules.
116  */
117 char *start_state_arg = NULL;
118 char *start_state;
119
120 /*
121  * -v, --verbose
122  *
123  * Increase the program verbosity.
124  */
125 unsigned int verbose = 0;
126
127 /*
128  * -V, --version
129  *
130  * Print the program version number.
131  */
132
133 /*
134  * -W LEVEL, --warning=LEVEL
135  *
136  * Set the warning level to LEVEL.
137  */
138 WarningLevel warning_level = WARN_LIGHT;
139
140
141 /*
142  * Static variables.
143  */
144
145 static struct option long_options[] =
146 {
147   {"define",            required_argument,      0, 'D'},
148   {"file",              required_argument,      0, 'f'},
149   {"help",              no_argument,            0, 'h'},
150   {"output",            required_argument,      0, 'o'},
151   {"path",              required_argument,      0, 'p'},
152   {"state",             required_argument,      0, 's'},
153   {"verbose",           no_argument,            0, 'v'},
154   {"version",           no_argument,            0, 'V'},
155   {"warning",           required_argument,      0, 'W'},
156
157   {NULL, 0, 0, 0},
158 };
159
160 /* Version string. */
161 char version[256];
162
163
164 /*
165  * Prototypes for static functions.
166  */
167
168 static void usage ___P ((void));
169
170
171 /*
172  * Global functions.
173  */
174
175 int
176 main (argc, argv)
177      int argc;
178      char *argv[];
179 {
180   int c;
181   VariableDef *vardef;
182   int option_index = 0;
183
184   /* Set defaults for options. */
185   ofp = stdout;
186
187   /* Get program's name. */
188   program = strrchr (argv[0], '/');
189   if (program == NULL)
190     program = argv[0];
191   else
192     program++;
193
194   /* Make getopt_long() to use our modified program name. */
195   argv[0] = program;
196
197   /* Format version string. */
198   sprintf (version, _("states for %s"), PACKAGE_STRING);
199
200   /* Internationalization. */
201 #if HAVE_SETLOCALE
202 #if HAVE_LC_MESSAGES
203   setlocale (LC_MESSAGES, "");
204 #endif
205 #endif
206 #if ENABLE_NLS
207   bindtextdomain (PACKAGE, LOCALEDIR);
208   textdomain (PACKAGE);
209 #endif
210
211   /* Init namespaces. */
212   ns_prims = strhash_init ();
213   ns_vars = strhash_init ();
214   ns_subs = strhash_init ();
215   ns_states = strhash_init ();
216
217   global_stmts = list ();
218   start_stmts = list ();
219   startrules = list ();
220   namerules = list ();
221
222   nvoid = node_alloc (nVOID);
223   inbuf = (char *) xmalloc (INBUFSIZE);
224
225   init_primitives ();
226
227   re_set_syntax (RE_SYNTAX_GNU_AWK | RE_INTERVALS);
228
229   /* Enter some system variables. */
230   enter_system_variable ("program", program);
231   enter_system_variable ("version", version);
232
233   /* Parse arguments. */
234   while ((c = getopt_long (argc, argv, "D:f:ho:p:s:vVW:", long_options,
235                            &option_index)) != -1) {
236       switch (c)
237         {
238         case 'D':               /* define variable */
239           vardef = (VariableDef *) xcalloc (1, sizeof (*vardef));
240           vardef->sym = (char *) xmalloc (strlen (optarg) + 1);
241           strcpy (vardef->sym, optarg);
242
243           vardef->val = strchr (vardef->sym, '=');
244           if (vardef->val == NULL)
245             {
246               fprintf (stderr, _("%s: malformed variable definition \"%s\"\n"),
247                        program, vardef->sym);
248               exit (1);
249             }
250           *vardef->val = '\0';
251           vardef->val++;
252
253           if (vardefs)
254             vardefs_tail->next = vardef;
255           else
256             vardefs = vardef;
257           vardefs_tail = vardef;
258           break;
259
260         case 'f':               /* definitions file */
261           defs_file = optarg;
262           break;
263
264         case 'h':               /* help */
265           usage ();
266           exit (0);
267           break;
268
269         case 'o':               /* output file */
270           ofp = fopen (optarg, "w");
271           if (ofp == NULL)
272             {
273               fprintf (stderr,
274                        _("%s: couldn't create output file \"%s\": %s\n"),
275                        program, optarg, strerror (errno));
276               exit (1);
277             }
278           break;
279
280         case 'p':               /* path */
281           path = optarg;
282           break;
283
284         case 's':               /* start state */
285           start_state_arg = optarg;
286           break;
287
288         case 'v':               /* verbose */
289           verbose++;
290           break;
291
292         case 'V':               /* version */
293           printf ("%s\n", version);
294           exit (0);
295           break;
296
297         case 'W':               /* warning level */
298           if (strcmp (optarg, "light") == 0)
299             warning_level = WARN_LIGHT;
300           else if (strcmp (optarg, "all") == 0)
301             warning_level = WARN_ALL;
302           else
303             {
304               fprintf (stderr,
305                        _("%s: unknown warning level `%s'\n"),
306                        program, optarg);
307               exit (1);
308             }
309           break;
310
311         case '?':               /* Errors found during getopt_long(). */
312           fprintf (stderr, _("Try `%s --help' for more information.\n"),
313                    program);
314           exit (1);
315           break;
316
317         default:
318           printf ("Hey! main() didn't handle option \"%c\" (%d)", c, c);
319           if (optarg)
320             printf (" with arg %s", optarg);
321           printf ("\n");
322           abort ();
323           break;
324         }
325     }
326
327   /* Pass all remaining arguments to States through `argv' array. */
328   {
329     Node *v, *n;
330     int i;
331
332     v = node_alloc (nARRAY);
333     v->u.array.allocated = argc - optind + 1;
334     v->u.array.len = argc - optind;
335     v->u.array.array = (Node **) xcalloc (v->u.array.allocated,
336                                           sizeof (Node *));
337     for (i = optind; i < argc; i++)
338       {
339         char *data;
340
341         n = node_alloc (nSTRING);
342         if (strcmp (argv[i], "-") == 0)
343           data = STDIN_NAME;
344         else
345           data = argv[i];
346
347         n->u.str.len = strlen (data);
348         n->u.str.data = xstrdup (data);
349         v->u.array.array[i - optind] = n;
350       }
351
352     if (!strhash_put (ns_vars, "argv", 4, v, (void **) &n))
353       {
354         fprintf (stderr, _("%s: out of memory\n"), program);
355         exit (1);
356       }
357     node_free (n);
358   }
359
360   /* Set some defaults if the user didn't give them. */
361   if (path == NULL)
362     {
363       char *cp;
364       cp = strrchr (defs_file, '/');
365       if (cp)
366         {
367           path = xmalloc (cp - defs_file + 3);
368           sprintf (path, ".%c%.*s", PATH_SEPARATOR, cp - defs_file, defs_file);
369         }
370       else
371         path = ".";
372     }
373
374   /* Parse config file. */
375   load_states_file (defs_file);
376
377   /* Define variables given at the command line. */
378   for (vardef = vardefs; vardef; vardef = vardef->next)
379     {
380       Node *val;
381       Node *old_val;
382
383       val = node_alloc (nSTRING);
384       val->u.str.len = strlen (vardef->val);
385       val->u.str.data = xstrdup (vardef->val);
386
387       if (!strhash_put (ns_vars, vardef->sym, strlen (vardef->sym),
388                         val, (void **) &old_val))
389         {
390           fprintf (stderr, _("%s: out of memory\n"), program);
391           exit (1);
392         }
393       node_free (old_val);
394     }
395
396   /* Process files. */
397   if (optind == argc)
398     {
399       ifp = stdin;
400       process_file (STDIN_NAME);
401     }
402   else
403     for (; optind < argc; optind++)
404       {
405         if (strcmp (argv[optind], "-") == 0)
406           {
407             ifp = stdin;
408             process_file (STDIN_NAME);
409           }
410         else
411           {
412             ifp = fopen (argv[optind], "r");
413             if (ifp == NULL)
414               {
415                 fprintf (stderr, _("%s: couldn't open input file `%s': %s\n"),
416                          program, argv[optind], strerror (errno));
417                 exit (1);
418               }
419             process_file (argv[optind]);
420             fclose (ifp);
421           }
422       }
423
424   /* Close output file. */
425   if (ofp != stdout)
426     fclose (ofp);
427
428   return 0;
429 }
430
431 \f
432 /*
433  * Static functions.
434  */
435
436 static void
437 usage ()
438 {
439   printf (_("\
440 Usage: %s [OPTION]... [FILE]...\n\
441 Mandatory arguments to long options are mandatory for short options too.\n"),
442           program);
443   printf (_("\
444   -D, --define=VAR=VAL       define variable VAR to have value VAR\n\
445   -f, --file=NAME            read state definitions from file NAME\n\
446   -h, --help                 print this help and exit\n\
447   -o, --output=NAME          save output to file NAME\n\
448   -p, --path=PATH            set the load path to PATH\n\
449   -s, --state=NAME           start from state NAME\n\
450   -v, --verbose              increase the program verbosity\n\
451   -V, --version              print version number\n\
452   -W, --warning=LEVEL        set the warning level to LEVEL\n"));
453 }