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/lexer/lexer.h>
34 #include <libpspp/alloc.h>
35 #include <libpspp/compiler.h>
36 #include <libpspp/magic.h>
37 #include <libpspp/message.h>
38 #include <libpspp/message.h>
39 #include <libpspp/misc.h>
40 #include <output/htmlP.h>
41 #include <output/manager.h>
42 #include <output/output.h>
43 #include <output/table.h>
46 #define _(msgid) gettext (msgid)
52 *variables=varlist("PV_NO_SCRATCH");
53 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
54 format=numbering:numbered/!unnumbered,
56 weight:weight/!noweight.
61 /* Layout for one output driver. */
64 int type; /* 0=Values and labels fit across the page. */
65 size_t n_vertical; /* Number of labels to list vertically. */
66 size_t header_rows; /* Number of header rows. */
67 char **header; /* The header itself. */
71 static struct cmd_list cmd;
73 /* Current case number. */
77 static struct string line_buffer;
79 /* TTY-style output functions. */
80 static unsigned n_lines_remaining (struct outp_driver *d);
81 static unsigned n_chars_width (struct outp_driver *d);
82 static void write_line (struct outp_driver *d, const char *s);
84 /* Other functions. */
85 static bool list_cases (struct ccase *, void *);
86 static void determine_layout (void);
87 static void clean_up (void);
88 static void write_header (struct outp_driver *);
89 static void write_all_headers (void *);
91 /* Returns the number of text lines that can fit on the remainder of
93 static inline unsigned
94 n_lines_remaining (struct outp_driver *d)
98 diff = d->length - d->cp_y;
99 return (diff > 0) ? (diff / d->font_height) : 0;
102 /* Returns the number of fixed-width character that can fit across the
104 static inline unsigned
105 n_chars_width (struct outp_driver *d)
107 return d->width / d->fixed_width;
110 /* Writes the line S at the current position and advances to the next
113 write_line (struct outp_driver *d, const char *s)
115 struct outp_text text;
117 assert (d->cp_y + d->font_height <= d->length);
118 text.font = OUTP_FIXED;
119 text.justification = OUTP_LEFT;
120 ls_init (&text.string, s, strlen (s));
123 text.h = text.v = INT_MAX;
124 d->class->text_draw (d, &text);
126 d->cp_y += d->font_height;
129 /* Parses and executes the LIST procedure. */
133 struct variable casenum_var;
136 if (!parse_list (&cmd))
139 /* Fill in defaults. */
140 if (cmd.step == NOT_LONG)
142 if (cmd.first == NOT_LONG)
144 if (cmd.last == NOT_LONG)
146 if (!cmd.sbc_variables)
147 dict_get_vars (default_dict, &cmd.v_variables, &cmd.n_variables,
148 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
149 if (cmd.n_variables == 0)
151 msg (SE, _("No variables specified."));
155 /* Verify arguments. */
156 if (cmd.first > cmd.last)
159 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
160 "specified. The values will be swapped."), cmd.first, cmd.last);
162 cmd.first = cmd.last;
167 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
168 "being reset to 1."), cmd.first);
173 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
174 "being reset to 1."), cmd.last);
179 msg (SW, _("The step value %ld is less than 1. The value is being "
180 "reset to 1."), cmd.step);
184 /* Weighting variable. */
185 if (cmd.weight == LST_WEIGHT)
187 if (dict_get_weight (default_dict) != NULL)
191 for (i = 0; i < cmd.n_variables; i++)
192 if (cmd.v_variables[i] == dict_get_weight (default_dict))
194 if (i >= cmd.n_variables)
196 /* Add the weight variable to the end of the variable list. */
198 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
199 sizeof *cmd.v_variables);
200 cmd.v_variables[cmd.n_variables - 1]
201 = dict_get_weight (default_dict);
205 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
209 if (cmd.numbering == LST_NUMBERED)
211 /* Initialize the case-number variable. */
212 strcpy (casenum_var.name, "Case#");
213 casenum_var.type = NUMERIC;
215 casenum_var.print = make_output_format (FMT_F,
216 (cmd.last == LONG_MAX
217 ? 5 : intlog10 (cmd.last)), 0);
219 /* Add the weight variable at the beginning of the variable list. */
221 cmd.v_variables = xnrealloc (cmd.v_variables,
222 cmd.n_variables, sizeof *cmd.v_variables);
223 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
224 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
225 cmd.v_variables[0] = &casenum_var;
231 ok = procedure_with_splits (write_all_headers, list_cases, NULL, NULL);
232 ds_destroy(&line_buffer);
236 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
239 /* Writes headers to all devices. This is done at the beginning of
240 each SPLIT FILE group. */
242 write_all_headers (void *aux UNUSED)
244 struct outp_driver *d;
246 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
248 if (!d->class->special)
250 d->cp_y += d->font_height; /* Blank line. */
253 else if (d->class == &html_class)
255 struct html_driver_ext *x = d->ext;
257 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
262 for (i = 0; i < cmd.n_variables; i++)
263 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
264 cmd.v_variables[i]->name);
267 fputs (" </TR>\n", x->file);
274 /* Writes the headers. Some of them might be vertical; most are
275 probably horizontal. */
277 write_header (struct outp_driver *d)
279 struct list_ext *prc = d->prc;
281 if (!prc->header_rows)
284 if (n_lines_remaining (d) < prc->header_rows + 1)
287 assert (n_lines_remaining (d) >= prc->header_rows + 1);
290 /* Design the header. */
296 /* Allocate, initialize header. */
297 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
299 int w = n_chars_width (d);
300 for (i = 0; i < prc->header_rows; i++)
302 prc->header[i] = xmalloc (w + 1);
303 memset (prc->header[i], ' ', w);
307 /* Put in vertical names. */
308 for (i = x = 0; i < prc->n_vertical; i++)
310 struct variable *v = cmd.v_variables[i];
313 memset (&prc->header[prc->header_rows - 1][x], '-', v->print.w);
315 for (j = 0; j < strlen (v->name); j++)
316 prc->header[strlen (v->name) - j - 1][x] = v->name[j];
320 /* Put in horizontal names. */
321 for (; i < cmd.n_variables; i++)
323 struct variable *v = cmd.v_variables[i];
325 memset (&prc->header[prc->header_rows - 1][x], '-',
326 max (v->print.w, (int) strlen (v->name)));
327 if ((int) strlen (v->name) < v->print.w)
328 x += v->print.w - strlen (v->name);
329 memcpy (&prc->header[0][x], v->name, strlen (v->name));
330 x += strlen (v->name) + 1;
333 /* Add null bytes. */
334 for (i = 0; i < prc->header_rows; i++)
336 for (x = n_chars_width (d); x >= 1; x--)
337 if (prc->header[i][x - 1] != ' ')
339 prc->header[i][x] = 0;
346 /* Write out the header, in back-to-front order except for the last line. */
347 if (prc->header_rows >= 2)
351 for (i = prc->header_rows - 1; i-- != 0; )
352 write_line (d, prc->header[i]);
354 write_line (d, prc->header[prc->header_rows - 1]);
358 /* Frees up all the memory we've allocated. */
362 struct outp_driver *d;
364 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
365 if (d->class->special == 0)
367 struct list_ext *prc = d->prc;
372 for (i = 0; i < prc->header_rows; i++)
373 free (prc->header[i]);
378 else if (d->class == &html_class)
382 struct html_driver_ext *x = d->ext;
384 fputs ("</TABLE>\n", x->file);
390 free (cmd.v_variables);
393 /* Writes string STRING at the current position. If the text would
394 fall off the side of the page, then advance to the next line,
395 indenting by amount INDENT. */
397 write_varname (struct outp_driver *d, char *string, int indent)
399 struct outp_text text;
402 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
404 d->cp_y += d->font_height;
405 if (d->cp_y + d->font_height > d->length)
410 text.font = OUTP_FIXED;
411 text.justification = OUTP_LEFT;
412 ls_init (&text.string, string, strlen (string));
415 text.h = text.v = INT_MAX;
416 d->class->text_draw (d, &text);
417 d->class->text_metrics (d, &text, &width, NULL);
421 /* When we can't fit all the values across the page, we write out all
422 the variable names just once. This is where we do it. */
424 write_fallback_headers (struct outp_driver *d)
426 const int max_width = n_chars_width(d) - 10;
432 const char *Line = _("Line");
433 char *leader = local_alloc (strlen (Line)
434 + INT_STRLEN_BOUND (line_number) + 1 + 1);
436 while (index < cmd.n_variables)
438 struct outp_text text;
440 /* Ensure that there is enough room for a line of text. */
441 if (d->cp_y + d->font_height > d->length)
444 /* The leader is a string like `Line 1: '. Write the leader. */
445 sprintf (leader, "%s %d:", Line, ++line_number);
446 text.font = OUTP_FIXED;
447 text.justification = OUTP_LEFT;
448 ls_init (&text.string, leader, strlen (leader));
451 text.h = text.v = INT_MAX;
452 d->class->text_draw (d, &text);
453 d->class->text_metrics (d, &text, &d->cp_x, NULL);
462 int var_width = cmd.v_variables[index]->print.w;
463 if (width + var_width > max_width && width != 0)
467 d->cp_y += d->font_height;
475 sprintf (varname, " %s", cmd.v_variables[index]->name);
476 write_varname (d, varname, text.h);
479 while (++index < cmd.n_variables);
483 d->cp_y += d->font_height;
488 /* There are three possible layouts for the LIST procedure:
490 1. If the values and their variables' name fit across the page,
491 then they are listed across the page in that way.
493 2. If the values can fit across the page, but not the variable
494 names, then as many variable names as necessary are printed
495 vertically to compensate.
497 3. If not even the values can fit across the page, the variable
498 names are listed just once, at the beginning, in a compact format,
499 and the values are listed with a variable name label at the
500 beginning of each line for easier reference.
502 This is complicated by the fact that we have to do all this for
503 every output driver, not just once. */
505 determine_layout (void)
507 struct outp_driver *d;
509 /* This is the largest page width of any driver, so we can tell what
510 size buffer to allocate. */
511 int largest_page_width = 0;
513 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
515 size_t column; /* Current column. */
516 int width; /* Accumulated width. */
517 int height; /* Height of vertical names. */
518 int max_width; /* Page width. */
520 struct list_ext *prc;
522 if (d->class == &html_class)
525 assert (d->class->special == 0);
529 max_width = n_chars_width (d);
530 largest_page_width = max (largest_page_width, max_width);
532 prc = d->prc = xmalloc (sizeof *prc);
538 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
540 struct variable *v = cmd.v_variables[column];
541 width += max (v->print.w, (int) strlen (v->name));
543 if (width <= max_width)
545 prc->header_rows = 2;
550 for (width = cmd.n_variables - 1, height = 0, column = 0;
551 column < cmd.n_variables && width <= max_width;
554 struct variable *v = cmd.v_variables[column];
556 if (strlen (v->name) > height)
557 height = strlen (v->name);
560 /* If it fit then we need to determine how many labels can be
561 written horizontally. */
562 if (width <= max_width && height <= SHORT_NAME_LEN)
565 prc->n_vertical = SIZE_MAX;
567 for (column = cmd.n_variables; column-- != 0; )
569 struct variable *v = cmd.v_variables[column];
570 int trial_width = (width - v->print.w
571 + max (v->print.w, (int) strlen (v->name)));
573 if (trial_width > max_width)
575 prc->n_vertical = column + 1;
580 assert (prc->n_vertical != SIZE_MAX);
582 prc->n_vertical = cmd.n_variables;
583 /* Finally determine the length of the headers. */
584 for (prc->header_rows = 0, column = 0;
585 column < prc->n_vertical;
587 prc->header_rows = max (prc->header_rows,
588 strlen (cmd.v_variables[column]->name));
593 /* Otherwise use the ugly fallback listing format. */
595 prc->header_rows = 0;
597 d->cp_y += d->font_height;
598 write_fallback_headers (d);
599 d->cp_y += d->font_height;
602 ds_init(&line_buffer, largest_page_width + 2);
605 /* Writes case C to output. */
607 list_cases (struct ccase *c, void *aux UNUSED)
609 struct outp_driver *d;
612 if (case_idx < cmd.first || case_idx > cmd.last
613 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
616 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
617 if (d->class->special == 0)
619 const struct list_ext *prc = d->prc;
620 const int max_width = n_chars_width (d);
623 if (!prc->header_rows)
625 ds_printf(&line_buffer, "%8s: ", cmd.v_variables[0]->name);
629 for (column = 0; column < cmd.n_variables; column++)
631 struct variable *v = cmd.v_variables[column];
634 if (prc->type == 0 && column >= prc->n_vertical)
635 width = max ((int) strlen (v->name), v->print.w);
639 if (width + ds_length(&line_buffer) > max_width &&
640 ds_length(&line_buffer) != 0)
642 if (!n_lines_remaining (d))
648 write_line (d, ds_c_str(&line_buffer));
649 ds_clear(&line_buffer);
651 if (!prc->header_rows)
653 ds_printf (&line_buffer, "%8s: ", v->name);
657 if (width > v->print.w)
659 ds_putc_multiple(&line_buffer, ' ', width - v->print.w);
662 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
664 data_out (ds_append_uninit(&line_buffer, v->print.w),
665 &v->print, case_data (c, v->fv));
669 union value case_idx_value;
670 case_idx_value.f = case_idx;
671 data_out (ds_append_uninit(&line_buffer,v->print.w),
672 &v->print, &case_idx_value);
675 ds_putc(&line_buffer, ' ');
678 if (!n_lines_remaining (d))
684 write_line (d, ds_c_str(&line_buffer));
685 ds_clear(&line_buffer);
687 else if (d->class == &html_class)
689 struct html_driver_ext *x = d->ext;
692 fputs (" <TR>\n", x->file);
694 for (column = 0; column < cmd.n_variables; column++)
696 struct variable *v = cmd.v_variables[column];
698 struct fixed_string s;
700 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
701 data_out (buf, &v->print, case_data (c, v->fv));
704 union value case_idx_value;
705 case_idx_value.f = case_idx;
706 data_out (buf, &v->print, &case_idx_value);
709 ls_init (&s, buf, v->print.w);
710 fputs (" <TD>", x->file);
711 html_put_cell_contents (d, TAB_FIX, &s);
712 fputs ("</TD>\n", x->file);
715 fputs (" </TR>\n", x->file);