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 strcpy (casenum_var.name, "Case#");
217 casenum_var.type = NUMERIC;
219 casenum_var.print = fmt_for_output (FMT_F,
220 (cmd.last == LONG_MAX
221 ? 5 : intlog10 (cmd.last)), 0);
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 cmd.v_variables[i]->name);
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];
318 memset (&prc->header[prc->header_rows - 1][x], '-', v->print.w);
320 for (j = 0; j < strlen (v->name); j++)
321 prc->header[strlen (v->name) - j - 1][x] = v->name[j];
325 /* Put in horizontal names. */
326 for (; i < cmd.n_variables; i++)
328 struct variable *v = cmd.v_variables[i];
330 memset (&prc->header[prc->header_rows - 1][x], '-',
331 MAX (v->print.w, (int) strlen (v->name)));
332 if ((int) strlen (v->name) < v->print.w)
333 x += v->print.w - strlen (v->name);
334 memcpy (&prc->header[0][x], v->name, strlen (v->name));
335 x += strlen (v->name) + 1;
338 /* Add null bytes. */
339 for (i = 0; i < prc->header_rows; i++)
341 for (x = n_chars_width (d); x >= 1; x--)
342 if (prc->header[i][x - 1] != ' ')
344 prc->header[i][x] = 0;
351 /* Write out the header, in back-to-front order except for the last line. */
352 if (prc->header_rows >= 2)
356 for (i = prc->header_rows - 1; i-- != 0; )
357 write_line (d, prc->header[i]);
359 write_line (d, prc->header[prc->header_rows - 1]);
363 /* Frees up all the memory we've allocated. */
367 struct outp_driver *d;
369 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
370 if (d->class->special == 0)
372 struct list_ext *prc = d->prc;
377 for (i = 0; i < prc->header_rows; i++)
378 free (prc->header[i]);
383 else if (d->class == &html_class)
387 struct html_driver_ext *x = d->ext;
389 fputs ("</TABLE>\n", x->file);
395 free (cmd.v_variables);
398 /* Writes string STRING at the current position. If the text would
399 fall off the side of the page, then advance to the next line,
400 indenting by amount INDENT. */
402 write_varname (struct outp_driver *d, char *string, int indent)
404 struct outp_text text;
407 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
409 d->cp_y += d->font_height;
410 if (d->cp_y + d->font_height > d->length)
415 text.font = OUTP_FIXED;
416 text.justification = OUTP_LEFT;
417 text.string = ss_cstr (string);
420 text.h = text.v = INT_MAX;
421 d->class->text_draw (d, &text);
422 d->class->text_metrics (d, &text, &width, NULL);
426 /* When we can't fit all the values across the page, we write out all
427 the variable names just once. This is where we do it. */
429 write_fallback_headers (struct outp_driver *d)
431 const int max_width = n_chars_width(d) - 10;
437 const char *Line = _("Line");
438 char *leader = local_alloc (strlen (Line)
439 + INT_STRLEN_BOUND (line_number) + 1 + 1);
441 while (index < cmd.n_variables)
443 struct outp_text text;
446 /* Ensure that there is enough room for a line of text. */
447 if (d->cp_y + d->font_height > d->length)
450 /* The leader is a string like `Line 1: '. Write the leader. */
451 sprintf (leader, "%s %d:", Line, ++line_number);
452 text.font = OUTP_FIXED;
453 text.justification = OUTP_LEFT;
454 text.string = ss_cstr (leader);
457 text.h = text.v = INT_MAX;
458 d->class->text_draw (d, &text);
459 d->class->text_metrics (d, &text, &leader_width, NULL);
460 d->cp_x = leader_width;
469 int var_width = cmd.v_variables[index]->print.w;
470 if (width + var_width > max_width && width != 0)
474 d->cp_y += d->font_height;
481 char varname[LONG_NAME_LEN + 2];
482 snprintf (varname, sizeof varname,
483 " %s", cmd.v_variables[index]->name);
484 write_varname (d, varname, leader_width);
487 while (++index < cmd.n_variables);
491 d->cp_y += d->font_height;
496 /* There are three possible layouts for the LIST procedure:
498 1. If the values and their variables' name fit across the page,
499 then they are listed across the page in that way.
501 2. If the values can fit across the page, but not the variable
502 names, then as many variable names as necessary are printed
503 vertically to compensate.
505 3. If not even the values can fit across the page, the variable
506 names are listed just once, at the beginning, in a compact format,
507 and the values are listed with a variable name label at the
508 beginning of each line for easier reference.
510 This is complicated by the fact that we have to do all this for
511 every output driver, not just once. */
513 determine_layout (void)
515 struct outp_driver *d;
517 /* This is the largest page width of any driver, so we can tell what
518 size buffer to allocate. */
519 int largest_page_width = 0;
521 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
523 size_t column; /* Current column. */
524 int width; /* Accumulated width. */
525 int height; /* Height of vertical names. */
526 int max_width; /* Page width. */
528 struct list_ext *prc;
530 if (d->class == &html_class)
533 assert (d->class->special == 0);
537 max_width = n_chars_width (d);
538 largest_page_width = MAX (largest_page_width, max_width);
540 prc = d->prc = xmalloc (sizeof *prc);
546 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
548 struct variable *v = cmd.v_variables[column];
549 width += MAX (v->print.w, (int) strlen (v->name));
551 if (width <= max_width)
553 prc->header_rows = 2;
558 for (width = cmd.n_variables - 1, height = 0, column = 0;
559 column < cmd.n_variables && width <= max_width;
562 struct variable *v = cmd.v_variables[column];
564 if (strlen (v->name) > height)
565 height = strlen (v->name);
568 /* If it fit then we need to determine how many labels can be
569 written horizontally. */
570 if (width <= max_width && height <= SHORT_NAME_LEN)
573 prc->n_vertical = SIZE_MAX;
575 for (column = cmd.n_variables; column-- != 0; )
577 struct variable *v = cmd.v_variables[column];
578 int trial_width = (width - v->print.w
579 + MAX (v->print.w, (int) strlen (v->name)));
581 if (trial_width > max_width)
583 prc->n_vertical = column + 1;
588 assert (prc->n_vertical != SIZE_MAX);
590 prc->n_vertical = cmd.n_variables;
591 /* Finally determine the length of the headers. */
592 for (prc->header_rows = 0, column = 0;
593 column < prc->n_vertical;
595 prc->header_rows = MAX (prc->header_rows,
596 strlen (cmd.v_variables[column]->name));
601 /* Otherwise use the ugly fallback listing format. */
603 prc->header_rows = 0;
605 d->cp_y += d->font_height;
606 write_fallback_headers (d);
607 d->cp_y += d->font_height;
610 ds_init_empty (&line_buffer);
613 /* Writes case C to output. */
615 list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds UNUSED)
617 struct outp_driver *d;
620 if (case_idx < cmd.first || case_idx > cmd.last
621 || (cmd.step != 1 && (case_idx - cmd.first) % cmd.step))
624 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
625 if (d->class->special == 0)
627 const struct list_ext *prc = d->prc;
628 const int max_width = n_chars_width (d);
631 if (!prc->header_rows)
633 ds_put_format(&line_buffer, "%8s: ", cmd.v_variables[0]->name);
637 for (column = 0; column < cmd.n_variables; column++)
639 struct variable *v = cmd.v_variables[column];
642 if (prc->type == 0 && column >= prc->n_vertical)
643 width = MAX ((int) strlen (v->name), v->print.w);
647 if (width + ds_length(&line_buffer) > max_width &&
648 ds_length(&line_buffer) != 0)
650 if (!n_lines_remaining (d))
656 write_line (d, ds_cstr (&line_buffer));
657 ds_clear(&line_buffer);
659 if (!prc->header_rows)
661 ds_put_format (&line_buffer, "%8s: ", v->name);
665 if (width > v->print.w)
667 ds_put_char_multiple(&line_buffer, ' ', width - v->print.w);
670 if (fmt_is_string (v->print.type) || v->fv != -1)
672 data_out (case_data (c, v->fv), &v->print,
673 ds_put_uninit (&line_buffer, v->print.w));
677 union value case_idx_value;
678 case_idx_value.f = case_idx;
679 data_out (&case_idx_value, &v->print,
680 ds_put_uninit (&line_buffer,v->print.w));
683 ds_put_char(&line_buffer, ' ');
686 if (!n_lines_remaining (d))
692 write_line (d, ds_cstr (&line_buffer));
693 ds_clear(&line_buffer);
695 else if (d->class == &html_class)
697 struct html_driver_ext *x = d->ext;
700 fputs (" <TR>\n", x->file);
702 for (column = 0; column < cmd.n_variables; column++)
704 struct variable *v = cmd.v_variables[column];
707 if (fmt_is_string (v->print.type) || v->fv != -1)
708 data_out (case_data (c, v->fv), &v->print, buf);
711 union value case_idx_value;
712 case_idx_value.f = case_idx;
713 data_out (&case_idx_value, &v->print, buf);
716 fputs (" <TD>", x->file);
717 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, v->print.w));
718 fputs ("</TD>\n", x->file);
721 fputs (" </TR>\n", x->file);