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