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