2b11a4abd21ce4deb551a893ad6d2e1a6c7efb41
[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 "error.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   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       goto error;
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             goto error;
138         }
139       else if (lex_match_id ("RECORD"))
140         {
141           lex_match ('=');
142           if (!parse_col_spec (&fty->record, "####RECD"))
143             goto error;
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               goto error;
152             }
153           
154           lex_match ('=');
155           if (!parse_col_spec (&fty->case_sbc, "####CASE"))
156             goto error;
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               goto error;
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               goto error;
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                   goto error;
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               goto error;
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               goto error;
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               goto error;
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               goto error;
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               goto error;
241             }
242         }
243       else
244         {
245           lex_error (_("while expecting a valid subcommand"));
246           goto error;
247         }
248     }
249
250   if (fty->record.name[0] == 0)
251     {
252       msg (SE, _("The required RECORD subcommand was not present."));
253       goto error;
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           goto error;
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           goto error;
269         }
270     }
271
272   if (!dfm_open_for_reading (fty->handle))
273     goto error;
274   default_handle = fty->handle;
275
276   create_col_var (&fty->record);
277   if (fty->case_sbc.name[0])
278     create_col_var (&fty->case_sbc);
279   vfm_source = create_case_source (&file_type_source_class, default_dict, fty);
280
281   return CMD_SUCCESS;
282
283  error:
284   free (fty);
285   return CMD_FAILURE;
286 }
287
288 /* Creates a variable with attributes specified by struct col_spec C, and
289    stores it into C->V. */
290 static void
291 create_col_var (struct col_spec *c)
292 {
293   int width;
294
295   if (formats[c->fmt].cat & FCAT_STRING)
296     width = c->nc;
297   else
298     width = 0;
299   c->v = dict_create_var (default_dict, c->name, width);
300 }
301
302 /* Parses variable, column, type specifications for a variable. */
303 static int
304 parse_col_spec (struct col_spec *c, const char *def_name)
305 {
306   struct fmt_spec spec;
307
308   /* Name. */
309   if (token == T_ID)
310     {
311       strcpy (c->name, tokid);
312       lex_get ();
313     }
314   else
315     strcpy (c->name, def_name);
316
317   /* First column. */
318   if (!lex_force_int ())
319     return 0;
320   c->fc = lex_integer ();
321   if (c->fc < 1)
322     {
323       msg (SE, _("Column value must be positive."));
324       return 0;
325     }
326   lex_get ();
327
328   /* Last column. */
329   lex_negative_to_dash ();
330   if (lex_match ('-'))
331     {
332       if (!lex_force_int ())
333         return 0;
334       c->nc = lex_integer ();
335       lex_get ();
336
337       if (c->nc < c->fc)
338         {
339           msg (SE, _("Ending column precedes beginning column."));
340           return 0;
341         }
342       
343       c->nc -= c->fc - 1;
344     }
345   else
346     c->nc = 1;
347
348   /* Format specifier. */
349   if (lex_match ('('))
350     {
351       const char *cp;
352       if (!lex_force_id ())
353         return 0;
354       c->fmt = parse_format_specifier_name (&cp, 0);
355       if (c->fmt == -1)
356         return 0;
357       if (*cp)
358         {
359           msg (SE, _("Bad format specifier name."));
360           return 0;
361         }
362       lex_get ();
363       if (!lex_force_match (')'))
364         return 0;
365     }
366   else
367     c->fmt = FMT_F;
368
369   spec.type = c->fmt;
370   spec.w = c->nc;
371   spec.d = 0;
372   return check_input_specifier (&spec);
373 }
374 \f
375 /* RECORD TYPE. */
376
377 /* Parse the RECORD TYPE command. */
378 int
379 cmd_record_type (void)
380 {
381   struct file_type_pgm *fty;
382   struct record_type *rct;
383
384   /* Make sure we're inside a FILE TYPE structure. */
385   if (pgm_state != STATE_INPUT
386       || !case_source_is_class (vfm_source, &file_type_source_class))
387     {
388       msg (SE, _("This command may only appear within a "
389                  "FILE TYPE/END FILE TYPE structure."));
390       return CMD_FAILURE;
391     }
392
393   fty = vfm_source->aux;
394
395   /* Initialize the record_type structure. */
396   rct = xmalloc (sizeof *rct);
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   if (fty->recs_tail && (fty->recs_tail->flags & RCT_OTHER))
410     {
411       msg (SE, _("OTHER may appear only on the last RECORD TYPE command."));
412       goto error;
413     }
414       
415   if (fty->recs_tail)
416     {
417       fty->recs_tail->lt = n_trns - 1;
418       if (!(fty->recs_tail->flags & RCT_SKIP)
419           && fty->recs_tail->ft == fty->recs_tail->lt)
420         {
421           msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
422                      "for above RECORD TYPE."));
423           goto error;
424         }
425     }
426
427   /* Parse record type values. */
428   if (lex_match_id ("OTHER"))
429     rct->flags |= RCT_OTHER;
430   else
431     {
432       int mv = 0;
433
434       while (token == T_NUM || token == T_STRING)
435         {
436           if (rct->nv >= mv)
437             {
438               mv += 16;
439               rct->v = xrealloc (rct->v, mv * sizeof *rct->v);
440             }
441
442           if (formats[fty->record.fmt].cat & FCAT_STRING)
443             {
444               if (!lex_force_string ())
445                 goto error;
446               rct->v[rct->nv].c = xmalloc (fty->record.nc + 1);
447               st_bare_pad_copy (rct->v[rct->nv].c, ds_c_str (&tokstr),
448                                 fty->record.nc + 1);
449             }
450           else
451             {
452               if (!lex_force_num ())
453                 goto error;
454               rct->v[rct->nv].f = tokval;
455             }
456           rct->nv++;
457           lex_get ();
458
459           lex_match (',');
460         }
461     }
462
463   /* Parse the rest of the subcommands. */
464   while (token != '.')
465     {
466       if (lex_match_id ("SKIP"))
467         rct->flags |= RCT_SKIP;
468       else if (lex_match_id ("CASE"))
469         {
470           if (fty->type == FTY_MIXED)
471             {
472               msg (SE, _("The CASE subcommand is not allowed on "
473                          "the RECORD TYPE command for FILE TYPE MIXED."));
474               goto error;
475             }
476
477           lex_match ('=');
478           if (!parse_col_spec (&rct->case_sbc, ""))
479             goto error;
480           if (rct->case_sbc.name[0])
481             {
482               msg (SE, _("No variable name may be specified for the "
483                          "CASE subcommand on RECORD TYPE."));
484               goto error;
485             }
486           
487           if ((formats[rct->case_sbc.fmt].cat ^ formats[fty->case_sbc.fmt].cat)
488               & FCAT_STRING)
489             {
490               msg (SE, _("The CASE column specification on RECORD TYPE "
491                          "must give a format specifier that is the "
492                          "same type as that of the CASE column "
493                          "specification given on FILE TYPE."));
494               goto error;
495             }
496         }
497       else if (lex_match_id ("DUPLICATE"))
498         {
499           lex_match ('=');
500           if (lex_match_id ("WARN"))
501             rct->flags |= RCT_DUPLICATE;
502           else if (lex_match_id ("NOWARN"))
503             rct->flags &= ~RCT_DUPLICATE;
504           else
505             {
506               msg (SE, _("WARN or NOWARN expected on DUPLICATE "
507                          "subcommand."));
508               goto error;
509             }
510         }
511       else if (lex_match_id ("MISSING"))
512         {
513           lex_match ('=');
514           if (lex_match_id ("WARN"))
515             rct->flags |= RCT_MISSING;
516           else if (lex_match_id ("NOWARN"))
517             rct->flags &= ~RCT_MISSING;
518           else
519             {
520               msg (SE, _("WARN or NOWARN expected on MISSING subcommand."));
521               goto error;
522             }
523         }
524       else if (lex_match_id ("SPREAD"))
525         {
526           lex_match ('=');
527           if (lex_match_id ("YES"))
528             rct->flags |= RCT_SPREAD;
529           else if (lex_match_id ("NO"))
530             rct->flags &= ~RCT_SPREAD;
531           else
532             {
533               msg (SE, _("YES or NO expected on SPREAD subcommand."));
534               goto error;
535             }
536         }
537       else
538         {
539           lex_error (_("while expecting a valid subcommand"));
540           goto error;
541         }
542     }
543
544   if (fty->recs_head)
545     fty->recs_tail = fty->recs_tail->next = xmalloc (sizeof *fty->recs_tail);
546   else
547     fty->recs_head = fty->recs_tail = xmalloc (sizeof *fty->recs_tail);
548   memcpy (fty->recs_tail, &rct, sizeof *fty->recs_tail);
549
550   return CMD_SUCCESS;
551
552  error:
553   if (formats[fty->record.fmt].cat & FCAT_STRING) 
554     {
555       int i;
556       
557       for (i = 0; i < rct->nv; i++)
558         free (rct->v[i].c); 
559     }
560   free (rct->v);
561   free (rct);
562
563   return CMD_FAILURE;
564 }
565 \f
566 /* END FILE TYPE. */
567
568 int
569 cmd_end_file_type (void)
570 {
571   struct file_type_pgm *fty;
572
573   if (pgm_state != STATE_INPUT
574       || case_source_is_class (vfm_source, &file_type_source_class))
575     {
576       msg (SE, _("This command may only appear within a "
577                  "FILE TYPE/END FILE TYPE structure."));
578       return CMD_FAILURE;
579     }
580   fty = vfm_source->aux;
581   fty->case_size = dict_get_case_size (default_dict);
582
583   if (fty->recs_tail)
584     {
585       fty->recs_tail->lt = n_trns - 1;
586       if (!(fty->recs_tail->flags & RCT_SKIP)
587           && fty->recs_tail->ft == fty->recs_tail->lt)
588         {
589           msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
590                      "on above RECORD TYPE."));
591           goto fail;
592         }
593     }
594   else
595     {
596       msg (SE, _("No commands between FILE TYPE and END FILE TYPE."));
597       goto fail;
598     }
599
600   f_trns = n_trns;
601
602   return lex_end_of_command ();
603
604  fail:
605   /* Come here on discovering catastrophic error. */
606   err_cond_fail ();
607   discard_variables ();
608   return CMD_FAILURE;
609 }
610 \f
611 /* FILE TYPE runtime. */
612
613 /*static void read_from_file_type_mixed(void);
614    static void read_from_file_type_grouped(void);
615    static void read_from_file_type_nested(void); */
616
617 /* Reads any number of cases into case C and calls write_case()
618    for each one.  Compare data-list.c:read_from_data_list. */
619 static void
620 file_type_source_read (struct case_source *source,
621                        struct ccase *c,
622                        write_case_func *write_case UNUSED,
623                        write_case_data wc_data UNUSED)
624 {
625   struct file_type_pgm *fty = source->aux;
626   struct fmt_spec format;
627
628   dfm_push (fty->handle);
629
630   format.type = fty->record.fmt;
631   format.w = fty->record.nc;
632   format.d = 0;
633   while (!dfm_eof (fty->handle))
634     {
635       struct len_string line;
636       struct record_type *iter;
637       union value v;
638       int i;
639
640       dfm_expand_tabs (fty->handle);
641       dfm_get_record (fty->handle, &line);
642       if (formats[fty->record.fmt].cat & FCAT_STRING)
643         {
644           struct data_in di;
645           
646           v.c = c->data[fty->record.v->fv].s;
647
648           data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
649                                fty->record.fc, fty->record.fc + fty->record.nc);
650           di.v = (union value *) v.c;
651           di.flags = 0;
652           di.f1 = fty->record.fc;
653           di.format = format;
654           data_in (&di);
655
656           for (iter = fty->recs_head; iter; iter = iter->next)
657             {
658               if (iter->flags & RCT_OTHER)
659                 goto found;
660               for (i = 0; i < iter->nv; i++)
661                 if (!memcmp (iter->v[i].c, v.c, fty->record.nc))
662                   goto found;
663             }
664           if (fty->wild)
665             msg (SW, _("Unknown record type \"%.*s\"."), fty->record.nc, v.c);
666         }
667       else
668         {
669           struct data_in di;
670
671           data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
672                                fty->record.fc, fty->record.fc + fty->record.nc);
673           di.v = &v;
674           di.flags = 0;
675           di.f1 = fty->record.fc;
676           di.format = format;
677           data_in (&di);
678
679           memcpy (&c->data[fty->record.v->fv].f, &v.f, sizeof v.f);
680           for (iter = fty->recs_head; iter; iter = iter->next)
681             {
682               if (iter->flags & RCT_OTHER)
683                 goto found;
684               for (i = 0; i < iter->nv; i++)
685                 if (iter->v[i].f == v.f)
686                   goto found;
687             }
688           if (fty->wild)
689             msg (SW, _("Unknown record type %g."), v.f);
690         }
691       dfm_forward_record (fty->handle);
692       continue;
693
694     found:
695       /* Arrive here if there is a matching record_type, which is in
696          iter. */
697       dfm_forward_record (fty->handle);
698     }
699
700 /*  switch(fty->type)
701    {
702    case FTY_MIXED: read_from_file_type_mixed(); break;
703    case FTY_GROUPED: read_from_file_type_grouped(); break;
704    case FTY_NESTED: read_from_file_type_nested(); break;
705    default: assert(0);
706    } */
707
708   dfm_pop (fty->handle);
709 }
710
711 static void
712 file_type_source_destroy (struct case_source *source)
713 {
714   struct file_type_pgm *fty = source->aux;
715   struct record_type *iter, *next;
716
717   cancel_transformations ();
718   for (iter = fty->recs_head; iter; iter = next)
719     {
720       next = iter->next;
721       free (iter);
722     }
723 }
724
725 const struct case_source_class file_type_source_class =
726   {
727     "FILE TYPE",
728     NULL,
729     file_type_source_read,
730     file_type_source_destroy,
731   };