1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 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/>. */
24 #include <data/casegrouper.h>
25 #include <data/casereader.h>
26 #include <data/dictionary.h>
27 #include <data/data-out.h>
28 #include <data/format.h>
29 #include <data/procedure.h>
30 #include <data/variable.h>
31 #include <language/command.h>
32 #include <language/dictionary/split-file.h>
33 #include <language/lexer/lexer.h>
34 #include <libpspp/alloc.h>
35 #include <libpspp/compiler.h>
36 #include <libpspp/message.h>
37 #include <libpspp/message.h>
38 #include <libpspp/misc.h>
39 #include <output/htmlP.h>
40 #include <output/manager.h>
41 #include <output/output.h>
42 #include <output/table.h>
47 #define _(msgid) gettext (msgid)
53 *variables=varlist("PV_NO_SCRATCH");
54 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
55 +format=numbering:numbered/!unnumbered,
57 weight:weight/!noweight.
62 /* Layout for one output driver. */
65 int type; /* 0=Values and labels fit across the page. */
66 size_t n_vertical; /* Number of labels to list vertically. */
67 size_t header_rows; /* Number of header rows. */
68 char **header; /* The header itself. */
72 static struct cmd_list cmd;
75 static struct string line_buffer;
77 /* TTY-style output functions. */
78 static unsigned n_lines_remaining (struct outp_driver *d);
79 static unsigned n_chars_width (struct outp_driver *d);
80 static void write_line (struct outp_driver *d, const char *s);
82 /* Other functions. */
83 static void list_case (struct ccase *, casenumber case_idx,
84 const struct dataset *);
85 static void determine_layout (void);
86 static void clean_up (void);
87 static void write_header (struct outp_driver *);
88 static void write_all_headers (struct casereader *, const struct dataset*);
90 /* Returns the number of text lines that can fit on the remainder of
92 static inline unsigned
93 n_lines_remaining (struct outp_driver *d)
97 diff = d->length - d->cp_y;
98 return (diff > 0) ? (diff / d->font_height) : 0;
101 /* Returns the number of fixed-width character that can fit across the
103 static inline unsigned
104 n_chars_width (struct outp_driver *d)
106 return d->width / d->fixed_width;
109 /* Writes the line S at the current position and advances to the next
112 write_line (struct outp_driver *d, const char *s)
114 struct outp_text text;
116 assert (d->cp_y + d->font_height <= d->length);
117 text.font = OUTP_FIXED;
118 text.justification = OUTP_LEFT;
119 text.string = ss_cstr (s);
122 text.h = text.v = INT_MAX;
123 d->class->text_draw (d, &text);
125 d->cp_y += d->font_height;
128 /* Parses and executes the LIST procedure. */
130 cmd_list (struct lexer *lexer, struct dataset *ds)
132 struct dictionary *dict = dataset_dict (ds);
133 struct variable *casenum_var = NULL;
134 struct casegrouper *grouper;
135 struct casereader *group;
139 if (!parse_list (lexer, ds, &cmd, NULL))
142 /* Fill in defaults. */
143 if (cmd.step == LONG_MIN)
145 if (cmd.first == LONG_MIN)
147 if (cmd.last == LONG_MIN)
149 if (!cmd.sbc_variables)
150 dict_get_vars (dict, &cmd.v_variables, &cmd.n_variables,
151 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
152 if (cmd.n_variables == 0)
154 msg (SE, _("No variables specified."));
158 /* Verify arguments. */
159 if (cmd.first > cmd.last)
162 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
163 "specified. The values will be swapped."), cmd.first, cmd.last);
165 cmd.first = cmd.last;
170 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
171 "being reset to 1."), cmd.first);
176 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
177 "being reset to 1."), cmd.last);
182 msg (SW, _("The step value %ld is less than 1. The value is being "
183 "reset to 1."), cmd.step);
187 /* Weighting variable. */
188 if (cmd.weight == LST_WEIGHT)
190 if (dict_get_weight (dict) != NULL)
194 for (i = 0; i < cmd.n_variables; i++)
195 if (cmd.v_variables[i] == dict_get_weight (dict))
197 if (i >= cmd.n_variables)
199 /* Add the weight variable to the end of the variable list. */
201 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
202 sizeof *cmd.v_variables);
203 cmd.v_variables[cmd.n_variables - 1]
204 = dict_get_weight (dict);
208 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
212 if (cmd.numbering == LST_NUMBERED)
214 /* Initialize the case-number variable. */
215 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
216 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
217 casenum_var = var_create ("Case#", 0);
218 var_set_both_formats (casenum_var, &format);
220 /* Add the weight variable at the beginning of the variable list. */
222 cmd.v_variables = xnrealloc (cmd.v_variables,
223 cmd.n_variables, sizeof *cmd.v_variables);
224 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
225 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
226 cmd.v_variables[0] = casenum_var;
232 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
233 casegrouper_get_next_group (grouper, &group);
234 casereader_destroy (group))
238 write_all_headers (group, ds);
239 for (; casereader_read (group, &c); case_destroy (&c))
242 if (case_idx >= cmd.first && case_idx <= cmd.last
243 && (case_idx - cmd.first) % cmd.step == 0)
244 list_case (&c, case_idx, ds);
247 ok = casegrouper_destroy (grouper);
248 ok = proc_commit (ds) && ok;
250 ds_destroy(&line_buffer);
254 var_destroy (casenum_var);
256 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
259 /* Writes headers to all devices. This is done at the beginning of
260 each SPLIT FILE group. */
262 write_all_headers (struct casereader *input, const struct dataset *ds)
264 struct outp_driver *d;
267 if (!casereader_peek (input, 0, &c))
269 output_split_file_values (ds, &c);
272 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
274 if (!d->class->special)
276 d->cp_y += d->font_height; /* Blank line. */
279 else if (d->class == &html_class)
281 struct html_driver_ext *x = d->ext;
283 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
288 for (i = 0; i < cmd.n_variables; i++)
289 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
290 var_get_name (cmd.v_variables[i]));
293 fputs (" </TR>\n", x->file);
300 /* Writes the headers. Some of them might be vertical; most are
301 probably horizontal. */
303 write_header (struct outp_driver *d)
305 struct list_ext *prc = d->prc;
307 if (!prc->header_rows)
310 if (n_lines_remaining (d) < prc->header_rows + 1)
313 assert (n_lines_remaining (d) >= prc->header_rows + 1);
316 /* Design the header. */
322 /* Allocate, initialize header. */
323 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
325 int w = n_chars_width (d);
326 for (i = 0; i < prc->header_rows; i++)
328 prc->header[i] = xmalloc (w + 1);
329 memset (prc->header[i], ' ', w);
333 /* Put in vertical names. */
334 for (i = x = 0; i < prc->n_vertical; i++)
336 const struct variable *v = cmd.v_variables[i];
337 const char *name = var_get_name (v);
338 size_t name_len = strlen (name);
339 const struct fmt_spec *print = var_get_print_format (v);
342 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
344 for (j = 0; j < name_len; j++)
345 prc->header[name_len - j - 1][x] = name[j];
349 /* Put in horizontal names. */
350 for (; i < cmd.n_variables; i++)
352 const struct variable *v = cmd.v_variables[i];
353 const char *name = var_get_name (v);
354 size_t name_len = strlen (name);
355 const struct fmt_spec *print = var_get_print_format (v);
357 memset (&prc->header[prc->header_rows - 1][x], '-',
358 MAX (print->w, (int) name_len));
359 if ((int) name_len < print->w)
360 x += print->w - name_len;
361 memcpy (&prc->header[0][x], name, name_len);
365 /* Add null bytes. */
366 for (i = 0; i < prc->header_rows; i++)
368 for (x = n_chars_width (d); x >= 1; x--)
369 if (prc->header[i][x - 1] != ' ')
371 prc->header[i][x] = 0;
378 /* Write out the header, in back-to-front order except for the last line. */
379 if (prc->header_rows >= 2)
383 for (i = prc->header_rows - 1; i-- != 0; )
384 write_line (d, prc->header[i]);
386 write_line (d, prc->header[prc->header_rows - 1]);
390 /* Frees up all the memory we've allocated. */
394 struct outp_driver *d;
396 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
397 if (d->class->special == 0)
399 struct list_ext *prc = d->prc;
404 for (i = 0; i < prc->header_rows; i++)
405 free (prc->header[i]);
410 else if (d->class == &html_class)
414 struct html_driver_ext *x = d->ext;
416 fputs ("</TABLE>\n", x->file);
422 free (cmd.v_variables);
425 /* Writes string STRING at the current position. If the text would
426 fall off the side of the page, then advance to the next line,
427 indenting by amount INDENT. */
429 write_varname (struct outp_driver *d, char *string, int indent)
431 struct outp_text text;
434 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
436 d->cp_y += d->font_height;
437 if (d->cp_y + d->font_height > d->length)
442 text.font = OUTP_FIXED;
443 text.justification = OUTP_LEFT;
444 text.string = ss_cstr (string);
447 text.h = text.v = INT_MAX;
448 d->class->text_draw (d, &text);
449 d->class->text_metrics (d, &text, &width, NULL);
453 /* When we can't fit all the values across the page, we write out all
454 the variable names just once. This is where we do it. */
456 write_fallback_headers (struct outp_driver *d)
458 const int max_width = n_chars_width(d) - 10;
464 const char *Line = _("Line");
465 char *leader = local_alloc (strlen (Line)
466 + INT_STRLEN_BOUND (line_number) + 1 + 1);
468 while (index < cmd.n_variables)
470 struct outp_text text;
473 /* Ensure that there is enough room for a line of text. */
474 if (d->cp_y + d->font_height > d->length)
477 /* The leader is a string like `Line 1: '. Write the leader. */
478 sprintf (leader, "%s %d:", Line, ++line_number);
479 text.font = OUTP_FIXED;
480 text.justification = OUTP_LEFT;
481 text.string = ss_cstr (leader);
484 text.h = text.v = INT_MAX;
485 d->class->text_draw (d, &text);
486 d->class->text_metrics (d, &text, &leader_width, NULL);
487 d->cp_x = leader_width;
496 int var_width = var_get_print_format (cmd.v_variables[index])->w;
497 if (width + var_width > max_width && width != 0)
501 d->cp_y += d->font_height;
508 char varname[LONG_NAME_LEN + 2];
509 snprintf (varname, sizeof varname,
510 " %s", var_get_name (cmd.v_variables[index]));
511 write_varname (d, varname, leader_width);
514 while (++index < cmd.n_variables);
518 d->cp_y += d->font_height;
523 /* There are three possible layouts for the LIST procedure:
525 1. If the values and their variables' name fit across the page,
526 then they are listed across the page in that way.
528 2. If the values can fit across the page, but not the variable
529 names, then as many variable names as necessary are printed
530 vertically to compensate.
532 3. If not even the values can fit across the page, the variable
533 names are listed just once, at the beginning, in a compact format,
534 and the values are listed with a variable name label at the
535 beginning of each line for easier reference.
537 This is complicated by the fact that we have to do all this for
538 every output driver, not just once. */
540 determine_layout (void)
542 struct outp_driver *d;
544 /* This is the largest page width of any driver, so we can tell what
545 size buffer to allocate. */
546 int largest_page_width = 0;
548 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
550 size_t column; /* Current column. */
551 int width; /* Accumulated width. */
552 int height; /* Height of vertical names. */
553 int max_width; /* Page width. */
555 struct list_ext *prc;
557 if (d->class == &html_class)
560 assert (d->class->special == 0);
564 max_width = n_chars_width (d);
565 largest_page_width = MAX (largest_page_width, max_width);
567 prc = d->prc = xmalloc (sizeof *prc);
573 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
575 const struct variable *v = cmd.v_variables[column];
576 int fmt_width = var_get_print_format (v)->w;
577 int name_len = strlen (var_get_name (v));
578 width += MAX (fmt_width, name_len);
580 if (width <= max_width)
582 prc->header_rows = 2;
587 for (width = cmd.n_variables - 1, height = 0, column = 0;
588 column < cmd.n_variables && width <= max_width;
591 const struct variable *v = cmd.v_variables[column];
592 int fmt_width = var_get_print_format (v)->w;
593 size_t name_len = strlen (var_get_name (v));
595 if (name_len > height)
599 /* If it fit then we need to determine how many labels can be
600 written horizontally. */
601 if (width <= max_width && height <= SHORT_NAME_LEN)
604 prc->n_vertical = SIZE_MAX;
606 for (column = cmd.n_variables; column-- != 0; )
608 const struct variable *v = cmd.v_variables[column];
609 int name_len = strlen (var_get_name (v));
610 int fmt_width = var_get_print_format (v)->w;
611 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
612 if (trial_width > max_width)
614 prc->n_vertical = column + 1;
619 assert (prc->n_vertical != SIZE_MAX);
621 prc->n_vertical = cmd.n_variables;
622 /* Finally determine the length of the headers. */
623 for (prc->header_rows = 0, column = 0;
624 column < prc->n_vertical;
627 const struct variable *var = cmd.v_variables[column];
628 size_t name_len = strlen (var_get_name (var));
629 prc->header_rows = MAX (prc->header_rows, name_len);
635 /* Otherwise use the ugly fallback listing format. */
637 prc->header_rows = 0;
639 d->cp_y += d->font_height;
640 write_fallback_headers (d);
641 d->cp_y += d->font_height;
644 ds_init_empty (&line_buffer);
647 /* Writes case C to output. */
649 list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
651 struct dictionary *dict = dataset_dict (ds);
652 struct outp_driver *d;
654 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
655 if (d->class->special == 0)
657 const struct list_ext *prc = d->prc;
658 const int max_width = n_chars_width (d);
661 if (!prc->header_rows)
663 ds_put_format(&line_buffer, "%8s: ",
664 var_get_name (cmd.v_variables[0]));
668 for (column = 0; column < cmd.n_variables; column++)
670 const struct variable *v = cmd.v_variables[column];
671 const struct fmt_spec *print = var_get_print_format (v);
674 if (prc->type == 0 && column >= prc->n_vertical)
676 int name_len = strlen (var_get_name (v));
677 width = MAX (name_len, print->w);
682 if (width + ds_length(&line_buffer) > max_width &&
683 ds_length(&line_buffer) != 0)
685 if (!n_lines_remaining (d))
691 write_line (d, ds_cstr (&line_buffer));
692 ds_clear(&line_buffer);
694 if (!prc->header_rows)
695 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
698 if (width > print->w)
699 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
701 if (fmt_is_string (print->type)
702 || dict_contains_var (dict, v))
704 data_out (case_data (c, v), print,
705 ds_put_uninit (&line_buffer, print->w));
709 union value case_idx_value;
710 case_idx_value.f = case_idx;
711 data_out (&case_idx_value, print,
712 ds_put_uninit (&line_buffer,print->w));
715 ds_put_char(&line_buffer, ' ');
718 if (!n_lines_remaining (d))
724 write_line (d, ds_cstr (&line_buffer));
725 ds_clear(&line_buffer);
727 else if (d->class == &html_class)
729 struct html_driver_ext *x = d->ext;
732 fputs (" <TR>\n", x->file);
734 for (column = 0; column < cmd.n_variables; column++)
736 const struct variable *v = cmd.v_variables[column];
737 const struct fmt_spec *print = var_get_print_format (v);
740 if (fmt_is_string (print->type)
741 || dict_contains_var (dict, v))
742 data_out (case_data (c, v), print, buf);
745 union value case_idx_value;
746 case_idx_value.f = case_idx;
747 data_out (&case_idx_value, print, buf);
750 fputs (" <TD>", x->file);
751 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
752 fputs ("</TD>\n", x->file);
755 fputs (" </TR>\n", x->file);