1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <data/case.h>
27 #include <data/dictionary.h>
28 #include <data/data-out.h>
29 #include <data/format.h>
30 #include <data/procedure.h>
31 #include <data/variable.h>
32 #include <language/command.h>
33 #include <language/dictionary/split-file.h>
34 #include <language/lexer/lexer.h>
35 #include <libpspp/alloc.h>
36 #include <libpspp/compiler.h>
37 #include <libpspp/magic.h>
38 #include <libpspp/message.h>
39 #include <libpspp/message.h>
40 #include <libpspp/misc.h>
41 #include <output/htmlP.h>
42 #include <output/manager.h>
43 #include <output/output.h>
44 #include <output/table.h>
49 #define _(msgid) gettext (msgid)
55 *variables=varlist("PV_NO_SCRATCH");
56 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
57 +format=numbering:numbered/!unnumbered,
59 weight:weight/!noweight.
64 /* Layout for one output driver. */
67 int type; /* 0=Values and labels fit across the page. */
68 size_t n_vertical; /* Number of labels to list vertically. */
69 size_t header_rows; /* Number of header rows. */
70 char **header; /* The header itself. */
74 static struct cmd_list cmd;
76 /* Current case number. */
80 static struct string line_buffer;
82 /* TTY-style output functions. */
83 static unsigned n_lines_remaining (struct outp_driver *d);
84 static unsigned n_chars_width (struct outp_driver *d);
85 static void write_line (struct outp_driver *d, const char *s);
87 /* Other functions. */
88 static bool list_cases (const struct ccase *, void *, const struct dataset *);
89 static void determine_layout (void);
90 static void clean_up (void);
91 static void write_header (struct outp_driver *);
92 static void write_all_headers (const struct ccase *, void *, const struct dataset*);
94 /* Returns the number of text lines that can fit on the remainder of
96 static inline unsigned
97 n_lines_remaining (struct outp_driver *d)
101 diff = d->length - d->cp_y;
102 return (diff > 0) ? (diff / d->font_height) : 0;
105 /* Returns the number of fixed-width character that can fit across the
107 static inline unsigned
108 n_chars_width (struct outp_driver *d)
110 return d->width / d->fixed_width;
113 /* Writes the line S at the current position and advances to the next
116 write_line (struct outp_driver *d, const char *s)
118 struct outp_text text;
120 assert (d->cp_y + d->font_height <= d->length);
121 text.font = OUTP_FIXED;
122 text.justification = OUTP_LEFT;
123 text.string = ss_cstr (s);
126 text.h = text.v = INT_MAX;
127 d->class->text_draw (d, &text);
129 d->cp_y += d->font_height;
132 /* Parses and executes the LIST procedure. */
134 cmd_list (struct lexer *lexer, struct dataset *ds)
136 struct variable *casenum_var = NULL;
139 if (!parse_list (lexer, ds, &cmd, NULL))
142 /* Fill in defaults. */
143 if (cmd.step == NOT_LONG)
145 if (cmd.first == NOT_LONG)
147 if (cmd.last == NOT_LONG)
149 if (!cmd.sbc_variables)
150 dict_get_vars (dataset_dict (ds), &cmd.v_variables, &cmd.n_variables,
151 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
152 if (cmd.n_variables == 0)
154 msg (SE, _("No variables specified."));
158 /* Verify arguments. */
159 if (cmd.first > cmd.last)
162 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
163 "specified. The values will be swapped."), cmd.first, cmd.last);
165 cmd.first = cmd.last;
170 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
171 "being reset to 1."), cmd.first);
176 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
177 "being reset to 1."), cmd.last);
182 msg (SW, _("The step value %ld is less than 1. The value is being "
183 "reset to 1."), cmd.step);
187 /* Weighting variable. */
188 if (cmd.weight == LST_WEIGHT)
190 if (dict_get_weight (dataset_dict (ds)) != NULL)
194 for (i = 0; i < cmd.n_variables; i++)
195 if (cmd.v_variables[i] == dict_get_weight (dataset_dict (ds)))
197 if (i >= cmd.n_variables)
199 /* Add the weight variable to the end of the variable list. */
201 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
202 sizeof *cmd.v_variables);
203 cmd.v_variables[cmd.n_variables - 1]
204 = dict_get_weight (dataset_dict (ds));
208 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
212 if (cmd.numbering == LST_NUMBERED)
214 /* Initialize the case-number variable. */
215 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
216 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
217 casenum_var = var_create ("Case#", 0);
218 var_set_both_formats (casenum_var, &format);
220 /* Add the weight variable at the beginning of the variable list. */
222 cmd.v_variables = xnrealloc (cmd.v_variables,
223 cmd.n_variables, sizeof *cmd.v_variables);
224 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
225 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
226 cmd.v_variables[0] = casenum_var;
232 ok = procedure_with_splits (ds, write_all_headers, list_cases, NULL, NULL);
233 ds_destroy(&line_buffer);
237 var_destroy (casenum_var);
239 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
242 /* Writes headers to all devices. This is done at the beginning of
243 each SPLIT FILE group. */
245 write_all_headers (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
247 struct outp_driver *d;
249 output_split_file_values (ds, c);
250 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
252 if (!d->class->special)
254 d->cp_y += d->font_height; /* Blank line. */
257 else if (d->class == &html_class)
259 struct html_driver_ext *x = d->ext;
261 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
266 for (i = 0; i < cmd.n_variables; i++)
267 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
268 var_get_name (cmd.v_variables[i]));
271 fputs (" </TR>\n", x->file);
278 /* Writes the headers. Some of them might be vertical; most are
279 probably horizontal. */
281 write_header (struct outp_driver *d)
283 struct list_ext *prc = d->prc;
285 if (!prc->header_rows)
288 if (n_lines_remaining (d) < prc->header_rows + 1)
291 assert (n_lines_remaining (d) >= prc->header_rows + 1);
294 /* Design the header. */
300 /* Allocate, initialize header. */
301 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
303 int w = n_chars_width (d);
304 for (i = 0; i < prc->header_rows; i++)
306 prc->header[i] = xmalloc (w + 1);
307 memset (prc->header[i], ' ', w);
311 /* Put in vertical names. */
312 for (i = x = 0; i < prc->n_vertical; i++)
314 const struct variable *v = cmd.v_variables[i];
315 const char *name = var_get_name (v);
316 size_t name_len = strlen (name);
317 const struct fmt_spec *print = var_get_print_format (v);
320 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
322 for (j = 0; j < name_len; j++)
323 prc->header[name_len - j - 1][x] = name[j];
327 /* Put in horizontal names. */
328 for (; i < cmd.n_variables; i++)
330 const struct variable *v = cmd.v_variables[i];
331 const char *name = var_get_name (v);
332 size_t name_len = strlen (name);
333 const struct fmt_spec *print = var_get_print_format (v);
335 memset (&prc->header[prc->header_rows - 1][x], '-',
336 MAX (print->w, (int) name_len));
337 if ((int) name_len < print->w)
338 x += print->w - name_len;
339 memcpy (&prc->header[0][x], name, name_len);
343 /* Add null bytes. */
344 for (i = 0; i < prc->header_rows; i++)
346 for (x = n_chars_width (d); x >= 1; x--)
347 if (prc->header[i][x - 1] != ' ')
349 prc->header[i][x] = 0;
356 /* Write out the header, in back-to-front order except for the last line. */
357 if (prc->header_rows >= 2)
361 for (i = prc->header_rows - 1; i-- != 0; )
362 write_line (d, prc->header[i]);
364 write_line (d, prc->header[prc->header_rows - 1]);
368 /* Frees up all the memory we've allocated. */
372 struct outp_driver *d;
374 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
375 if (d->class->special == 0)
377 struct list_ext *prc = d->prc;
382 for (i = 0; i < prc->header_rows; i++)
383 free (prc->header[i]);
388 else if (d->class == &html_class)
392 struct html_driver_ext *x = d->ext;
394 fputs ("</TABLE>\n", x->file);
400 free (cmd.v_variables);
403 /* Writes string STRING at the current position. If the text would
404 fall off the side of the page, then advance to the next line,
405 indenting by amount INDENT. */
407 write_varname (struct outp_driver *d, char *string, int indent)
409 struct outp_text text;
412 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
414 d->cp_y += d->font_height;
415 if (d->cp_y + d->font_height > d->length)
420 text.font = OUTP_FIXED;
421 text.justification = OUTP_LEFT;
422 text.string = ss_cstr (string);
425 text.h = text.v = INT_MAX;
426 d->class->text_draw (d, &text);
427 d->class->text_metrics (d, &text, &width, NULL);
431 /* When we can't fit all the values across the page, we write out all
432 the variable names just once. This is where we do it. */
434 write_fallback_headers (struct outp_driver *d)
436 const int max_width = n_chars_width(d) - 10;
442 const char *Line = _("Line");
443 char *leader = local_alloc (strlen (Line)
444 + INT_STRLEN_BOUND (line_number) + 1 + 1);
446 while (index < cmd.n_variables)
448 struct outp_text text;
451 /* Ensure that there is enough room for a line of text. */
452 if (d->cp_y + d->font_height > d->length)
455 /* The leader is a string like `Line 1: '. Write the leader. */
456 sprintf (leader, "%s %d:", Line, ++line_number);
457 text.font = OUTP_FIXED;
458 text.justification = OUTP_LEFT;
459 text.string = ss_cstr (leader);
462 text.h = text.v = INT_MAX;
463 d->class->text_draw (d, &text);
464 d->class->text_metrics (d, &text, &leader_width, NULL);
465 d->cp_x = leader_width;
474 int var_width = var_get_print_format (cmd.v_variables[index])->w;
475 if (width + var_width > max_width && width != 0)
479 d->cp_y += d->font_height;
486 char varname[LONG_NAME_LEN + 2];
487 snprintf (varname, sizeof varname,
488 " %s", var_get_name (cmd.v_variables[index]));
489 write_varname (d, varname, leader_width);
492 while (++index < cmd.n_variables);
496 d->cp_y += d->font_height;
501 /* There are three possible layouts for the LIST procedure:
503 1. If the values and their variables' name fit across the page,
504 then they are listed across the page in that way.
506 2. If the values can fit across the page, but not the variable
507 names, then as many variable names as necessary are printed
508 vertically to compensate.
510 3. If not even the values can fit across the page, the variable
511 names are listed just once, at the beginning, in a compact format,
512 and the values are listed with a variable name label at the
513 beginning of each line for easier reference.
515 This is complicated by the fact that we have to do all this for
516 every output driver, not just once. */
518 determine_layout (void)
520 struct outp_driver *d;
522 /* This is the largest page width of any driver, so we can tell what
523 size buffer to allocate. */
524 int largest_page_width = 0;
526 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
528 size_t column; /* Current column. */
529 int width; /* Accumulated width. */
530 int height; /* Height of vertical names. */
531 int max_width; /* Page width. */
533 struct list_ext *prc;
535 if (d->class == &html_class)
538 assert (d->class->special == 0);
542 max_width = n_chars_width (d);
543 largest_page_width = MAX (largest_page_width, max_width);
545 prc = d->prc = xmalloc (sizeof *prc);
551 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
553 const struct variable *v = cmd.v_variables[column];
554 int fmt_width = var_get_print_format (v)->w;
555 int name_len = strlen (var_get_name (v));
556 width += MAX (fmt_width, name_len);
558 if (width <= max_width)
560 prc->header_rows = 2;
565 for (width = cmd.n_variables - 1, height = 0, column = 0;
566 column < cmd.n_variables && width <= max_width;
569 const struct variable *v = cmd.v_variables[column];
570 int fmt_width = var_get_print_format (v)->w;
571 size_t name_len = strlen (var_get_name (v));
573 if (name_len > height)
577 /* If it fit then we need to determine how many labels can be
578 written horizontally. */
579 if (width <= max_width && height <= SHORT_NAME_LEN)
582 prc->n_vertical = SIZE_MAX;
584 for (column = cmd.n_variables; column-- != 0; )
586 const struct variable *v = cmd.v_variables[column];
587 int name_len = strlen (var_get_name (v));
588 int fmt_width = var_get_print_format (v)->w;
589 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
590 if (trial_width > max_width)
592 prc->n_vertical = column + 1;
597 assert (prc->n_vertical != SIZE_MAX);
599 prc->n_vertical = cmd.n_variables;
600 /* Finally determine the length of the headers. */
601 for (prc->header_rows = 0, column = 0;
602 column < prc->n_vertical;
605 const struct variable *var = cmd.v_variables[column];
606 size_t name_len = strlen (var_get_name (var));
607 prc->header_rows = MAX (prc->header_rows, name_len);
613 /* Otherwise use the ugly fallback listing format. */
615 prc->header_rows = 0;
617 d->cp_y += d->font_height;
618 write_fallback_headers (d);
619 d->cp_y += d->font_height;
622 ds_init_empty (&line_buffer);
625 /* Writes case C to output. */
627 list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
629 struct outp_driver *d;
632 if (case_idx < cmd.first || case_idx > cmd.last
633 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
636 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
637 if (d->class->special == 0)
639 const struct list_ext *prc = d->prc;
640 const int max_width = n_chars_width (d);
643 if (!prc->header_rows)
645 ds_put_format(&line_buffer, "%8s: ",
646 var_get_name (cmd.v_variables[0]));
650 for (column = 0; column < cmd.n_variables; column++)
652 const struct variable *v = cmd.v_variables[column];
653 const struct fmt_spec *print = var_get_print_format (v);
656 if (prc->type == 0 && column >= prc->n_vertical)
658 int name_len = strlen (var_get_name (v));
659 width = MAX (name_len, print->w);
664 if (width + ds_length(&line_buffer) > max_width &&
665 ds_length(&line_buffer) != 0)
667 if (!n_lines_remaining (d))
673 write_line (d, ds_cstr (&line_buffer));
674 ds_clear(&line_buffer);
676 if (!prc->header_rows)
677 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
680 if (width > print->w)
681 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
683 if (fmt_is_string (print->type)
684 || dict_contains_var (dataset_dict (ds), v))
686 data_out (case_data (c, v), print,
687 ds_put_uninit (&line_buffer, print->w));
691 union value case_idx_value;
692 case_idx_value.f = case_idx;
693 data_out (&case_idx_value, print,
694 ds_put_uninit (&line_buffer,print->w));
697 ds_put_char(&line_buffer, ' ');
700 if (!n_lines_remaining (d))
706 write_line (d, ds_cstr (&line_buffer));
707 ds_clear(&line_buffer);
709 else if (d->class == &html_class)
711 struct html_driver_ext *x = d->ext;
714 fputs (" <TR>\n", x->file);
716 for (column = 0; column < cmd.n_variables; column++)
718 const struct variable *v = cmd.v_variables[column];
719 const struct fmt_spec *print = var_get_print_format (v);
722 if (fmt_is_string (print->type)
723 || dict_contains_var (dataset_dict (ds), v))
724 data_out (case_data (c, v), print, buf);
727 union value case_idx_value;
728 case_idx_value.f = case_idx;
729 data_out (&case_idx_value, print, buf);
732 fputs (" <TD>", x->file);
733 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
734 fputs ("</TD>\n", x->file);
737 fputs (" </TR>\n", x->file);