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
27 #include "file-handle.h"
34 /* Defines the three types of complex files read by FILE TYPE. */
42 /* Limited variable column specifications. */
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. */
51 /* RCT_* record type constants. */
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. */
61 /* Represents a RECORD TYPE command. */
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. */
72 /* Represents a FILE TYPE input program. Does not contain a
73 trns_header because it's never submitted as a transformation. */
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
87 struct record_type *recs_head; /* List of record types. */
88 struct record_type *recs_tail; /* Last in list of record types. */
91 /* Current FILE TYPE input program. */
92 static struct file_type_pgm fty;
94 static int parse_col_spec (struct col_spec *, const char *);
95 static void create_col_var (struct col_spec *c);
97 /* Parses FILE TYPE command. */
102 discard_variables ();
103 fty.handle = inline_file;
104 fty.record.name[0] = 0;
105 fty.case_sbc.name[0] = 0;
106 fty.wild = fty.duplicate = fty.missing = fty.ordered = 0;
107 fty.had_rec_type = 0;
108 fty.recs_head = fty.recs_tail = NULL;
110 lex_match_id ("TYPE");
111 if (lex_match_id ("MIXED"))
112 fty.type = FTY_MIXED;
113 else if (lex_match_id ("GROUPED"))
115 fty.type = FTY_GROUPED;
121 else if (lex_match_id ("NESTED"))
122 fty.type = FTY_NESTED;
125 msg (SE, _("MIXED, GROUPED, or NESTED expected."));
131 if (lex_match_id ("FILE"))
134 fty.handle = fh_parse_file_handle ();
138 else if (lex_match_id ("RECORD"))
141 if (!parse_col_spec (&fty.record, "####RECD"))
144 else if (lex_match_id ("CASE"))
146 if (fty.type == FTY_MIXED)
148 msg (SE, _("The CASE subcommand is not valid on FILE TYPE "
154 if (!parse_col_spec (&fty.case_sbc, "####CASE"))
157 else if (lex_match_id ("WILD"))
160 if (lex_match_id ("WARN"))
162 else if (lex_match_id ("NOWARN"))
166 msg (SE, _("WARN or NOWARN expected after WILD."));
170 else if (lex_match_id ("DUPLICATE"))
172 if (fty.type == FTY_MIXED)
174 msg (SE, _("The DUPLICATE subcommand is not valid on "
175 "FILE TYPE MIXED."));
180 if (lex_match_id ("WARN"))
182 else if (lex_match_id ("NOWARN"))
184 else if (lex_match_id ("CASE"))
186 if (fty.type != FTY_NESTED)
188 msg (SE, _("DUPLICATE=CASE is only valid on "
189 "FILE TYPE NESTED."));
197 msg (SE, _("WARN%s expected after DUPLICATE."),
198 (fty.type == FTY_NESTED ? _(", NOWARN, or CASE")
203 else if (lex_match_id ("MISSING"))
205 if (fty.type == FTY_MIXED)
207 msg (SE, _("The MISSING subcommand is not valid on "
208 "FILE TYPE MIXED."));
213 if (lex_match_id ("NOWARN"))
215 else if (lex_match_id ("WARN"))
219 msg (SE, _("WARN or NOWARN after MISSING."));
223 else if (lex_match_id ("ORDERED"))
225 if (fty.type != FTY_GROUPED)
227 msg (SE, _("ORDERED is only valid on FILE TYPE GROUPED."));
232 if (lex_match_id ("YES"))
234 else if (lex_match_id ("NO"))
238 msg (SE, _("YES or NO expected after ORDERED."));
244 lex_error (_("while expecting a valid subcommand"));
249 if (fty.record.name[0] == 0)
251 msg (SE, _("The required RECORD subcommand was not present."));
255 if (fty.type == FTY_GROUPED)
257 if (fty.case_sbc.name[0] == 0)
259 msg (SE, _("The required CASE subcommand was not present."));
263 if (!strcmp (fty.case_sbc.name, fty.record.name))
265 msg (SE, _("CASE and RECORD must specify different variable "
271 default_handle = fty.handle;
273 vfm_source = &file_type_source;
274 create_col_var (&fty.record);
275 if (fty.case_sbc.name[0])
276 create_col_var (&fty.case_sbc);
281 /* Creates a variable with attributes specified by struct col_spec C, and
282 stores it into C->V. */
284 create_col_var (struct col_spec *c)
288 if (formats[c->fmt].cat & FCAT_STRING)
292 c->v = dict_create_var (default_dict, c->name, width);
295 /* Parses variable, column, type specifications for a variable. */
297 parse_col_spec (struct col_spec *c, const char *def_name)
299 struct fmt_spec spec;
303 strcpy (c->name, tokid);
307 strcpy (c->name, def_name);
309 if (!lex_force_int ())
311 c->fc = lex_integer ();
314 msg (SE, _("Column value must be positive."));
319 lex_negative_to_dash ();
322 if (!lex_force_int ())
324 c->nc = lex_integer ();
329 msg (SE, _("Ending column precedes beginning column."));
341 if (!lex_force_id ())
343 c->fmt = parse_format_specifier_name (&cp, 0);
348 msg (SE, _("Bad format specifier name."));
352 if (!lex_force_match (')'))
361 return check_input_specifier (&spec);
366 /* Structure being filled in by internal_cmd_record_type. */
367 static struct record_type rct;
369 static int internal_cmd_record_type (void);
371 /* Parse the RECORD TYPE command. */
373 cmd_record_type (void)
375 int result = internal_cmd_record_type ();
377 if (result == CMD_FAILURE)
381 if (formats[fty.record.fmt].cat & FCAT_STRING)
382 for (i = 0; i < rct.nv; i++)
391 internal_cmd_record_type (void)
393 /* Initialize the record_type structure. */
397 rct.flags |= RCT_DUPLICATE;
399 rct.flags |= RCT_MISSING;
403 if (fty.case_sbc.name[0])
404 rct.case_sbc = fty.case_sbc;
406 /* Make sure we're inside a FILE TYPE structure. */
407 if (pgm_state != STATE_INPUT || vfm_source != &file_type_source)
409 msg (SE, _("This command may only appear within a "
410 "FILE TYPE/END FILE TYPE structure."));
414 if (fty.recs_tail && (fty.recs_tail->flags & RCT_OTHER))
416 msg (SE, _("OTHER may appear only on the last RECORD TYPE command."));
422 fty.recs_tail->lt = n_trns - 1;
423 if (!(fty.recs_tail->flags & RCT_SKIP)
424 && fty.recs_tail->ft == fty.recs_tail->lt)
426 msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
427 "for above RECORD TYPE."));
432 lex_match_id ("RECORD");
433 lex_match_id ("TYPE");
435 /* Parse record type values. */
436 if (lex_match_id ("OTHER"))
437 rct.flags |= RCT_OTHER;
442 while (token == T_NUM || token == T_STRING)
447 rct.v = xrealloc (rct.v, mv * sizeof *rct.v);
450 if (formats[fty.record.fmt].cat & FCAT_STRING)
452 if (!lex_force_string ())
454 rct.v[rct.nv].c = xmalloc (fty.record.nc + 1);
455 st_bare_pad_copy (rct.v[rct.nv].c, ds_value (&tokstr),
460 if (!lex_force_num ())
462 rct.v[rct.nv].f = tokval;
471 /* Parse the rest of the subcommands. */
474 if (lex_match_id ("SKIP"))
475 rct.flags |= RCT_SKIP;
476 else if (lex_match_id ("CASE"))
478 if (fty.type == FTY_MIXED)
480 msg (SE, _("The CASE subcommand is not allowed on "
481 "the RECORD TYPE command for FILE TYPE MIXED."));
486 if (!parse_col_spec (&rct.case_sbc, ""))
488 if (rct.case_sbc.name[0])
490 msg (SE, _("No variable name may be specified for the "
491 "CASE subcommand on RECORD TYPE."));
495 if ((formats[rct.case_sbc.fmt].cat ^ formats[fty.case_sbc.fmt].cat)
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."));
505 else if (lex_match_id ("DUPLICATE"))
508 if (lex_match_id ("WARN"))
509 rct.flags |= RCT_DUPLICATE;
510 else if (lex_match_id ("NOWARN"))
511 rct.flags &= ~RCT_DUPLICATE;
514 msg (SE, _("WARN or NOWARN expected on DUPLICATE "
519 else if (lex_match_id ("MISSING"))
522 if (lex_match_id ("WARN"))
523 rct.flags |= RCT_MISSING;
524 else if (lex_match_id ("NOWARN"))
525 rct.flags &= ~RCT_MISSING;
528 msg (SE, _("WARN or NOWARN expected on MISSING subcommand."));
532 else if (lex_match_id ("SPREAD"))
535 if (lex_match_id ("YES"))
536 rct.flags |= RCT_SPREAD;
537 else if (lex_match_id ("NO"))
538 rct.flags &= ~RCT_SPREAD;
541 msg (SE, _("YES or NO expected on SPREAD subcommand."));
547 lex_error (_("while expecting a valid subcommand"));
553 fty.recs_tail = fty.recs_tail->next = xmalloc (sizeof *fty.recs_tail);
555 fty.recs_head = fty.recs_tail = xmalloc (sizeof *fty.recs_tail);
556 memcpy (fty.recs_tail, &rct, sizeof *fty.recs_tail);
564 cmd_end_file_type (void)
566 if (pgm_state != STATE_INPUT || vfm_source != &file_type_source)
568 msg (SE, _("This command may only appear within a "
569 "FILE TYPE/END FILE TYPE structure."));
573 lex_match_id ("TYPE");
577 fty.recs_tail->lt = n_trns - 1;
578 if (!(fty.recs_tail->flags & RCT_SKIP)
579 && fty.recs_tail->ft == fty.recs_tail->lt)
581 msg (SE, _("No input commands (DATA LIST, REPEATING DATA) "
582 "on above RECORD TYPE."));
588 msg (SE, _("No commands between FILE TYPE and END FILE TYPE."));
594 return lex_end_of_command ();
597 /* Come here on discovering catastrophic error. */
599 discard_variables ();
603 /* FILE TYPE runtime. */
605 /*static void read_from_file_type_mixed(void);
606 static void read_from_file_type_grouped(void);
607 static void read_from_file_type_nested(void); */
609 /* Reads any number of cases into temp_case and calls write_case() for
610 each one. Compare data-list.c:read_from_data_list. */
612 file_type_source_read (write_case_func *write_case UNUSED,
613 write_case_data wc_data UNUSED)
618 struct fmt_spec format;
620 dfm_push (fty.handle);
622 format.type = fty.record.fmt;
623 format.w = fty.record.nc;
625 while (NULL != (line = dfm_get_record (fty.handle, &len)))
627 struct record_type *iter;
631 if (formats[fty.record.fmt].cat & FCAT_STRING)
635 v.c = temp_case->data[fty.record.v->fv].s;
637 data_in_finite_line (&di, line, len,
638 fty.record.fc, fty.record.fc + fty.record.nc);
639 di.v = (union value *) v.c;
641 di.f1 = fty.record.fc;
645 for (iter = fty.recs_head; iter; iter = iter->next)
647 if (iter->flags & RCT_OTHER)
649 for (i = 0; i < iter->nv; i++)
650 if (!memcmp (iter->v[i].c, v.c, fty.record.nc))
654 msg (SW, _("Unknown record type \"%.*s\"."), fty.record.nc, v.c);
660 data_in_finite_line (&di, line, len,
661 fty.record.fc, fty.record.fc + fty.record.nc);
664 di.f1 = fty.record.fc;
668 memcpy (&temp_case->data[fty.record.v->fv].f, &v.f, sizeof v.f);
669 for (iter = fty.recs_head; iter; iter = iter->next)
671 if (iter->flags & RCT_OTHER)
673 for (i = 0; i < iter->nv; i++)
674 if (iter->v[i].f == v.f)
678 msg (SW, _("Unknown record type %g."), v.f);
680 dfm_fwd_record (fty.handle);
684 /* Arrive here if there is a matching record_type, which is in
686 dfm_fwd_record (fty.handle);
691 case FTY_MIXED: read_from_file_type_mixed(); break;
692 case FTY_GROUPED: read_from_file_type_grouped(); break;
693 case FTY_NESTED: read_from_file_type_nested(); break;
697 dfm_pop (fty.handle);
701 file_type_source_destroy_source (void)
703 struct record_type *iter, *next;
705 cancel_transformations ();
706 for (iter = fty.recs_head; iter; iter = next)
713 struct case_stream file_type_source =
716 file_type_source_read,
719 file_type_source_destroy_source,