ff97f8dbbfbfd868c7adcd72c10672fb3a32bf1b
[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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include "alloc.h"
23 #include "case.h"
24 #include "command.h"
25 #include "data-in.h"
26 #include "dfm-read.h"
27 #include "dictionary.h"
28 #include "error.h"
29 #include "file-handle.h"
30 #include "format.h"
31 #include "lexer.h"
32 #include "str.h"
33 #include "var.h"
34 #include "vfm.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38
39 /* Defines the three types of complex files read by FILE TYPE. */
40 enum
41   {
42     FTY_MIXED,
43     FTY_GROUPED,
44     FTY_NESTED
45   };
46
47 /* Limited variable column specifications. */
48 struct col_spec
49   {
50     char name[LONG_NAME_LEN + 1]; /* Variable name. */
51     int fc, nc;                 /* First column (1-based), # of columns. */
52     int fmt;                    /* Format type. */
53     struct variable *v;         /* Variable. */
54   };
55
56 /* RCT_* record type constants. */
57 enum
58   {
59     RCT_OTHER = 001,            /* 1=OTHER. */
60     RCT_SKIP = 002,             /* 1=SKIP. */
61     RCT_DUPLICATE = 004,        /* DUPLICATE: 0=NOWARN, 1=WARN. */
62     RCT_MISSING = 010,          /* MISSING: 0=NOWARN, 1=WARN. */
63     RCT_SPREAD = 020            /* SPREAD: 0=NO, 1=YES. */
64   };
65
66 /* Represents a RECORD TYPE command. */
67 struct record_type
68   {
69     struct record_type *next;
70     unsigned flags;             /* RCT_* constants. */
71     union value *v;             /* Vector of values for this record type. */
72     int nv;                     /* Length of vector V. */
73     struct col_spec case_sbc;   /* CASE subcommand. */
74     int ft, lt;                 /* First, last transformation index. */
75   };                            /* record_type */
76
77 /* Represents a FILE TYPE input program. */
78 struct file_type_pgm
79   {
80     int type;                   /* One of the FTY_* constants. */
81     struct dfm_reader *reader;  /* Data file to read. */
82     struct col_spec record;     /* RECORD subcommand. */
83     struct col_spec case_sbc;   /* CASE subcommand. */
84     int wild;                   /* 0=NOWARN, 1=WARN. */
85     int duplicate;              /* 0=NOWARN, 1=WARN. */
86     int missing;                /* 0=NOWARN, 1=WARN, 2=CASE. */
87     int ordered;                /* 0=NO, 1=YES. */
88     int had_rec_type;           /* 1=Had a RECORD TYPE command.
89                                    RECORD TYPE must precede the first
90                                    DATA LIST. */
91     struct record_type *recs_head;      /* List of record types. */
92     struct record_type *recs_tail;      /* Last in list of record types. */
93     size_t case_size;           /* Case size in bytes. */
94   };
95
96 static int parse_col_spec (struct col_spec *, const char *);
97 static void create_col_var (struct col_spec *c);
98
99 int cmd_file_type (void);
100
101 /* Parses FILE TYPE command. */
102 int
103 cmd_file_type (void)
104 {
105   static struct file_type_pgm *fty;     /* FIXME: static? WTF? */
106   struct file_handle *fh = NULL;
107
108   /* Initialize. */
109   discard_variables ();
110
111   fty = xmalloc (sizeof *fty);
112   fty->reader = NULL;
113   fty->record.name[0] = 0;
114   fty->case_sbc.name[0] = 0;
115   fty->wild = fty->duplicate = fty->missing = fty->ordered = 0;
116   fty->had_rec_type = 0;
117   fty->recs_head = fty->recs_tail = NULL;
118
119   if (lex_match_id ("MIXED"))
120     fty->type = FTY_MIXED;
121   else if (lex_match_id ("GROUPED"))
122     {
123       fty->type = FTY_GROUPED;
124       fty->wild = 1;
125       fty->duplicate = 1;
126       fty->missing = 1;
127       fty->ordered = 1;
128     }
129   else if (lex_match_id ("NESTED"))
130     fty->type = FTY_NESTED;
131   else
132     {
133       msg (SE, _("MIXED, GROUPED, or NESTED expected."));
134       goto error;
135     }
136
137   while (token != '.')
138     {
139       if (lex_match_id ("FILE"))
140         {
141           lex_match ('=');
142           fh = fh_parse ();
143           if (fh == NULL)
144             goto error;
145         }
146       else if (lex_match_id ("RECORD"))
147         {
148           lex_match ('=');
149           if (!parse_col_spec (&fty->record, "####RECD"))
150             goto error;
151         }
152       else if (lex_match_id ("CASE"))
153         {
154           if (fty->type == FTY_MIXED)
155             {
156               msg (SE, _("The CASE subcommand is not valid on FILE TYPE "
157                          "MIXED."));
158               goto error;
159             }
160           
161           lex_match ('=');
162           if (!parse_col_spec (&fty->case_sbc, "####CASE"))
163             goto error;
164         }
165       else if (lex_match_id ("WILD"))
166         {
167           lex_match ('=');
168           if (lex_match_id ("WARN"))
169             fty->wild = 1;
170           else if (lex_match_id ("NOWARN"))
171             fty->wild = 0;
172           else
173             {
174               msg (SE, _("WARN or NOWARN expected after WILD."));
175               goto error;
176             }
177         }
178       else if (lex_match_id ("DUPLICATE"))
179         {
180           if (fty->type == FTY_MIXED)
181             {
182               msg (SE, _("The DUPLICATE subcommand is not valid on "
183                          "FILE TYPE MIXED."));
184               goto error;
185             }
186
187           lex_match ('=');
188           if (lex_match_id ("WARN"))
189             fty->duplicate = 1;
190           else if (lex_match_id ("NOWARN"))
191             fty->duplicate = 0;
192           else if (lex_match_id ("CASE"))
193             {
194               if (fty->type != FTY_NESTED)
195                 {
196                   msg (SE, _("DUPLICATE=CASE is only valid on "
197                              "FILE TYPE NESTED."));
198                   goto error;
199                 }
200               
201               fty->duplicate = 2;
202             }
203           else
204             {
205               msg (SE, _("WARN%s expected after DUPLICATE."),
206                    (fty->type == FTY_NESTED ? _(", NOWARN, or CASE")
207                     : _(" or NOWARN")));
208               goto error;
209             }
210         }
211       else if (lex_match_id ("MISSING"))
212         {
213           if (fty->type == FTY_MIXED)
214             {
215               msg (SE, _("The MISSING subcommand is not valid on "
216                          "FILE TYPE MIXED."));
217               goto error;
218             }
219           
220           lex_match ('=');
221           if (lex_match_id ("NOWARN"))
222             fty->missing = 0;
223           else if (lex_match_id ("WARN"))
224             fty->missing = 1;
225           else
226             {
227               msg (SE, _("WARN or NOWARN after MISSING."));
228               goto error;
229             }
230         }
231       else if (lex_match_id ("ORDERED"))
232         {
233           if (fty->type != FTY_GROUPED)
234             {
235               msg (SE, _("ORDERED is only valid on FILE TYPE GROUPED."));
236               goto error;
237             }
238           
239           lex_match ('=');
240           if (lex_match_id ("YES"))
241             fty->ordered = 1;
242           else if (lex_match_id ("NO"))
243             fty->ordered = 0;
244           else
245             {
246               msg (SE, _("YES or NO expected after ORDERED."));
247               goto error;
248             }
249         }
250       else
251         {
252           lex_error (_("while expecting a valid subcommand"));
253           goto error;
254         }
255     }
256
257   if (fty->record.name[0] == 0)
258     {
259       msg (SE, _("The required RECORD subcommand was not present."));
260       goto error;
261     }
262
263   if (fty->type == FTY_GROUPED)
264     {
265       if (fty->case_sbc.name[0] == 0)
266         {
267           msg (SE, _("The required CASE subcommand was not present."));
268           goto error;
269         }
270       
271       if (!strcasecmp (fty->case_sbc.name, fty->record.name))
272         {
273           msg (SE, _("CASE and RECORD must specify different variable "
274                      "names."));
275           goto error;
276         }
277     }
278
279   fty->reader = dfm_open_reader (fh);
280   if (fty->reader == NULL)
281     goto error;
282   default_handle = fh;
283
284   create_col_var (&fty->record);
285   if (fty->case_sbc.name[0])
286     create_col_var (&fty->case_sbc);
287   vfm_source = create_case_source (&file_type_source_class, fty);
288
289   return CMD_SUCCESS;
290
291  error:
292   free (fty);
293   return CMD_FAILURE;
294 }
295
296 /* Creates a variable with attributes specified by struct col_spec C, and
297    stores it into C->V. */
298 static void
299 create_col_var (struct col_spec *c)
300 {
301   int width;
302
303   if (formats[c->fmt].cat & FCAT_STRING)
304     width = c->nc;
305   else
306     width = 0;
307   c->v = dict_create_var (default_dict, c->name, width);
308 }
309
310 /* Parses variable, column, type specifications for a variable. */
311 static int
312 parse_col_spec (struct col_spec *c, const char *def_name)
313 {
314   struct fmt_spec spec;
315
316   /* Name. */
317   if (token == T_ID)
318     {
319       strcpy (c->name, tokid);
320       lex_get ();
321     }
322   else
323     strcpy (c->name, def_name);
324
325   /* First column. */
326   if (!lex_force_int ())
327     return 0;
328   c->fc = lex_integer ();
329   if (c->fc < 1)
330     {
331       msg (SE, _("Column value must be positive."));
332       return 0;
333     }
334   lex_get ();
335
336   /* Last column. */
337   lex_negative_to_dash ();
338   if (lex_match ('-'))
339     {
340       if (!lex_force_int ())
341         return 0;
342       c->nc = lex_integer ();
343       lex_get ();
344
345       if (c->nc < c->fc)
346         {
347           msg (SE, _("Ending column precedes beginning column."));
348           return 0;
349         }
350       
351       c->nc -= c->fc - 1;
352     }
353   else
354     c->nc = 1;
355
356   /* Format specifier. */
357   if (lex_match ('('))
358     {
359       const char *cp;
360       if (!lex_force_id ())
361         return 0;
362       c->fmt = parse_format_specifier_name (&cp, 0);
363       if (c->fmt == -1)
364         return 0;
365       if (*cp)
366         {
367           msg (SE, _("Bad format specifier name."));
368           return 0;
369         }
370       lex_get ();
371       if (!lex_force_match (')'))
372         return 0;
373     }
374   else
375     c->fmt = FMT_F;
376
377   spec.type = c->fmt;
378   spec.w = c->nc;
379   spec.d = 0;
380   return check_input_specifier (&spec, 1);
381 }
382 \f
383 /* RECORD TYPE. */
384
385 /* Parse the RECORD TYPE command. */
386 int
387 cmd_record_type (void)
388 {
389   struct file_type_pgm *fty;
390   struct record_type *rct;
391
392   /* Make sure we're inside a FILE TYPE structure. */
393   if (pgm_state != STATE_INPUT
394       || !case_source_is_class (vfm_source, &file_type_source_class))
395     {
396       msg (SE, _("This command may only appear within a "
397                  "FILE TYPE/END FILE TYPE structure."));
398       return CMD_FAILURE;
399     }
400
401   fty = vfm_source->aux;
402
403   /* Initialize the record_type structure. */
404   rct = xmalloc (sizeof *rct);
405   rct->next = NULL;
406   rct->flags = 0;
407   if (fty->duplicate)
408     rct->flags |= RCT_DUPLICATE;
409   if (fty->missing)
410     rct->flags |= RCT_MISSING;
411   rct->v = NULL;
412   rct->nv = 0;
413   rct->ft = n_trns;
414   if (fty->case_sbc.name[0])
415     rct->case_sbc = fty->case_sbc;
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       goto error;
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           goto error;
432         }
433     }
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 (lex_is_number () || token == T_STRING)
443         {
444           if (rct->nv >= mv)
445             {
446               mv += 16;
447               rct->v = xnrealloc (rct->v, mv, sizeof *rct->v);
448             }
449
450           if (formats[fty->record.fmt].cat & FCAT_STRING)
451             {
452               if (!lex_force_string ())
453                 goto error;
454               rct->v[rct->nv].c = xmalloc (fty->record.nc + 1);
455               buf_copy_str_rpad (rct->v[rct->nv].c, fty->record.nc + 1,
456                                  ds_c_str (&tokstr));
457             }
458           else
459             {
460               if (!lex_force_num ())
461                 goto error;
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               goto error;
483             }
484
485           lex_match ('=');
486           if (!parse_col_spec (&rct->case_sbc, ""))
487             goto error;
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               goto error;
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               goto error;
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               goto error;
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               goto error;
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               goto error;
543             }
544         }
545       else
546         {
547           lex_error (_("while expecting a valid subcommand"));
548           goto error;
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  error:
561   if (formats[fty->record.fmt].cat & FCAT_STRING) 
562     {
563       int i;
564       
565       for (i = 0; i < rct->nv; i++)
566         free (rct->v[i].c); 
567     }
568   free (rct->v);
569   free (rct);
570
571   return CMD_FAILURE;
572 }
573 \f
574 /* END FILE TYPE. */
575
576 int cmd_end_file_type (void);
577 int
578 cmd_end_file_type (void)
579 {
580   struct file_type_pgm *fty;
581
582   if (pgm_state != STATE_INPUT
583       || case_source_is_class (vfm_source, &file_type_source_class))
584     {
585       msg (SE, _("This command may only appear within a "
586                  "FILE TYPE/END FILE TYPE structure."));
587       return CMD_FAILURE;
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 discovering catastrophic error. */
615   err_cond_fail ();
616   discard_variables ();
617   return CMD_FAILURE;
618 }
619 \f
620 /* FILE TYPE runtime. */
621
622 /*static void read_from_file_type_mixed(void);
623    static void read_from_file_type_grouped(void);
624    static void read_from_file_type_nested(void); */
625
626 /* Reads any number of cases into case C and calls write_case()
627    for each one.  Compare data-list.c:read_from_data_list. */
628 static void
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
720 static void
721 file_type_source_destroy (struct case_source *source)
722 {
723   struct file_type_pgm *fty = source->aux;
724   struct record_type *iter, *next;
725
726   cancel_transformations ();
727   dfm_close_reader (fty->reader);
728   for (iter = fty->recs_head; iter; iter = next)
729     {
730       next = iter->next;
731       free (iter);
732     }
733 }
734
735 const struct case_source_class file_type_source_class =
736   {
737     "FILE TYPE",
738     NULL,
739     file_type_source_read,
740     file_type_source_destroy,
741   };