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/magic.h>
37 #include <libpspp/message.h>
38 #include <libpspp/message.h>
39 #include <libpspp/misc.h>
40 #include <output/htmlP.h>
41 #include <output/manager.h>
42 #include <output/output.h>
43 #include <output/table.h>
48 #define _(msgid) gettext (msgid)
54 *variables=varlist("PV_NO_SCRATCH");
55 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
56 +format=numbering:numbered/!unnumbered,
58 weight:weight/!noweight.
63 /* Layout for one output driver. */
66 int type; /* 0=Values and labels fit across the page. */
67 size_t n_vertical; /* Number of labels to list vertically. */
68 size_t header_rows; /* Number of header rows. */
69 char **header; /* The header itself. */
73 static struct cmd_list cmd;
76 static struct string line_buffer;
78 /* TTY-style output functions. */
79 static unsigned n_lines_remaining (struct outp_driver *d);
80 static unsigned n_chars_width (struct outp_driver *d);
81 static void write_line (struct outp_driver *d, const char *s);
83 /* Other functions. */
84 static void list_case (struct ccase *, casenumber case_idx,
85 const struct dataset *);
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 (struct casereader *, const struct dataset*);
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, const 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 text.string = ss_cstr (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. */
131 cmd_list (struct lexer *lexer, struct dataset *ds)
133 struct dictionary *dict = dataset_dict (ds);
134 struct variable *casenum_var = NULL;
135 struct casegrouper *grouper;
136 struct casereader *group;
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 (dict, &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 (dict) != NULL)
195 for (i = 0; i < cmd.n_variables; i++)
196 if (cmd.v_variables[i] == dict_get_weight (dict))
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 (dict);
209 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
213 if (cmd.numbering == LST_NUMBERED)
215 /* Initialize the case-number variable. */
216 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
217 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
218 casenum_var = var_create ("Case#", 0);
219 var_set_both_formats (casenum_var, &format);
221 /* Add the weight variable at the beginning of the variable list. */
223 cmd.v_variables = xnrealloc (cmd.v_variables,
224 cmd.n_variables, sizeof *cmd.v_variables);
225 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
226 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
227 cmd.v_variables[0] = casenum_var;
233 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
234 casegrouper_get_next_group (grouper, &group);
235 casereader_destroy (group))
239 write_all_headers (group, ds);
240 for (; casereader_read (group, &c); case_destroy (&c))
243 if (case_idx >= cmd.first && case_idx <= cmd.last
244 && (case_idx - cmd.first) % cmd.step == 0)
245 list_case (&c, case_idx, ds);
248 ok = casegrouper_destroy (grouper);
249 ok = proc_commit (ds) && ok;
251 ds_destroy(&line_buffer);
255 var_destroy (casenum_var);
257 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
260 /* Writes headers to all devices. This is done at the beginning of
261 each SPLIT FILE group. */
263 write_all_headers (struct casereader *input, const struct dataset *ds)
265 struct outp_driver *d;
268 if (!casereader_peek (input, 0, &c))
270 output_split_file_values (ds, &c);
273 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
275 if (!d->class->special)
277 d->cp_y += d->font_height; /* Blank line. */
280 else if (d->class == &html_class)
282 struct html_driver_ext *x = d->ext;
284 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
289 for (i = 0; i < cmd.n_variables; i++)
290 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
291 var_get_name (cmd.v_variables[i]));
294 fputs (" </TR>\n", x->file);
301 /* Writes the headers. Some of them might be vertical; most are
302 probably horizontal. */
304 write_header (struct outp_driver *d)
306 struct list_ext *prc = d->prc;
308 if (!prc->header_rows)
311 if (n_lines_remaining (d) < prc->header_rows + 1)
314 assert (n_lines_remaining (d) >= prc->header_rows + 1);
317 /* Design the header. */
323 /* Allocate, initialize header. */
324 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
326 int w = n_chars_width (d);
327 for (i = 0; i < prc->header_rows; i++)
329 prc->header[i] = xmalloc (w + 1);
330 memset (prc->header[i], ' ', w);
334 /* Put in vertical names. */
335 for (i = x = 0; i < prc->n_vertical; 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);
343 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
345 for (j = 0; j < name_len; j++)
346 prc->header[name_len - j - 1][x] = name[j];
350 /* Put in horizontal names. */
351 for (; i < cmd.n_variables; i++)
353 const struct variable *v = cmd.v_variables[i];
354 const char *name = var_get_name (v);
355 size_t name_len = strlen (name);
356 const struct fmt_spec *print = var_get_print_format (v);
358 memset (&prc->header[prc->header_rows - 1][x], '-',
359 MAX (print->w, (int) name_len));
360 if ((int) name_len < print->w)
361 x += print->w - name_len;
362 memcpy (&prc->header[0][x], name, name_len);
366 /* Add null bytes. */
367 for (i = 0; i < prc->header_rows; i++)
369 for (x = n_chars_width (d); x >= 1; x--)
370 if (prc->header[i][x - 1] != ' ')
372 prc->header[i][x] = 0;
379 /* Write out the header, in back-to-front order except for the last line. */
380 if (prc->header_rows >= 2)
384 for (i = prc->header_rows - 1; i-- != 0; )
385 write_line (d, prc->header[i]);
387 write_line (d, prc->header[prc->header_rows - 1]);
391 /* Frees up all the memory we've allocated. */
395 struct outp_driver *d;
397 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
398 if (d->class->special == 0)
400 struct list_ext *prc = d->prc;
405 for (i = 0; i < prc->header_rows; i++)
406 free (prc->header[i]);
411 else if (d->class == &html_class)
415 struct html_driver_ext *x = d->ext;
417 fputs ("</TABLE>\n", x->file);
423 free (cmd.v_variables);
426 /* Writes string STRING at the current position. If the text would
427 fall off the side of the page, then advance to the next line,
428 indenting by amount INDENT. */
430 write_varname (struct outp_driver *d, char *string, int indent)
432 struct outp_text text;
435 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
437 d->cp_y += d->font_height;
438 if (d->cp_y + d->font_height > d->length)
443 text.font = OUTP_FIXED;
444 text.justification = OUTP_LEFT;
445 text.string = ss_cstr (string);
448 text.h = text.v = INT_MAX;
449 d->class->text_draw (d, &text);
450 d->class->text_metrics (d, &text, &width, NULL);
454 /* When we can't fit all the values across the page, we write out all
455 the variable names just once. This is where we do it. */
457 write_fallback_headers (struct outp_driver *d)
459 const int max_width = n_chars_width(d) - 10;
465 const char *Line = _("Line");
466 char *leader = local_alloc (strlen (Line)
467 + INT_STRLEN_BOUND (line_number) + 1 + 1);
469 while (index < cmd.n_variables)
471 struct outp_text text;
474 /* Ensure that there is enough room for a line of text. */
475 if (d->cp_y + d->font_height > d->length)
478 /* The leader is a string like `Line 1: '. Write the leader. */
479 sprintf (leader, "%s %d:", Line, ++line_number);
480 text.font = OUTP_FIXED;
481 text.justification = OUTP_LEFT;
482 text.string = ss_cstr (leader);
485 text.h = text.v = INT_MAX;
486 d->class->text_draw (d, &text);
487 d->class->text_metrics (d, &text, &leader_width, NULL);
488 d->cp_x = leader_width;
497 int var_width = var_get_print_format (cmd.v_variables[index])->w;
498 if (width + var_width > max_width && width != 0)
502 d->cp_y += d->font_height;
509 char varname[LONG_NAME_LEN + 2];
510 snprintf (varname, sizeof varname,
511 " %s", var_get_name (cmd.v_variables[index]));
512 write_varname (d, varname, leader_width);
515 while (++index < cmd.n_variables);
519 d->cp_y += d->font_height;
524 /* There are three possible layouts for the LIST procedure:
526 1. If the values and their variables' name fit across the page,
527 then they are listed across the page in that way.
529 2. If the values can fit across the page, but not the variable
530 names, then as many variable names as necessary are printed
531 vertically to compensate.
533 3. If not even the values can fit across the page, the variable
534 names are listed just once, at the beginning, in a compact format,
535 and the values are listed with a variable name label at the
536 beginning of each line for easier reference.
538 This is complicated by the fact that we have to do all this for
539 every output driver, not just once. */
541 determine_layout (void)
543 struct outp_driver *d;
545 /* This is the largest page width of any driver, so we can tell what
546 size buffer to allocate. */
547 int largest_page_width = 0;
549 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
551 size_t column; /* Current column. */
552 int width; /* Accumulated width. */
553 int height; /* Height of vertical names. */
554 int max_width; /* Page width. */
556 struct list_ext *prc;
558 if (d->class == &html_class)
561 assert (d->class->special == 0);
565 max_width = n_chars_width (d);
566 largest_page_width = MAX (largest_page_width, max_width);
568 prc = d->prc = xmalloc (sizeof *prc);
574 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
576 const struct variable *v = cmd.v_variables[column];
577 int fmt_width = var_get_print_format (v)->w;
578 int name_len = strlen (var_get_name (v));
579 width += MAX (fmt_width, name_len);
581 if (width <= max_width)
583 prc->header_rows = 2;
588 for (width = cmd.n_variables - 1, height = 0, column = 0;
589 column < cmd.n_variables && width <= max_width;
592 const struct variable *v = cmd.v_variables[column];
593 int fmt_width = var_get_print_format (v)->w;
594 size_t name_len = strlen (var_get_name (v));
596 if (name_len > height)
600 /* If it fit then we need to determine how many labels can be
601 written horizontally. */
602 if (width <= max_width && height <= SHORT_NAME_LEN)
605 prc->n_vertical = SIZE_MAX;
607 for (column = cmd.n_variables; column-- != 0; )
609 const struct variable *v = cmd.v_variables[column];
610 int name_len = strlen (var_get_name (v));
611 int fmt_width = var_get_print_format (v)->w;
612 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
613 if (trial_width > max_width)
615 prc->n_vertical = column + 1;
620 assert (prc->n_vertical != SIZE_MAX);
622 prc->n_vertical = cmd.n_variables;
623 /* Finally determine the length of the headers. */
624 for (prc->header_rows = 0, column = 0;
625 column < prc->n_vertical;
628 const struct variable *var = cmd.v_variables[column];
629 size_t name_len = strlen (var_get_name (var));
630 prc->header_rows = MAX (prc->header_rows, name_len);
636 /* Otherwise use the ugly fallback listing format. */
638 prc->header_rows = 0;
640 d->cp_y += d->font_height;
641 write_fallback_headers (d);
642 d->cp_y += d->font_height;
645 ds_init_empty (&line_buffer);
648 /* Writes case C to output. */
650 list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
652 struct dictionary *dict = dataset_dict (ds);
653 struct outp_driver *d;
655 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
656 if (d->class->special == 0)
658 const struct list_ext *prc = d->prc;
659 const int max_width = n_chars_width (d);
662 if (!prc->header_rows)
664 ds_put_format(&line_buffer, "%8s: ",
665 var_get_name (cmd.v_variables[0]));
669 for (column = 0; column < cmd.n_variables; column++)
671 const struct variable *v = cmd.v_variables[column];
672 const struct fmt_spec *print = var_get_print_format (v);
675 if (prc->type == 0 && column >= prc->n_vertical)
677 int name_len = strlen (var_get_name (v));
678 width = MAX (name_len, print->w);
683 if (width + ds_length(&line_buffer) > max_width &&
684 ds_length(&line_buffer) != 0)
686 if (!n_lines_remaining (d))
692 write_line (d, ds_cstr (&line_buffer));
693 ds_clear(&line_buffer);
695 if (!prc->header_rows)
696 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
699 if (width > print->w)
700 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
702 if (fmt_is_string (print->type)
703 || dict_contains_var (dict, v))
705 data_out (case_data (c, v), print,
706 ds_put_uninit (&line_buffer, print->w));
710 union value case_idx_value;
711 case_idx_value.f = case_idx;
712 data_out (&case_idx_value, print,
713 ds_put_uninit (&line_buffer,print->w));
716 ds_put_char(&line_buffer, ' ');
719 if (!n_lines_remaining (d))
725 write_line (d, ds_cstr (&line_buffer));
726 ds_clear(&line_buffer);
728 else if (d->class == &html_class)
730 struct html_driver_ext *x = d->ext;
733 fputs (" <TR>\n", x->file);
735 for (column = 0; column < cmd.n_variables; column++)
737 const struct variable *v = cmd.v_variables[column];
738 const struct fmt_spec *print = var_get_print_format (v);
741 if (fmt_is_string (print->type)
742 || dict_contains_var (dict, v))
743 data_out (case_data (c, v), print, buf);
746 union value case_idx_value;
747 case_idx_value.f = case_idx;
748 data_out (&case_idx_value, print, buf);
751 fputs (" <TD>", x->file);
752 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
753 fputs ("</TD>\n", x->file);
756 fputs (" </TR>\n", x->file);