Improve the way we handle the various parsing "states". Until now
[pspp-builds.git] / src / language / data-io / file-type.c
1 /* PSPP - computes sample statistics.
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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <language/data-io/file-type.h>
23
24 #include <stdlib.h>
25
26 #include <data/case.h>
27 #include <data/data-in.h>
28 #include <data/dictionary.h>
29 #include <data/format.h>
30 #include <data/variable.h>
31 #include <language/command.h>
32 #include <language/data-io/data-reader.h>
33 #include <language/data-io/file-handle.h>
34 #include <language/lexer/lexer.h>
35 #include <libpspp/alloc.h>
36 #include <libpspp/compiler.h>
37 #include <libpspp/message.h>
38 #include <libpspp/str.h>
39 #include <procedure.h>
40
41 #include "gettext.h"
42 #define _(msgid) gettext (msgid)
43
44 /* Defines the three types of complex files read by FILE TYPE. */
45 enum
46   {
47     FTY_MIXED,
48     FTY_GROUPED,
49     FTY_NESTED
50   };
51
52 /* Limited variable column specifications. */
53 struct col_spec
54   {
55     char name[LONG_NAME_LEN + 1]; /* Variable name. */
56     int fc, nc;                 /* First column (1-based), # of columns. */
57     int fmt;                    /* Format type. */
58     struct variable *v;         /* Variable. */
59   };
60
61 /* RCT_* record type constants. */
62 enum
63   {
64     RCT_OTHER = 001,            /* 1=OTHER. */
65     RCT_SKIP = 002,             /* 1=SKIP. */
66     RCT_DUPLICATE = 004,        /* DUPLICATE: 0=NOWARN, 1=WARN. */
67     RCT_MISSING = 010,          /* MISSING: 0=NOWARN, 1=WARN. */
68     RCT_SPREAD = 020            /* SPREAD: 0=NO, 1=YES. */
69   };
70
71 /* Represents a RECORD TYPE command. */
72 struct record_type
73   {
74     struct record_type *next;
75     unsigned flags;             /* RCT_* constants. */
76     union value *v;             /* Vector of values for this record type. */
77     int nv;                     /* Length of vector V. */
78     struct col_spec case_sbc;   /* CASE subcommand. */
79     int ft, lt;                 /* First, last transformation index. */
80   };                            /* record_type */
81
82 /* Represents a FILE TYPE input program. */
83 struct file_type_pgm
84   {
85     int type;                   /* One of the FTY_* constants. */
86     struct dfm_reader *reader;  /* Data file to read. */
87     struct col_spec record;     /* RECORD subcommand. */
88     struct col_spec case_sbc;   /* CASE subcommand. */
89     int wild;                   /* 0=NOWARN, 1=WARN. */
90     int duplicate;              /* 0=NOWARN, 1=WARN. */
91     int missing;                /* 0=NOWARN, 1=WARN, 2=CASE. */
92     int ordered;                /* 0=NO, 1=YES. */
93     int had_rec_type;           /* 1=Had a RECORD TYPE command.
94                                    RECORD TYPE must precede the first
95                                    DATA LIST. */
96     struct record_type *recs_head;      /* List of record types. */
97     struct record_type *recs_tail;      /* Last in list of record types. */
98     size_t case_size;           /* Case size in bytes. */
99   };
100
101 static int parse_col_spec (struct col_spec *, const char *);
102 static void create_col_var (struct col_spec *c);
103 static const struct case_source_class file_type_source_class;
104
105 static bool inside_file_type;
106
107 /* Returns true if we're parsing the inside of a FILE TYPE...END
108    FILE TYPE construct, false otherwise. */
109 bool
110 in_file_type (void) 
111 {
112   return inside_file_type;
113 }
114
115 int cmd_file_type (void);
116
117 /* Parses FILE TYPE command. */
118 int
119 cmd_file_type (void)
120 {
121   static struct file_type_pgm *fty;     /* FIXME: static? WTF? */
122   struct file_handle *fh = fh_inline_file ();
123
124   /* Initialize. */
125   discard_variables ();
126
127   fty = xmalloc (sizeof *fty);
128   fty->reader = NULL;
129   fty->record.name[0] = 0;
130   fty->case_sbc.name[0] = 0;
131   fty->wild = fty->duplicate = fty->missing = fty->ordered = 0;
132   fty->had_rec_type = 0;
133   fty->recs_head = fty->recs_tail = NULL;
134
135   if (lex_match_id ("MIXED"))
136     fty->type = FTY_MIXED;
137   else if (lex_match_id ("GROUPED"))
138     {
139       fty->type = FTY_GROUPED;
140       fty->wild = 1;
141       fty->duplicate = 1;
142       fty->missing = 1;
143       fty->ordered = 1;
144     }
145   else if (lex_match_id ("NESTED"))
146     fty->type = FTY_NESTED;
147   else
148     {
149       msg (SE, _("MIXED, GROUPED, or NESTED expected."));
150       goto error;
151     }
152
153   while (token != '.')
154     {
155       if (lex_match_id ("FILE"))
156         {
157           lex_match ('=');
158           fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
159           if (fh == NULL)
160             goto error;
161         }
162       else if (lex_match_id ("RECORD"))
163         {
164           lex_match ('=');
165           if (!parse_col_spec (&fty->record, "####RECD"))
166             goto error;
167         }
168       else if (lex_match_id ("CASE"))
169         {
170           if (fty->type == FTY_MIXED)
171             {
172               msg (SE, _("The CASE subcommand is not valid on FILE TYPE "
173                          "MIXED."));
174               goto error;
175             }
176           
177           lex_match ('=');
178           if (!parse_col_spec (&fty->case_sbc, "####CASE"))
179             goto error;
180         }
181       else if (lex_match_id ("WILD"))
182         {
183           lex_match ('=');
184           if (lex_match_id ("WARN"))
185             fty->wild = 1;
186           else if (lex_match_id ("NOWARN"))
187             fty->wild = 0;
188           else
189             {
190               msg (SE, _("WARN or NOWARN expected after WILD."));
191               goto error;
192             }
193         }
194       else if (lex_match_id ("DUPLICATE"))
195         {
196           if (fty->type == FTY_MIXED)
197             {
198               msg (SE, _("The DUPLICATE subcommand is not valid on "
199                          "FILE TYPE MIXED."));
200               goto error;
201             }
202
203           lex_match ('=');
204           if (lex_match_id ("WARN"))
205             fty->duplicate = 1;
206           else if (lex_match_id ("NOWARN"))
207             fty->duplicate = 0;
208           else if (lex_match_id ("CASE"))
209             {
210               if (fty->type != FTY_NESTED)
211                 {
212                   msg (SE, _("DUPLICATE=CASE is only valid on "
213                              "FILE TYPE NESTED."));
214                   goto error;
215                 }
216               
217               fty->duplicate = 2;
218             }
219           else
220             {
221               msg (SE, _("WARN%s expected after DUPLICATE."),
222                    (fty->type == FTY_NESTED ? _(", NOWARN, or CASE")
223                     : _(" or NOWARN")));
224               goto error;
225             }
226         }
227       else if (lex_match_id ("MISSING"))
228         {
229           if (fty->type == FTY_MIXED)
230             {
231               msg (SE, _("The MISSING subcommand is not valid on "
232                          "FILE TYPE MIXED."));
233               goto error;
234             }
235           
236           lex_match ('=');
237           if (lex_match_id ("NOWARN"))
238             fty->missing = 0;
239           else if (lex_match_id ("WARN"))
240             fty->missing = 1;
241           else
242             {
243               msg (SE, _("WARN or NOWARN after MISSING."));
244               goto error;
245             }
246         }
247       else if (lex_match_id ("ORDERED"))
248         {
249           if (fty->type != FTY_GROUPED)
250             {
251               msg (SE, _("ORDERED is only valid on FILE TYPE GROUPED."));
252               goto error;
253             }
254           
255           lex_match ('=');
256           if (lex_match_id ("YES"))
257             fty->ordered = 1;
258           else if (lex_match_id ("NO"))
259             fty->ordered = 0;
260           else
261             {
262               msg (SE, _("YES or NO expected after ORDERED."));
263               goto error;
264             }
265         }
266       else
267         {
268           lex_error (_("while expecting a valid subcommand"));
269           goto error;
270         }
271     }
272
273   if (fty->record.name[0] == 0)
274     {
275       msg (SE, _("The required RECORD subcommand was not present."));
276       goto error;
277     }
278
279   if (fty->type == FTY_GROUPED)
280     {
281       if (fty->case_sbc.name[0] == 0)
282         {
283           msg (SE, _("The required CASE subcommand was not present."));
284           goto error;
285         }
286       
287       if (!strcasecmp (fty->case_sbc.name, fty->record.name))
288         {
289           msg (SE, _("CASE and RECORD must specify different variable "
290                      "names."));
291           goto error;
292         }
293     }
294
295   fty->reader = dfm_open_reader (fh);
296   if (fty->reader == NULL)
297     goto error;
298   fh_set_default_handle (fh);
299
300   create_col_var (&fty->record);
301   if (fty->case_sbc.name[0])
302     create_col_var (&fty->case_sbc);
303   vfm_source = create_case_source (&file_type_source_class, fty);
304
305   return CMD_SUCCESS;
306
307  error:
308   free (fty);
309   return CMD_CASCADING_FAILURE;
310 }
311
312 /* Creates a variable with attributes specified by struct col_spec C, and
313    stores it into C->V. */
314 static void
315 create_col_var (struct col_spec *c)
316 {
317   int width;
318
319   if (formats[c->fmt].cat & FCAT_STRING)
320     width = c->nc;
321   else
322     width = 0;
323   c->v = dict_create_var (default_dict, c->name, width);
324 }
325
326 /* Parses variable, column, type specifications for a variable. */
327 static int
328 parse_col_spec (struct col_spec *c, const char *def_name)
329 {
330   struct fmt_spec spec;
331
332   /* Name. */
333   if (token == T_ID)
334     {
335       strcpy (c->name, tokid);
336       lex_get ();
337     }
338   else
339     strcpy (c->name, def_name);
340
341   /* First column. */
342   if (!lex_force_int ())
343     return 0;
344   c->fc = lex_integer ();
345   if (c->fc < 1)
346     {
347       msg (SE, _("Column value must be positive."));
348       return 0;
349     }
350   lex_get ();
351
352   /* Last column. */
353   lex_negative_to_dash ();
354   if (lex_match ('-'))
355     {
356       if (!lex_force_int ())
357         return 0;
358       c->nc = lex_integer ();
359       lex_get ();
360
361       if (c->nc < c->fc)
362         {
363           msg (SE, _("Ending column precedes beginning column."));
364           return 0;
365         }
366       
367       c->nc -= c->fc - 1;
368     }
369   else
370     c->nc = 1;
371
372   /* Format specifier. */
373   if (lex_match ('('))
374     {
375       const char *cp;
376       if (!lex_force_id ())
377         return 0;
378       c->fmt = parse_format_specifier_name (&cp, 0);
379       if (c->fmt == -1)
380         return 0;
381       if (*cp)
382         {
383           msg (SE, _("Bad format specifier name."));
384           return 0;
385         }
386       lex_get ();
387       if (!lex_force_match (')'))
388         return 0;
389     }
390   else
391     c->fmt = FMT_F;
392
393   spec.type = c->fmt;
394   spec.w = c->nc;
395   spec.d = 0;
396   return check_input_specifier (&spec, 1);
397 }
398 \f
399 /* RECORD TYPE. */
400
401 /* Parse the RECORD TYPE command. */
402 int
403 cmd_record_type (void)
404 {
405   struct file_type_pgm *fty;
406   struct record_type *rct;
407
408   fty = vfm_source->aux;
409
410   /* Initialize the record_type structure. */
411   rct = xmalloc (sizeof *rct);
412   rct->next = NULL;
413   rct->flags = 0;
414   if (fty->duplicate)
415     rct->flags |= RCT_DUPLICATE;
416   if (fty->missing)
417     rct->flags |= RCT_MISSING;
418   rct->v = NULL;
419   rct->nv = 0;
420   rct->ft = n_trns;
421   if (fty->case_sbc.name[0])
422     rct->case_sbc = fty->case_sbc;
423
424   if (fty->recs_tail && (fty->recs_tail->flags & RCT_OTHER))
425     {
426       msg (SE, _("OTHER may appear only on the last RECORD TYPE command."));
427       goto error;
428     }
429       
430   if (fty->recs_tail)
431     {
432       fty->recs_tail->lt = n_trns - 1;
433       if (!(fty->recs_tail->flags & RCT_SKIP)
434           && fty->recs_tail->ft == fty->recs_tail->lt)
435         {
436           msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
437                      "for above RECORD TYPE."));
438           goto error;
439         }
440     }
441
442   /* Parse record type values. */
443   if (lex_match_id ("OTHER"))
444     rct->flags |= RCT_OTHER;
445   else
446     {
447       int mv = 0;
448
449       while (lex_is_number () || token == T_STRING)
450         {
451           if (rct->nv >= mv)
452             {
453               mv += 16;
454               rct->v = xnrealloc (rct->v, mv, sizeof *rct->v);
455             }
456
457           if (formats[fty->record.fmt].cat & FCAT_STRING)
458             {
459               if (!lex_force_string ())
460                 goto error;
461               rct->v[rct->nv].c = xmalloc (fty->record.nc + 1);
462               buf_copy_str_rpad (rct->v[rct->nv].c, fty->record.nc + 1,
463                                  ds_c_str (&tokstr));
464             }
465           else
466             {
467               if (!lex_force_num ())
468                 goto error;
469               rct->v[rct->nv].f = tokval;
470             }
471           rct->nv++;
472           lex_get ();
473
474           lex_match (',');
475         }
476     }
477
478   /* Parse the rest of the subcommands. */
479   while (token != '.')
480     {
481       if (lex_match_id ("SKIP"))
482         rct->flags |= RCT_SKIP;
483       else if (lex_match_id ("CASE"))
484         {
485           if (fty->type == FTY_MIXED)
486             {
487               msg (SE, _("The CASE subcommand is not allowed on "
488                          "the RECORD TYPE command for FILE TYPE MIXED."));
489               goto error;
490             }
491
492           lex_match ('=');
493           if (!parse_col_spec (&rct->case_sbc, ""))
494             goto error;
495           if (rct->case_sbc.name[0])
496             {
497               msg (SE, _("No variable name may be specified for the "
498                          "CASE subcommand on RECORD TYPE."));
499               goto error;
500             }
501           
502           if ((formats[rct->case_sbc.fmt].cat ^ formats[fty->case_sbc.fmt].cat)
503               & FCAT_STRING)
504             {
505               msg (SE, _("The CASE column specification on RECORD TYPE "
506                          "must give a format specifier that is the "
507                          "same type as that of the CASE column "
508                          "specification given on FILE TYPE."));
509               goto error;
510             }
511         }
512       else if (lex_match_id ("DUPLICATE"))
513         {
514           lex_match ('=');
515           if (lex_match_id ("WARN"))
516             rct->flags |= RCT_DUPLICATE;
517           else if (lex_match_id ("NOWARN"))
518             rct->flags &= ~RCT_DUPLICATE;
519           else
520             {
521               msg (SE, _("WARN or NOWARN expected on DUPLICATE "
522                          "subcommand."));
523               goto error;
524             }
525         }
526       else if (lex_match_id ("MISSING"))
527         {
528           lex_match ('=');
529           if (lex_match_id ("WARN"))
530             rct->flags |= RCT_MISSING;
531           else if (lex_match_id ("NOWARN"))
532             rct->flags &= ~RCT_MISSING;
533           else
534             {
535               msg (SE, _("WARN or NOWARN expected on MISSING subcommand."));
536               goto error;
537             }
538         }
539       else if (lex_match_id ("SPREAD"))
540         {
541           lex_match ('=');
542           if (lex_match_id ("YES"))
543             rct->flags |= RCT_SPREAD;
544           else if (lex_match_id ("NO"))
545             rct->flags &= ~RCT_SPREAD;
546           else
547             {
548               msg (SE, _("YES or NO expected on SPREAD subcommand."));
549               goto error;
550             }
551         }
552       else
553         {
554           lex_error (_("while expecting a valid subcommand"));
555           goto error;
556         }
557     }
558
559   if (fty->recs_head)
560     fty->recs_tail = fty->recs_tail->next = xmalloc (sizeof *fty->recs_tail);
561   else
562     fty->recs_head = fty->recs_tail = xmalloc (sizeof *fty->recs_tail);
563   memcpy (fty->recs_tail, &rct, sizeof *fty->recs_tail);
564
565   return CMD_SUCCESS;
566
567  error:
568   if (formats[fty->record.fmt].cat & FCAT_STRING) 
569     {
570       int i;
571       
572       for (i = 0; i < rct->nv; i++)
573         free (rct->v[i].c); 
574     }
575   free (rct->v);
576   free (rct);
577
578   return CMD_CASCADING_FAILURE;
579 }
580 \f
581 /* END FILE TYPE. */
582
583 int cmd_end_file_type (void);
584 int
585 cmd_end_file_type (void)
586 {
587   struct file_type_pgm *fty;
588
589   fty = vfm_source->aux;
590   fty->case_size = dict_get_case_size (default_dict);
591
592   if (fty->recs_tail)
593     {
594       fty->recs_tail->lt = n_trns - 1;
595       if (!(fty->recs_tail->flags & RCT_SKIP)
596           && fty->recs_tail->ft == fty->recs_tail->lt)
597         {
598           msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
599                      "on above RECORD TYPE."));
600           goto fail;
601         }
602     }
603   else
604     {
605       msg (SE, _("No commands between FILE TYPE and END FILE TYPE."));
606       goto fail;
607     }
608
609   f_trns = n_trns;
610
611   return lex_end_of_command ();
612
613  fail:
614   /* Come here on I/O error. */
615   discard_variables ();
616   return CMD_CASCADING_FAILURE;
617 }
618 \f
619 /* FILE TYPE runtime. */
620
621 /*static void read_from_file_type_mixed(void);
622    static void read_from_file_type_grouped(void);
623    static void read_from_file_type_nested(void); */
624
625 /* Reads any number of cases into case C and calls write_case()
626    for each one.  Compare data-list.c:read_from_data_list.
627    Returns true if successful, false if an I/O error occurred. */
628 static bool
629 file_type_source_read (struct case_source *source,
630                        struct ccase *c,
631                        write_case_func *write_case UNUSED,
632                        write_case_data wc_data UNUSED)
633 {
634   struct file_type_pgm *fty = source->aux;
635   struct fmt_spec format;
636
637   dfm_push (fty->reader);
638
639   format.type = fty->record.fmt;
640   format.w = fty->record.nc;
641   format.d = 0;
642   while (!dfm_eof (fty->reader))
643     {
644       struct fixed_string line;
645       struct record_type *iter;
646       union value v;
647       int i;
648
649       dfm_expand_tabs (fty->reader);
650       dfm_get_record (fty->reader, &line);
651       if (formats[fty->record.fmt].cat & FCAT_STRING)
652         {
653           struct data_in di;
654           
655           v.c = case_data_rw (c, fty->record.v->fv)->s;
656
657           data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
658                                fty->record.fc, fty->record.fc + fty->record.nc);
659           di.v = (union value *) v.c;
660           di.flags = 0;
661           di.f1 = fty->record.fc;
662           di.format = format;
663           data_in (&di);
664
665           for (iter = fty->recs_head; iter; iter = iter->next)
666             {
667               if (iter->flags & RCT_OTHER)
668                 goto found;
669               for (i = 0; i < iter->nv; i++)
670                 if (!memcmp (iter->v[i].c, v.c, fty->record.nc))
671                   goto found;
672             }
673           if (fty->wild)
674             msg (SW, _("Unknown record type \"%.*s\"."), fty->record.nc, v.c);
675         }
676       else
677         {
678           struct data_in di;
679
680           data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
681                                fty->record.fc, fty->record.fc + fty->record.nc);
682           di.v = &v;
683           di.flags = 0;
684           di.f1 = fty->record.fc;
685           di.format = format;
686           data_in (&di);
687
688           case_data_rw (c, fty->record.v->fv)->f = v.f;
689           for (iter = fty->recs_head; iter; iter = iter->next)
690             {
691               if (iter->flags & RCT_OTHER)
692                 goto found;
693               for (i = 0; i < iter->nv; i++)
694                 if (iter->v[i].f == v.f)
695                   goto found;
696             }
697           if (fty->wild)
698             msg (SW, _("Unknown record type %g."), v.f);
699         }
700       dfm_forward_record (fty->reader);
701       continue;
702
703     found:
704       /* Arrive here if there is a matching record_type, which is in
705          iter. */
706       dfm_forward_record (fty->reader);
707     }
708
709 /*  switch(fty->type)
710    {
711    case FTY_MIXED: read_from_file_type_mixed(); break;
712    case FTY_GROUPED: read_from_file_type_grouped(); break;
713    case FTY_NESTED: read_from_file_type_nested(); break;
714    default: assert(0);
715    } */
716
717   dfm_pop (fty->reader);
718
719   return !dfm_reader_error (fty->reader);
720 }
721
722 static void
723 file_type_source_destroy (struct case_source *source)
724 {
725   struct file_type_pgm *fty = source->aux;
726   struct record_type *iter, *next;
727
728   cancel_transformations ();
729   dfm_close_reader (fty->reader);
730   for (iter = fty->recs_head; iter; iter = next)
731     {
732       next = iter->next;
733       free (iter);
734     }
735 }
736
737 static const struct case_source_class file_type_source_class =
738   {
739     "FILE TYPE",
740     NULL,
741     file_type_source_read,
742     file_type_source_destroy,
743   };