1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 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)
48 #include <libpspp/debug-print.h>
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 char *line_buf;
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, 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, 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);
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 line_buf = xmalloc (max (1022, 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);
624 if (!prc->header_rows)
625 x = nsprintf (line_buf, "%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 + x > max_width && x != 0)
639 if (!n_lines_remaining (d))
646 write_line (d, line_buf);
649 if (!prc->header_rows)
650 x = nsprintf (line_buf, "%8s: ", v->name);
653 if (width > v->print.w)
655 memset(&line_buf[x], ' ', width - v->print.w);
656 x += width - v->print.w;
659 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
660 data_out (&line_buf[x], &v->print, case_data (c, v->fv));
663 union value case_idx_value;
664 case_idx_value.f = case_idx;
665 data_out (&line_buf[x], &v->print, &case_idx_value);
672 if (!n_lines_remaining (d))
679 write_line (d, line_buf);
681 else if (d->class == &html_class)
683 struct html_driver_ext *x = d->ext;
686 fputs (" <TR>\n", x->file);
688 for (column = 0; column < cmd.n_variables; column++)
690 struct variable *v = cmd.v_variables[column];
692 struct fixed_string s;
694 if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
695 data_out (buf, &v->print, case_data (c, v->fv));
698 union value case_idx_value;
699 case_idx_value.f = case_idx;
700 data_out (buf, &v->print, &case_idx_value);
703 ls_init (&s, buf, v->print.w);
704 fputs (" <TD>", x->file);
705 html_put_cell_contents (d, TAB_FIX, &s);
706 fputs ("</TD>\n", x->file);
709 fputs (" </TR>\n", x->file);