1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 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
27 #include <data/case.h>
28 #include <data/dictionary.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>
47 #define _(msgid) gettext (msgid)
53 *variables=varlist("PV_NO_SCRATCH");
54 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
55 format=numbering:numbered/!unnumbered,
57 weight:weight/!noweight.
62 /* Layout for one output driver. */
65 int type; /* 0=Values and labels fit across the page. */
66 size_t n_vertical; /* Number of labels to list vertically. */
67 size_t header_rows; /* Number of header rows. */
68 char **header; /* The header itself. */
72 static struct cmd_list cmd;
74 /* Current case number. */
78 static struct string line_buffer;
80 /* TTY-style output functions. */
81 static unsigned n_lines_remaining (struct outp_driver *d);
82 static unsigned n_chars_width (struct outp_driver *d);
83 static void write_line (struct outp_driver *d, const char *s);
85 /* Other functions. */
86 static bool list_cases (const struct ccase *, void *);
87 static void determine_layout (void);
88 static void clean_up (void);
89 static void write_header (struct outp_driver *);
90 static void write_all_headers (const struct ccase *, void *);
92 /* Returns the number of text lines that can fit on the remainder of
94 static inline unsigned
95 n_lines_remaining (struct outp_driver *d)
99 diff = d->length - d->cp_y;
100 return (diff > 0) ? (diff / d->font_height) : 0;
103 /* Returns the number of fixed-width character that can fit across the
105 static inline unsigned
106 n_chars_width (struct outp_driver *d)
108 return d->width / d->fixed_width;
111 /* Writes the line S at the current position and advances to the next
114 write_line (struct outp_driver *d, const char *s)
116 struct outp_text text;
118 assert (d->cp_y + d->font_height <= d->length);
119 text.font = OUTP_FIXED;
120 text.justification = OUTP_LEFT;
121 ls_init (&text.string, s, strlen (s));
124 text.h = text.v = INT_MAX;
125 d->class->text_draw (d, &text);
127 d->cp_y += d->font_height;
130 /* Parses and executes the LIST procedure. */
134 struct variable casenum_var;
137 if (!parse_list (&cmd))
140 /* Fill in defaults. */
141 if (cmd.step == NOT_LONG)
143 if (cmd.first == NOT_LONG)
145 if (cmd.last == NOT_LONG)
147 if (!cmd.sbc_variables)
148 dict_get_vars (default_dict, &cmd.v_variables, &cmd.n_variables,
149 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
150 if (cmd.n_variables == 0)
152 msg (SE, _("No variables specified."));
156 /* Verify arguments. */
157 if (cmd.first > cmd.last)
160 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
161 "specified. The values will be swapped."), cmd.first, cmd.last);
163 cmd.first = cmd.last;
168 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
169 "being reset to 1."), cmd.first);
174 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
175 "being reset to 1."), cmd.last);
180 msg (SW, _("The step value %ld is less than 1. The value is being "
181 "reset to 1."), cmd.step);
185 /* Weighting variable. */
186 if (cmd.weight == LST_WEIGHT)
188 if (dict_get_weight (default_dict) != NULL)
192 for (i = 0; i < cmd.n_variables; i++)
193 if (cmd.v_variables[i] == dict_get_weight (default_dict))
195 if (i >= cmd.n_variables)
197 /* Add the weight variable to the end of the variable list. */
199 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
200 sizeof *cmd.v_variables);
201 cmd.v_variables[cmd.n_variables - 1]
202 = dict_get_weight (default_dict);
206 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
210 if (cmd.numbering == LST_NUMBERED)
212 /* Initialize the case-number variable. */
213 strcpy (casenum_var.name, "Case#");
214 casenum_var.type = NUMERIC;
216 casenum_var.print = make_output_format (FMT_F,
217 (cmd.last == LONG_MAX
218 ? 5 : intlog10 (cmd.last)), 0);
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 (write_all_headers, list_cases, NULL, NULL);
233 ds_destroy(&line_buffer);
237 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
240 /* Writes headers to all devices. This is done at the beginning of
241 each SPLIT FILE group. */
243 write_all_headers (const struct ccase *c, void *aux UNUSED)
245 struct outp_driver *d;
247 output_split_file_values (c);
248 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
250 if (!d->class->special)
252 d->cp_y += d->font_height; /* Blank line. */
255 else if (d->class == &html_class)
257 struct html_driver_ext *x = d->ext;
259 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
264 for (i = 0; i < cmd.n_variables; i++)
265 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
266 cmd.v_variables[i]->name);
269 fputs (" </TR>\n", x->file);
276 /* Writes the headers. Some of them might be vertical; most are
277 probably horizontal. */
279 write_header (struct outp_driver *d)
281 struct list_ext *prc = d->prc;
283 if (!prc->header_rows)
286 if (n_lines_remaining (d) < prc->header_rows + 1)
289 assert (n_lines_remaining (d) >= prc->header_rows + 1);
292 /* Design the header. */
298 /* Allocate, initialize header. */
299 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
301 int w = n_chars_width (d);
302 for (i = 0; i < prc->header_rows; i++)
304 prc->header[i] = xmalloc (w + 1);
305 memset (prc->header[i], ' ', w);
309 /* Put in vertical names. */
310 for (i = x = 0; i < prc->n_vertical; i++)
312 struct variable *v = cmd.v_variables[i];
315 memset (&prc->header[prc->header_rows - 1][x], '-', v->print.w);
317 for (j = 0; j < strlen (v->name); j++)
318 prc->header[strlen (v->name) - j - 1][x] = v->name[j];
322 /* Put in horizontal names. */
323 for (; i < cmd.n_variables; i++)
325 struct variable *v = cmd.v_variables[i];
327 memset (&prc->header[prc->header_rows - 1][x], '-',
328 max (v->print.w, (int) strlen (v->name)));
329 if ((int) strlen (v->name) < v->print.w)
330 x += v->print.w - strlen (v->name);
331 memcpy (&prc->header[0][x], v->name, strlen (v->name));
332 x += strlen (v->name) + 1;
335 /* Add null bytes. */
336 for (i = 0; i < prc->header_rows; i++)
338 for (x = n_chars_width (d); x >= 1; x--)
339 if (prc->header[i][x - 1] != ' ')
341 prc->header[i][x] = 0;
348 /* Write out the header, in back-to-front order except for the last line. */
349 if (prc->header_rows >= 2)
353 for (i = prc->header_rows - 1; i-- != 0; )
354 write_line (d, prc->header[i]);
356 write_line (d, prc->header[prc->header_rows - 1]);
360 /* Frees up all the memory we've allocated. */
364 struct outp_driver *d;
366 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
367 if (d->class->special == 0)
369 struct list_ext *prc = d->prc;
374 for (i = 0; i < prc->header_rows; i++)
375 free (prc->header[i]);
380 else if (d->class == &html_class)
384 struct html_driver_ext *x = d->ext;
386 fputs ("</TABLE>\n", x->file);
392 free (cmd.v_variables);
395 /* Writes string STRING at the current position. If the text would
396 fall off the side of the page, then advance to the next line,
397 indenting by amount INDENT. */
399 write_varname (struct outp_driver *d, char *string, int indent)
401 struct outp_text text;
404 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
406 d->cp_y += d->font_height;
407 if (d->cp_y + d->font_height > d->length)
412 text.font = OUTP_FIXED;
413 text.justification = OUTP_LEFT;
414 ls_init (&text.string, string, strlen (string));
417 text.h = text.v = INT_MAX;
418 d->class->text_draw (d, &text);
419 d->class->text_metrics (d, &text, &width, NULL);
423 /* When we can't fit all the values across the page, we write out all
424 the variable names just once. This is where we do it. */
426 write_fallback_headers (struct outp_driver *d)
428 const int max_width = n_chars_width(d) - 10;
434 const char *Line = _("Line");
435 char *leader = local_alloc (strlen (Line)
436 + INT_STRLEN_BOUND (line_number) + 1 + 1);
438 while (index < cmd.n_variables)
440 struct outp_text text;
442 /* Ensure that there is enough room for a line of text. */
443 if (d->cp_y + d->font_height > d->length)
446 /* The leader is a string like `Line 1: '. Write the leader. */
447 sprintf (leader, "%s %d:", Line, ++line_number);
448 text.font = OUTP_FIXED;
449 text.justification = OUTP_LEFT;
450 ls_init (&text.string, leader, strlen (leader));
453 text.h = text.v = INT_MAX;
454 d->class->text_draw (d, &text);
455 d->class->text_metrics (d, &text, &d->cp_x, NULL);
464 int var_width = cmd.v_variables[index]->print.w;
465 if (width + var_width > max_width && width != 0)
469 d->cp_y += d->font_height;
477 sprintf (varname, " %s", cmd.v_variables[index]->name);
478 write_varname (d, varname, text.h);
481 while (++index < cmd.n_variables);
485 d->cp_y += d->font_height;
490 /* There are three possible layouts for the LIST procedure:
492 1. If the values and their variables' name fit across the page,
493 then they are listed across the page in that way.
495 2. If the values can fit across the page, but not the variable
496 names, then as many variable names as necessary are printed
497 vertically to compensate.
499 3. If not even the values can fit across the page, the variable
500 names are listed just once, at the beginning, in a compact format,
501 and the values are listed with a variable name label at the
502 beginning of each line for easier reference.
504 This is complicated by the fact that we have to do all this for
505 every output driver, not just once. */
507 determine_layout (void)
509 struct outp_driver *d;
511 /* This is the largest page width of any driver, so we can tell what
512 size buffer to allocate. */
513 int largest_page_width = 0;
515 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
517 size_t column; /* Current column. */
518 int width; /* Accumulated width. */
519 int height; /* Height of vertical names. */
520 int max_width; /* Page width. */
522 struct list_ext *prc;
524 if (d->class == &html_class)
527 assert (d->class->special == 0);
531 max_width = n_chars_width (d);
532 largest_page_width = max (largest_page_width, max_width);
534 prc = d->prc = xmalloc (sizeof *prc);
540 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
542 struct variable *v = cmd.v_variables[column];
543 width += max (v->print.w, (int) strlen (v->name));
545 if (width <= max_width)
547 prc->header_rows = 2;
552 for (width = cmd.n_variables - 1, height = 0, column = 0;
553 column < cmd.n_variables && width <= max_width;
556 struct variable *v = cmd.v_variables[column];
558 if (strlen (v->name) > height)
559 height = strlen (v->name);
562 /* If it fit then we need to determine how many labels can be
563 written horizontally. */
564 if (width <= max_width && height <= SHORT_NAME_LEN)
567 prc->n_vertical = SIZE_MAX;
569 for (column = cmd.n_variables; column-- != 0; )
571 struct variable *v = cmd.v_variables[column];
572 int trial_width = (width - v->print.w
573 + max (v->print.w, (int) strlen (v->name)));
575 if (trial_width > max_width)
577 prc->n_vertical = column + 1;
582 assert (prc->n_vertical != SIZE_MAX);
584 prc->n_vertical = cmd.n_variables;
585 /* Finally determine the length of the headers. */
586 for (prc->header_rows = 0, column = 0;
587 column < prc->n_vertical;
589 prc->header_rows = max (prc->header_rows,
590 strlen (cmd.v_variables[column]->name));
595 /* Otherwise use the ugly fallback listing format. */
597 prc->header_rows = 0;
599 d->cp_y += d->font_height;
600 write_fallback_headers (d);
601 d->cp_y += d->font_height;
604 ds_init(&line_buffer, largest_page_width + 2);
607 /* Writes case C to output. */
609 list_cases (const struct ccase *c, void *aux UNUSED)
611 struct outp_driver *d;
614 if (case_idx < cmd.first || case_idx > cmd.last
615 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
618 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
619 if (d->class->special == 0)
621 const struct list_ext *prc = d->prc;
622 const int max_width = n_chars_width (d);
625 if (!prc->header_rows)
627 ds_printf(&line_buffer, "%8s: ", cmd.v_variables[0]->name);
631 for (column = 0; column < cmd.n_variables; column++)
633 struct variable *v = cmd.v_variables[column];
636 if (prc->type == 0 && column >= prc->n_vertical)
637 width = max ((int) strlen (v->name), v->print.w);
641 if (width + ds_length(&line_buffer) > max_width &&
642 ds_length(&line_buffer) != 0)
644 if (!n_lines_remaining (d))
650 write_line (d, ds_c_str(&line_buffer));
651 ds_clear(&line_buffer);
653 if (!prc->header_rows)
655 ds_printf (&line_buffer, "%8s: ", v->name);
659 if (width > v->print.w)
661 ds_putc_multiple(&line_buffer, ' ', width - v->print.w);
664 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
666 data_out (ds_append_uninit(&line_buffer, v->print.w),
667 &v->print, case_data (c, v->fv));
671 union value case_idx_value;
672 case_idx_value.f = case_idx;
673 data_out (ds_append_uninit(&line_buffer,v->print.w),
674 &v->print, &case_idx_value);
677 ds_putc(&line_buffer, ' ');
680 if (!n_lines_remaining (d))
686 write_line (d, ds_c_str(&line_buffer));
687 ds_clear(&line_buffer);
689 else if (d->class == &html_class)
691 struct html_driver_ext *x = d->ext;
694 fputs (" <TR>\n", x->file);
696 for (column = 0; column < cmd.n_variables; column++)
698 struct variable *v = cmd.v_variables[column];
700 struct fixed_string s;
702 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
703 data_out (buf, &v->print, case_data (c, v->fv));
706 union value case_idx_value;
707 case_idx_value.f = case_idx;
708 data_out (buf, &v->print, &case_idx_value);
711 ls_init (&s, buf, v->print.w);
712 fputs (" <TD>", x->file);
713 html_put_cell_contents (d, TAB_FIX, &s);
714 fputs ("</TD>\n", x->file);
717 fputs (" </TR>\n", x->file);