1c330dc65679ba045990e8da73df32904f901b87
[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
183   /* Set defaults for options. */
184   ofp = stdout;
185
186   /* Get program's name. */
187   program = strrchr (argv[0], '/');
188   if (program == NULL)
189     program = argv[0];
190   else
191     program++;
192
193   /* Make getopt_long() to use our modified program name. */
194   argv[0] = program;
195
196   /* Format version string. */
197   sprintf (version, _("states for %s"), PACKAGE_STRING);
198
199   /* Internationalization. */
200 #if HAVE_SETLOCALE
201 #if HAVE_LC_MESSAGES
202   setlocale (LC_MESSAGES, "");
203 #endif
204 #endif
205 #if ENABLE_NLS
206   bindtextdomain (PACKAGE, LOCALEDIR);
207   textdomain (PACKAGE);
208 #endif
209
210   /* Init namespaces. */
211   ns_prims = strhash_init ();
212   ns_vars = strhash_init ();
213   ns_subs = strhash_init ();
214   ns_states = strhash_init ();
215
216   global_stmts = list ();
217   start_stmts = list ();
218   startrules = list ();
219   namerules = list ();
220
221   nvoid = node_alloc (nVOID);
222   inbuf = (char *) xmalloc (INBUFSIZE);
223
224   init_primitives ();
225
226   re_set_syntax (RE_SYNTAX_GNU_AWK | RE_INTERVALS);
227
228   /* Enter some system variables. */
229   enter_system_variable ("program", program);
230   enter_system_variable ("version", version);
231
232   /* Parse arguments. */
233   while (1)
234     {
235       int option_index = 0;
236
237       c = getopt_long (argc, argv, "D:f:ho:p:s:vVW:", long_options,
238                        &option_index);
239       if (c == -1)
240         break;
241
242       switch (c)
243         {
244         case 'D':               /* define variable */
245           vardef = (VariableDef *) xcalloc (1, sizeof (*vardef));
246           vardef->sym = (char *) xmalloc (strlen (optarg) + 1);
247           strcpy (vardef->sym, optarg);
248
249           vardef->val = strchr (vardef->sym, '=');
250           if (vardef->val == NULL)
251             {
252               fprintf (stderr, _("%s: malformed variable definition \"%s\"\n"),
253                        program, vardef->sym);
254               exit (1);
255             }
256           *vardef->val = '\0';
257           vardef->val++;
258
259           if (vardefs)
260             vardefs_tail->next = vardef;
261           else
262             vardefs = vardef;
263           vardefs_tail = vardef;
264           break;
265
266         case 'f':               /* definitions file */
267           defs_file = optarg;
268           break;
269
270         case 'h':               /* help */
271           usage ();
272           exit (0);
273           break;
274
275         case 'o':               /* output file */
276           ofp = fopen (optarg, "w");
277           if (ofp == NULL)
278             {
279               fprintf (stderr,
280                        _("%s: couldn't create output file \"%s\": %s\n"),
281                        program, optarg, strerror (errno));
282               exit (1);
283             }
284           break;
285
286         case 'p':               /* path */
287           path = optarg;
288           break;
289
290         case 's':               /* start state */
291           start_state_arg = optarg;
292           break;
293
294         case 'v':               /* verbose */
295           verbose++;
296           break;
297
298         case 'V':               /* version */
299           printf ("%s\n", version);
300           exit (0);
301           break;
302
303         case 'W':               /* warning level */
304           if (strcmp (optarg, "light") == 0)
305             warning_level = WARN_LIGHT;
306           else if (strcmp (optarg, "all") == 0)
307             warning_level = WARN_ALL;
308           else
309             {
310               fprintf (stderr,
311                        _("%s: unknown warning level `%s'\n"),
312                        program, optarg);
313               exit (1);
314             }
315           break;
316
317         case '?':               /* Errors found during getopt_long(). */
318           fprintf (stderr, _("Try `%s --help' for more information.\n"),
319                    program);
320           exit (1);
321           break;
322
323         default:
324           printf ("Hey! main() didn't handle option \"%c\" (%d)", c, c);
325           if (optarg)
326             printf (" with arg %s", optarg);
327           printf ("\n");
328           abort ();
329           break;
330         }
331     }
332
333   /* Pass all remaining arguments to States through `argv' array. */
334   {
335     Node *v, *n;
336     int i;
337
338     v = node_alloc (nARRAY);
339     v->u.array.allocated = argc - optind + 1;
340     v->u.array.len = argc - optind;
341     v->u.array.array = (Node **) xcalloc (v->u.array.allocated,
342                                           sizeof (Node *));
343     for (i = optind; i < argc; i++)
344       {
345         char *data;
346
347         n = node_alloc (nSTRING);
348         if (strcmp (argv[i], "-") == 0)
349           data = STDIN_NAME;
350         else
351           data = argv[i];
352
353         n->u.str.len = strlen (data);
354         n->u.str.data = xstrdup (data);
355         v->u.array.array[i - optind] = n;
356       }
357
358     if (!strhash_put (ns_vars, "argv", 4, v, (void **) &n))
359       {
360         fprintf (stderr, _("%s: out of memory\n"), program);
361         exit (1);
362       }
363     node_free (n);
364   }
365
366   /* Set some defaults if the user didn't give them. */
367   if (path == NULL)
368     {
369       char *cp;
370       cp = strrchr (defs_file, '/');
371       if (cp)
372         {
373           path = xmalloc (cp - defs_file + 3);
374           sprintf (path, ".%c%.*s", PATH_SEPARATOR, cp - defs_file, defs_file);
375         }
376       else
377         path = ".";
378     }
379
380   /* Parse config file. */
381   load_states_file (defs_file);
382
383   /* Define variables given at the command line. */
384   for (vardef = vardefs; vardef; vardef = vardef->next)
385     {
386       Node *val;
387       Node *old_val;
388
389       val = node_alloc (nSTRING);
390       val->u.str.len = strlen (vardef->val);
391       val->u.str.data = xstrdup (vardef->val);
392
393       if (!strhash_put (ns_vars, vardef->sym, strlen (vardef->sym),
394                         val, (void **) &old_val))
395         {
396           fprintf (stderr, _("%s: out of memory\n"), program);
397           exit (1);
398         }
399       node_free (old_val);
400     }
401
402   /* Process files. */
403   if (optind == argc)
404     {
405       ifp = stdin;
406       process_file (STDIN_NAME);
407     }
408   else
409     for (; optind < argc; optind++)
410       {
411         if (strcmp (argv[optind], "-") == 0)
412           {
413             ifp = stdin;
414             process_file (STDIN_NAME);
415           }
416         else
417           {
418             ifp = fopen (argv[optind], "r");
419             if (ifp == NULL)
420               {
421                 fprintf (stderr, _("%s: couldn't open input file `%s': %s\n"),
422                          program, argv[optind], strerror (errno));
423                 exit (1);
424               }
425             process_file (argv[optind]);
426             fclose (ifp);
427           }
428       }
429
430   /* Close output file. */
431   if (ofp != stdout)
432     fclose (ofp);
433
434   return 0;
435 }
436
437 \f
438 /*
439  * Static functions.
440  */
441
442 static void
443 usage ()
444 {
445   printf (_("\
446 Usage: %s [OPTION]... [FILE]...\n\
447 Mandatory arguments to long options are mandatory for short options too.\n"),
448           program);
449   printf (_("\
450   -D, --define=VAR=VAL       define variable VAR to have value VAL\n\
451   -f, --file=NAME            read state definitions from file NAME\n\
452   -h, --help                 print this help and exit\n\
453   -o, --output=NAME          save output to file NAME\n\
454   -p, --path=PATH            set the load path to PATH\n\
455   -s, --state=NAME           start from state NAME\n\
456   -v, --verbose              increase the program verbosity\n\
457   -V, --version              print version number\n\
458   -W, --warning=LEVEL        set the warning level to LEVEL\n"));
459 }