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>
50 #define _(msgid) gettext (msgid)
56 *variables=varlist("PV_NO_SCRATCH");
57 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
58 +format=numbering:numbered/!unnumbered,
60 weight:weight/!noweight.
65 /* Layout for one output driver. */
68 int type; /* 0=Values and labels fit across the page. */
69 size_t n_vertical; /* Number of labels to list vertically. */
70 size_t header_rows; /* Number of header rows. */
71 char **header; /* The header itself. */
75 static struct cmd_list cmd;
77 /* Current case number. */
81 static struct string line_buffer;
83 /* TTY-style output functions. */
84 static unsigned n_lines_remaining (struct outp_driver *d);
85 static unsigned n_chars_width (struct outp_driver *d);
86 static void write_line (struct outp_driver *d, const char *s);
88 /* Other functions. */
89 static bool list_cases (const struct ccase *, void *, const struct dataset *);
90 static void determine_layout (void);
91 static void clean_up (void);
92 static void write_header (struct outp_driver *);
93 static void write_all_headers (const struct ccase *, void *, const struct dataset*);
95 /* Returns the number of text lines that can fit on the remainder of
97 static inline unsigned
98 n_lines_remaining (struct outp_driver *d)
102 diff = d->length - d->cp_y;
103 return (diff > 0) ? (diff / d->font_height) : 0;
106 /* Returns the number of fixed-width character that can fit across the
108 static inline unsigned
109 n_chars_width (struct outp_driver *d)
111 return d->width / d->fixed_width;
114 /* Writes the line S at the current position and advances to the next
117 write_line (struct outp_driver *d, const char *s)
119 struct outp_text text;
121 assert (d->cp_y + d->font_height <= d->length);
122 text.font = OUTP_FIXED;
123 text.justification = OUTP_LEFT;
124 text.string = ss_cstr (s);
127 text.h = text.v = INT_MAX;
128 d->class->text_draw (d, &text);
130 d->cp_y += d->font_height;
133 /* Parses and executes the LIST procedure. */
135 cmd_list (struct lexer *lexer, struct dataset *ds)
137 struct variable casenum_var;
140 if (!parse_list (lexer, ds, &cmd, NULL))
143 /* Fill in defaults. */
144 if (cmd.step == NOT_LONG)
146 if (cmd.first == NOT_LONG)
148 if (cmd.last == NOT_LONG)
150 if (!cmd.sbc_variables)
151 dict_get_vars (dataset_dict (ds), &cmd.v_variables, &cmd.n_variables,
152 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
153 if (cmd.n_variables == 0)
155 msg (SE, _("No variables specified."));
159 /* Verify arguments. */
160 if (cmd.first > cmd.last)
163 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
164 "specified. The values will be swapped."), cmd.first, cmd.last);
166 cmd.first = cmd.last;
171 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
172 "being reset to 1."), cmd.first);
177 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
178 "being reset to 1."), cmd.last);
183 msg (SW, _("The step value %ld is less than 1. The value is being "
184 "reset to 1."), cmd.step);
188 /* Weighting variable. */
189 if (cmd.weight == LST_WEIGHT)
191 if (dict_get_weight (dataset_dict (ds)) != NULL)
195 for (i = 0; i < cmd.n_variables; i++)
196 if (cmd.v_variables[i] == dict_get_weight (dataset_dict (ds)))
198 if (i >= cmd.n_variables)
200 /* Add the weight variable to the end of the variable list. */
202 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
203 sizeof *cmd.v_variables);
204 cmd.v_variables[cmd.n_variables - 1]
205 = dict_get_weight (dataset_dict (ds));
209 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
213 if (cmd.numbering == LST_NUMBERED)
215 /* Initialize the case-number variable. */
216 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
217 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
218 var_set_name (&casenum_var, "Case#");
219 casenum_var.width = 0;
221 var_set_both_formats (&casenum_var, &format);
223 /* Add the weight variable at the beginning of the variable list. */
225 cmd.v_variables = xnrealloc (cmd.v_variables,
226 cmd.n_variables, sizeof *cmd.v_variables);
227 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
228 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
229 cmd.v_variables[0] = &casenum_var;
235 ok = procedure_with_splits (ds, write_all_headers, list_cases, NULL, NULL);
236 ds_destroy(&line_buffer);
240 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
243 /* Writes headers to all devices. This is done at the beginning of
244 each SPLIT FILE group. */
246 write_all_headers (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
248 struct outp_driver *d;
250 output_split_file_values (ds, c);
251 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
253 if (!d->class->special)
255 d->cp_y += d->font_height; /* Blank line. */
258 else if (d->class == &html_class)
260 struct html_driver_ext *x = d->ext;
262 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
267 for (i = 0; i < cmd.n_variables; i++)
268 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
269 var_get_name (cmd.v_variables[i]));
272 fputs (" </TR>\n", x->file);
279 /* Writes the headers. Some of them might be vertical; most are
280 probably horizontal. */
282 write_header (struct outp_driver *d)
284 struct list_ext *prc = d->prc;
286 if (!prc->header_rows)
289 if (n_lines_remaining (d) < prc->header_rows + 1)
292 assert (n_lines_remaining (d) >= prc->header_rows + 1);
295 /* Design the header. */
301 /* Allocate, initialize header. */
302 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
304 int w = n_chars_width (d);
305 for (i = 0; i < prc->header_rows; i++)
307 prc->header[i] = xmalloc (w + 1);
308 memset (prc->header[i], ' ', w);
312 /* Put in vertical names. */
313 for (i = x = 0; i < prc->n_vertical; i++)
315 struct variable *v = cmd.v_variables[i];
316 const char *name = var_get_name (v);
317 size_t name_len = strlen (name);
318 const struct fmt_spec *print = var_get_print_format (v);
321 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
323 for (j = 0; j < name_len; j++)
324 prc->header[name_len - j - 1][x] = name[j];
328 /* Put in horizontal names. */
329 for (; i < cmd.n_variables; i++)
331 struct variable *v = cmd.v_variables[i];
332 const char *name = var_get_name (v);
333 size_t name_len = strlen (name);
334 const struct fmt_spec *print = var_get_print_format (v);
336 memset (&prc->header[prc->header_rows - 1][x], '-',
337 MAX (print->w, (int) name_len));
338 if ((int) name_len < print->w)
339 x += print->w - name_len;
340 memcpy (&prc->header[0][x], name, name_len);
344 /* Add null bytes. */
345 for (i = 0; i < prc->header_rows; i++)
347 for (x = n_chars_width (d); x >= 1; x--)
348 if (prc->header[i][x - 1] != ' ')
350 prc->header[i][x] = 0;
357 /* Write out the header, in back-to-front order except for the last line. */
358 if (prc->header_rows >= 2)
362 for (i = prc->header_rows - 1; i-- != 0; )
363 write_line (d, prc->header[i]);
365 write_line (d, prc->header[prc->header_rows - 1]);
369 /* Frees up all the memory we've allocated. */
373 struct outp_driver *d;
375 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
376 if (d->class->special == 0)
378 struct list_ext *prc = d->prc;
383 for (i = 0; i < prc->header_rows; i++)
384 free (prc->header[i]);
389 else if (d->class == &html_class)
393 struct html_driver_ext *x = d->ext;
395 fputs ("</TABLE>\n", x->file);
401 free (cmd.v_variables);
404 /* Writes string STRING at the current position. If the text would
405 fall off the side of the page, then advance to the next line,
406 indenting by amount INDENT. */
408 write_varname (struct outp_driver *d, char *string, int indent)
410 struct outp_text text;
413 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
415 d->cp_y += d->font_height;
416 if (d->cp_y + d->font_height > d->length)
421 text.font = OUTP_FIXED;
422 text.justification = OUTP_LEFT;
423 text.string = ss_cstr (string);
426 text.h = text.v = INT_MAX;
427 d->class->text_draw (d, &text);
428 d->class->text_metrics (d, &text, &width, NULL);
432 /* When we can't fit all the values across the page, we write out all
433 the variable names just once. This is where we do it. */
435 write_fallback_headers (struct outp_driver *d)
437 const int max_width = n_chars_width(d) - 10;
443 const char *Line = _("Line");
444 char *leader = local_alloc (strlen (Line)
445 + INT_STRLEN_BOUND (line_number) + 1 + 1);
447 while (index < cmd.n_variables)
449 struct outp_text text;
452 /* Ensure that there is enough room for a line of text. */
453 if (d->cp_y + d->font_height > d->length)
456 /* The leader is a string like `Line 1: '. Write the leader. */
457 sprintf (leader, "%s %d:", Line, ++line_number);
458 text.font = OUTP_FIXED;
459 text.justification = OUTP_LEFT;
460 text.string = ss_cstr (leader);
463 text.h = text.v = INT_MAX;
464 d->class->text_draw (d, &text);
465 d->class->text_metrics (d, &text, &leader_width, NULL);
466 d->cp_x = leader_width;
475 int var_width = var_get_print_format (cmd.v_variables[index])->w;
476 if (width + var_width > max_width && width != 0)
480 d->cp_y += d->font_height;
487 char varname[LONG_NAME_LEN + 2];
488 snprintf (varname, sizeof varname,
489 " %s", var_get_name (cmd.v_variables[index]));
490 write_varname (d, varname, leader_width);
493 while (++index < cmd.n_variables);
497 d->cp_y += d->font_height;
502 /* There are three possible layouts for the LIST procedure:
504 1. If the values and their variables' name fit across the page,
505 then they are listed across the page in that way.
507 2. If the values can fit across the page, but not the variable
508 names, then as many variable names as necessary are printed
509 vertically to compensate.
511 3. If not even the values can fit across the page, the variable
512 names are listed just once, at the beginning, in a compact format,
513 and the values are listed with a variable name label at the
514 beginning of each line for easier reference.
516 This is complicated by the fact that we have to do all this for
517 every output driver, not just once. */
519 determine_layout (void)
521 struct outp_driver *d;
523 /* This is the largest page width of any driver, so we can tell what
524 size buffer to allocate. */
525 int largest_page_width = 0;
527 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
529 size_t column; /* Current column. */
530 int width; /* Accumulated width. */
531 int height; /* Height of vertical names. */
532 int max_width; /* Page width. */
534 struct list_ext *prc;
536 if (d->class == &html_class)
539 assert (d->class->special == 0);
543 max_width = n_chars_width (d);
544 largest_page_width = MAX (largest_page_width, max_width);
546 prc = d->prc = xmalloc (sizeof *prc);
552 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
554 struct variable *v = cmd.v_variables[column];
555 int fmt_width = var_get_print_format (v)->w;
556 int name_len = strlen (var_get_name (v));
557 width += MAX (fmt_width, name_len);
559 if (width <= max_width)
561 prc->header_rows = 2;
566 for (width = cmd.n_variables - 1, height = 0, column = 0;
567 column < cmd.n_variables && width <= max_width;
570 struct variable *v = cmd.v_variables[column];
571 int fmt_width = var_get_print_format (v)->w;
572 size_t name_len = strlen (var_get_name (v));
574 if (name_len > height)
578 /* If it fit then we need to determine how many labels can be
579 written horizontally. */
580 if (width <= max_width && height <= SHORT_NAME_LEN)
583 prc->n_vertical = SIZE_MAX;
585 for (column = cmd.n_variables; column-- != 0; )
587 struct variable *v = cmd.v_variables[column];
588 int name_len = strlen (var_get_name (v));
589 int fmt_width = var_get_print_format (v)->w;
590 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
591 if (trial_width > max_width)
593 prc->n_vertical = column + 1;
598 assert (prc->n_vertical != SIZE_MAX);
600 prc->n_vertical = cmd.n_variables;
601 /* Finally determine the length of the headers. */
602 for (prc->header_rows = 0, column = 0;
603 column < prc->n_vertical;
606 struct variable *var = cmd.v_variables[column];
607 size_t name_len = strlen (var_get_name (var));
608 prc->header_rows = MAX (prc->header_rows, name_len);
614 /* Otherwise use the ugly fallback listing format. */
616 prc->header_rows = 0;
618 d->cp_y += d->font_height;
619 write_fallback_headers (d);
620 d->cp_y += d->font_height;
623 ds_init_empty (&line_buffer);
626 /* Writes case C to output. */
628 list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds UNUSED)
630 struct outp_driver *d;
633 if (case_idx < cmd.first || case_idx > cmd.last
634 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
637 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
638 if (d->class->special == 0)
640 const struct list_ext *prc = d->prc;
641 const int max_width = n_chars_width (d);
644 if (!prc->header_rows)
646 ds_put_format(&line_buffer, "%8s: ",
647 var_get_name (cmd.v_variables[0]));
651 for (column = 0; column < cmd.n_variables; column++)
653 struct variable *v = cmd.v_variables[column];
654 const struct fmt_spec *print = var_get_print_format (v);
657 if (prc->type == 0 && column >= prc->n_vertical)
659 int name_len = strlen (var_get_name (v));
660 width = MAX (name_len, print->w);
665 if (width + ds_length(&line_buffer) > max_width &&
666 ds_length(&line_buffer) != 0)
668 if (!n_lines_remaining (d))
674 write_line (d, ds_cstr (&line_buffer));
675 ds_clear(&line_buffer);
677 if (!prc->header_rows)
678 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
681 if (width > print->w)
682 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
684 if (fmt_is_string (print->type) || v->fv != -1)
686 data_out (case_data (c, v->fv), 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 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) || v->fv != -1)
723 data_out (case_data (c, v->fv), print, buf);
726 union value case_idx_value;
727 case_idx_value.f = case_idx;
728 data_out (&case_idx_value, print, buf);
731 fputs (" <TD>", x->file);
732 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
733 fputs ("</TD>\n", x->file);
736 fputs (" </TR>\n", x->file);