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
21 #include <libpspp/message.h>
24 #include <libpspp/alloc.h>
25 #include <data/case.h>
26 #include <language/command.h>
27 #include <libpspp/compiler.h>
28 #include <data/dictionary.h>
30 #include <language/lexer/lexer.h>
31 #include <libpspp/message.h>
32 #include <libpspp/magic.h>
33 #include <libpspp/misc.h>
34 #include <output/htmlP.h>
35 #include <output/output.h>
37 #include <output/manager.h>
38 #include <output/table.h>
39 #include <data/variable.h>
40 #include <procedure.h>
41 #include <data/format.h>
44 #define _(msgid) gettext (msgid)
50 *variables=varlist("PV_NO_SCRATCH");
51 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
52 format=numbering:numbered/!unnumbered,
54 weight:weight/!noweight.
59 /* Layout for one output driver. */
62 int type; /* 0=Values and labels fit across the page. */
63 size_t n_vertical; /* Number of labels to list vertically. */
64 size_t header_rows; /* Number of header rows. */
65 char **header; /* The header itself. */
69 static struct cmd_list cmd;
71 /* Current case number. */
75 static struct string line_buffer;
77 /* TTY-style output functions. */
78 static unsigned n_lines_remaining (struct outp_driver *d);
79 static unsigned n_chars_width (struct outp_driver *d);
80 static void write_line (struct outp_driver *d, const char *s);
82 /* Other functions. */
83 static bool list_cases (struct ccase *, void *);
84 static void determine_layout (void);
85 static void clean_up (void);
86 static void write_header (struct outp_driver *);
87 static void write_all_headers (void *);
89 /* Returns the number of text lines that can fit on the remainder of
91 static inline unsigned
92 n_lines_remaining (struct outp_driver *d)
96 diff = d->length - d->cp_y;
97 return (diff > 0) ? (diff / d->font_height) : 0;
100 /* Returns the number of fixed-width character that can fit across the
102 static inline unsigned
103 n_chars_width (struct outp_driver *d)
105 return d->width / d->fixed_width;
108 /* Writes the line S at the current position and advances to the next
111 write_line (struct outp_driver *d, const char *s)
113 struct outp_text text;
115 assert (d->cp_y + d->font_height <= d->length);
116 text.font = OUTP_FIXED;
117 text.justification = OUTP_LEFT;
118 ls_init (&text.string, s, strlen (s));
121 text.h = text.v = INT_MAX;
122 d->class->text_draw (d, &text);
124 d->cp_y += d->font_height;
127 /* Parses and executes the LIST procedure. */
131 struct variable casenum_var;
134 if (!parse_list (&cmd))
137 /* Fill in defaults. */
138 if (cmd.step == NOT_LONG)
140 if (cmd.first == NOT_LONG)
142 if (cmd.last == NOT_LONG)
144 if (!cmd.sbc_variables)
145 dict_get_vars (default_dict, &cmd.v_variables, &cmd.n_variables,
146 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
147 if (cmd.n_variables == 0)
149 msg (SE, _("No variables specified."));
153 /* Verify arguments. */
154 if (cmd.first > cmd.last)
157 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
158 "specified. The values will be swapped."), cmd.first, cmd.last);
160 cmd.first = cmd.last;
165 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
166 "being reset to 1."), cmd.first);
171 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
172 "being reset to 1."), cmd.last);
177 msg (SW, _("The step value %ld is less than 1. The value is being "
178 "reset to 1."), cmd.step);
182 /* Weighting variable. */
183 if (cmd.weight == LST_WEIGHT)
185 if (dict_get_weight (default_dict) != NULL)
189 for (i = 0; i < cmd.n_variables; i++)
190 if (cmd.v_variables[i] == dict_get_weight (default_dict))
192 if (i >= cmd.n_variables)
194 /* Add the weight variable to the end of the variable list. */
196 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
197 sizeof *cmd.v_variables);
198 cmd.v_variables[cmd.n_variables - 1]
199 = dict_get_weight (default_dict);
203 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
207 if (cmd.numbering == LST_NUMBERED)
209 /* Initialize the case-number variable. */
210 strcpy (casenum_var.name, "Case#");
211 casenum_var.type = NUMERIC;
213 casenum_var.print = make_output_format (FMT_F,
214 (cmd.last == LONG_MAX
215 ? 5 : intlog10 (cmd.last)), 0);
217 /* Add the weight variable at the beginning of the variable list. */
219 cmd.v_variables = xnrealloc (cmd.v_variables,
220 cmd.n_variables, sizeof *cmd.v_variables);
221 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
222 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
223 cmd.v_variables[0] = &casenum_var;
229 ok = procedure_with_splits (write_all_headers, list_cases, NULL, NULL);
230 ds_destroy(&line_buffer);
234 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
237 /* Writes headers to all devices. This is done at the beginning of
238 each SPLIT FILE group. */
240 write_all_headers (void *aux UNUSED)
242 struct outp_driver *d;
244 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
246 if (!d->class->special)
248 d->cp_y += d->font_height; /* Blank line. */
251 else if (d->class == &html_class)
253 struct html_driver_ext *x = d->ext;
255 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
260 for (i = 0; i < cmd.n_variables; i++)
261 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
262 cmd.v_variables[i]->name);
265 fputs (" </TR>\n", x->file);
272 /* Writes the headers. Some of them might be vertical; most are
273 probably horizontal. */
275 write_header (struct outp_driver *d)
277 struct list_ext *prc = d->prc;
279 if (!prc->header_rows)
282 if (n_lines_remaining (d) < prc->header_rows + 1)
285 assert (n_lines_remaining (d) >= prc->header_rows + 1);
288 /* Design the header. */
294 /* Allocate, initialize header. */
295 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
297 int w = n_chars_width (d);
298 for (i = 0; i < prc->header_rows; i++)
300 prc->header[i] = xmalloc (w + 1);
301 memset (prc->header[i], ' ', w);
305 /* Put in vertical names. */
306 for (i = x = 0; i < prc->n_vertical; i++)
308 struct variable *v = cmd.v_variables[i];
311 memset (&prc->header[prc->header_rows - 1][x], '-', v->print.w);
313 for (j = 0; j < strlen (v->name); j++)
314 prc->header[strlen (v->name) - j - 1][x] = v->name[j];
318 /* Put in horizontal names. */
319 for (; i < cmd.n_variables; i++)
321 struct variable *v = cmd.v_variables[i];
323 memset (&prc->header[prc->header_rows - 1][x], '-',
324 max (v->print.w, (int) strlen (v->name)));
325 if ((int) strlen (v->name) < v->print.w)
326 x += v->print.w - strlen (v->name);
327 memcpy (&prc->header[0][x], v->name, strlen (v->name));
328 x += strlen (v->name) + 1;
331 /* Add null bytes. */
332 for (i = 0; i < prc->header_rows; i++)
334 for (x = n_chars_width (d); x >= 1; x--)
335 if (prc->header[i][x - 1] != ' ')
337 prc->header[i][x] = 0;
344 /* Write out the header, in back-to-front order except for the last line. */
345 if (prc->header_rows >= 2)
349 for (i = prc->header_rows - 1; i-- != 0; )
350 write_line (d, prc->header[i]);
352 write_line (d, prc->header[prc->header_rows - 1]);
356 /* Frees up all the memory we've allocated. */
360 struct outp_driver *d;
362 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
363 if (d->class->special == 0)
365 struct list_ext *prc = d->prc;
370 for (i = 0; i < prc->header_rows; i++)
371 free (prc->header[i]);
376 else if (d->class == &html_class)
380 struct html_driver_ext *x = d->ext;
382 fputs ("</TABLE>\n", x->file);
388 free (cmd.v_variables);
391 /* Writes string STRING at the current position. If the text would
392 fall off the side of the page, then advance to the next line,
393 indenting by amount INDENT. */
395 write_varname (struct outp_driver *d, char *string, int indent)
397 struct outp_text text;
400 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
402 d->cp_y += d->font_height;
403 if (d->cp_y + d->font_height > d->length)
408 text.font = OUTP_FIXED;
409 text.justification = OUTP_LEFT;
410 ls_init (&text.string, string, strlen (string));
413 text.h = text.v = INT_MAX;
414 d->class->text_draw (d, &text);
415 d->class->text_metrics (d, &text, &width, NULL);
419 /* When we can't fit all the values across the page, we write out all
420 the variable names just once. This is where we do it. */
422 write_fallback_headers (struct outp_driver *d)
424 const int max_width = n_chars_width(d) - 10;
430 const char *Line = _("Line");
431 char *leader = local_alloc (strlen (Line)
432 + INT_STRLEN_BOUND (line_number) + 1 + 1);
434 while (index < cmd.n_variables)
436 struct outp_text text;
438 /* Ensure that there is enough room for a line of text. */
439 if (d->cp_y + d->font_height > d->length)
442 /* The leader is a string like `Line 1: '. Write the leader. */
443 sprintf (leader, "%s %d:", Line, ++line_number);
444 text.font = OUTP_FIXED;
445 text.justification = OUTP_LEFT;
446 ls_init (&text.string, leader, strlen (leader));
449 text.h = text.v = INT_MAX;
450 d->class->text_draw (d, &text);
451 d->class->text_metrics (d, &text, &d->cp_x, NULL);
460 int var_width = cmd.v_variables[index]->print.w;
461 if (width + var_width > max_width && width != 0)
465 d->cp_y += d->font_height;
473 sprintf (varname, " %s", cmd.v_variables[index]->name);
474 write_varname (d, varname, text.h);
477 while (++index < cmd.n_variables);
481 d->cp_y += d->font_height;
486 /* There are three possible layouts for the LIST procedure:
488 1. If the values and their variables' name fit across the page,
489 then they are listed across the page in that way.
491 2. If the values can fit across the page, but not the variable
492 names, then as many variable names as necessary are printed
493 vertically to compensate.
495 3. If not even the values can fit across the page, the variable
496 names are listed just once, at the beginning, in a compact format,
497 and the values are listed with a variable name label at the
498 beginning of each line for easier reference.
500 This is complicated by the fact that we have to do all this for
501 every output driver, not just once. */
503 determine_layout (void)
505 struct outp_driver *d;
507 /* This is the largest page width of any driver, so we can tell what
508 size buffer to allocate. */
509 int largest_page_width = 0;
511 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
513 size_t column; /* Current column. */
514 int width; /* Accumulated width. */
515 int height; /* Height of vertical names. */
516 int max_width; /* Page width. */
518 struct list_ext *prc;
520 if (d->class == &html_class)
523 assert (d->class->special == 0);
527 max_width = n_chars_width (d);
528 largest_page_width = max (largest_page_width, max_width);
530 prc = d->prc = xmalloc (sizeof *prc);
536 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
538 struct variable *v = cmd.v_variables[column];
539 width += max (v->print.w, (int) strlen (v->name));
541 if (width <= max_width)
543 prc->header_rows = 2;
548 for (width = cmd.n_variables - 1, height = 0, column = 0;
549 column < cmd.n_variables && width <= max_width;
552 struct variable *v = cmd.v_variables[column];
554 if (strlen (v->name) > height)
555 height = strlen (v->name);
558 /* If it fit then we need to determine how many labels can be
559 written horizontally. */
560 if (width <= max_width && height <= SHORT_NAME_LEN)
563 prc->n_vertical = SIZE_MAX;
565 for (column = cmd.n_variables; column-- != 0; )
567 struct variable *v = cmd.v_variables[column];
568 int trial_width = (width - v->print.w
569 + max (v->print.w, (int) strlen (v->name)));
571 if (trial_width > max_width)
573 prc->n_vertical = column + 1;
578 assert (prc->n_vertical != SIZE_MAX);
580 prc->n_vertical = cmd.n_variables;
581 /* Finally determine the length of the headers. */
582 for (prc->header_rows = 0, column = 0;
583 column < prc->n_vertical;
585 prc->header_rows = max (prc->header_rows,
586 strlen (cmd.v_variables[column]->name));
591 /* Otherwise use the ugly fallback listing format. */
593 prc->header_rows = 0;
595 d->cp_y += d->font_height;
596 write_fallback_headers (d);
597 d->cp_y += d->font_height;
600 ds_init(&line_buffer, largest_page_width + 2);
603 /* Writes case C to output. */
605 list_cases (struct ccase *c, void *aux UNUSED)
607 struct outp_driver *d;
610 if (case_idx < cmd.first || case_idx > cmd.last
611 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
614 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
615 if (d->class->special == 0)
617 const struct list_ext *prc = d->prc;
618 const int max_width = n_chars_width (d);
621 if (!prc->header_rows)
623 ds_printf(&line_buffer, "%8s: ", cmd.v_variables[0]->name);
627 for (column = 0; column < cmd.n_variables; column++)
629 struct variable *v = cmd.v_variables[column];
632 if (prc->type == 0 && column >= prc->n_vertical)
633 width = max ((int) strlen (v->name), v->print.w);
637 if (width + ds_length(&line_buffer) > max_width &&
638 ds_length(&line_buffer) != 0)
640 if (!n_lines_remaining (d))
646 write_line (d, ds_c_str(&line_buffer));
647 ds_clear(&line_buffer);
649 if (!prc->header_rows)
651 ds_printf (&line_buffer, "%8s: ", v->name);
655 if (width > v->print.w)
657 ds_putc_multiple(&line_buffer, ' ', width - v->print.w);
660 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
662 data_out (ds_append_uninit(&line_buffer, v->print.w),
663 &v->print, case_data (c, v->fv));
667 union value case_idx_value;
668 case_idx_value.f = case_idx;
669 data_out (ds_append_uninit(&line_buffer,v->print.w),
670 &v->print, &case_idx_value);
673 ds_putc(&line_buffer, ' ');
676 if (!n_lines_remaining (d))
682 write_line (d, ds_c_str(&line_buffer));
683 ds_clear(&line_buffer);
685 else if (d->class == &html_class)
687 struct html_driver_ext *x = d->ext;
690 fputs (" <TR>\n", x->file);
692 for (column = 0; column < cmd.n_variables; column++)
694 struct variable *v = cmd.v_variables[column];
696 struct fixed_string s;
698 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
699 data_out (buf, &v->print, case_data (c, v->fv));
702 union value case_idx_value;
703 case_idx_value.f = case_idx;
704 data_out (buf, &v->print, &case_idx_value);
707 ls_init (&s, buf, v->print.w);
708 fputs (" <TD>", x->file);
709 html_put_cell_contents (d, TAB_FIX, &s);
710 fputs ("</TD>\n", x->file);
713 fputs (" </TR>\n", x->file);