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/>. */
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/variable.h>
33 #include <language/command.h>
34 #include <language/dictionary/split-file.h>
35 #include <language/lexer/lexer.h>
36 #include <libpspp/compiler.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>
49 #define _(msgid) gettext (msgid)
55 *variables=varlist("PV_NO_SCRATCH");
56 cases=:from n:first,"%s>0"/by n:step,"%s>0"/ *to n:last,"%s>0";
57 +format=numbering:numbered/!unnumbered,
59 weight:weight/!noweight.
64 /* Layout for one output driver. */
67 int type; /* 0=Values and labels fit across the page. */
68 size_t n_vertical; /* Number of labels to list vertically. */
69 size_t header_rows; /* Number of header rows. */
70 char **header; /* The header itself. */
74 static struct cmd_list cmd;
77 static struct string line_buffer;
79 /* TTY-style output functions. */
80 static unsigned n_lines_remaining (struct outp_driver *d);
81 static unsigned n_chars_width (struct outp_driver *d);
82 static void write_line (struct outp_driver *d, const char *s);
84 /* Other functions. */
85 static void list_case (struct ccase *, casenumber case_idx,
86 const struct dataset *);
87 static void determine_layout (void);
88 static void clean_up (void);
89 static void write_header (struct outp_driver *);
90 static void write_all_headers (struct casereader *, const struct dataset*);
92 /* Returns the number of text lines that can fit on the remainder of
94 static inline unsigned
95 n_lines_remaining (struct outp_driver *d)
99 diff = d->length - d->cp_y;
100 return (diff > 0) ? (diff / d->font_height) : 0;
103 /* Returns the number of fixed-width character that can fit across the
105 static inline unsigned
106 n_chars_width (struct outp_driver *d)
108 return d->width / d->fixed_width;
111 /* Writes the line S at the current position and advances to the next
114 write_line (struct outp_driver *d, const char *s)
116 struct outp_text text;
118 assert (d->cp_y + d->font_height <= d->length);
119 text.font = OUTP_FIXED;
120 text.justification = OUTP_LEFT;
121 text.string = ss_cstr (s);
124 text.h = text.v = INT_MAX;
125 d->class->text_draw (d, &text);
127 d->cp_y += d->font_height;
130 /* Parses and executes the LIST procedure. */
132 cmd_list (struct lexer *lexer, struct dataset *ds)
134 struct dictionary *dict = dataset_dict (ds);
135 struct variable *casenum_var = NULL;
136 struct casegrouper *grouper;
137 struct casereader *group;
141 if (!parse_list (lexer, ds, &cmd, NULL))
144 /* Fill in defaults. */
145 if (cmd.step == LONG_MIN)
147 if (cmd.first == LONG_MIN)
149 if (cmd.last == LONG_MIN)
151 if (!cmd.sbc_variables)
152 dict_get_vars (dict, &cmd.v_variables, &cmd.n_variables,
153 (1u << DC_SYSTEM) | (1u << DC_SCRATCH));
154 if (cmd.n_variables == 0)
156 msg (SE, _("No variables specified."));
160 /* Verify arguments. */
161 if (cmd.first > cmd.last)
164 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
165 "specified. The values will be swapped."), cmd.first, cmd.last);
167 cmd.first = cmd.last;
172 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
173 "being reset to 1."), cmd.first);
178 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
179 "being reset to 1."), cmd.last);
184 msg (SW, _("The step value %ld is less than 1. The value is being "
185 "reset to 1."), cmd.step);
189 /* Weighting variable. */
190 if (cmd.weight == LST_WEIGHT)
192 if (dict_get_weight (dict) != NULL)
196 for (i = 0; i < cmd.n_variables; i++)
197 if (cmd.v_variables[i] == dict_get_weight (dict))
199 if (i >= cmd.n_variables)
201 /* Add the weight variable to the end of the variable list. */
203 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
204 sizeof *cmd.v_variables);
205 cmd.v_variables[cmd.n_variables - 1]
206 = dict_get_weight (dict);
210 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
214 if (cmd.numbering == LST_NUMBERED)
216 /* Initialize the case-number variable. */
217 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
218 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
219 casenum_var = var_create ("Case#", 0);
220 var_set_both_formats (casenum_var, &format);
222 /* Add the weight variable at the beginning of the variable list. */
224 cmd.v_variables = xnrealloc (cmd.v_variables,
225 cmd.n_variables, sizeof *cmd.v_variables);
226 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
227 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
228 cmd.v_variables[0] = casenum_var;
234 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
235 casegrouper_get_next_group (grouper, &group);
236 casereader_destroy (group))
240 write_all_headers (group, ds);
241 for (; casereader_read (group, &c); case_destroy (&c))
244 if (case_idx >= cmd.first && case_idx <= cmd.last
245 && (case_idx - cmd.first) % cmd.step == 0)
246 list_case (&c, case_idx, ds);
249 ok = casegrouper_destroy (grouper);
250 ok = proc_commit (ds) && ok;
252 ds_destroy(&line_buffer);
256 var_destroy (casenum_var);
258 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
261 /* Writes headers to all devices. This is done at the beginning of
262 each SPLIT FILE group. */
264 write_all_headers (struct casereader *input, const struct dataset *ds)
266 struct outp_driver *d;
269 if (!casereader_peek (input, 0, &c))
271 output_split_file_values (ds, &c);
274 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
276 if (!d->class->special)
278 d->cp_y += d->font_height; /* Blank line. */
281 else if (d->class == &html_class)
283 struct html_driver_ext *x = d->ext;
285 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
290 for (i = 0; i < cmd.n_variables; i++)
291 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
292 var_get_name (cmd.v_variables[i]));
295 fputs (" </TR>\n", x->file);
302 /* Writes the headers. Some of them might be vertical; most are
303 probably horizontal. */
305 write_header (struct outp_driver *d)
307 struct list_ext *prc = d->prc;
309 if (!prc->header_rows)
312 if (n_lines_remaining (d) < prc->header_rows + 1)
315 assert (n_lines_remaining (d) >= prc->header_rows + 1);
318 /* Design the header. */
324 /* Allocate, initialize header. */
325 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
327 int w = n_chars_width (d);
328 for (i = 0; i < prc->header_rows; i++)
330 prc->header[i] = xmalloc (w + 1);
331 memset (prc->header[i], ' ', w);
335 /* Put in vertical names. */
336 for (i = x = 0; i < prc->n_vertical; i++)
338 const struct variable *v = cmd.v_variables[i];
339 const char *name = var_get_name (v);
340 size_t name_len = strlen (name);
341 const struct fmt_spec *print = var_get_print_format (v);
344 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
346 for (j = 0; j < name_len; j++)
347 prc->header[name_len - j - 1][x] = name[j];
351 /* Put in horizontal names. */
352 for (; i < cmd.n_variables; i++)
354 const struct variable *v = cmd.v_variables[i];
355 const char *name = var_get_name (v);
356 size_t name_len = strlen (name);
357 const struct fmt_spec *print = var_get_print_format (v);
359 memset (&prc->header[prc->header_rows - 1][x], '-',
360 MAX (print->w, (int) name_len));
361 if ((int) name_len < print->w)
362 x += print->w - name_len;
363 memcpy (&prc->header[0][x], name, name_len);
367 /* Add null bytes. */
368 for (i = 0; i < prc->header_rows; i++)
370 for (x = n_chars_width (d); x >= 1; x--)
371 if (prc->header[i][x - 1] != ' ')
373 prc->header[i][x] = 0;
380 /* Write out the header, in back-to-front order except for the last line. */
381 if (prc->header_rows >= 2)
385 for (i = prc->header_rows - 1; i-- != 0; )
386 write_line (d, prc->header[i]);
388 write_line (d, prc->header[prc->header_rows - 1]);
392 /* Frees up all the memory we've allocated. */
396 struct outp_driver *d;
398 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
399 if (d->class->special == 0)
401 struct list_ext *prc = d->prc;
406 for (i = 0; i < prc->header_rows; i++)
407 free (prc->header[i]);
412 else if (d->class == &html_class)
416 struct html_driver_ext *x = d->ext;
418 fputs ("</TABLE>\n", x->file);
424 free (cmd.v_variables);
427 /* Writes string STRING at the current position. If the text would
428 fall off the side of the page, then advance to the next line,
429 indenting by amount INDENT. */
431 write_varname (struct outp_driver *d, char *string, int indent)
433 struct outp_text text;
436 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
438 d->cp_y += d->font_height;
439 if (d->cp_y + d->font_height > d->length)
444 text.font = OUTP_FIXED;
445 text.justification = OUTP_LEFT;
446 text.string = ss_cstr (string);
449 text.h = text.v = INT_MAX;
450 d->class->text_draw (d, &text);
451 d->class->text_metrics (d, &text, &width, NULL);
455 /* When we can't fit all the values across the page, we write out all
456 the variable names just once. This is where we do it. */
458 write_fallback_headers (struct outp_driver *d)
460 const int max_width = n_chars_width(d) - 10;
466 const char *Line = _("Line");
467 char *leader = xmalloca (strlen (Line)
468 + INT_STRLEN_BOUND (line_number) + 1 + 1);
470 while (index < cmd.n_variables)
472 struct outp_text text;
475 /* Ensure that there is enough room for a line of text. */
476 if (d->cp_y + d->font_height > d->length)
479 /* The leader is a string like `Line 1: '. Write the leader. */
480 sprintf (leader, "%s %d:", Line, ++line_number);
481 text.font = OUTP_FIXED;
482 text.justification = OUTP_LEFT;
483 text.string = ss_cstr (leader);
486 text.h = text.v = INT_MAX;
487 d->class->text_draw (d, &text);
488 d->class->text_metrics (d, &text, &leader_width, NULL);
489 d->cp_x = leader_width;
498 int var_width = var_get_print_format (cmd.v_variables[index])->w;
499 if (width + var_width > max_width && width != 0)
503 d->cp_y += d->font_height;
510 char varname[LONG_NAME_LEN + 2];
511 snprintf (varname, sizeof varname,
512 " %s", var_get_name (cmd.v_variables[index]));
513 write_varname (d, varname, leader_width);
516 while (++index < cmd.n_variables);
520 d->cp_y += d->font_height;
525 /* There are three possible layouts for the LIST procedure:
527 1. If the values and their variables' name fit across the page,
528 then they are listed across the page in that way.
530 2. If the values can fit across the page, but not the variable
531 names, then as many variable names as necessary are printed
532 vertically to compensate.
534 3. If not even the values can fit across the page, the variable
535 names are listed just once, at the beginning, in a compact format,
536 and the values are listed with a variable name label at the
537 beginning of each line for easier reference.
539 This is complicated by the fact that we have to do all this for
540 every output driver, not just once. */
542 determine_layout (void)
544 struct outp_driver *d;
546 /* This is the largest page width of any driver, so we can tell what
547 size buffer to allocate. */
548 int largest_page_width = 0;
550 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
552 size_t column; /* Current column. */
553 int width; /* Accumulated width. */
554 int height; /* Height of vertical names. */
555 int max_width; /* Page width. */
557 struct list_ext *prc;
559 if (d->class == &html_class)
562 assert (d->class->special == 0);
566 max_width = n_chars_width (d);
567 largest_page_width = MAX (largest_page_width, max_width);
569 prc = d->prc = xmalloc (sizeof *prc);
575 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
577 const struct variable *v = cmd.v_variables[column];
578 int fmt_width = var_get_print_format (v)->w;
579 int name_len = strlen (var_get_name (v));
580 width += MAX (fmt_width, name_len);
582 if (width <= max_width)
584 prc->header_rows = 2;
589 for (width = cmd.n_variables - 1, height = 0, column = 0;
590 column < cmd.n_variables && width <= max_width;
593 const struct variable *v = cmd.v_variables[column];
594 int fmt_width = var_get_print_format (v)->w;
595 size_t name_len = strlen (var_get_name (v));
597 if (name_len > height)
601 /* If it fit then we need to determine how many labels can be
602 written horizontally. */
603 if (width <= max_width && height <= SHORT_NAME_LEN)
606 prc->n_vertical = SIZE_MAX;
608 for (column = cmd.n_variables; column-- != 0; )
610 const struct variable *v = cmd.v_variables[column];
611 int name_len = strlen (var_get_name (v));
612 int fmt_width = var_get_print_format (v)->w;
613 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
614 if (trial_width > max_width)
616 prc->n_vertical = column + 1;
621 assert (prc->n_vertical != SIZE_MAX);
623 prc->n_vertical = cmd.n_variables;
624 /* Finally determine the length of the headers. */
625 for (prc->header_rows = 0, column = 0;
626 column < prc->n_vertical;
629 const struct variable *var = cmd.v_variables[column];
630 size_t name_len = strlen (var_get_name (var));
631 prc->header_rows = MAX (prc->header_rows, name_len);
637 /* Otherwise use the ugly fallback listing format. */
639 prc->header_rows = 0;
641 d->cp_y += d->font_height;
642 write_fallback_headers (d);
643 d->cp_y += d->font_height;
646 ds_init_empty (&line_buffer);
649 /* Writes case C to output. */
651 list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
653 struct dictionary *dict = dataset_dict (ds);
654 struct outp_driver *d;
656 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
657 if (d->class->special == 0)
659 const struct list_ext *prc = d->prc;
660 const int max_width = n_chars_width (d);
663 if (!prc->header_rows)
665 ds_put_format(&line_buffer, "%8s: ",
666 var_get_name (cmd.v_variables[0]));
670 for (column = 0; column < cmd.n_variables; column++)
672 const struct variable *v = cmd.v_variables[column];
673 const struct fmt_spec *print = var_get_print_format (v);
676 if (prc->type == 0 && column >= prc->n_vertical)
678 int name_len = strlen (var_get_name (v));
679 width = MAX (name_len, print->w);
684 if (width + ds_length(&line_buffer) > max_width &&
685 ds_length(&line_buffer) != 0)
687 if (!n_lines_remaining (d))
693 write_line (d, ds_cstr (&line_buffer));
694 ds_clear(&line_buffer);
696 if (!prc->header_rows)
697 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
700 if (width > print->w)
701 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
703 if (fmt_is_string (print->type)
704 || dict_contains_var (dict, v))
706 data_out (case_data (c, v), print,
707 ds_put_uninit (&line_buffer, print->w));
711 union value case_idx_value;
712 case_idx_value.f = case_idx;
713 data_out (&case_idx_value, print,
714 ds_put_uninit (&line_buffer,print->w));
717 ds_put_char(&line_buffer, ' ');
720 if (!n_lines_remaining (d))
726 write_line (d, ds_cstr (&line_buffer));
727 ds_clear(&line_buffer);
729 else if (d->class == &html_class)
731 struct html_driver_ext *x = d->ext;
734 fputs (" <TR>\n", x->file);
736 for (column = 0; column < cmd.n_variables; column++)
738 const struct variable *v = cmd.v_variables[column];
739 const struct fmt_spec *print = var_get_print_format (v);
742 if (fmt_is_string (print->type)
743 || dict_contains_var (dict, v))
744 data_out (case_data (c, v), print, buf);
747 union value case_idx_value;
748 case_idx_value.f = case_idx;
749 data_out (&case_idx_value, print, buf);
752 fputs (" <TD>", x->file);
753 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
754 fputs ("</TD>\n", x->file);
757 fputs (" </TR>\n", x->file);