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/data-out.h>
30 #include <data/format.h>
31 #include <data/procedure.h>
32 #include <data/variable.h>
33 #include <language/command.h>
34 #include <language/dictionary/split-file.h>
35 #include <language/lexer/lexer.h>
36 #include <libpspp/alloc.h>
37 #include <libpspp/compiler.h>
38 #include <libpspp/magic.h>
39 #include <libpspp/message.h>
40 #include <libpspp/message.h>
41 #include <libpspp/misc.h>
42 #include <output/htmlP.h>
43 #include <output/manager.h>
44 #include <output/output.h>
45 #include <output/table.h>
48 #define _(msgid) gettext (msgid)
54 *variables=varlist("PV_NO_SCRATCH");
55 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
56 +format=numbering:numbered/!unnumbered,
58 weight:weight/!noweight.
63 /* Layout for one output driver. */
66 int type; /* 0=Values and labels fit across the page. */
67 size_t n_vertical; /* Number of labels to list vertically. */
68 size_t header_rows; /* Number of header rows. */
69 char **header; /* The header itself. */
73 static struct cmd_list cmd;
75 /* Current case number. */
79 static struct string line_buffer;
81 /* TTY-style output functions. */
82 static unsigned n_lines_remaining (struct outp_driver *d);
83 static unsigned n_chars_width (struct outp_driver *d);
84 static void write_line (struct outp_driver *d, const char *s);
86 /* Other functions. */
87 static bool list_cases (const struct ccase *, void *, const struct dataset *);
88 static void determine_layout (void);
89 static void clean_up (void);
90 static void write_header (struct outp_driver *);
91 static void write_all_headers (const struct ccase *, void *, const struct dataset*);
93 /* Returns the number of text lines that can fit on the remainder of
95 static inline unsigned
96 n_lines_remaining (struct outp_driver *d)
100 diff = d->length - d->cp_y;
101 return (diff > 0) ? (diff / d->font_height) : 0;
104 /* Returns the number of fixed-width character that can fit across the
106 static inline unsigned
107 n_chars_width (struct outp_driver *d)
109 return d->width / d->fixed_width;
112 /* Writes the line S at the current position and advances to the next
115 write_line (struct outp_driver *d, const char *s)
117 struct outp_text text;
119 assert (d->cp_y + d->font_height <= d->length);
120 text.font = OUTP_FIXED;
121 text.justification = OUTP_LEFT;
122 text.string = ss_cstr (s);
125 text.h = text.v = INT_MAX;
126 d->class->text_draw (d, &text);
128 d->cp_y += d->font_height;
131 /* Parses and executes the LIST procedure. */
133 cmd_list (struct lexer *lexer, struct dataset *ds)
135 struct variable casenum_var;
138 if (!parse_list (lexer, ds, &cmd, NULL))
141 /* Fill in defaults. */
142 if (cmd.step == NOT_LONG)
144 if (cmd.first == NOT_LONG)
146 if (cmd.last == NOT_LONG)
148 if (!cmd.sbc_variables)
149 dict_get_vars (dataset_dict (ds), &cmd.v_variables, &cmd.n_variables,
150 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
151 if (cmd.n_variables == 0)
153 msg (SE, _("No variables specified."));
157 /* Verify arguments. */
158 if (cmd.first > cmd.last)
161 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
162 "specified. The values will be swapped."), cmd.first, cmd.last);
164 cmd.first = cmd.last;
169 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
170 "being reset to 1."), cmd.first);
175 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
176 "being reset to 1."), cmd.last);
181 msg (SW, _("The step value %ld is less than 1. The value is being "
182 "reset to 1."), cmd.step);
186 /* Weighting variable. */
187 if (cmd.weight == LST_WEIGHT)
189 if (dict_get_weight (dataset_dict (ds)) != NULL)
193 for (i = 0; i < cmd.n_variables; i++)
194 if (cmd.v_variables[i] == dict_get_weight (dataset_dict (ds)))
196 if (i >= cmd.n_variables)
198 /* Add the weight variable to the end of the variable list. */
200 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
201 sizeof *cmd.v_variables);
202 cmd.v_variables[cmd.n_variables - 1]
203 = dict_get_weight (dataset_dict (ds));
207 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
211 if (cmd.numbering == LST_NUMBERED)
213 /* Initialize the case-number variable. */
214 strcpy (casenum_var.name, "Case#");
215 casenum_var.type = NUMERIC;
217 casenum_var.print = fmt_for_output (FMT_F,
218 (cmd.last == LONG_MAX
219 ? 5 : intlog10 (cmd.last)), 0);
221 /* Add the weight variable at the beginning of the variable list. */
223 cmd.v_variables = xnrealloc (cmd.v_variables,
224 cmd.n_variables, sizeof *cmd.v_variables);
225 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
226 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
227 cmd.v_variables[0] = &casenum_var;
233 ok = procedure_with_splits (ds, write_all_headers, list_cases, NULL, NULL);
234 ds_destroy(&line_buffer);
238 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
241 /* Writes headers to all devices. This is done at the beginning of
242 each SPLIT FILE group. */
244 write_all_headers (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
246 struct outp_driver *d;
248 output_split_file_values (ds, c);
249 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
251 if (!d->class->special)
253 d->cp_y += d->font_height; /* Blank line. */
256 else if (d->class == &html_class)
258 struct html_driver_ext *x = d->ext;
260 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
265 for (i = 0; i < cmd.n_variables; i++)
266 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
267 cmd.v_variables[i]->name);
270 fputs (" </TR>\n", x->file);
277 /* Writes the headers. Some of them might be vertical; most are
278 probably horizontal. */
280 write_header (struct outp_driver *d)
282 struct list_ext *prc = d->prc;
284 if (!prc->header_rows)
287 if (n_lines_remaining (d) < prc->header_rows + 1)
290 assert (n_lines_remaining (d) >= prc->header_rows + 1);
293 /* Design the header. */
299 /* Allocate, initialize header. */
300 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
302 int w = n_chars_width (d);
303 for (i = 0; i < prc->header_rows; i++)
305 prc->header[i] = xmalloc (w + 1);
306 memset (prc->header[i], ' ', w);
310 /* Put in vertical names. */
311 for (i = x = 0; i < prc->n_vertical; i++)
313 struct variable *v = cmd.v_variables[i];
316 memset (&prc->header[prc->header_rows - 1][x], '-', v->print.w);
318 for (j = 0; j < strlen (v->name); j++)
319 prc->header[strlen (v->name) - j - 1][x] = v->name[j];
323 /* Put in horizontal names. */
324 for (; i < cmd.n_variables; i++)
326 struct variable *v = cmd.v_variables[i];
328 memset (&prc->header[prc->header_rows - 1][x], '-',
329 max (v->print.w, (int) strlen (v->name)));
330 if ((int) strlen (v->name) < v->print.w)
331 x += v->print.w - strlen (v->name);
332 memcpy (&prc->header[0][x], v->name, strlen (v->name));
333 x += strlen (v->name) + 1;
336 /* Add null bytes. */
337 for (i = 0; i < prc->header_rows; i++)
339 for (x = n_chars_width (d); x >= 1; x--)
340 if (prc->header[i][x - 1] != ' ')
342 prc->header[i][x] = 0;
349 /* Write out the header, in back-to-front order except for the last line. */
350 if (prc->header_rows >= 2)
354 for (i = prc->header_rows - 1; i-- != 0; )
355 write_line (d, prc->header[i]);
357 write_line (d, prc->header[prc->header_rows - 1]);
361 /* Frees up all the memory we've allocated. */
365 struct outp_driver *d;
367 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
368 if (d->class->special == 0)
370 struct list_ext *prc = d->prc;
375 for (i = 0; i < prc->header_rows; i++)
376 free (prc->header[i]);
381 else if (d->class == &html_class)
385 struct html_driver_ext *x = d->ext;
387 fputs ("</TABLE>\n", x->file);
393 free (cmd.v_variables);
396 /* Writes string STRING at the current position. If the text would
397 fall off the side of the page, then advance to the next line,
398 indenting by amount INDENT. */
400 write_varname (struct outp_driver *d, char *string, int indent)
402 struct outp_text text;
405 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
407 d->cp_y += d->font_height;
408 if (d->cp_y + d->font_height > d->length)
413 text.font = OUTP_FIXED;
414 text.justification = OUTP_LEFT;
415 text.string = ss_cstr (string);
418 text.h = text.v = INT_MAX;
419 d->class->text_draw (d, &text);
420 d->class->text_metrics (d, &text, &width, NULL);
424 /* When we can't fit all the values across the page, we write out all
425 the variable names just once. This is where we do it. */
427 write_fallback_headers (struct outp_driver *d)
429 const int max_width = n_chars_width(d) - 10;
435 const char *Line = _("Line");
436 char *leader = local_alloc (strlen (Line)
437 + INT_STRLEN_BOUND (line_number) + 1 + 1);
439 while (index < cmd.n_variables)
441 struct outp_text text;
444 /* Ensure that there is enough room for a line of text. */
445 if (d->cp_y + d->font_height > d->length)
448 /* The leader is a string like `Line 1: '. Write the leader. */
449 sprintf (leader, "%s %d:", Line, ++line_number);
450 text.font = OUTP_FIXED;
451 text.justification = OUTP_LEFT;
452 text.string = ss_cstr (leader);
455 text.h = text.v = INT_MAX;
456 d->class->text_draw (d, &text);
457 d->class->text_metrics (d, &text, &leader_width, NULL);
458 d->cp_x = leader_width;
467 int var_width = cmd.v_variables[index]->print.w;
468 if (width + var_width > max_width && width != 0)
472 d->cp_y += d->font_height;
479 char varname[LONG_NAME_LEN + 2];
480 snprintf (varname, sizeof varname,
481 " %s", cmd.v_variables[index]->name);
482 write_varname (d, varname, leader_width);
485 while (++index < cmd.n_variables);
489 d->cp_y += d->font_height;
494 /* There are three possible layouts for the LIST procedure:
496 1. If the values and their variables' name fit across the page,
497 then they are listed across the page in that way.
499 2. If the values can fit across the page, but not the variable
500 names, then as many variable names as necessary are printed
501 vertically to compensate.
503 3. If not even the values can fit across the page, the variable
504 names are listed just once, at the beginning, in a compact format,
505 and the values are listed with a variable name label at the
506 beginning of each line for easier reference.
508 This is complicated by the fact that we have to do all this for
509 every output driver, not just once. */
511 determine_layout (void)
513 struct outp_driver *d;
515 /* This is the largest page width of any driver, so we can tell what
516 size buffer to allocate. */
517 int largest_page_width = 0;
519 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
521 size_t column; /* Current column. */
522 int width; /* Accumulated width. */
523 int height; /* Height of vertical names. */
524 int max_width; /* Page width. */
526 struct list_ext *prc;
528 if (d->class == &html_class)
531 assert (d->class->special == 0);
535 max_width = n_chars_width (d);
536 largest_page_width = max (largest_page_width, max_width);
538 prc = d->prc = xmalloc (sizeof *prc);
544 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
546 struct variable *v = cmd.v_variables[column];
547 width += max (v->print.w, (int) strlen (v->name));
549 if (width <= max_width)
551 prc->header_rows = 2;
556 for (width = cmd.n_variables - 1, height = 0, column = 0;
557 column < cmd.n_variables && width <= max_width;
560 struct variable *v = cmd.v_variables[column];
562 if (strlen (v->name) > height)
563 height = strlen (v->name);
566 /* If it fit then we need to determine how many labels can be
567 written horizontally. */
568 if (width <= max_width && height <= SHORT_NAME_LEN)
571 prc->n_vertical = SIZE_MAX;
573 for (column = cmd.n_variables; column-- != 0; )
575 struct variable *v = cmd.v_variables[column];
576 int trial_width = (width - v->print.w
577 + max (v->print.w, (int) strlen (v->name)));
579 if (trial_width > max_width)
581 prc->n_vertical = column + 1;
586 assert (prc->n_vertical != SIZE_MAX);
588 prc->n_vertical = cmd.n_variables;
589 /* Finally determine the length of the headers. */
590 for (prc->header_rows = 0, column = 0;
591 column < prc->n_vertical;
593 prc->header_rows = max (prc->header_rows,
594 strlen (cmd.v_variables[column]->name));
599 /* Otherwise use the ugly fallback listing format. */
601 prc->header_rows = 0;
603 d->cp_y += d->font_height;
604 write_fallback_headers (d);
605 d->cp_y += d->font_height;
608 ds_init_empty (&line_buffer);
611 /* Writes case C to output. */
613 list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds UNUSED)
615 struct outp_driver *d;
618 if (case_idx < cmd.first || case_idx > cmd.last
619 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
622 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
623 if (d->class->special == 0)
625 const struct list_ext *prc = d->prc;
626 const int max_width = n_chars_width (d);
629 if (!prc->header_rows)
631 ds_put_format(&line_buffer, "%8s: ", cmd.v_variables[0]->name);
635 for (column = 0; column < cmd.n_variables; column++)
637 struct variable *v = cmd.v_variables[column];
640 if (prc->type == 0 && column >= prc->n_vertical)
641 width = max ((int) strlen (v->name), v->print.w);
645 if (width + ds_length(&line_buffer) > max_width &&
646 ds_length(&line_buffer) != 0)
648 if (!n_lines_remaining (d))
654 write_line (d, ds_cstr (&line_buffer));
655 ds_clear(&line_buffer);
657 if (!prc->header_rows)
659 ds_put_format (&line_buffer, "%8s: ", v->name);
663 if (width > v->print.w)
665 ds_put_char_multiple(&line_buffer, ' ', width - v->print.w);
668 if (fmt_is_string (v->print.type) || v->fv != -1)
670 data_out (case_data (c, v->fv), &v->print,
671 ds_put_uninit (&line_buffer, v->print.w));
675 union value case_idx_value;
676 case_idx_value.f = case_idx;
677 data_out (&case_idx_value, &v->print,
678 ds_put_uninit (&line_buffer,v->print.w));
681 ds_put_char(&line_buffer, ' ');
684 if (!n_lines_remaining (d))
690 write_line (d, ds_cstr (&line_buffer));
691 ds_clear(&line_buffer);
693 else if (d->class == &html_class)
695 struct html_driver_ext *x = d->ext;
698 fputs (" <TR>\n", x->file);
700 for (column = 0; column < cmd.n_variables; column++)
702 struct variable *v = cmd.v_variables[column];
705 if (fmt_is_string (v->print.type) || v->fv != -1)
706 data_out (case_data (c, v->fv), &v->print, buf);
709 union value case_idx_value;
710 case_idx_value.f = case_idx;
711 data_out (&case_idx_value, &v->print, buf);
714 fputs (" <TD>", x->file);
715 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, v->print.w));
716 fputs ("</TD>\n", x->file);
719 fputs (" </TR>\n", x->file);