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,
64 /* Layout for one output driver. */
68 struct outp_driver *driver;
69 int type; /* 0=Values and labels fit across the page. */
70 size_t n_vertical; /* Number of labels to list vertically. */
71 size_t header_rows; /* Number of header rows. */
72 char **header; /* The header itself. */
76 static struct cmd_list cmd;
79 static struct string line_buffer;
81 /* TTY-style output functions. */
82 static unsigned n_lines_remaining (struct outp_driver *d);
83 static unsigned n_chars_width (struct outp_driver *d);
84 static void write_line (struct outp_driver *d, const char *s);
86 /* Other functions. */
87 static void list_case (const struct ccase *, casenumber case_idx,
88 const struct dataset *, struct ll_list *targets);
89 static void determine_layout (struct ll_list *targets);
90 static void clean_up (struct ll_list *targets);
91 static void write_header (struct list_target *);
92 static void write_all_headers (struct casereader *, const struct dataset *,
93 struct ll_list *targets);
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 dictionary *dict = dataset_dict (ds);
138 struct variable *casenum_var = NULL;
139 struct casegrouper *grouper;
140 struct casereader *group;
141 struct ll_list targets;
145 if (!parse_list (lexer, ds, &cmd, NULL))
148 /* Fill in defaults. */
149 if (cmd.step == LONG_MIN)
151 if (cmd.first == LONG_MIN)
153 if (cmd.last == LONG_MIN)
155 if (!cmd.sbc_variables)
156 dict_get_vars (dict, &cmd.v_variables, &cmd.n_variables,
157 DC_SYSTEM | DC_SCRATCH);
158 if (cmd.n_variables == 0)
160 msg (SE, _("No variables specified."));
164 /* Verify arguments. */
165 if (cmd.first > cmd.last)
168 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
169 "specified. The values will be swapped."), cmd.first, cmd.last);
171 cmd.first = cmd.last;
176 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
177 "being reset to 1."), cmd.first);
182 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
183 "being reset to 1."), cmd.last);
188 msg (SW, _("The step value %ld is less than 1. The value is being "
189 "reset to 1."), cmd.step);
194 if (cmd.numbering == LST_NUMBERED)
196 /* Initialize the case-number variable. */
197 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
198 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
199 casenum_var = var_create ("Case#", 0);
200 var_set_both_formats (casenum_var, &format);
202 /* Add the case-number variable at the beginning of the variable list. */
204 cmd.v_variables = xnrealloc (cmd.v_variables,
205 cmd.n_variables, sizeof *cmd.v_variables);
206 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
207 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
208 cmd.v_variables[0] = casenum_var;
211 determine_layout (&targets);
214 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
215 casegrouper_get_next_group (grouper, &group);
216 casereader_destroy (group))
220 write_all_headers (group, ds, &targets);
221 for (; (c = casereader_read (group)) != NULL; case_unref (c))
224 if (case_idx >= cmd.first && case_idx <= cmd.last
225 && (case_idx - cmd.first) % cmd.step == 0)
226 list_case (c, case_idx, ds, &targets);
229 ok = casegrouper_destroy (grouper);
230 ok = proc_commit (ds) && ok;
232 ds_destroy(&line_buffer);
236 var_destroy (casenum_var);
238 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
241 /* Writes headers to all devices. This is done at the beginning of
242 each SPLIT FILE group. */
244 write_all_headers (struct casereader *input, const struct dataset *ds,
245 struct ll_list *targets)
247 struct list_target *target;
250 c = casereader_peek (input, 0);
253 output_split_file_values (ds, c);
256 ll_for_each (target, struct list_target, ll, targets)
258 struct outp_driver *d = target->driver;
259 if (!d->class->special)
261 d->cp_y += d->font_height; /* Blank line. */
262 write_header (target);
264 else if (d->class == &html_class)
266 struct html_driver_ext *x = d->ext;
268 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
273 for (i = 0; i < cmd.n_variables; i++)
274 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
275 var_get_name (cmd.v_variables[i]));
278 fputs (" </TR>\n", x->file);
285 /* Writes the headers. Some of them might be vertical; most are
286 probably horizontal. */
288 write_header (struct list_target *target)
290 struct outp_driver *d = target->driver;
292 if (d->class->special || !target->header_rows)
295 if (n_lines_remaining (d) < target->header_rows + 1)
298 assert (n_lines_remaining (d) >= target->header_rows + 1);
301 /* Design the header. */
307 /* Allocate, initialize header. */
308 target->header = xnmalloc (target->header_rows, sizeof *target->header);
310 int w = n_chars_width (d);
311 for (i = 0; i < target->header_rows; i++)
313 target->header[i] = xmalloc (w + 1);
314 memset (target->header[i], ' ', w);
318 /* Put in vertical names. */
319 for (i = x = 0; i < target->n_vertical; i++)
321 const struct variable *v = cmd.v_variables[i];
322 const char *name = var_get_name (v);
323 size_t name_len = strlen (name);
324 const struct fmt_spec *print = var_get_print_format (v);
327 memset (&target->header[target->header_rows - 1][x], '-', print->w);
329 for (j = 0; j < name_len; j++)
330 target->header[name_len - j - 1][x] = name[j];
334 /* Put in horizontal names. */
335 for (; i < cmd.n_variables; i++)
337 const struct variable *v = cmd.v_variables[i];
338 const char *name = var_get_name (v);
339 size_t name_len = strlen (name);
340 const struct fmt_spec *print = var_get_print_format (v);
342 memset (&target->header[target->header_rows - 1][x], '-',
343 MAX (print->w, (int) name_len));
344 if ((int) name_len < print->w)
345 x += print->w - name_len;
346 memcpy (&target->header[0][x], name, name_len);
350 /* Add null bytes. */
351 for (i = 0; i < target->header_rows; i++)
353 for (x = n_chars_width (d); x >= 1; x--)
354 if (target->header[i][x - 1] != ' ')
356 target->header[i][x] = 0;
363 /* Write out the header, in back-to-front order except for the last line. */
364 if (target->header_rows >= 2)
368 for (i = target->header_rows - 1; i-- != 0; )
369 write_line (d, target->header[i]);
371 write_line (d, target->header[target->header_rows - 1]);
375 /* Frees up all the memory we've allocated. */
377 clean_up (struct ll_list *targets)
379 struct list_target *target, *next;
381 ll_for_each_safe (target, next, struct list_target, ll, targets)
383 struct outp_driver *d = target->driver;
384 if (d->class->special == 0)
389 for (i = 0; i < target->header_rows; i++)
390 free (target->header[i]);
391 free (target->header);
394 else if (d->class == &html_class)
398 struct html_driver_ext *x = d->ext;
400 fputs ("</TABLE>\n", x->file);
406 ll_remove (&target->ll);
410 free (cmd.v_variables);
413 /* Writes string STRING at the current position. If the text would
414 fall off the side of the page, then advance to the next line,
415 indenting by amount INDENT. */
417 write_varname (struct outp_driver *d, char *string, int indent)
419 struct outp_text text;
422 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
424 d->cp_y += d->font_height;
425 if (d->cp_y + d->font_height > d->length)
430 text.font = OUTP_FIXED;
431 text.justification = OUTP_LEFT;
432 text.string = ss_cstr (string);
435 text.h = text.v = INT_MAX;
436 d->class->text_draw (d, &text);
437 d->class->text_metrics (d, &text, &width, NULL);
441 /* When we can't fit all the values across the page, we write out all
442 the variable names just once. This is where we do it. */
444 write_fallback_headers (struct outp_driver *d)
446 const int max_width = n_chars_width(d) - 10;
452 const char *Line = _("Line");
453 char *leader = xmalloca (strlen (Line)
454 + INT_STRLEN_BOUND (line_number) + 1 + 1);
456 while (index < cmd.n_variables)
458 struct outp_text text;
461 /* Ensure that there is enough room for a line of text. */
462 if (d->cp_y + d->font_height > d->length)
465 /* The leader is a string like `Line 1: '. Write the leader. */
466 sprintf (leader, "%s %d:", Line, ++line_number);
467 text.font = OUTP_FIXED;
468 text.justification = OUTP_LEFT;
469 text.string = ss_cstr (leader);
472 text.h = text.v = INT_MAX;
473 d->class->text_draw (d, &text);
474 d->class->text_metrics (d, &text, &leader_width, NULL);
475 d->cp_x = leader_width;
484 int var_width = var_get_print_format (cmd.v_variables[index])->w;
485 if (width + var_width > max_width && width != 0)
489 d->cp_y += d->font_height;
496 char varname[VAR_NAME_LEN + 2];
497 snprintf (varname, sizeof varname,
498 " %s", var_get_name (cmd.v_variables[index]));
499 write_varname (d, varname, leader_width);
502 while (++index < cmd.n_variables);
506 d->cp_y += d->font_height;
511 /* There are three possible layouts for the LIST procedure:
513 1. If the values and their variables' name fit across the page,
514 then they are listed across the page in that way.
516 2. If the values can fit across the page, but not the variable
517 names, then as many variable names as necessary are printed
518 vertically to compensate.
520 3. If not even the values can fit across the page, the variable
521 names are listed just once, at the beginning, in a compact format,
522 and the values are listed with a variable name label at the
523 beginning of each line for easier reference.
525 This is complicated by the fact that we have to do all this for
526 every output driver, not just once. */
528 determine_layout (struct ll_list *targets)
530 struct outp_driver *d;
532 /* This is the largest page width of any driver, so we can tell what
533 size buffer to allocate. */
534 int largest_page_width = 0;
537 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
539 size_t column; /* Current column. */
540 int width; /* Accumulated width. */
541 int height; /* Height of vertical names. */
542 int max_width; /* Page width. */
544 struct list_target *target;
546 target = xmalloc (sizeof *target);
547 ll_push_tail (targets, &target->ll);
550 target->n_vertical = 0;
551 target->header = NULL;
553 if (d->class == &html_class)
555 assert (d->class->special == 0);
559 max_width = n_chars_width (d);
560 largest_page_width = MAX (largest_page_width, max_width);
563 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
565 const struct variable *v = cmd.v_variables[column];
566 int fmt_width = var_get_print_format (v)->w;
567 int name_len = strlen (var_get_name (v));
568 width += MAX (fmt_width, name_len);
570 if (width <= max_width)
572 target->header_rows = 2;
577 for (width = cmd.n_variables - 1, height = 0, column = 0;
578 column < cmd.n_variables && width <= max_width;
581 const struct variable *v = cmd.v_variables[column];
582 int fmt_width = var_get_print_format (v)->w;
583 size_t name_len = strlen (var_get_name (v));
585 if (name_len > height)
589 /* If it fit then we need to determine how many labels can be
590 written horizontally. */
591 if (width <= max_width && height <= SHORT_NAME_LEN)
594 target->n_vertical = SIZE_MAX;
596 for (column = cmd.n_variables; column-- != 0; )
598 const struct variable *v = cmd.v_variables[column];
599 int name_len = strlen (var_get_name (v));
600 int fmt_width = var_get_print_format (v)->w;
601 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
602 if (trial_width > max_width)
604 target->n_vertical = column + 1;
609 assert (target->n_vertical != SIZE_MAX);
611 target->n_vertical = cmd.n_variables;
612 /* Finally determine the length of the headers. */
613 for (target->header_rows = 0, column = 0;
614 column < target->n_vertical;
617 const struct variable *var = cmd.v_variables[column];
618 size_t name_len = strlen (var_get_name (var));
619 target->header_rows = MAX (target->header_rows, name_len);
621 target->header_rows++;
625 /* Otherwise use the ugly fallback listing format. */
627 target->header_rows = 0;
629 d->cp_y += d->font_height;
630 write_fallback_headers (d);
631 d->cp_y += d->font_height;
634 ds_init_empty (&line_buffer);
637 /* Writes case C to output. */
639 list_case (const struct ccase *c, casenumber case_idx,
640 const struct dataset *ds, struct ll_list *targets)
642 struct dictionary *dict = dataset_dict (ds);
643 const char *encoding = dict_get_encoding (dict);
644 struct list_target *target;
646 ll_for_each (target, struct list_target, ll, targets)
648 struct outp_driver *d = target->driver;
650 if (d->class->special == 0)
652 const int max_width = n_chars_width (d);
655 if (!target->header_rows)
657 ds_put_format(&line_buffer, "%8s: ",
658 var_get_name (cmd.v_variables[0]));
662 for (column = 0; column < cmd.n_variables; column++)
664 const struct variable *v = cmd.v_variables[column];
665 const struct fmt_spec *print = var_get_print_format (v);
669 if (target->type == 0 && column >= target->n_vertical)
671 int name_len = strlen (var_get_name (v));
672 width = MAX (name_len, print->w);
677 if (width + ds_length(&line_buffer) > max_width &&
678 ds_length(&line_buffer) != 0)
680 if (!n_lines_remaining (d))
683 write_header (target);
686 write_line (d, ds_cstr (&line_buffer));
687 ds_clear(&line_buffer);
689 if (!target->header_rows)
690 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
693 if (width > print->w)
694 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
696 if (fmt_is_string (print->type) || dict_contains_var (dict, v))
697 s = data_out (case_data (c, v), encoding, print);
700 union value case_idx_value;
701 case_idx_value.f = case_idx;
702 s = data_out (&case_idx_value, encoding, print);
705 ds_put_cstr (&line_buffer, s);
707 ds_put_char(&line_buffer, ' ');
710 if (!n_lines_remaining (d))
713 write_header (target);
716 write_line (d, ds_cstr (&line_buffer));
717 ds_clear(&line_buffer);
719 else if (d->class == &html_class)
721 struct html_driver_ext *x = d->ext;
724 fputs (" <TR>\n", x->file);
726 for (column = 0; column < cmd.n_variables; column++)
728 const struct variable *v = cmd.v_variables[column];
729 const struct fmt_spec *print = var_get_print_format (v);
732 if (fmt_is_string (print->type)
733 || dict_contains_var (dict, v))
734 s = data_out (case_data (c, v), encoding, print);
737 union value case_idx_value;
738 case_idx_value.f = case_idx;
739 s = data_out (&case_idx_value, encoding, print);
742 fputs (" <TD>", x->file);
743 html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
744 fputs ("</TD>\n", x->file);
749 fputs (" </TR>\n", x->file);