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