Added framework for the ONEWAY command.
[pspp-builds.git] / src / q2c.c
1 /* q2c - parser generator for PSPP procedures.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <time.h>
27 #include <errno.h>
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include "str.h"
32
33
34 /* Brokenness. */
35 #ifndef EXIT_SUCCESS
36 #define EXIT_SUCCESS 0
37 #endif
38
39 #ifndef EXIT_FAILURE
40 #define EXIT_FAILURE 1
41 #endif
42
43 #if !HAVE_STRERROR
44 #include "misc/strerror.c"
45 #endif
46      
47 #include "debug-print.h"
48
49 /* Max length of an input line. */
50 #define MAX_LINE_LEN 1024
51
52 /* Max token length. */
53 #define MAX_TOK_LEN 1024
54
55 /* argv[0]. */
56 char *pgmname;
57
58 /* Have the input and output files been opened yet? */
59 int is_open;
60
61 /* Input, output files. */
62 FILE *in, *out;
63
64 /* Input, output file names. */
65 char *ifn, *ofn;
66
67 /* Input, output file line number. */
68 int ln, oln = 1;
69
70 /* Input line buffer, current position. */
71 char *buf, *cp;
72
73 /* Token types. */
74 enum
75   {
76     T_STRING = 256,     /* String literal. */
77     T_ID = 257          /* Identifier.  */
78   };
79
80 /* Current token: either one of the above, or a single character. */
81 int token;
82
83 /* Token string value. */
84 char *tokstr;
85 \f
86 /* Utility functions. */
87
88 char nullstr[] = "";
89
90 /* Close all open files and delete the output file, on failure. */
91 static void
92 finish_up (void)
93 {
94   if (!is_open)
95     return;
96   is_open = 0;
97   fclose (in);
98   fclose (out);
99   if (remove (ofn) == -1)
100     fprintf (stderr, "%s: %s: remove: %s\n", pgmname, ofn, strerror (errno));
101 }
102
103 void hcf (void) NO_RETURN;
104
105 /* Terminate unsuccessfully. */
106 void
107 hcf (void)
108 {
109   finish_up ();
110   exit (EXIT_FAILURE);
111 }
112
113 int fail (const char *, ...) PRINTF_FORMAT (1, 2);
114 int error (const char *, ...) PRINTF_FORMAT (1, 2);
115
116 /* Output an error message and terminate unsuccessfully. */
117 int
118 fail (const char *format, ...)
119 {
120   va_list args;
121
122   va_start (args, format);
123   fprintf (stderr, "%s: ", pgmname);
124   vfprintf (stderr, format, args);
125   fprintf (stderr, "\n");
126   va_end (args);
127
128   hcf ();
129 }
130
131 /* Output a context-dependent error message and terminate
132    unsuccessfully. */
133 int
134 error (const char *format,...)
135 {
136   va_list args;
137
138   va_start (args, format);
139   fprintf (stderr, "%s:%d: (column %d) ", ifn, ln, (int) (cp - buf));
140   vfprintf (stderr, format, args);
141   fprintf (stderr, "\n");
142   va_end (args);
143
144   hcf ();
145 }
146
147 #define VME "virtual memory exhausted"
148
149 /* Allocate a block of SIZE bytes and return a pointer to its
150    beginning. */
151 static void *
152 xmalloc (size_t size)
153 {
154   void *vp;
155   
156   if (size == 0)
157     return NULL;
158   
159   vp = malloc (size);
160   if (!vp)
161     fail ("xmalloc(%lu): %s", (unsigned long) size, VME);
162   
163   return vp;
164 }
165
166 /* Make a dynamically allocated copy of string S and return a pointer
167    to the first character. */
168 static char *
169 xstrdup (const char *s)
170 {
171   size_t size;
172   char *t;
173
174   assert (s != NULL);
175   size = strlen (s) + 1;
176   
177   t = malloc (size);
178   if (!t)
179     fail ("xstrdup(%lu): %s", (unsigned long) strlen (s), VME);
180     
181   memcpy (t, s, size);
182   return t;
183 }
184
185 /* Returns a pointer to one of 8 static buffers.  The buffers are used
186    in rotation. */
187 static char *
188 get_buffer (void)
189 {
190   static char b[8][256];
191   static int cb;
192
193   if (++cb >= 8)
194     cb = 0;
195   
196   return b[cb];
197 }
198    
199 /* Copies a string to a static buffer, converting it to lowercase in
200    the process, and returns a pointer to the static buffer. */
201 static char *
202 st_lower (const char *s)
203 {
204   char *p, *cp;
205   
206   p = cp = get_buffer ();
207   while (*s)
208     *cp++ = tolower ((unsigned char) (*s++));
209   *cp++ = '\0';
210   
211   return p;
212 }
213
214 /* Copies a string to a static buffer, converting it to uppercase in
215    the process, and returns a pointer to the static buffer. */
216 static char *
217 st_upper (const char *s)
218 {
219   char *p, *cp;
220
221   p = cp = get_buffer ();
222   while (*s)
223     *cp++ = toupper ((unsigned char) (*s++));
224   *cp++ = '\0';
225   
226   return p;
227 }
228
229 /* Returns the address of the first non-whitespace character in S, or
230    the address of the null terminator if none. */
231 static char *
232 skip_ws (const char *s)
233 {
234   while (isspace ((unsigned char) *s))
235     s++;
236   return (char *) s;
237 }
238
239 /* Read one line from the input file into buf.  Lines having special
240    formats are handled specially. */
241 static int
242 get_line (void)
243 {
244   ln++;
245   if (0 == fgets (buf, MAX_LINE_LEN, in))
246     {
247       if (ferror (in))
248         fail ("%s: fgets: %s", ifn, strerror (errno));
249       return 0;
250     }
251
252   cp = strchr (buf, '\n');
253   if (cp != NULL)
254     *cp = '\0';
255
256   cp = buf;
257   return 1;
258 }
259 \f
260 /* Symbol table manager. */
261
262 /* Symbol table entry. */
263 typedef struct symbol symbol;
264 struct symbol
265   {
266     symbol *next;               /* Next symbol in symbol table. */
267     char *name;                 /* Symbol name. */
268     int unique;                 /* 1=Name must be unique in this file. */
269     int ln;                     /* Line number of definition. */
270     int value;                  /* Symbol value. */
271   };
272
273 /* Symbol table. */
274 symbol *symtab;
275
276 /* Add a symbol to the symbol table having name NAME, uniqueness
277    UNIQUE, and value VALUE.  If a symbol having the same name is found
278    in the symbol table, its sequence number is returned and the symbol
279    table is not modified.  Otherwise, the symbol is added and the next
280    available sequence number is returned. */
281 static int
282 add_symbol (const char *name, int unique, int value)
283 {
284   symbol *iter, *sym;
285   int x;
286
287   sym = xmalloc (sizeof (symbol));
288   sym->name = xstrdup (name);
289   sym->unique = unique;
290   sym->value = value;
291   sym->next = NULL;
292   sym->ln = ln;
293   if (!symtab)
294     {
295       symtab = sym;
296       return 1;
297     }
298   iter = symtab;
299   x = 1;
300   for (;;)
301     {
302       if (!strcmp (iter->name, name))
303         {
304           if (iter->unique)
305             {
306               fprintf (stderr, "%s:%d: `%s' is already defined above\n", ifn,
307                        ln, name);
308               fprintf (stderr, "%s:%d: location of previous definition\n", ifn,
309                        iter->ln);
310               hcf ();
311             }
312           free (sym->name);
313           free (sym);
314           return x;
315         }
316       if (!iter->next)
317         break;
318       iter = iter->next;
319       x++;
320     }
321   iter->next = sym;
322   return ++x;
323 }
324
325 /* Finds the symbol having given sequence number X within the symbol
326    table, and returns the associated symbol structure. */
327 static symbol *
328 find_symbol (int x)
329 {
330   symbol *iter;
331
332   iter = symtab;
333   while (x > 1 && iter)
334     {
335       iter = iter->next;
336       x--;
337     }
338   assert (iter);
339   return iter;
340 }
341
342 #if DEBUGGING 
343 /* Writes a printable representation of the current token to
344    stdout. */
345 void
346 dump_token (void)
347 {
348   switch (token)
349     {
350     case T_TSTRING:
351       printf ("TR_STRING\t\"%s\"\n", tokstr);
352       break;
353     case T_STRING:
354       printf ("STRING\t\"%s\"\n", tokstr);
355       break;
356     case T_ID:
357       printf ("ID\t%s\n", tokstr);
358       break;
359     default:
360       printf ("PUNCT\t%c\n", token);
361     }
362 }
363 #endif /* DEBUGGING */
364
365 /* Reads a token from the input file. */
366 static int
367 lex_get (void)
368 {
369   /* Skip whitespace and check for end of file. */
370   for (;;)
371     {
372       cp = skip_ws (cp);
373       if (*cp != '\0')
374         break;
375       
376       if (!get_line ())
377         fail ("%s: Unexpected end of file.", ifn);
378     }
379   
380   if (*cp == '"')
381     {
382       char *dest = tokstr;
383       token = T_STRING;
384       cp++;
385       while (*cp != '"' && *cp)
386         {
387           if (*cp == '\\')
388             {
389               cp++;
390               if (!*cp)
391                 error ("Unterminated string literal.");
392               *dest++ = *cp++;
393             }
394           else
395             *dest++ = *cp++;
396         }
397       *dest++ = 0;
398       if (!*cp)
399         error ("Unterminated string literal.");
400       cp++;
401     }
402   else if (*cp == '_' || isalnum ((unsigned char) *cp))
403     {
404       char *dest = tokstr;
405       token = T_ID;
406       while (*cp == '_' || isalnum ((unsigned char) *cp))
407         *dest++ = toupper ((unsigned char) (*cp++));
408       *dest++ = '\0';
409     }
410   else
411     token = *cp++;
412   
413 #if DEBUGGING
414   dump_token ();
415 #endif
416   
417   return token;
418 }
419
420 /* Force the current token to be an identifier token. */
421 static void
422 force_id (void)
423 {
424   if (token != T_ID)
425     error ("Identifier expected.");
426 }
427
428 /* Force the current token to be a string token. */
429 static void
430 force_string (void)
431 {
432   if (token != T_STRING)
433     error ("String expected.");
434 }
435
436 /* Checks whether the current token is the identifier S; if so, skips
437    the token and returns 1; otherwise, returns 0. */
438 static int
439 match_id (const char *s)
440 {
441   if (token == T_ID && !strcmp (tokstr, s))
442     {
443       lex_get ();
444       return 1;
445     }
446   return 0;
447 }
448
449 /* Checks whether the current token is T.  If so, skips the token and
450    returns 1; otherwise, returns 0. */
451 static int
452 match_token (int t)
453 {
454   if (token == t)
455     {
456       lex_get ();
457       return 1;
458     }
459   return 0;
460 }
461
462 /* Force the current token to be T, and skip it. */
463 static void
464 skip_token (int t)
465 {
466   if (token != t)
467     error ("`%c' expected.", t);
468   lex_get ();
469 }
470 \f
471 /* Structures. */
472
473 /* Some specifiers have associated values. */
474 enum
475   {
476     VAL_NONE,   /* No value. */
477     VAL_INT,    /* Integer value. */
478     VAL_DBL     /* Floating point value. */
479   };
480
481 /* For those specifiers with values, the syntax of those values. */
482 enum
483   {
484     VT_PLAIN,   /* Unadorned value. */
485     VT_PAREN    /* Value must be enclosed in parentheses. */
486   };
487
488 /* Forward definition. */
489 typedef struct specifier specifier;
490
491 /* A single setting. */
492 typedef struct setting setting;
493 struct setting
494   {
495     specifier *parent;  /* Owning specifier. */
496     setting *next;      /* Next in the chain. */
497     char *specname;     /* Name of the setting. */
498     int con;            /* Sequence number. */
499
500     /* Values. */
501     int valtype;        /* One of VT_*. */
502     int value;          /* One of VAL_*. */
503     int optvalue;       /* 1=value is optional, 0=value is required. */
504     char *valname;      /* Variable name for the value. */
505     char *restriction;  /* !=NULL: expression specifying valid values. */
506   };
507
508 /* A single specifier. */
509 struct specifier
510   {
511     specifier *next;    /* Next in the chain. */
512     char *varname;      /* Variable name. */
513     setting *s;         /* Associated settings. */
514
515     setting *def;       /* Default setting. */
516     setting *omit_kw;   /* Setting for which the keyword can be omitted. */
517     
518     int index;          /* Next array index. */
519   };
520
521 /* Subcommand types. */
522 typedef enum
523   {
524     SBC_PLAIN,          /* The usual case. */
525     SBC_VARLIST,        /* Variable list. */
526     SBC_INT,            /* Integer value. */
527     SBC_PINT,           /* Integer inside parentheses. */
528     SBC_DBL,            /* Floating point value. */
529     SBC_INT_LIST,       /* List of integers (?). */
530     SBC_DBL_LIST,       /* List of floating points (?). */
531     SBC_CUSTOM,         /* Custom. */
532     SBC_ARRAY,          /* Array of boolean values. */
533     SBC_STRING,         /* String value. */
534     SBC_VAR             /* Single variable name. */
535   }
536 subcommand_type;
537
538 /* A single subcommand. */
539 typedef struct subcommand subcommand;
540 struct subcommand
541   {
542     subcommand *next;           /* Next in the chain. */
543     char *name;                 /* Subcommand name. */
544     subcommand_type type;       /* One of SBC_*. */
545     int once;                   /* 1=Subcommand may appear only once. */
546     int narray;                 /* Index of next array element. */
547     const char *prefix;         /* Prefix for variable and constant names. */
548     specifier *spec;            /* Array of specifiers. */
549     
550     /* SBC_STRING and SBC_INT only. */
551     char *restriction;          /* Expression restricting string length. */
552     char *message;              /* Error message. */
553     int translatable;           /* Error message is translatable */
554   };
555
556 typedef struct aux_subcommand aux_subcommand;
557 struct aux_subcommand
558   {
559     aux_subcommand *next;       /* Next in the chain. */
560     char *name;                 /* Subcommand name. */
561     char *value;                /* Subcommand value */
562   };
563
564 static aux_subcommand *aux_subcommands ;
565
566 /* Name of the command; i.e., DESCRIPTIVES. */
567 char *cmdname;
568
569 /* Short prefix for the command; i.e., `dsc_'. */
570 char *prefix;
571
572 /* List of subcommands. */
573 subcommand *subcommands;
574
575 /* Default subcommand if any, or NULL. */
576 subcommand *def;
577 \f
578 /* Parsing. */
579
580 void parse_subcommands (void);
581
582 /* Parse an entire specification. */
583 static void
584 parse (void)
585 {
586   /* Get the command name and prefix. */
587   if (token != T_STRING && token != T_ID)
588     error ("Command name expected.");
589   cmdname = xstrdup (tokstr);
590   lex_get ();
591   skip_token ('(');
592   force_id ();
593   prefix = xstrdup (tokstr);
594   lex_get ();
595   skip_token (')');
596   skip_token (':');
597
598   /* Read all the subcommands. */
599   subcommands = NULL;
600   def = NULL;
601   parse_subcommands ();
602 }
603
604 /* Parses a single setting into S, given subcommand information SBC
605    and specifier information SPEC. */
606 static void
607 parse_setting (setting *s, specifier *spec)
608 {
609   s->parent = spec;
610   
611   if (match_token ('*'))
612     {
613       if (spec->omit_kw)
614         error ("Cannot have two settings with omittable keywords.");
615       else
616         spec->omit_kw = s;
617     }
618   
619   if (match_token ('!'))
620     {
621       if (spec->def)
622         error ("Cannot have two default settings.");
623       else
624         spec->def = s;
625     }
626   
627   force_id ();
628   s->specname = xstrdup (tokstr);
629   s->con = add_symbol (s->specname, 0, 0);
630   s->value = VAL_NONE;
631
632   lex_get ();
633
634   /* Parse setting value info if necessary. */
635   if (token != '/' && token != ';' && token != '.' && token != ',')
636     {
637       if (token == '(')
638         {
639           s->valtype = VT_PAREN;
640           lex_get ();
641         }
642       else
643         s->valtype = VT_PLAIN;
644
645       s->optvalue = match_token ('*');
646       
647       if (match_id ("N"))
648         s->value = VAL_INT;
649       else if (match_id ("D"))
650         s->value = VAL_DBL;
651       else
652         error ("`n' or `d' expected.");
653       
654       skip_token (':');
655       
656       force_id ();
657       s->valname = xstrdup (tokstr);
658       lex_get ();
659       
660       if (token == ',')
661         {
662           lex_get ();
663           force_string ();
664           s->restriction = xstrdup (tokstr);
665           lex_get ();
666         }
667       else
668         s->restriction = NULL;
669       
670       if (s->valtype == VT_PAREN)
671         skip_token (')');
672     }
673 }
674
675 /* Parse a single specifier into SPEC, given subcommand information
676    SBC. */
677 static void
678 parse_specifier (specifier *spec, subcommand *sbc)
679 {
680   spec->index = 0;
681   spec->s = NULL;
682   spec->def = NULL;
683   spec->omit_kw = NULL;
684   spec->varname = NULL;
685
686   if (token == T_ID)
687     {
688       spec->varname = xstrdup (st_lower (tokstr));
689       lex_get ();
690     }
691   
692   /* Handle array elements. */
693   if (token != ':')
694     {
695       spec->index = sbc->narray;
696       if (sbc->type == SBC_ARRAY)
697         {
698           if (token == '|')
699             token = ',';
700           else
701             sbc->narray++;
702         }
703       spec->s = NULL;
704       return;
705     }
706   skip_token (':');
707   
708   /* Parse all the settings. */
709   {
710     setting **s = &spec->s;
711     
712     for (;;)
713       {
714         *s = xmalloc (sizeof (setting));
715         parse_setting (*s, spec);
716         if (token == ',' || token == ';' || token == '.')
717           break;
718         skip_token ('/');
719         s = &(*s)->next;
720       }
721     (*s)->next = NULL;
722   }
723 }
724
725 /* Parse a list of specifiers for subcommand SBC. */
726 static void
727 parse_specifiers (subcommand *sbc)
728 {
729   specifier **spec = &sbc->spec;
730
731   if (token == ';' || token == '.')
732     {
733       *spec = NULL;
734       return;
735     }
736   
737   for (;;)
738     {
739       *spec = xmalloc (sizeof (specifier));
740       parse_specifier (*spec, sbc);
741       if (token == ';' || token == '.')
742         break;
743       skip_token (',');
744       spec = &(*spec)->next;
745     }
746   (*spec)->next = NULL;
747 }
748
749 /* Parse a subcommand into SBC. */
750 static void
751 parse_subcommand (subcommand *sbc)
752 {
753   if (match_token ('*'))
754     {
755       if (def)
756         error ("Multiple default subcommands.");
757       def = sbc;
758     }
759
760   sbc->once = match_token ('+');
761
762   force_id ();
763   sbc->name = xstrdup (tokstr);
764   lex_get ();
765   
766   sbc->narray = 0;
767   sbc->type = SBC_PLAIN;
768   sbc->spec = NULL;
769   sbc->translatable = 0;
770
771   if (match_token ('['))
772     {
773       force_id ();
774       sbc->prefix = xstrdup (st_lower (tokstr));
775       lex_get ();
776       
777       skip_token (']');
778       skip_token ('=');
779       
780       sbc->type = SBC_ARRAY;
781       parse_specifiers (sbc);
782     }
783   else
784     {
785       if (match_token ('('))
786         {
787           force_id ();
788           sbc->prefix = xstrdup (st_lower (tokstr));
789           lex_get ();
790           
791           skip_token (')');
792         }
793       else
794         sbc->prefix = "";
795       
796       skip_token ('=');
797
798       if (match_id ("VAR"))
799         sbc->type = SBC_VAR;
800       if (match_id ("VARLIST"))
801         {
802           if (match_token ('('))
803             {
804               force_string ();
805               sbc->message = xstrdup (tokstr);
806               lex_get();
807               
808               skip_token (')');
809             }
810           else sbc->message = NULL;
811
812           sbc->type = SBC_VARLIST;
813         }
814       else if (match_id ("INTEGER"))
815        {
816         sbc->type = match_id ("LIST") ? SBC_INT_LIST : SBC_INT;
817         if ( token == T_STRING) 
818          {
819               sbc->restriction = xstrdup (tokstr);
820               lex_get ();
821               if ( match_id("N_") )
822                {
823                 skip_token('(');
824                 force_string ();
825                 lex_get();
826                 skip_token(')');
827                 sbc->translatable = 1;
828                }
829               else {
830                 force_string ();
831                 lex_get ();
832               }
833               sbc->message = xstrdup (tokstr);
834          }
835         else
836             sbc->restriction = NULL;
837        }
838       else if (match_id ("PINT"))
839         sbc->type = SBC_PINT;
840       else if (match_id ("DOUBLE"))
841         {
842           if ( match_id ("LIST") )
843             sbc->type = SBC_DBL_LIST;
844           else
845             sbc->type = SBC_DBL;
846         }
847       else if (match_id ("STRING"))
848         {
849           sbc->type = SBC_STRING;
850           if (token == T_STRING)
851             {
852               sbc->restriction = xstrdup (tokstr);
853               lex_get ();
854               force_string ();
855               sbc->message = xstrdup (tokstr);
856               lex_get ();
857             }
858           else
859             sbc->restriction = NULL;
860         }
861       else if (match_id ("CUSTOM"))
862         sbc->type = SBC_CUSTOM;
863       else
864         parse_specifiers (sbc);
865     }
866 }
867
868 /* Parse all the subcommands. */
869 void
870 parse_subcommands (void)
871 {
872   subcommand **sbc = &subcommands;
873   
874   for (;;)
875     {
876       *sbc = xmalloc (sizeof (subcommand));
877       (*sbc)->next = NULL;
878
879       parse_subcommand (*sbc);
880
881       if (token == '.')
882         return;
883
884       skip_token (';');
885       sbc = &(*sbc)->next;
886     }
887 }
888 \f
889 /* Output. */
890
891 #define BASE_INDENT 2           /* Starting indent. */
892 #define INC_INDENT 2            /* Indent increment. */
893
894 /* Increment the indent. */
895 #define indent() indent += INC_INDENT
896 #define outdent() indent -= INC_INDENT
897
898 /* Size of the indent from the left margin. */
899 int indent;
900
901 void dump (int, const char *, ...) PRINTF_FORMAT (2, 3);
902
903 /* Write line FORMAT to the output file, formatted as with printf,
904    indented `indent' characters from the left margin.  If INDENTION is
905    greater than 0, indents BASE_INDENT * INDENTION characters after
906    writing the line; if INDENTION is less than 0, dedents BASE_INDENT
907    * INDENTION characters _before_ writing the line. */
908 void
909 dump (int indention, const char *format, ...)
910 {
911   va_list args;
912   int i;
913
914   if (indention < 0)
915     indent += BASE_INDENT * indention;
916   
917   oln++;
918   va_start (args, format);
919   for (i = 0; i < indent; i++)
920     putc (' ', out);
921   vfprintf (out, format, args);
922   putc ('\n', out);
923   va_end (args);
924
925   if (indention > 0)
926     indent += BASE_INDENT * indention;
927 }
928
929 /* Write the structure members for specifier SPEC to the output file.
930    SBC is the including subcommand. */
931 static void
932 dump_specifier_vars (const specifier *spec, const subcommand *sbc)
933 {
934   if (spec->varname)
935     dump (0, "long %s%s;", sbc->prefix, spec->varname);
936   
937   {
938     setting *s;
939
940     for (s = spec->s; s; s = s->next)
941       {
942         if (s->value != VAL_NONE)
943           {
944             const char *typename;
945
946             assert (s->value == VAL_INT || s->value == VAL_DBL);
947             typename = s->value == VAL_INT ? "long" : "double";
948
949             dump (0, "%s %s%s;", typename, sbc->prefix, st_lower (s->valname));
950           }
951       }
952   }
953 }
954
955 /* Returns 1 if string T is a PSPP keyword, 0 otherwise. */
956 static int
957 is_keyword (const char *t)
958 {
959   static const char *kw[] =
960     {
961       "AND", "OR", "NOT", "EQ", "GE", "GT", "LE", "LT",
962       "NE", "ALL", "BY", "TO", "WITH", 0,
963     };
964   const char **cp;
965
966   for (cp = kw; *cp; cp++)
967     if (!strcmp (t, *cp))
968       return 1;
969   return 0;
970 }
971
972 /* Transforms a string NAME into a valid C identifier: makes
973    everything lowercase and maps nonalphabetic characters to
974    underscores.  Returns a pointer to a static buffer. */
975 static char *
976 make_identifier (const char *name)
977 {
978   char *p = get_buffer ();
979   char *cp;
980
981   for (cp = p; *name; name++)
982     if (isalpha ((unsigned char) *name))
983       *cp++ = tolower ((unsigned char) (*name));
984     else
985       *cp++ = '_';
986   *cp = '\0';
987   
988   return p;
989 }
990
991 /* Writes the struct and enum declarations for the parser. */
992 static void
993 dump_declarations (void)
994 {
995   indent = 0;
996
997   /* Write out enums for all the identifiers in the symbol table. */
998   {
999     int f, k;
1000     symbol *sym;
1001     char *buf = NULL;
1002
1003     /* Note the squirmings necessary to make sure that the last enum
1004        is not followed by a comma, as mandated by ANSI C89. */
1005     for (sym = symtab, f = k = 0; sym; sym = sym->next)
1006       if (!sym->unique && !is_keyword (sym->name))
1007         {
1008           if (!f)
1009             {
1010               dump (0, "/* Settings for subcommand specifiers. */");
1011               dump (1, "enum");
1012               dump (1, "{");
1013               f = 1;
1014             }
1015
1016           if (buf == NULL)
1017             buf = xmalloc (1024);
1018           else
1019             dump (0, buf);
1020           
1021           if (k)
1022             sprintf (buf, "%s%s,", st_upper (prefix), sym->name);
1023           else
1024             {
1025               k = 1;
1026               sprintf (buf, "%s%s = 1000,", st_upper (prefix), sym->name);
1027             }
1028         }
1029     if (buf)
1030       {
1031         buf[strlen (buf) - 1] = 0;
1032         dump (0, buf);
1033         free (buf);
1034       }
1035     if (f)
1036       {
1037         dump (-1, "};");
1038         dump (-1, nullstr);
1039       }
1040   }
1041
1042   /* Write out some type definitions */
1043   {
1044     dump (0, "#define MAXLISTS 10");
1045   }
1046
1047
1048   /* For every array subcommand, write out the associated enumerated
1049      values. */
1050   {
1051     subcommand *sbc;
1052
1053     for (sbc = subcommands; sbc; sbc = sbc->next)
1054       if (sbc->type == SBC_ARRAY && sbc->narray)
1055         {
1056           dump (0, "/* Array indices for %s subcommand. */", sbc->name);
1057           
1058           dump (1, "enum");
1059           dump (1, "{");
1060
1061           {
1062             specifier *spec;
1063
1064             for (spec = sbc->spec; spec; spec = spec->next)
1065               if (!spec->s)
1066                 dump (0, "%s%s%s = %d,",
1067                       st_upper (prefix), st_upper (sbc->prefix),
1068                       st_upper (spec->varname), spec->index);
1069
1070             dump (0, "%s%scount", st_upper (prefix), st_upper (sbc->prefix));
1071
1072             dump (-1, "};");
1073             dump (-1, nullstr);
1074           }
1075         }
1076   }
1077
1078   /* Write out structure declaration. */
1079   {
1080     subcommand *sbc;
1081
1082     dump (0, "/* %s structure. */", cmdname);
1083     dump (1, "struct cmd_%s", make_identifier (cmdname));
1084     dump (1, "{");
1085     for (sbc = subcommands; sbc; sbc = sbc->next)
1086       {
1087         int f = 0;
1088
1089         if (sbc != subcommands)
1090           dump (0, nullstr);
1091         
1092         dump (0, "/* %s subcommand. */", sbc->name);
1093         dump (0, "int sbc_%s;", st_lower (sbc->name));
1094
1095         switch (sbc->type)
1096           {
1097           case SBC_ARRAY:
1098           case SBC_PLAIN:
1099             {
1100               specifier *spec;
1101             
1102               for (spec = sbc->spec; spec; spec = spec->next)
1103                 {
1104                   if (spec->s == 0)
1105                     {
1106                       if (sbc->type == SBC_PLAIN)
1107                         dump (0, "long int %s%s;", st_lower (sbc->prefix),
1108                               spec->varname);
1109                       else if (f == 0)
1110                         {
1111                           dump (0, "int a_%s[%d];", 
1112                                 st_lower (sbc->name), sbc->narray);
1113                           f = 1;
1114                         }
1115                     }
1116                   else
1117                     dump_specifier_vars (spec, sbc);
1118                 }
1119             }
1120             break;
1121
1122           case SBC_VARLIST:
1123             dump (0, "int %sn_%s;", st_lower (sbc->prefix),
1124                   st_lower (sbc->name));
1125             dump (0, "struct variable **%sv_%s;", st_lower (sbc->prefix),
1126                   st_lower (sbc->name));
1127             break;
1128
1129           case SBC_VAR:
1130             dump (0, "struct variable *%sv_%s;", st_lower (sbc->prefix),
1131                   st_lower (sbc->name));
1132             break;
1133
1134           case SBC_STRING:
1135             dump (0, "char *s_%s;", st_lower (sbc->name));
1136             break;
1137
1138           case SBC_INT:
1139           case SBC_PINT:
1140             dump (0, "long n_%s;", st_lower (sbc->name));
1141             break;
1142
1143           case SBC_DBL:
1144             dump (0, "double n_%s;", st_lower (sbc->name));
1145             break;
1146
1147           case SBC_DBL_LIST:
1148             dump (0, "subc_list_double dl_%s[MAXLISTS];",
1149                   st_lower(sbc->name));
1150             break;
1151
1152           case SBC_INT_LIST:
1153             dump (0, "subc_list_int il_%s[MAXLISTS];",
1154                   st_lower(sbc->name));
1155             break;
1156
1157
1158           default:;
1159             /* nothing */
1160           }
1161       }
1162
1163     dump (-1, "};");
1164     dump (-1, nullstr);
1165   }
1166
1167   /* Write out prototypes for custom_*() functions as necessary. */
1168   {
1169     int seen = 0;
1170     subcommand *sbc;
1171
1172     for (sbc = subcommands; sbc; sbc = sbc->next)
1173       if (sbc->type == SBC_CUSTOM)
1174         {
1175           if (!seen)
1176             {
1177               seen = 1;
1178               dump (0, "/* Prototype for custom subcommands of %s. */",
1179                     cmdname);
1180             }
1181           dump (0, "static int %scustom_%s (struct cmd_%s *);",
1182                 st_lower (prefix), st_lower (sbc->name),
1183                 make_identifier (cmdname));
1184         }
1185
1186     if (seen)
1187       dump (0, nullstr);
1188   }
1189
1190   /* Prototypes for parsing and freeing functions. */
1191   {
1192     dump (0, "/* Command parsing functions. */");
1193     dump (0, "static int parse_%s (struct cmd_%s *);",
1194           make_identifier (cmdname), make_identifier (cmdname));
1195     dump (0, "static void free_%s (struct cmd_%s *);",
1196           make_identifier (cmdname), make_identifier (cmdname));
1197     dump (0, nullstr);
1198   }
1199 }
1200
1201 /* Writes out code to initialize all the variables that need
1202    initialization for particular specifier SPEC inside subcommand SBC. */
1203 static void
1204 dump_specifier_init (const specifier *spec, const subcommand *sbc)
1205 {
1206   if (spec->varname)
1207     {
1208       char s[256];
1209
1210       if (spec->def)
1211         sprintf (s, "%s%s",
1212                  st_upper (prefix), find_symbol (spec->def->con)->name);
1213       else
1214         strcpy (s, "-1");
1215       dump (0, "p->%s%s = %s;", sbc->prefix, spec->varname, s);
1216     }
1217   
1218   {
1219     setting *s;
1220
1221     for (s = spec->s; s; s = s->next)
1222       {
1223         if (s->value != VAL_NONE)
1224           {
1225             const char *init;
1226
1227             assert (s->value == VAL_INT || s->value == VAL_DBL);
1228             init = s->value == VAL_INT ? "NOT_LONG" : "SYSMIS";
1229
1230             dump (0, "p->%s%s = %s;", sbc->prefix, st_lower (s->valname), init);
1231           }
1232       }
1233   }
1234 }
1235
1236 /* Write code to initialize all variables. */
1237 static void
1238 dump_vars_init (int persistent)
1239 {
1240   /* Loop through all the subcommands. */
1241   {
1242     subcommand *sbc;
1243     
1244     for (sbc = subcommands; sbc; sbc = sbc->next)
1245       {
1246         int f = 0;
1247         
1248         dump (0, "p->sbc_%s = 0;", st_lower (sbc->name));
1249         if ( ! persistent ) 
1250           {
1251             switch (sbc->type)
1252               {
1253               case SBC_INT_LIST:
1254                 break;
1255
1256               case SBC_DBL_LIST:
1257                 dump (0, "int i;");
1258                 dump (0, "for (i = 0; i < MAXLISTS; ++i)");
1259                 dump (1, "{");
1260                 dump (0, "subc_list_double_create(&p->dl_%s[i]) ;",
1261                       st_lower (sbc->name)
1262                       );
1263                 dump (-1, "}");
1264                 break;
1265
1266               case SBC_DBL:
1267               case SBC_CUSTOM:
1268                 /* nothing */
1269                 break;
1270             
1271               case SBC_PLAIN:
1272               case SBC_ARRAY:
1273                 {
1274                   specifier *spec;
1275             
1276                   for (spec = sbc->spec; spec; spec = spec->next)
1277                     if (spec->s == NULL)
1278                       {
1279                         if (sbc->type == SBC_PLAIN)
1280                           dump (0, "p->%s%s = 0;", sbc->prefix, spec->varname);
1281                         else if (f == 0)
1282                           {
1283                             dump (0, "memset (p->a_%s, 0, sizeof p->a_%s);",
1284                                   st_lower (sbc->name), st_lower (sbc->name));
1285                             f = 1;
1286                           }
1287                       }
1288                     else
1289                       dump_specifier_init (spec, sbc);
1290                 }
1291                 break;
1292
1293               case SBC_VARLIST:
1294                 dump (0, "p->%sn_%s = 0;",
1295                       st_lower (sbc->prefix), st_lower (sbc->name));
1296                 dump (0, "p->%sv_%s = NULL;",
1297                       st_lower (sbc->prefix), st_lower (sbc->name));
1298                 break;
1299             
1300               case SBC_VAR:
1301                 dump (0, "p->%sv_%s = NULL;",
1302                       st_lower (sbc->prefix), st_lower (sbc->name));
1303                 break;
1304
1305               case SBC_STRING:
1306                 dump (0, "p->s_%s = NULL;", st_lower (sbc->name));
1307                 break;
1308
1309               case SBC_INT:
1310               case SBC_PINT:
1311                 dump (0, "p->n_%s = NOT_LONG;", st_lower (sbc->name));
1312                 break;
1313
1314               default:
1315                 assert (0);
1316               }
1317           }
1318       }
1319   }
1320 }
1321
1322 /* Return a pointer to a static buffer containing an expression that
1323    will match token T. */
1324 static char *
1325 make_match (const char *t)
1326 {
1327   char *s;
1328
1329   s = get_buffer ();
1330
1331   while (*t == '_')
1332     t++;
1333       
1334   if (is_keyword (t))
1335     sprintf (s, "lex_match (T_%s)", t);
1336   else if (!strcmp (t, "ON") || !strcmp (t, "YES"))
1337     strcpy (s, "(lex_match_id (\"ON\") || lex_match_id (\"YES\") "
1338             "|| lex_match_id (\"TRUE\"))");
1339   else if (!strcmp (t, "OFF") || !strcmp (t, "NO"))
1340     strcpy (s, "(lex_match_id (\"OFF\") || lex_match_id (\"NO\") "
1341             "|| lex_match_id (\"FALSE\"))");
1342   else if (isdigit ((unsigned char) t[0]))
1343     sprintf (s, "lex_match_int (%s)", t);
1344   else
1345     sprintf (s, "lex_match_id (\"%s\")", t);
1346   
1347   return s;
1348 }
1349
1350 /* Write out the parsing code for specifier SPEC within subcommand
1351    SBC. */
1352 static void
1353 dump_specifier_parse (const specifier *spec, const subcommand *sbc)
1354 {
1355   setting *s;
1356
1357   if (spec->omit_kw && spec->omit_kw->next)
1358     error ("Omittable setting is not last setting in `%s' specifier.",
1359            spec->varname);
1360   if (spec->omit_kw && spec->omit_kw->parent->next)
1361     error ("Default specifier is not in last specifier in `%s' "
1362            "subcommand.", sbc->name);
1363   
1364   for (s = spec->s; s; s = s->next)
1365     {
1366       int first = spec == sbc->spec && s == spec->s;
1367
1368       /* Match the setting's keyword. */
1369       if (spec->omit_kw == s)
1370         {
1371           if (!first)
1372             {
1373               dump (1, "else");
1374               dump (1, "{");
1375             }
1376           dump (1, "%s;", make_match (s->specname));
1377         }
1378       else
1379         dump (1, "%sif (%s)", first ? "" : "else ",
1380               make_match (s->specname));
1381
1382       /* Handle values. */
1383       if (s->value == VAL_NONE)
1384         dump (0, "p->%s%s = %s%s;", sbc->prefix, spec->varname,
1385               st_upper (prefix), find_symbol (s->con)->name);
1386       else
1387         {
1388           if (spec->omit_kw != s)
1389             dump (1, "{");
1390           
1391           if (spec->varname)
1392             dump (0, "p->%s%s = %s%s;", sbc->prefix, spec->varname,
1393                   st_upper (prefix), find_symbol (s->con)->name);
1394           
1395           if (s->valtype == VT_PAREN)
1396             {
1397               if (s->optvalue)
1398                 {
1399                   dump (1, "if (lex_match ('('))");
1400                   dump (1, "{");
1401                 }
1402               else
1403                 {
1404                   dump (1, "if (!lex_match ('('))");
1405                   dump (1, "{");
1406                   dump (0, "msg (SE, _(\"`(' expected after %s "
1407                         "specifier of %s subcommand.\"));",
1408                         s->specname, sbc->name);
1409                   dump (0, "goto lossage;");
1410                   dump (-1, "}");
1411                   outdent ();
1412                 }
1413             }
1414
1415           if (s->value == VAL_INT)
1416             {
1417               dump (1, "if (!lex_integer_p ())");
1418               dump (1, "{");
1419               dump (0, "msg (SE, _(\"%s specifier of %s subcommand "
1420                     "requires an integer argument.\"));",
1421                     s->specname, sbc->name);
1422               dump (0, "goto lossage;");
1423               dump (-1, "}");
1424               dump (-1, "p->%s%s = lex_integer ();",
1425                     sbc->prefix, st_lower (s->valname));
1426             }
1427           else
1428             {
1429               dump (1, "if (token != T_NUM)");
1430               dump (1, "{");
1431               dump (0, "msg (SE, _(\"Number expected after %s "
1432                     "specifier of %s subcommand.\"));",
1433                     s->specname, sbc->name);
1434               dump (0, "goto lossage;");
1435               dump (-1, "}");
1436               dump (-1, "p->%s%s = tokval;", sbc->prefix,
1437                     st_lower (s->valname));
1438             }
1439           
1440           if (s->restriction)
1441             {
1442               {
1443                 char *str, *str2;
1444                 str = xmalloc (MAX_TOK_LEN);
1445                 str2 = xmalloc (MAX_TOK_LEN);
1446                 sprintf (str2, "p->%s%s", sbc->prefix, st_lower (s->valname));
1447                 sprintf (str, s->restriction, str2, str2, str2, str2,
1448                          str2, str2, str2, str2);
1449                 dump (1, "if (!(%s))", str);
1450                 free (str);
1451                 free (str2);
1452               }
1453               
1454               dump (1, "{");
1455               dump (0, "msg (SE, _(\"Bad argument for %s "
1456                     "specifier of %s subcommand.\"));",
1457                     s->specname, sbc->name);
1458               dump (0, "goto lossage;");
1459               dump (-1, "}");
1460               outdent ();
1461             }
1462           
1463           dump (0, "lex_get ();");
1464           
1465           if (s->valtype == VT_PAREN)
1466             {
1467               dump (1, "if (!lex_match (')'))");
1468               dump (1, "{");
1469               dump (0, "msg (SE, _(\"`)' expected after argument for "
1470                     "%s specifier of %s.\"));",
1471                     s->specname, sbc->name);
1472               dump (0, "goto lossage;");
1473               dump (-1, "}");
1474               outdent ();
1475               if (s->optvalue)
1476                 {
1477                   dump (-1, "}");
1478                   outdent ();
1479                 }
1480             }
1481           
1482           if (s != spec->omit_kw)
1483             dump (-1, "}");
1484         }
1485       
1486       if (s == spec->omit_kw)
1487         {
1488           dump (-1, "}");
1489           outdent ();
1490         }
1491       outdent ();
1492     }
1493 }
1494
1495 /* Write out the code to parse subcommand SBC. */
1496 static void
1497 dump_subcommand (const subcommand *sbc)
1498 {
1499   if (sbc->type == SBC_PLAIN || sbc->type == SBC_ARRAY)
1500     {
1501       int count;
1502
1503       dump (1, "while (token != '/' && token != '.')");
1504       dump (1, "{");
1505       
1506       {
1507         specifier *spec;
1508
1509         for (count = 0, spec = sbc->spec; spec; spec = spec->next)
1510           {
1511             if (spec->s)
1512               dump_specifier_parse (spec, sbc);
1513             else
1514               {
1515                 count++;
1516                 dump (1, "%sif (%s)", spec != sbc->spec ? "else " : "",
1517                       make_match (st_upper (spec->varname)));
1518                 if (sbc->type == SBC_PLAIN)
1519                   dump (0, "p->%s%s = 1;", st_lower (sbc->prefix),
1520                         spec->varname);
1521                 else
1522                   dump (0, "p->a_%s[%s%s%s] = 1;",
1523                         st_lower (sbc->name),
1524                         st_upper (prefix), st_upper (sbc->prefix),
1525                         st_upper (spec->varname));
1526                 outdent ();
1527               }
1528           }
1529       }
1530       
1531       {
1532         specifier *spec;
1533         setting *s;
1534
1535         /* This code first finds the last specifier in sbc.  Then it
1536            finds the last setting within that last specifier.  Either
1537            or both might be NULL. */
1538         spec = sbc->spec;
1539         s = NULL;
1540         if (spec)
1541           {
1542             while (spec->next)
1543               spec = spec->next;
1544             s = spec->s;
1545             if (s)
1546               while (s->next)
1547                 s = s->next;
1548           }
1549
1550         if (spec && (!spec->s || !spec->omit_kw))
1551           {
1552             dump (1, "else");
1553             dump (1, "{");
1554             dump (0, "lex_error (NULL);");
1555             dump (0, "goto lossage;");
1556             dump (-1, "}");
1557             outdent ();
1558           }
1559       }
1560
1561       dump (0, "lex_match (',');");
1562       dump (-1, "}");
1563       outdent ();
1564     }
1565   else if (sbc->type == SBC_VARLIST)
1566     {
1567       dump (1, "if (!parse_variables (default_dict, &p->%sv_%s, &p->%sn_%s, "
1568             "PV_APPEND%s%s))",
1569             st_lower (sbc->prefix), st_lower (sbc->name),
1570             st_lower (sbc->prefix), st_lower (sbc->name),
1571             sbc->message ? " |" : "",
1572             sbc->message ? sbc->message : "");
1573       dump (0, "goto lossage;");
1574       outdent ();
1575     }
1576   else if (sbc->type == SBC_VAR)
1577     {
1578       dump (0, "p->%sv_%s = parse_variable ();",
1579             st_lower (sbc->prefix), st_lower (sbc->name));
1580       dump (1, "if (p->%sv_%s)",
1581             st_lower (sbc->prefix), st_lower (sbc->name));
1582       dump (0, "goto lossage;");
1583       outdent ();
1584     }
1585   else if (sbc->type == SBC_STRING)
1586     {
1587       if (sbc->restriction)
1588         {
1589           dump (1, "{");
1590           dump (0, "int x;");
1591         }
1592       dump (1, "if (!lex_force_string ())");
1593       dump (0, "return 0;");
1594       outdent ();
1595       if (sbc->restriction)
1596         {
1597           dump (0, "x = ds_length (&tokstr);");
1598           dump (1, "if (!(%s))", sbc->restriction);
1599           dump (1, "{");
1600           dump (0, "msg (SE, _(\"String for %s must be %s.\"));",
1601                 sbc->name, sbc->message);
1602           dump (0, "goto lossage;");
1603           dump (-1, "}");
1604           outdent ();
1605         }
1606       dump (0, "free(p->s_%s);", st_lower(sbc->name) );
1607       dump (0, "p->s_%s = xstrdup (ds_c_str (&tokstr));",
1608             st_lower (sbc->name));
1609       dump (0, "lex_get ();");
1610       if (sbc->restriction)
1611         dump (-1, "}");
1612     }
1613   else if (sbc->type == SBC_DBL)
1614     {
1615       dump (1, "if (!lex_force_num ())");
1616       dump (0, "goto lossage;");
1617       dump (-1, "p->n_%s = lex_double ();", st_lower (sbc->name));
1618       dump (0, "lex_get();");
1619     }
1620   else if (sbc->type == SBC_INT)
1621     {
1622       dump(1, "{");
1623       dump(0, "int x;");
1624       dump (1, "if (!lex_force_int ())");
1625       dump (0, "goto lossage;");
1626       dump (-1, "x = lex_integer ();");
1627       dump (0, "lex_get();");
1628       if (sbc->restriction)
1629        {
1630           char buf[1024];
1631           dump (1, "if (!(%s))", sbc->restriction);
1632           dump (1, "{"); 
1633           sprintf(buf,sbc->message,sbc->name);
1634           if ( sbc->translatable ) 
1635                   dump (0, "msg (SE, gettext(\"%s\"));",buf);
1636           else
1637                   dump (0, "msg (SE, \"%s\");",buf);
1638           dump (0, "goto lossage;");
1639           dump (-1, "}");
1640       }
1641       dump (-1, "p->n_%s = x;", st_lower (sbc->name));
1642       dump (-1,"}");
1643     }
1644   else if (sbc->type == SBC_PINT)
1645     {
1646       dump (0, "lex_match ('(');");
1647       dump (1, "if (!lex_force_int ())");
1648       dump (0, "goto lossage;");
1649       dump (-1, "p->n_%s = lex_integer ();", st_lower (sbc->name));
1650       dump (0, "lex_match (')');");
1651     }
1652   else if (sbc->type == SBC_DBL_LIST)
1653     {
1654       dump (0, "if ( p->sbc_%s > MAXLISTS)",st_lower(sbc->name));
1655       dump (1, "{");
1656       dump (0, "msg (SE, \"No more than %%d %s subcommands allowed\",MAXLISTS);",st_lower(sbc->name));
1657       dump (0, "goto lossage;");
1658       dump (-1,"}");
1659
1660       dump (1, "while (token != '/' && token != '.')");
1661       dump (1, "{");
1662       dump (0, "lex_match(',');");
1663       dump (0, "if (!lex_force_num ())");
1664       dump (1, "{");
1665       dump (0, "goto lossage;");
1666       dump (-1,"}");
1667
1668       dump (0, "subc_list_double_push(&p->dl_%s[p->sbc_%s-1],lex_double ());", 
1669             st_lower (sbc->name),st_lower (sbc->name)
1670             );
1671
1672       dump (0, "lex_get();");
1673       dump (-1,"}");
1674
1675     }
1676   else if (sbc->type == SBC_CUSTOM)
1677     {
1678       dump (1, "switch (%scustom_%s (p))",
1679             st_lower (prefix), st_lower (sbc->name));
1680       dump (0, "{");
1681       dump (1, "case 0:");
1682       dump (0, "goto lossage;");
1683       dump (-1, "case 1:");
1684       indent ();
1685       dump (0, "break;");
1686       dump (-1, "case 2:");
1687       indent ();
1688       dump (0, "lex_error (NULL);");
1689       dump (0, "goto lossage;");
1690       dump (-1, "default:");
1691       indent ();
1692       dump (0, "assert (0);");
1693       dump (-1, "}");
1694       outdent ();
1695     }
1696 }
1697
1698 /* Write out entire parser. */
1699 static void
1700 dump_parser (int persistent)
1701 {
1702   int f;
1703
1704   indent = 0;
1705
1706   dump (0, "static int");
1707   dump (0, "parse_%s (struct cmd_%s *p)", make_identifier (cmdname),
1708         make_identifier (cmdname));
1709   dump (1, "{");
1710
1711   dump_vars_init (persistent);
1712
1713   dump (1, "for (;;)");
1714   dump (1, "{");
1715
1716   f = 0;
1717   if (def && (def->type == SBC_VARLIST))
1718     {
1719       if (def->type == SBC_VARLIST)
1720         dump (1, "if (token == T_ID "
1721               "&& dict_lookup_var (default_dict, tokid) != NULL "
1722               "&& lex_look_ahead () != '=')");
1723       else
1724         {
1725           dump (0, "if ((token == T_ID "
1726                 "&& dict_lookup_var (default_dict, tokid) "
1727                 "&& lex_look_ahead () != '=')");
1728           dump (1, "     || token == T_ALL)");
1729         }
1730       dump (1, "{");
1731       dump (0, "p->sbc_%s++;", st_lower (def->name));
1732       dump (1, "if (!parse_variables (default_dict, &p->%sv_%s, &p->%sn_%s, "
1733             "PV_APPEND))",
1734             st_lower (def->prefix), st_lower (def->name),
1735             st_lower (def->prefix), st_lower (def->name));
1736       dump (0, "goto lossage;");
1737       dump (-2, "}");
1738       outdent ();
1739       f = 1;
1740     }
1741   else if (def && def->type == SBC_CUSTOM)
1742     {
1743       dump (1, "switch (%scustom_%s (p))",
1744             st_lower (prefix), st_lower (def->name));
1745       dump (0, "{");
1746       dump (1, "case 0:");
1747       dump (0, "goto lossage;");
1748       dump (-1, "case 1:");
1749       indent ();
1750       dump (0, "p->sbc_%s++;", st_lower (def->name));
1751       dump (0, "continue;");
1752       dump (-1, "case 2:");
1753       indent ();
1754       dump (0, "break;");
1755       dump (-1, "default:");
1756       indent ();
1757       dump (0, "assert (0);");
1758       dump (-1, "}");
1759       outdent ();
1760     }
1761   
1762   {
1763     subcommand *sbc;
1764
1765     for (sbc = subcommands; sbc; sbc = sbc->next)
1766       {
1767         dump (1, "%sif (%s)", f ? "else " : "", make_match (sbc->name));
1768         f = 1;
1769         dump (1, "{");
1770
1771         dump (0, "lex_match ('=');");
1772         dump (0, "p->sbc_%s++;", st_lower (sbc->name));
1773         if (sbc->once)
1774           {
1775             dump (1, "if (p->sbc_%s > 1)", st_lower (sbc->name));
1776             dump (1, "{");
1777             dump (0, "msg (SE, _(\"%s subcommand may be given only once.\"));",
1778                   sbc->name);
1779             dump (0, "goto lossage;");
1780             dump (-1, "}");
1781             outdent ();
1782           }
1783         dump_subcommand (sbc);
1784         dump (-1, "}");
1785         outdent ();
1786       }
1787   }
1788   /* Now deal with the /ALGORITHM subcommand implicit to all commands */
1789   dump(1,"else if ( get_syntax() != COMPATIBLE && lex_match_id(\"ALGORITHM\"))");
1790   dump(1,"{");
1791
1792   dump (0, "lex_match ('=');");
1793
1794   dump(1,"if (lex_match_id(\"COMPATIBLE\"))");
1795   dump(0,"set_cmd_algorithm(COMPATIBLE);");
1796   outdent();
1797   dump(1,"else if (lex_match_id(\"ENHANCED\"))");
1798   dump(0,"set_cmd_algorithm(ENHANCED);");
1799
1800   dump (-1, "}");
1801   outdent ();
1802
1803   
1804   dump (1, "if (!lex_match ('/'))");
1805   dump (0, "break;");
1806   dump (-2, "}");
1807   outdent ();
1808   dump (0, nullstr);
1809   dump (1, "if (token != '.')");
1810   dump (1, "{");
1811   dump (0, "lex_error (_(\"expecting end of command\"));");
1812   dump (0, "goto lossage;");
1813   dump (-1, "}");
1814   dump (0, nullstr);
1815   dump (-1, "return 1;");
1816   dump (0, nullstr);
1817   dump (-1, "lossage:");
1818   indent ();
1819   dump (0, "free_%s (p);", make_identifier (cmdname));
1820   dump (0, "return 0;");
1821   dump (-1, "}");
1822   dump (0, nullstr);
1823 }
1824
1825
1826 /* Write out the code to parse aux subcommand SBC. */
1827 static void
1828 dump_aux_subcommand (const subcommand *sbc)
1829 {
1830   if (sbc->type == SBC_PLAIN )
1831     {
1832       specifier *spec;
1833         
1834       for (spec = sbc->spec; spec; spec = spec->next)
1835         {
1836           char buf[80];
1837           sprintf(buf,"p->%s%s",st_lower(sbc->prefix),spec->varname);
1838
1839           dump (0, "msg(MM,\"%s is %%s\",",sbc->name);
1840           dump (0, "(%s < 1000)?\"not set\":settings[%s - 1000]", buf, buf);
1841       
1842           dump (0, ");");
1843         }
1844     }
1845   else if (sbc->type == SBC_STRING)
1846     {
1847       dump (0, "msg(MM,\"%s is \\\"%%s\\\"\",p->s_%s);", sbc->name,st_lower(sbc->name) );
1848     }
1849   else if (sbc->type == SBC_INT)
1850     {
1851       dump (0, "msg(MM,\"%s is %%ld\",p->n_%s);", sbc->name,st_lower(sbc->name) ); 
1852     }
1853   else if (sbc->type == SBC_CUSTOM)
1854     {
1855       dump (0, "aux_%scustom_%s(p);",st_lower(prefix),make_identifier(sbc->name));
1856     }
1857   else
1858     assert(0);
1859 }
1860
1861
1862
1863 /* Write out auxilliary parser. */
1864 static void
1865 dump_aux_parser (void)
1866 {
1867   int f=0;
1868   subcommand *sbc;
1869   aux_subcommand *asbc;
1870
1871   /* Write out English strings for all the identifiers in the symbol table. */
1872   {
1873     int f, k;
1874     symbol *sym;
1875     char *buf = NULL;
1876
1877     /* Note the squirmings necessary to make sure that the last string
1878        is not followed by a comma (is it necessary to do that ?? ) */
1879     for (sym = symtab, f = k = 0; sym; sym = sym->next)
1880       if (!sym->unique && !is_keyword (sym->name))
1881         {
1882           if (!f)
1883             {
1884               dump (0, "/* Strings for subcommand specifiers. */");
1885               dump (1, "static const char *settings[]=");
1886               dump (1, "{");
1887               f = 1;
1888             }
1889
1890           if (buf == NULL)
1891             buf = xmalloc (1024);
1892           else
1893             dump (0, buf);
1894
1895           sprintf (buf, "\"%s\",",sym->name);
1896         }
1897     if (buf)
1898       {
1899         buf[strlen (buf) - 1] = 0;
1900         dump (0, buf);
1901         free (buf);
1902       }
1903     if (f)
1904       {
1905         dump (-1, "};");
1906         dump (-1, nullstr);
1907       }
1908   }
1909
1910   
1911   indent = 0;
1912
1913   dump (0, "static int");
1914   dump (0, "aux_parse_%s (struct cmd_%s *p)", make_identifier (cmdname),
1915         make_identifier (cmdname));
1916   dump (1, "{");
1917
1918   dump (1, "for (;;)");
1919   dump (1, "{");
1920
1921
1922   for (sbc = subcommands; sbc; sbc = sbc->next)
1923     {
1924       dump (1, "%sif (%s)", f ? "else " : "", make_match (sbc->name));
1925       f = 1;
1926       dump (1, "{");
1927
1928       dump_aux_subcommand (sbc);
1929
1930       dump (-1, "}");
1931       outdent ();
1932     }
1933
1934   for (asbc = aux_subcommands ; asbc ; asbc = asbc->next)
1935     {
1936       dump (1, "%sif (%s)", f ? "else " : "", make_match (asbc->name));
1937       f = 1;
1938       dump (1, "{");
1939       dump(0,"aux_%s();",make_identifier(asbc->value));
1940       dump (-1, "}");
1941       outdent ();
1942     }
1943   
1944   dump (1, "if (!lex_match ('/'))");
1945   dump (0, "break;");
1946   dump (-2, "}");
1947   outdent ();
1948   dump (0, nullstr);
1949   dump (1, "if (token != '.')");
1950   dump (1, "{");
1951   dump (0, "lex_error (_(\"expecting end of command\"));");
1952   dump (0, "goto lossage;");
1953   dump (-1, "}");
1954   dump (0, nullstr);
1955   dump (-1, "return 1;");
1956   dump (0, nullstr);
1957   dump (-1, "lossage:");
1958   indent ();
1959   dump (0, "free_%s (p);", make_identifier (cmdname));
1960   dump (0, "return 0;");
1961   dump (-1, "} /* aux_parse_%s (struct cmd_%s *p) */", 
1962         make_identifier (cmdname), make_identifier (cmdname));
1963   dump (0, nullstr);
1964 }
1965
1966
1967 /* Write the output file header. */
1968 static void
1969 dump_header (void)
1970 {
1971   time_t curtime;
1972   struct tm *loctime;
1973   char *timep;
1974
1975   indent = 0;
1976   curtime = time (NULL);
1977   loctime = localtime (&curtime);
1978   timep = asctime (loctime);
1979   timep[strlen (timep) - 1] = 0;
1980   dump (0,   "/* %s\t\t-*- mode: c; buffer-read-only: t -*-", ofn);
1981   dump (0, nullstr);
1982   dump (0, "   Generated by q2c from %s on %s.", ifn, timep);
1983   dump (0, "   Do not modify!");
1984   dump (0, " */");
1985
1986   dump (0, nullstr);
1987   dump (0, "#include \"settings.h\"");
1988   dump (0, "#include \"subclist.h\"");
1989   dump (0, nullstr);
1990 }
1991
1992 /* Write out commands to free variable state. */
1993 static void
1994 dump_free (int persistent)
1995 {
1996   subcommand *sbc;
1997   int used;
1998
1999   indent = 0;
2000
2001   used = 0;
2002   if ( ! persistent ) 
2003     {
2004       for (sbc = subcommands; sbc; sbc = sbc->next)
2005         {
2006         if (sbc->type == SBC_STRING)
2007           used = 1;
2008         if (sbc->type == SBC_DBL_LIST)
2009           used = 1;
2010         }
2011
2012     }
2013
2014   dump (0, "static void");
2015   dump (0, "free_%s (struct cmd_%s *p%s)", make_identifier (cmdname),
2016         make_identifier (cmdname), used ? "" : " UNUSED");
2017   dump (1, "{");
2018
2019   if ( ! persistent ) 
2020     {
2021
2022       for (sbc = subcommands; sbc; sbc = sbc->next)
2023         {
2024           switch (sbc->type) 
2025             {
2026             case SBC_STRING:
2027               dump (0, "free (p->s_%s);", st_lower (sbc->name));
2028               break;
2029             case SBC_DBL_LIST:
2030               dump (0, "subc_list_double_destroy(p->dl_%s);", st_lower (sbc->name));
2031               break;
2032             default:
2033               break;
2034             }
2035         }
2036     }
2037
2038   dump (-1, "}");
2039
2040 }
2041
2042
2043
2044 /* Returns the name of a directive found on the current input line, if
2045    any, or a null pointer if none found. */
2046 static const char *
2047 recognize_directive (void)
2048 {
2049   static char directive[16];
2050   char *sp, *ep;
2051   
2052   sp = skip_ws (buf);
2053   if (strncmp (sp, "/*", 2))
2054     return NULL;
2055   sp = skip_ws (sp + 2);
2056   if (*sp != '(')
2057     return NULL;
2058   sp++;
2059
2060   ep = strchr (sp, ')');
2061   if (ep == NULL)
2062     return NULL;
2063
2064   if (ep - sp > 15)
2065     ep = sp + 15;
2066   memcpy (directive, sp, ep - sp);
2067   directive[ep - sp] = '\0';
2068   return directive;
2069 }
2070   
2071 static void aux_parse (void);
2072
2073 int
2074 main (int argc, char *argv[])
2075 {
2076   pgmname = argv[0];
2077   if (argc != 3)
2078     fail ("Syntax: q2c input.q output.c");
2079
2080   ifn = argv[1];
2081   in = fopen (ifn, "r");
2082   if (!in)
2083     fail ("%s: open: %s.", ifn, strerror (errno));
2084
2085   ofn = argv[2];
2086   out = fopen (ofn, "w");
2087   if (!out)
2088     fail ("%s: open: %s.", ofn, strerror (errno));
2089
2090   is_open = 1;
2091   buf = xmalloc (MAX_LINE_LEN);
2092   tokstr = xmalloc (MAX_TOK_LEN);
2093
2094   dump_header ();
2095
2096
2097   indent = 0;
2098   dump (0, "#line %d \"%s\"", ln + 1, ifn);
2099   while (get_line ())
2100     {
2101       const char *directive = recognize_directive ();
2102       if (directive == NULL)
2103         {
2104           dump (0, "%s", buf);
2105           continue;
2106         }
2107       
2108       dump (0, "#line %d \"%s\"", oln - 1, ofn);
2109       if (!strcmp (directive, "specification"))
2110         {
2111           /* Skip leading slash-star line. */
2112           get_line ();
2113           lex_get ();
2114
2115           parse ();
2116
2117           /* Skip trailing star-slash line. */
2118           get_line ();
2119         }
2120       else if (!strcmp (directive, "headers"))
2121         {
2122           indent = 0;
2123
2124           dump (0, "#include <stdlib.h>");
2125           dump (0, "#include \"alloc.h\"");
2126           dump (0, "#include \"error.h\"");
2127           dump (0, "#include \"lexer.h\"");
2128           dump (0, "#include \"str.h\"");
2129           dump (0, "#include \"var.h\"");
2130
2131           dump (0, nullstr);
2132         }
2133       else if (!strcmp (directive, "declarations"))
2134         dump_declarations ();
2135       else if (!strcmp (directive, "functions"))
2136         {
2137           dump_parser (0);
2138           dump_free (0); 
2139         }
2140       else if (!strcmp (directive, "_functions"))
2141         {
2142           dump_parser (1);
2143           dump_free (1); 
2144         }
2145       else if (!strcmp (directive, "aux_functions"))
2146         {
2147           aux_parse();
2148           dump_aux_parser ();
2149         }
2150       else
2151         error ("unknown directive `%s'", directive);
2152       indent = 0;
2153       dump (0, "#line %d \"%s\"", ln + 1, ifn);
2154     }
2155
2156
2157
2158   return EXIT_SUCCESS;
2159 }
2160
2161 /* Parse an entire auxilliary specification. */
2162 static void
2163 aux_parse (void)
2164 {
2165   aux_subcommand *sbc;
2166   aux_subcommand *prevsbc = 0 ;
2167   get_line();
2168   lex_get();
2169
2170   for (;;)
2171     {
2172         sbc = xmalloc(sizeof(aux_subcommand));
2173         sbc->next = prevsbc;
2174         sbc->name = xstrdup (tokstr);
2175         lex_get();
2176         skip_token('=');
2177         sbc->value = xstrdup (tokstr);
2178         lex_get();
2179       if (token == '.')
2180         break;
2181         skip_token(';');
2182         prevsbc = sbc;
2183
2184     }
2185   /* Skip trailing star-slash line. */
2186   get_line ();
2187   aux_subcommands = sbc;
2188 }
2189
2190