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