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