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., 51 Franklin Street, Fifth Floor, Boston, MA
20 /* FIXME: seems like a lot of code duplication with data-list.c. */
26 #include <data/case.h>
27 #include <data/procedure.h>
28 #include <data/transformations.h>
29 #include <data/variable.h>
30 #include <language/command.h>
31 #include <language/data-io/data-writer.h>
32 #include <language/data-io/file-handle.h>
33 #include <language/expressions/public.h>
34 #include <language/lexer/lexer.h>
35 #include <language/lexer/variable-parser.h>
36 #include <libpspp/alloc.h>
37 #include <libpspp/assertion.h>
38 #include <libpspp/compiler.h>
39 #include <libpspp/message.h>
40 #include <libpspp/message.h>
41 #include <libpspp/misc.h>
42 #include <output/manager.h>
43 #include <output/table.h>
46 #define _(msgid) gettext (msgid)
48 /* Describes what to do when an output field is encountered. */
51 PRT_ERROR, /* Invalid value. */
52 PRT_NEWLINE, /* Newline. */
53 PRT_CONST, /* Constant string. */
54 PRT_VAR, /* Variable. */
55 PRT_SPACE /* A single space. */
58 /* Describes how to output one field. */
61 struct prt_out_spec *next;
62 int type; /* PRT_* constant. */
63 int fc; /* 0-based first column. */
66 char *c; /* PRT_CONST: Associated string. */
69 struct variable *v; /* PRT_VAR: Associated variable. */
70 struct fmt_spec f; /* PRT_VAR: Output spec. */
77 /* Enums for use with print_trns's `options' field. */
80 PRT_CMD_MASK = 1, /* Command type mask. */
81 PRT_PRINT = 0, /* PRINT transformation identifier. */
82 PRT_WRITE = 1, /* WRITE transformation identifier. */
83 PRT_EJECT = 002, /* Can be combined with CMD_PRINT only. */
84 PRT_BINARY = 004 /* File is binary, omit newlines. */
87 /* PRINT, PRINT EJECT, WRITE private data structure. */
90 struct dfm_writer *writer; /* Output file, NULL=listing file. */
91 int options; /* PRT_* bitmapped field. */
92 struct prt_out_spec *spec; /* Output specifications. */
93 int max_width; /* Maximum line width including null. */
94 char *line; /* Buffer for sticking lines in. */
97 /* PRT_PRINT or PRT_WRITE. */
100 /* Holds information on parsing the data file. */
101 static struct print_trns prt;
103 /* Last prt_out_spec in the chain. Used for building the linked-list. */
104 static struct prt_out_spec *next;
106 /* Number of records. */
109 static int internal_cmd_print (int flags);
110 static trns_proc_func print_trns_proc;
111 static trns_free_func print_trns_free;
112 static int parse_specs (void);
113 static void dump_table (const struct file_handle *);
114 static void append_var_spec (struct prt_out_spec *);
115 static void alloc_line (void);
119 /* Parses PRINT command. */
123 return internal_cmd_print (PRT_PRINT);
126 /* Parses PRINT EJECT command. */
128 cmd_print_eject (void)
130 return internal_cmd_print (PRT_PRINT | PRT_EJECT);
133 /* Parses WRITE command. */
137 return internal_cmd_print (PRT_WRITE);
140 /* Parses the output commands. F is PRT_PRINT, PRT_WRITE, or
141 PRT_PRINT|PRT_EJECT. */
143 internal_cmd_print (int f)
145 int table = 0; /* Print table? */
146 struct print_trns *trns = NULL; /* malloc()'d transformation. */
147 struct file_handle *fh = NULL;
149 /* Fill in prt to facilitate error-handling. */
157 which_cmd = f & PRT_CMD_MASK;
159 /* Parse the command options. */
160 while (!lex_match ('/'))
162 if (lex_match_id ("OUTFILE"))
166 fh = fh_parse (FH_REF_FILE);
170 else if (lex_match_id ("RECORDS"))
174 if (!lex_force_int ())
176 nrec = lex_integer ();
180 else if (lex_match_id ("TABLE"))
182 else if (lex_match_id ("NOTABLE"))
186 lex_error (_("expecting a valid subcommand"));
191 /* Parse variables and strings. */
197 prt.writer = dfm_open_writer (fh);
198 if (prt.writer == NULL)
201 if (fh_get_mode (fh) == FH_MODE_BINARY)
202 prt.options |= PRT_BINARY;
205 /* Output the variable table if requested. */
209 /* Count the maximum line width. Allocate linebuffer if
213 /* Put the transformation in the queue. */
214 trns = xmalloc (sizeof *trns);
215 memcpy (trns, &prt, sizeof *trns);
216 add_transformation (print_trns_proc, print_trns_free, trns);
221 print_trns_free (&prt);
225 /* Appends the field output specification SPEC to the list maintained
228 append_var_spec (struct prt_out_spec *spec)
231 prt.spec = next = xmalloc (sizeof *spec);
233 next = next->next = xmalloc (sizeof *spec);
235 memcpy (next, spec, sizeof *spec);
239 /* Field parsing. Mostly stolen from data-list.c. */
241 /* Used for chaining together fortran-like format specifiers. */
244 struct fmt_list *next;
247 struct fmt_list *down;
250 /* Used as "local" variables among the fixed-format parsing funcs. If
251 it were guaranteed that PSPP were going to be compiled by gcc,
252 I'd make all these functions a single set of nested functions. */
255 struct variable **v; /* variable list */
256 size_t nv; /* number of variables in list */
257 size_t cv; /* number of variables from list used up so far
258 by the FORTRAN-like format specifiers */
260 int recno; /* current 1-based record number */
261 int sc; /* 1-based starting column for next variable */
263 struct prt_out_spec spec; /* next format spec to append to list */
264 int fc, lc; /* first, last 1-based column number of current
267 int level; /* recursion level for FORTRAN-like format
272 static int fixed_parse_compatible (void);
273 static struct fmt_list *fixed_parse_fortran (void);
275 static int parse_string_argument (void);
276 static int parse_variable_argument (void);
278 /* Parses all the variable and string specifications on a single
279 PRINT, PRINT EJECT, or WRITE command into the prt structure.
284 /* Return code from called function. */
292 while (lex_match ('/'))
294 int prev_recno = fx.recno;
297 if (lex_is_number ())
299 if (!lex_force_int ())
301 if (lex_integer () < fx.recno)
303 msg (SE, _("The record number specified, %ld, is "
304 "before the previous record, %d. Data "
305 "fields must be listed in order of "
306 "increasing record number."),
307 lex_integer (), fx.recno - 1);
310 fx.recno = lex_integer ();
314 fx.spec.type = PRT_NEWLINE;
315 while (prev_recno++ < fx.recno)
316 append_var_spec (&fx.spec);
321 if (token == T_STRING)
322 code = parse_string_argument ();
324 code = parse_variable_argument ();
328 fx.spec.type = PRT_NEWLINE;
329 append_var_spec (&fx.spec);
333 else if (fx.recno > nrec)
335 msg (SE, _("Variables are specified on records that "
336 "should not exist according to RECORDS subcommand."));
342 lex_error (_("expecting end of command"));
349 /* Parses a string argument to the PRINT commands. Returns success. */
351 parse_string_argument (void)
353 fx.spec.type = PRT_CONST;
354 fx.spec.fc = fx.sc - 1;
355 fx.spec.u.c = ds_xstrdup (&tokstr);
358 /* Parse the included column range. */
359 if (lex_is_number ())
361 /* Width of column range in characters. */
364 /* Width of constant string in characters. */
367 /* 1-based index of last column in range. */
370 if (!lex_is_integer () || lex_integer () <= 0)
372 msg (SE, _("%g is not a valid column location."), tokval);
375 fx.spec.fc = lex_integer () - 1;
378 lex_negative_to_dash ();
381 if (!lex_is_integer ())
383 msg (SE, _("Column location expected following `%d-'."),
387 if (lex_integer () <= 0)
389 msg (SE, _("%g is not a valid column location."), tokval);
392 if (lex_integer () < fx.spec.fc + 1)
394 msg (SE, _("%d-%ld is not a valid column range. The second "
395 "column must be greater than or equal to the first."),
396 fx.spec.fc + 1, lex_integer ());
399 lc = lex_integer () - 1;
404 /* If only a starting location is specified then the field is
405 the width of the provided string. */
406 lc = fx.spec.fc + strlen (fx.spec.u.c) - 1;
408 /* Apply the range. */
409 c_len = lc - fx.spec.fc + 1;
410 s_len = strlen (fx.spec.u.c);
412 fx.spec.u.c[c_len] = 0;
413 else if (s_len < c_len)
415 fx.spec.u.c = xrealloc (fx.spec.u.c, c_len + 1);
416 memset (&fx.spec.u.c[s_len], ' ', c_len - s_len);
417 fx.spec.u.c[c_len] = 0;
423 /* If nothing is provided then the field is the width of the
425 fx.sc += strlen (fx.spec.u.c);
427 append_var_spec (&fx.spec);
435 /* Parses a variable argument to the PRINT commands by passing it off
436 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
439 parse_variable_argument (void)
441 if (!parse_variables (default_dict, &fx.v, &fx.nv, PV_DUPLICATE))
444 if (lex_is_number ())
446 if (!fixed_parse_compatible ())
449 else if (token == '(')
453 if (!fixed_parse_fortran ())
458 /* User wants dictionary format specifiers. */
462 for (i = 0; i < fx.nv; i++)
465 fx.spec.type = PRT_VAR;
466 fx.spec.fc = fx.sc - 1;
467 fx.spec.u.v.v = fx.v[i];
468 fx.spec.u.v.f = fx.v[i]->print;
469 append_var_spec (&fx.spec);
470 fx.sc += fx.v[i]->print.w;
473 fx.spec.type = PRT_SPACE;
474 fx.spec.fc = fx.sc - 1;
475 append_var_spec (&fx.spec);
488 /* Verifies that FORMAT doesn't need a variable wider than WIDTH.
489 Returns true iff that is the case. */
491 check_string_width (const struct fmt_spec *format, const struct variable *v)
493 if (get_format_var_width (format) > v->width)
495 msg (SE, _("Variable %s has width %d so it cannot be output "
497 v->name, v->width, fmt_to_string (format));
503 /* Parses a column specification for parse_specs(). */
505 fixed_parse_compatible (void)
507 int individual_var_width;
511 type = fx.v[0]->type;
512 for (i = 1; i < fx.nv; i++)
513 if (type != fx.v[i]->type)
515 msg (SE, _("%s is not of the same type as %s. To specify "
516 "variables of different types in the same variable "
517 "list, use a FORTRAN-like format specifier."),
518 fx.v[i]->name, fx.v[0]->name);
522 if (!lex_force_int ())
524 fx.fc = lex_integer () - 1;
527 msg (SE, _("Column positions for fields must be positive."));
532 lex_negative_to_dash ();
535 if (!lex_force_int ())
537 fx.lc = lex_integer () - 1;
540 msg (SE, _("Column positions for fields must be positive."));
543 else if (fx.lc < fx.fc)
545 msg (SE, _("The ending column for a field must not "
546 "be less than the starting column."));
554 fx.spec.u.v.f.w = fx.lc - fx.fc + 1;
557 struct fmt_desc *fdp;
563 fx.spec.u.v.f.type = parse_format_specifier_name (&cp, 0);
564 if (fx.spec.u.v.f.type == -1)
568 msg (SE, _("A format specifier on this line "
569 "has extra characters on the end."));
576 fx.spec.u.v.f.type = FMT_F;
578 if (lex_is_number ())
580 if (!lex_force_int ())
582 if (lex_integer () < 1)
584 msg (SE, _("The value for number of decimal places "
585 "must be at least 1."));
588 fx.spec.u.v.f.d = lex_integer ();
594 fdp = &formats[fx.spec.u.v.f.type];
595 if (fdp->n_args < 2 && fx.spec.u.v.f.d)
597 msg (SE, _("Input format %s doesn't accept decimal places."),
601 if (fx.spec.u.v.f.d > 16)
602 fx.spec.u.v.f.d = 16;
604 if (!lex_force_match (')'))
609 fx.spec.u.v.f.type = FMT_F;
615 if ((fx.lc - fx.fc + 1) % fx.nv)
617 msg (SE, _("The %d columns %d-%d can't be evenly divided into %u "
619 fx.lc - fx.fc + 1, fx.fc + 1, fx.lc + 1, (unsigned) fx.nv);
623 individual_var_width = (fx.lc - fx.fc + 1) / fx.nv;
624 fx.spec.u.v.f.w = individual_var_width;
625 if (!check_output_specifier (&fx.spec.u.v.f, true)
626 || !check_specifier_type (&fx.spec.u.v.f, type, true))
630 for (i = 0; i < fx.nv; i++)
631 if (!check_string_width (&fx.spec.u.v.f, fx.v[i]))
635 fx.spec.type = PRT_VAR;
636 for (i = 0; i < fx.nv; i++)
638 fx.spec.fc = fx.fc + individual_var_width * i;
639 fx.spec.u.v.v = fx.v[i];
640 append_var_spec (&fx.spec);
645 /* Destroy a format list and, optionally, all its sublists. */
647 destroy_fmt_list (struct fmt_list *f, int recurse)
649 struct fmt_list *next;
654 if (recurse && f->f.type == FMT_DESCEND)
655 destroy_fmt_list (f->down, 1);
660 /* Recursively puts the format list F (which represents a set of
661 FORTRAN-like format specifications, like 4(F10,2X)) into the
664 dump_fmt_list (struct fmt_list *f)
668 for (; f; f = f->next)
669 if (f->f.type == FMT_X)
671 else if (f->f.type == FMT_T)
673 else if (f->f.type == FMT_NEWREC)
675 fx.recno += f->count;
677 fx.spec.type = PRT_NEWLINE;
678 for (i = 0; i < f->count; i++)
679 append_var_spec (&fx.spec);
682 for (i = 0; i < f->count; i++)
683 if (f->f.type == FMT_DESCEND)
685 if (!dump_fmt_list (f->down))
694 msg (SE, _("The number of format "
695 "specifications exceeds the number of variable "
701 if (!check_output_specifier (&f->f, true)
702 || !check_specifier_type (&f->f, v->type, true)
703 || !check_string_width (&f->f, v))
706 fx.spec.type = PRT_VAR;
708 fx.spec.u.v.f = f->f;
709 fx.spec.fc = fx.sc - 1;
710 append_var_spec (&fx.spec);
717 /* Recursively parses a list of FORTRAN-like format specifiers. Calls
718 itself to parse nested levels of parentheses. Returns to its
719 original caller NULL, to indicate error, non-NULL, but nothing
720 useful, to indicate success (it returns a free()'d block). */
721 static struct fmt_list *
722 fixed_parse_fortran (void)
724 struct fmt_list *head = NULL;
725 struct fmt_list *fl = NULL;
727 lex_get (); /* skip opening parenthesis */
731 fl = fl->next = xmalloc (sizeof *fl);
733 head = fl = xmalloc (sizeof *fl);
735 if (lex_is_number ())
737 if (!lex_is_integer ())
739 fl->count = lex_integer ();
747 fl->f.type = FMT_DESCEND;
749 fl->down = fixed_parse_fortran ();
754 else if (lex_match ('/'))
755 fl->f.type = FMT_NEWREC;
756 else if (!parse_format_specifier (&fl->f, FMTP_ALLOW_XT)
757 || !check_output_specifier (&fl->f, 1))
769 dump_fmt_list (head);
770 destroy_fmt_list (head, 1);
773 msg (SE, _("There aren't enough format specifications "
774 "to match the number of variable names given."));
781 destroy_fmt_list (head, 0);
786 /* Prints the table produced by the TABLE subcommand to the listing
789 dump_table (const struct file_handle *fh)
791 struct prt_out_spec *spec;
796 for (nspec = 0, spec = prt.spec; spec; spec = spec->next)
797 if (spec->type == PRT_CONST || spec->type == PRT_VAR)
799 t = tab_create (4, nspec + 1, 0);
800 tab_columns (t, TAB_COL_DOWN, 1);
801 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, nspec);
802 tab_hline (t, TAL_2, 0, 3, 1);
803 tab_headers (t, 0, 0, 1, 0);
804 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
805 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
806 tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
807 tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
808 tab_dim (t, tab_natural_dimensions);
809 for (nspec = recno = 0, spec = prt.spec; spec; spec = spec->next)
817 int len = strlen (spec->u.c);
819 tab_text (t, 0, nspec, TAB_LEFT | TAB_FIX | TAT_PRINTF,
820 "\"%s\"", spec->u.c);
821 tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
822 tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
823 spec->fc + 1, spec->fc + len);
824 tab_text (t, 3, nspec, TAB_LEFT | TAB_FIX | TAT_PRINTF,
831 tab_text (t, 0, nspec, TAB_LEFT, spec->u.v.v->name);
832 tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
833 tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
834 spec->fc + 1, spec->fc + spec->u.v.f.w);
835 tab_text (t, 3, nspec, TAB_LEFT | TAB_FIX,
836 fmt_to_string (&spec->u.v.f));
846 tab_title (t, ngettext ("Writing %d record to %s.",
847 "Writing %d records to %s.", recno),
848 recno, fh_get_name (fh));
850 tab_title (t, ngettext ("Writing %d record.",
851 "Writing %d records.", recno), recno);
855 /* Calculates the maximum possible line width and allocates a buffer
856 big enough to contain it */
860 /* Cumulative maximum line width (excluding null terminator) so far. */
863 /* Width required by current this prt_out_spec. */
864 int pot_w; /* Potential w. */
867 struct prt_out_spec *i;
869 for (i = prt.spec; i; i = i->next)
877 pot_w = i->fc + strlen (i->u.c);
880 pot_w = i->fc + i->u.v.f.w;
892 prt.max_width = w + 2;
893 prt.line = xmalloc (prt.max_width);
896 /* Transformation. */
898 /* Performs the transformation inside print_trns T on case C. */
900 print_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED)
902 /* Transformation. */
903 struct print_trns *t = trns_;
906 struct prt_out_spec *i;
911 /* Length of the line in buf. */
913 memset (buf, ' ', t->max_width);
915 if (t->options & PRT_EJECT)
918 /* Note that a field written to a place where a field has
919 already been written truncates the record. `PRINT /A B
920 (T10,F8,T1,F8).' only outputs B. */
921 for (i = t->spec; i; i = i->next)
925 if (t->writer == NULL)
928 tab_output_text (TAB_FIX | TAT_NOWRAP, buf);
932 if ((t->options & PRT_CMD_MASK) == PRT_PRINT
933 || !(t->options & PRT_BINARY))
936 dfm_put_record (t->writer, buf, len);
939 memset (buf, ' ', t->max_width);
944 /* FIXME: Should be revised to keep track of the string's
945 length outside the loop, probably in i->u.c[0]. */
946 memcpy (&buf[i->fc], i->u.c, strlen (i->u.c));
947 len = i->fc + strlen (i->u.c);
951 data_out (&buf[i->fc], &i->u.v.f, case_data (c, i->u.v.v->fv));
952 len = i->fc + i->u.v.f.w;
956 /* PRT_SPACE always immediately follows PRT_VAR. */
964 if (t->writer != NULL && dfm_write_error (t->writer))
966 return TRNS_CONTINUE;
969 /* Frees all the data inside print_trns PRT. Does not free PRT. */
971 print_trns_free (void *prt_)
973 struct print_trns *prt = prt_;
974 struct prt_out_spec *i, *n;
977 for (i = prt->spec; i; i = n)
995 if (prt->writer != NULL)
996 ok = dfm_close_writer (prt->writer);
1003 /* PRINT SPACE transformation. */
1004 struct print_space_trns
1006 struct dfm_writer *writer; /* Output data file. */
1007 struct expression *e; /* Number of lines; NULL=1. */
1011 static trns_proc_func print_space_trns_proc;
1012 static trns_free_func print_space_trns_free;
1015 cmd_print_space (void)
1017 struct print_space_trns *t;
1018 struct file_handle *fh;
1019 struct expression *e;
1020 struct dfm_writer *writer;
1022 if (lex_match_id ("OUTFILE"))
1026 fh = fh_parse (FH_REF_FILE);
1036 e = expr_parse (default_dict, EXPR_NUMBER);
1040 lex_error (_("expecting end of command"));
1049 writer = dfm_open_writer (fh);
1059 t = xmalloc (sizeof *t);
1063 add_transformation (print_space_trns_proc, print_space_trns_free, t);
1067 /* Executes a PRINT SPACE transformation. */
1069 print_space_trns_proc (void *t_, struct ccase *c,
1070 int case_num UNUSED)
1072 struct print_space_trns *t = t_;
1078 double f = expr_evaluate_num (t->e, c, case_num);
1080 msg (SW, _("The expression on PRINT SPACE evaluated to the "
1081 "system-missing value."));
1082 else if (f < 0 || f > INT_MAX)
1083 msg (SW, _("The expression on PRINT SPACE evaluated to %g."), f);
1089 if (t->writer == NULL)
1092 dfm_put_record (t->writer, "\n", 1);
1094 if (t->writer != NULL && dfm_write_error (t->writer))
1096 return TRNS_CONTINUE;
1099 /* Frees a PRINT SPACE transformation.
1100 Returns true if successful, false if an I/O error occurred. */
1102 print_space_trns_free (void *trns_)
1104 struct print_space_trns *trns = trns_;
1105 bool ok = dfm_close_writer (trns->writer);
1106 expr_free (trns->e);