1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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
28 #include "file-handle.h"
35 /* Defines the three types of complex files read by FILE TYPE. */
43 /* Limited variable column specifications. */
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. */
52 /* RCT_* record type constants. */
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. */
62 /* Represents a RECORD TYPE command. */
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. */
73 /* Represents a FILE TYPE input program. Does not contain a
74 trns_header because it's never submitted as a transformation. */
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
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. */
93 static int parse_col_spec (struct col_spec *, const char *);
94 static void create_col_var (struct col_spec *c);
96 /* Parses FILE TYPE command. */
100 static struct file_type_pgm *fty;
103 discard_variables ();
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;
113 if (lex_match_id ("MIXED"))
114 fty->type = FTY_MIXED;
115 else if (lex_match_id ("GROUPED"))
117 fty->type = FTY_GROUPED;
123 else if (lex_match_id ("NESTED"))
124 fty->type = FTY_NESTED;
127 msg (SE, _("MIXED, GROUPED, or NESTED expected."));
133 if (lex_match_id ("FILE"))
136 fty->handle = fh_parse_file_handle ();
140 else if (lex_match_id ("RECORD"))
143 if (!parse_col_spec (&fty->record, "####RECD"))
146 else if (lex_match_id ("CASE"))
148 if (fty->type == FTY_MIXED)
150 msg (SE, _("The CASE subcommand is not valid on FILE TYPE "
156 if (!parse_col_spec (&fty->case_sbc, "####CASE"))
159 else if (lex_match_id ("WILD"))
162 if (lex_match_id ("WARN"))
164 else if (lex_match_id ("NOWARN"))
168 msg (SE, _("WARN or NOWARN expected after WILD."));
172 else if (lex_match_id ("DUPLICATE"))
174 if (fty->type == FTY_MIXED)
176 msg (SE, _("The DUPLICATE subcommand is not valid on "
177 "FILE TYPE MIXED."));
182 if (lex_match_id ("WARN"))
184 else if (lex_match_id ("NOWARN"))
186 else if (lex_match_id ("CASE"))
188 if (fty->type != FTY_NESTED)
190 msg (SE, _("DUPLICATE=CASE is only valid on "
191 "FILE TYPE NESTED."));
199 msg (SE, _("WARN%s expected after DUPLICATE."),
200 (fty->type == FTY_NESTED ? _(", NOWARN, or CASE")
205 else if (lex_match_id ("MISSING"))
207 if (fty->type == FTY_MIXED)
209 msg (SE, _("The MISSING subcommand is not valid on "
210 "FILE TYPE MIXED."));
215 if (lex_match_id ("NOWARN"))
217 else if (lex_match_id ("WARN"))
221 msg (SE, _("WARN or NOWARN after MISSING."));
225 else if (lex_match_id ("ORDERED"))
227 if (fty->type != FTY_GROUPED)
229 msg (SE, _("ORDERED is only valid on FILE TYPE GROUPED."));
234 if (lex_match_id ("YES"))
236 else if (lex_match_id ("NO"))
240 msg (SE, _("YES or NO expected after ORDERED."));
246 lex_error (_("while expecting a valid subcommand"));
251 if (fty->record.name[0] == 0)
253 msg (SE, _("The required RECORD subcommand was not present."));
257 if (fty->type == FTY_GROUPED)
259 if (fty->case_sbc.name[0] == 0)
261 msg (SE, _("The required CASE subcommand was not present."));
265 if (!strcmp (fty->case_sbc.name, fty->record.name))
267 msg (SE, _("CASE and RECORD must specify different variable "
273 if (!dfm_open_for_reading (fty->handle))
275 default_handle = fty->handle;
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);
289 /* Creates a variable with attributes specified by struct col_spec C, and
290 stores it into C->V. */
292 create_col_var (struct col_spec *c)
296 if (formats[c->fmt].cat & FCAT_STRING)
300 c->v = dict_create_var (default_dict, c->name, width);
303 /* Parses variable, column, type specifications for a variable. */
305 parse_col_spec (struct col_spec *c, const char *def_name)
307 struct fmt_spec spec;
312 strcpy (c->name, tokid);
316 strcpy (c->name, def_name);
319 if (!lex_force_int ())
321 c->fc = lex_integer ();
324 msg (SE, _("Column value must be positive."));
330 lex_negative_to_dash ();
333 if (!lex_force_int ())
335 c->nc = lex_integer ();
340 msg (SE, _("Ending column precedes beginning column."));
349 /* Format specifier. */
353 if (!lex_force_id ())
355 c->fmt = parse_format_specifier_name (&cp, 0);
360 msg (SE, _("Bad format specifier name."));
364 if (!lex_force_match (')'))
373 return check_input_specifier (&spec);
378 /* Parse the RECORD TYPE command. */
380 cmd_record_type (void)
382 struct file_type_pgm *fty;
383 struct record_type *rct;
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))
389 msg (SE, _("This command may only appear within a "
390 "FILE TYPE/END FILE TYPE structure."));
394 fty = vfm_source->aux;
396 /* Initialize the record_type structure. */
397 rct = xmalloc (sizeof *rct);
401 rct->flags |= RCT_DUPLICATE;
403 rct->flags |= RCT_MISSING;
407 if (fty->case_sbc.name[0])
408 rct->case_sbc = fty->case_sbc;
410 if (fty->recs_tail && (fty->recs_tail->flags & RCT_OTHER))
412 msg (SE, _("OTHER may appear only on the last RECORD TYPE command."));
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)
422 msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
423 "for above RECORD TYPE."));
428 /* Parse record type values. */
429 if (lex_match_id ("OTHER"))
430 rct->flags |= RCT_OTHER;
435 while (token == T_NUM || token == T_STRING)
440 rct->v = xrealloc (rct->v, mv * sizeof *rct->v);
443 if (formats[fty->record.fmt].cat & FCAT_STRING)
445 if (!lex_force_string ())
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),
453 if (!lex_force_num ())
455 rct->v[rct->nv].f = tokval;
464 /* Parse the rest of the subcommands. */
467 if (lex_match_id ("SKIP"))
468 rct->flags |= RCT_SKIP;
469 else if (lex_match_id ("CASE"))
471 if (fty->type == FTY_MIXED)
473 msg (SE, _("The CASE subcommand is not allowed on "
474 "the RECORD TYPE command for FILE TYPE MIXED."));
479 if (!parse_col_spec (&rct->case_sbc, ""))
481 if (rct->case_sbc.name[0])
483 msg (SE, _("No variable name may be specified for the "
484 "CASE subcommand on RECORD TYPE."));
488 if ((formats[rct->case_sbc.fmt].cat ^ formats[fty->case_sbc.fmt].cat)
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."));
498 else if (lex_match_id ("DUPLICATE"))
501 if (lex_match_id ("WARN"))
502 rct->flags |= RCT_DUPLICATE;
503 else if (lex_match_id ("NOWARN"))
504 rct->flags &= ~RCT_DUPLICATE;
507 msg (SE, _("WARN or NOWARN expected on DUPLICATE "
512 else if (lex_match_id ("MISSING"))
515 if (lex_match_id ("WARN"))
516 rct->flags |= RCT_MISSING;
517 else if (lex_match_id ("NOWARN"))
518 rct->flags &= ~RCT_MISSING;
521 msg (SE, _("WARN or NOWARN expected on MISSING subcommand."));
525 else if (lex_match_id ("SPREAD"))
528 if (lex_match_id ("YES"))
529 rct->flags |= RCT_SPREAD;
530 else if (lex_match_id ("NO"))
531 rct->flags &= ~RCT_SPREAD;
534 msg (SE, _("YES or NO expected on SPREAD subcommand."));
540 lex_error (_("while expecting a valid subcommand"));
546 fty->recs_tail = fty->recs_tail->next = xmalloc (sizeof *fty->recs_tail);
548 fty->recs_head = fty->recs_tail = xmalloc (sizeof *fty->recs_tail);
549 memcpy (fty->recs_tail, &rct, sizeof *fty->recs_tail);
554 if (formats[fty->record.fmt].cat & FCAT_STRING)
558 for (i = 0; i < rct->nv; i++)
570 cmd_end_file_type (void)
572 struct file_type_pgm *fty;
574 if (pgm_state != STATE_INPUT
575 || case_source_is_class (vfm_source, &file_type_source_class))
577 msg (SE, _("This command may only appear within a "
578 "FILE TYPE/END FILE TYPE structure."));
581 fty = vfm_source->aux;
582 fty->case_size = dict_get_case_size (default_dict);
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)
590 msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
591 "on above RECORD TYPE."));
597 msg (SE, _("No commands between FILE TYPE and END FILE TYPE."));
603 return lex_end_of_command ();
606 /* Come here on discovering catastrophic error. */
608 discard_variables ();
612 /* FILE TYPE runtime. */
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); */
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. */
621 file_type_source_read (struct case_source *source,
623 write_case_func *write_case UNUSED,
624 write_case_data wc_data UNUSED)
626 struct file_type_pgm *fty = source->aux;
627 struct fmt_spec format;
629 dfm_push (fty->handle);
631 format.type = fty->record.fmt;
632 format.w = fty->record.nc;
634 while (!dfm_eof (fty->handle))
636 struct len_string line;
637 struct record_type *iter;
641 dfm_expand_tabs (fty->handle);
642 dfm_get_record (fty->handle, &line);
643 if (formats[fty->record.fmt].cat & FCAT_STRING)
647 v.c = case_data_rw (c, fty->record.v->fv)->s;
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;
653 di.f1 = fty->record.fc;
657 for (iter = fty->recs_head; iter; iter = iter->next)
659 if (iter->flags & RCT_OTHER)
661 for (i = 0; i < iter->nv; i++)
662 if (!memcmp (iter->v[i].c, v.c, fty->record.nc))
666 msg (SW, _("Unknown record type \"%.*s\"."), fty->record.nc, v.c);
672 data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
673 fty->record.fc, fty->record.fc + fty->record.nc);
676 di.f1 = fty->record.fc;
680 case_data_rw (c, fty->record.v->fv)->f = v.f;
681 for (iter = fty->recs_head; iter; iter = iter->next)
683 if (iter->flags & RCT_OTHER)
685 for (i = 0; i < iter->nv; i++)
686 if (iter->v[i].f == v.f)
690 msg (SW, _("Unknown record type %g."), v.f);
692 dfm_forward_record (fty->handle);
696 /* Arrive here if there is a matching record_type, which is in
698 dfm_forward_record (fty->handle);
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;
709 dfm_pop (fty->handle);
713 file_type_source_destroy (struct case_source *source)
715 struct file_type_pgm *fty = source->aux;
716 struct record_type *iter, *next;
718 cancel_transformations ();
719 for (iter = fty->recs_head; iter; iter = next)
726 const struct case_source_class file_type_source_class =
730 file_type_source_read,
731 file_type_source_destroy,