1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 #include <data/casegrouper.h>
27 #include <data/casereader.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/short-names.h>
33 #include <data/variable.h>
34 #include <language/command.h>
35 #include <language/dictionary/split-file.h>
36 #include <language/lexer/lexer.h>
37 #include <libpspp/compiler.h>
38 #include <libpspp/ll.h>
39 #include <libpspp/message.h>
40 #include <libpspp/misc.h>
41 #include <output/htmlP.h>
42 #include <output/manager.h>
43 #include <output/output.h>
44 #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. */
69 struct outp_driver *driver;
70 int type; /* 0=Values and labels fit across the page. */
71 size_t n_vertical; /* Number of labels to list vertically. */
72 size_t header_rows; /* Number of header rows. */
73 char **header; /* The header itself. */
77 static struct cmd_list cmd;
80 static struct string line_buffer;
82 /* TTY-style output functions. */
83 static unsigned n_lines_remaining (struct outp_driver *d);
84 static unsigned n_chars_width (struct outp_driver *d);
85 static void write_line (struct outp_driver *d, const char *s);
87 /* Other functions. */
88 static void list_case (const struct ccase *, casenumber case_idx,
89 const struct dataset *, struct ll_list *targets);
90 static void determine_layout (struct ll_list *targets);
91 static void clean_up (struct ll_list *targets);
92 static void write_header (struct list_target *);
93 static void write_all_headers (struct casereader *, const struct dataset *,
94 struct ll_list *targets);
96 /* Returns the number of text lines that can fit on the remainder of
98 static inline unsigned
99 n_lines_remaining (struct outp_driver *d)
103 diff = d->length - d->cp_y;
104 return (diff > 0) ? (diff / d->font_height) : 0;
107 /* Returns the number of fixed-width character that can fit across the
109 static inline unsigned
110 n_chars_width (struct outp_driver *d)
112 return d->width / d->fixed_width;
115 /* Writes the line S at the current position and advances to the next
118 write_line (struct outp_driver *d, const char *s)
120 struct outp_text text;
122 assert (d->cp_y + d->font_height <= d->length);
123 text.font = OUTP_FIXED;
124 text.justification = OUTP_LEFT;
125 text.string = ss_cstr (s);
128 text.h = text.v = INT_MAX;
129 d->class->text_draw (d, &text);
131 d->cp_y += d->font_height;
134 /* Parses and executes the LIST procedure. */
136 cmd_list (struct lexer *lexer, struct dataset *ds)
138 struct dictionary *dict = dataset_dict (ds);
139 struct variable *casenum_var = NULL;
140 struct casegrouper *grouper;
141 struct casereader *group;
142 struct ll_list targets;
146 if (!parse_list (lexer, ds, &cmd, NULL))
149 /* Fill in defaults. */
150 if (cmd.step == LONG_MIN)
152 if (cmd.first == LONG_MIN)
154 if (cmd.last == LONG_MIN)
156 if (!cmd.sbc_variables)
157 dict_get_vars (dict, &cmd.v_variables, &cmd.n_variables,
158 DC_SYSTEM | DC_SCRATCH);
159 if (cmd.n_variables == 0)
161 msg (SE, _("No variables specified."));
165 /* Verify arguments. */
166 if (cmd.first > cmd.last)
169 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
170 "specified. The values will be swapped."), cmd.first, cmd.last);
172 cmd.first = cmd.last;
177 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
178 "being reset to 1."), cmd.first);
183 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
184 "being reset to 1."), cmd.last);
189 msg (SW, _("The step value %ld is less than 1. The value is being "
190 "reset to 1."), cmd.step);
194 /* Weighting variable. */
195 if (cmd.weight == LST_WEIGHT)
197 if (dict_get_weight (dict) != NULL)
201 for (i = 0; i < cmd.n_variables; i++)
202 if (cmd.v_variables[i] == dict_get_weight (dict))
204 if (i >= cmd.n_variables)
206 /* Add the weight variable to the end of the variable list. */
208 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
209 sizeof *cmd.v_variables);
210 cmd.v_variables[cmd.n_variables - 1]
211 = dict_get_weight (dict);
215 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
219 if (cmd.numbering == LST_NUMBERED)
221 /* Initialize the case-number variable. */
222 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
223 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
224 casenum_var = var_create ("Case#", 0);
225 var_set_both_formats (casenum_var, &format);
227 /* Add the weight variable at the beginning of the variable list. */
229 cmd.v_variables = xnrealloc (cmd.v_variables,
230 cmd.n_variables, sizeof *cmd.v_variables);
231 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
232 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
233 cmd.v_variables[0] = casenum_var;
236 determine_layout (&targets);
239 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
240 casegrouper_get_next_group (grouper, &group);
241 casereader_destroy (group))
245 write_all_headers (group, ds, &targets);
246 for (; (c = casereader_read (group)) != NULL; case_unref (c))
249 if (case_idx >= cmd.first && case_idx <= cmd.last
250 && (case_idx - cmd.first) % cmd.step == 0)
251 list_case (c, case_idx, ds, &targets);
254 ok = casegrouper_destroy (grouper);
255 ok = proc_commit (ds) && ok;
257 ds_destroy(&line_buffer);
261 var_destroy (casenum_var);
263 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
266 /* Writes headers to all devices. This is done at the beginning of
267 each SPLIT FILE group. */
269 write_all_headers (struct casereader *input, const struct dataset *ds,
270 struct ll_list *targets)
272 struct list_target *target;
275 c = casereader_peek (input, 0);
278 output_split_file_values (ds, c);
281 ll_for_each (target, struct list_target, ll, targets)
283 struct outp_driver *d = target->driver;
284 if (!d->class->special)
286 d->cp_y += d->font_height; /* Blank line. */
287 write_header (target);
289 else if (d->class == &html_class)
291 struct html_driver_ext *x = d->ext;
293 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
298 for (i = 0; i < cmd.n_variables; i++)
299 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
300 var_get_name (cmd.v_variables[i]));
303 fputs (" </TR>\n", x->file);
310 /* Writes the headers. Some of them might be vertical; most are
311 probably horizontal. */
313 write_header (struct list_target *target)
315 struct outp_driver *d = target->driver;
317 if (d->class->special || !target->header_rows)
320 if (n_lines_remaining (d) < target->header_rows + 1)
323 assert (n_lines_remaining (d) >= target->header_rows + 1);
326 /* Design the header. */
332 /* Allocate, initialize header. */
333 target->header = xnmalloc (target->header_rows, sizeof *target->header);
335 int w = n_chars_width (d);
336 for (i = 0; i < target->header_rows; i++)
338 target->header[i] = xmalloc (w + 1);
339 memset (target->header[i], ' ', w);
343 /* Put in vertical names. */
344 for (i = x = 0; i < target->n_vertical; i++)
346 const struct variable *v = cmd.v_variables[i];
347 const char *name = var_get_name (v);
348 size_t name_len = strlen (name);
349 const struct fmt_spec *print = var_get_print_format (v);
352 memset (&target->header[target->header_rows - 1][x], '-', print->w);
354 for (j = 0; j < name_len; j++)
355 target->header[name_len - j - 1][x] = name[j];
359 /* Put in horizontal names. */
360 for (; i < cmd.n_variables; i++)
362 const struct variable *v = cmd.v_variables[i];
363 const char *name = var_get_name (v);
364 size_t name_len = strlen (name);
365 const struct fmt_spec *print = var_get_print_format (v);
367 memset (&target->header[target->header_rows - 1][x], '-',
368 MAX (print->w, (int) name_len));
369 if ((int) name_len < print->w)
370 x += print->w - name_len;
371 memcpy (&target->header[0][x], name, name_len);
375 /* Add null bytes. */
376 for (i = 0; i < target->header_rows; i++)
378 for (x = n_chars_width (d); x >= 1; x--)
379 if (target->header[i][x - 1] != ' ')
381 target->header[i][x] = 0;
388 /* Write out the header, in back-to-front order except for the last line. */
389 if (target->header_rows >= 2)
393 for (i = target->header_rows - 1; i-- != 0; )
394 write_line (d, target->header[i]);
396 write_line (d, target->header[target->header_rows - 1]);
400 /* Frees up all the memory we've allocated. */
402 clean_up (struct ll_list *targets)
404 struct list_target *target, *next;
406 ll_for_each_safe (target, next, struct list_target, ll, targets)
408 struct outp_driver *d = target->driver;
409 if (d->class->special == 0)
414 for (i = 0; i < target->header_rows; i++)
415 free (target->header[i]);
416 free (target->header);
419 else if (d->class == &html_class)
423 struct html_driver_ext *x = d->ext;
425 fputs ("</TABLE>\n", x->file);
431 ll_remove (&target->ll);
435 free (cmd.v_variables);
438 /* Writes string STRING at the current position. If the text would
439 fall off the side of the page, then advance to the next line,
440 indenting by amount INDENT. */
442 write_varname (struct outp_driver *d, char *string, int indent)
444 struct outp_text text;
447 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
449 d->cp_y += d->font_height;
450 if (d->cp_y + d->font_height > d->length)
455 text.font = OUTP_FIXED;
456 text.justification = OUTP_LEFT;
457 text.string = ss_cstr (string);
460 text.h = text.v = INT_MAX;
461 d->class->text_draw (d, &text);
462 d->class->text_metrics (d, &text, &width, NULL);
466 /* When we can't fit all the values across the page, we write out all
467 the variable names just once. This is where we do it. */
469 write_fallback_headers (struct outp_driver *d)
471 const int max_width = n_chars_width(d) - 10;
477 const char *Line = _("Line");
478 char *leader = xmalloca (strlen (Line)
479 + INT_STRLEN_BOUND (line_number) + 1 + 1);
481 while (index < cmd.n_variables)
483 struct outp_text text;
486 /* Ensure that there is enough room for a line of text. */
487 if (d->cp_y + d->font_height > d->length)
490 /* The leader is a string like `Line 1: '. Write the leader. */
491 sprintf (leader, "%s %d:", Line, ++line_number);
492 text.font = OUTP_FIXED;
493 text.justification = OUTP_LEFT;
494 text.string = ss_cstr (leader);
497 text.h = text.v = INT_MAX;
498 d->class->text_draw (d, &text);
499 d->class->text_metrics (d, &text, &leader_width, NULL);
500 d->cp_x = leader_width;
509 int var_width = var_get_print_format (cmd.v_variables[index])->w;
510 if (width + var_width > max_width && width != 0)
514 d->cp_y += d->font_height;
521 char varname[VAR_NAME_LEN + 2];
522 snprintf (varname, sizeof varname,
523 " %s", var_get_name (cmd.v_variables[index]));
524 write_varname (d, varname, leader_width);
527 while (++index < cmd.n_variables);
531 d->cp_y += d->font_height;
536 /* There are three possible layouts for the LIST procedure:
538 1. If the values and their variables' name fit across the page,
539 then they are listed across the page in that way.
541 2. If the values can fit across the page, but not the variable
542 names, then as many variable names as necessary are printed
543 vertically to compensate.
545 3. If not even the values can fit across the page, the variable
546 names are listed just once, at the beginning, in a compact format,
547 and the values are listed with a variable name label at the
548 beginning of each line for easier reference.
550 This is complicated by the fact that we have to do all this for
551 every output driver, not just once. */
553 determine_layout (struct ll_list *targets)
555 struct outp_driver *d;
557 /* This is the largest page width of any driver, so we can tell what
558 size buffer to allocate. */
559 int largest_page_width = 0;
562 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
564 size_t column; /* Current column. */
565 int width; /* Accumulated width. */
566 int height; /* Height of vertical names. */
567 int max_width; /* Page width. */
569 struct list_target *target;
571 target = xmalloc (sizeof *target);
572 ll_push_tail (targets, &target->ll);
575 target->n_vertical = 0;
576 target->header = NULL;
578 if (d->class == &html_class)
580 assert (d->class->special == 0);
584 max_width = n_chars_width (d);
585 largest_page_width = MAX (largest_page_width, max_width);
588 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
590 const struct variable *v = cmd.v_variables[column];
591 int fmt_width = var_get_print_format (v)->w;
592 int name_len = strlen (var_get_name (v));
593 width += MAX (fmt_width, name_len);
595 if (width <= max_width)
597 target->header_rows = 2;
602 for (width = cmd.n_variables - 1, height = 0, column = 0;
603 column < cmd.n_variables && width <= max_width;
606 const struct variable *v = cmd.v_variables[column];
607 int fmt_width = var_get_print_format (v)->w;
608 size_t name_len = strlen (var_get_name (v));
610 if (name_len > height)
614 /* If it fit then we need to determine how many labels can be
615 written horizontally. */
616 if (width <= max_width && height <= SHORT_NAME_LEN)
619 target->n_vertical = SIZE_MAX;
621 for (column = cmd.n_variables; column-- != 0; )
623 const struct variable *v = cmd.v_variables[column];
624 int name_len = strlen (var_get_name (v));
625 int fmt_width = var_get_print_format (v)->w;
626 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
627 if (trial_width > max_width)
629 target->n_vertical = column + 1;
634 assert (target->n_vertical != SIZE_MAX);
636 target->n_vertical = cmd.n_variables;
637 /* Finally determine the length of the headers. */
638 for (target->header_rows = 0, column = 0;
639 column < target->n_vertical;
642 const struct variable *var = cmd.v_variables[column];
643 size_t name_len = strlen (var_get_name (var));
644 target->header_rows = MAX (target->header_rows, name_len);
646 target->header_rows++;
650 /* Otherwise use the ugly fallback listing format. */
652 target->header_rows = 0;
654 d->cp_y += d->font_height;
655 write_fallback_headers (d);
656 d->cp_y += d->font_height;
659 ds_init_empty (&line_buffer);
662 /* Writes case C to output. */
664 list_case (const struct ccase *c, casenumber case_idx,
665 const struct dataset *ds, struct ll_list *targets)
667 struct dictionary *dict = dataset_dict (ds);
668 struct list_target *target;
670 ll_for_each (target, struct list_target, ll, targets)
672 struct outp_driver *d = target->driver;
674 if (d->class->special == 0)
676 const int max_width = n_chars_width (d);
679 if (!target->header_rows)
681 ds_put_format(&line_buffer, "%8s: ",
682 var_get_name (cmd.v_variables[0]));
686 for (column = 0; column < cmd.n_variables; column++)
688 const struct variable *v = cmd.v_variables[column];
689 const struct fmt_spec *print = var_get_print_format (v);
692 if (target->type == 0 && column >= target->n_vertical)
694 int name_len = strlen (var_get_name (v));
695 width = MAX (name_len, print->w);
700 if (width + ds_length(&line_buffer) > max_width &&
701 ds_length(&line_buffer) != 0)
703 if (!n_lines_remaining (d))
706 write_header (target);
709 write_line (d, ds_cstr (&line_buffer));
710 ds_clear(&line_buffer);
712 if (!target->header_rows)
713 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
716 if (width > print->w)
717 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
719 if (fmt_is_string (print->type)
720 || dict_contains_var (dict, v))
722 data_out (case_data (c, v), print,
723 ds_put_uninit (&line_buffer, print->w));
727 union value case_idx_value;
728 case_idx_value.f = case_idx;
729 data_out (&case_idx_value, print,
730 ds_put_uninit (&line_buffer,print->w));
733 ds_put_char(&line_buffer, ' ');
736 if (!n_lines_remaining (d))
739 write_header (target);
742 write_line (d, ds_cstr (&line_buffer));
743 ds_clear(&line_buffer);
745 else if (d->class == &html_class)
747 struct html_driver_ext *x = d->ext;
750 fputs (" <TR>\n", x->file);
752 for (column = 0; column < cmd.n_variables; column++)
754 const struct variable *v = cmd.v_variables[column];
755 const struct fmt_spec *print = var_get_print_format (v);
758 if (fmt_is_string (print->type)
759 || dict_contains_var (dict, v))
760 data_out (case_data (c, v), print, buf);
763 union value case_idx_value;
764 case_idx_value.f = case_idx;
765 data_out (&case_idx_value, print, buf);
768 fputs (" <TD>", x->file);
769 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
770 fputs ("</TD>\n", x->file);
773 fputs (" </TR>\n", x->file);