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