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/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/message.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. */
68 int type; /* 0=Values and labels fit across the page. */
69 size_t n_vertical; /* Number of labels to list vertically. */
70 size_t header_rows; /* Number of header rows. */
71 char **header; /* The header itself. */
75 static struct cmd_list cmd;
78 static struct string line_buffer;
80 /* TTY-style output functions. */
81 static unsigned n_lines_remaining (struct outp_driver *d);
82 static unsigned n_chars_width (struct outp_driver *d);
83 static void write_line (struct outp_driver *d, const char *s);
85 /* Other functions. */
86 static void list_case (struct ccase *, casenumber case_idx,
87 const struct dataset *);
88 static void determine_layout (void);
89 static void clean_up (void);
90 static void write_header (struct outp_driver *);
91 static void write_all_headers (struct casereader *, const struct dataset*);
93 /* Returns the number of text lines that can fit on the remainder of
95 static inline unsigned
96 n_lines_remaining (struct outp_driver *d)
100 diff = d->length - d->cp_y;
101 return (diff > 0) ? (diff / d->font_height) : 0;
104 /* Returns the number of fixed-width character that can fit across the
106 static inline unsigned
107 n_chars_width (struct outp_driver *d)
109 return d->width / d->fixed_width;
112 /* Writes the line S at the current position and advances to the next
115 write_line (struct outp_driver *d, const char *s)
117 struct outp_text text;
119 assert (d->cp_y + d->font_height <= d->length);
120 text.font = OUTP_FIXED;
121 text.justification = OUTP_LEFT;
122 text.string = ss_cstr (s);
125 text.h = text.v = INT_MAX;
126 d->class->text_draw (d, &text);
128 d->cp_y += d->font_height;
131 /* Parses and executes the LIST procedure. */
133 cmd_list (struct lexer *lexer, struct dataset *ds)
135 struct dictionary *dict = dataset_dict (ds);
136 struct variable *casenum_var = NULL;
137 struct casegrouper *grouper;
138 struct casereader *group;
142 if (!parse_list (lexer, ds, &cmd, NULL))
145 /* Fill in defaults. */
146 if (cmd.step == LONG_MIN)
148 if (cmd.first == LONG_MIN)
150 if (cmd.last == LONG_MIN)
152 if (!cmd.sbc_variables)
153 dict_get_vars (dict, &cmd.v_variables, &cmd.n_variables,
154 DC_SYSTEM | DC_SCRATCH);
155 if (cmd.n_variables == 0)
157 msg (SE, _("No variables specified."));
161 /* Verify arguments. */
162 if (cmd.first > cmd.last)
165 msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
166 "specified. The values will be swapped."), cmd.first, cmd.last);
168 cmd.first = cmd.last;
173 msg (SW, _("The first case (%ld) to list is less than 1. The value is "
174 "being reset to 1."), cmd.first);
179 msg (SW, _("The last case (%ld) to list is less than 1. The value is "
180 "being reset to 1."), cmd.last);
185 msg (SW, _("The step value %ld is less than 1. The value is being "
186 "reset to 1."), cmd.step);
190 /* Weighting variable. */
191 if (cmd.weight == LST_WEIGHT)
193 if (dict_get_weight (dict) != NULL)
197 for (i = 0; i < cmd.n_variables; i++)
198 if (cmd.v_variables[i] == dict_get_weight (dict))
200 if (i >= cmd.n_variables)
202 /* Add the weight variable to the end of the variable list. */
204 cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
205 sizeof *cmd.v_variables);
206 cmd.v_variables[cmd.n_variables - 1]
207 = dict_get_weight (dict);
211 msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
215 if (cmd.numbering == LST_NUMBERED)
217 /* Initialize the case-number variable. */
218 int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
219 struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
220 casenum_var = var_create ("Case#", 0);
221 var_set_both_formats (casenum_var, &format);
223 /* Add the weight variable at the beginning of the variable list. */
225 cmd.v_variables = xnrealloc (cmd.v_variables,
226 cmd.n_variables, sizeof *cmd.v_variables);
227 memmove (&cmd.v_variables[1], &cmd.v_variables[0],
228 (cmd.n_variables - 1) * sizeof *cmd.v_variables);
229 cmd.v_variables[0] = casenum_var;
235 for (grouper = casegrouper_create_splits (proc_open (ds), dict);
236 casegrouper_get_next_group (grouper, &group);
237 casereader_destroy (group))
241 write_all_headers (group, ds);
242 for (; casereader_read (group, &c); case_destroy (&c))
245 if (case_idx >= cmd.first && case_idx <= cmd.last
246 && (case_idx - cmd.first) % cmd.step == 0)
247 list_case (&c, case_idx, ds);
250 ok = casegrouper_destroy (grouper);
251 ok = proc_commit (ds) && ok;
253 ds_destroy(&line_buffer);
257 var_destroy (casenum_var);
259 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
262 /* Writes headers to all devices. This is done at the beginning of
263 each SPLIT FILE group. */
265 write_all_headers (struct casereader *input, const struct dataset *ds)
267 struct outp_driver *d;
270 if (!casereader_peek (input, 0, &c))
272 output_split_file_values (ds, &c);
275 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
277 if (!d->class->special)
279 d->cp_y += d->font_height; /* Blank line. */
282 else if (d->class == &html_class)
284 struct html_driver_ext *x = d->ext;
286 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
291 for (i = 0; i < cmd.n_variables; i++)
292 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
293 var_get_name (cmd.v_variables[i]));
296 fputs (" </TR>\n", x->file);
303 /* Writes the headers. Some of them might be vertical; most are
304 probably horizontal. */
306 write_header (struct outp_driver *d)
308 struct list_ext *prc = d->prc;
310 if (!prc->header_rows)
313 if (n_lines_remaining (d) < prc->header_rows + 1)
316 assert (n_lines_remaining (d) >= prc->header_rows + 1);
319 /* Design the header. */
325 /* Allocate, initialize header. */
326 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
328 int w = n_chars_width (d);
329 for (i = 0; i < prc->header_rows; i++)
331 prc->header[i] = xmalloc (w + 1);
332 memset (prc->header[i], ' ', w);
336 /* Put in vertical names. */
337 for (i = x = 0; i < prc->n_vertical; i++)
339 const struct variable *v = cmd.v_variables[i];
340 const char *name = var_get_name (v);
341 size_t name_len = strlen (name);
342 const struct fmt_spec *print = var_get_print_format (v);
345 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
347 for (j = 0; j < name_len; j++)
348 prc->header[name_len - j - 1][x] = name[j];
352 /* Put in horizontal names. */
353 for (; i < cmd.n_variables; i++)
355 const struct variable *v = cmd.v_variables[i];
356 const char *name = var_get_name (v);
357 size_t name_len = strlen (name);
358 const struct fmt_spec *print = var_get_print_format (v);
360 memset (&prc->header[prc->header_rows - 1][x], '-',
361 MAX (print->w, (int) name_len));
362 if ((int) name_len < print->w)
363 x += print->w - name_len;
364 memcpy (&prc->header[0][x], name, name_len);
368 /* Add null bytes. */
369 for (i = 0; i < prc->header_rows; i++)
371 for (x = n_chars_width (d); x >= 1; x--)
372 if (prc->header[i][x - 1] != ' ')
374 prc->header[i][x] = 0;
381 /* Write out the header, in back-to-front order except for the last line. */
382 if (prc->header_rows >= 2)
386 for (i = prc->header_rows - 1; i-- != 0; )
387 write_line (d, prc->header[i]);
389 write_line (d, prc->header[prc->header_rows - 1]);
393 /* Frees up all the memory we've allocated. */
397 struct outp_driver *d;
399 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
400 if (d->class->special == 0)
402 struct list_ext *prc = d->prc;
407 for (i = 0; i < prc->header_rows; i++)
408 free (prc->header[i]);
413 else if (d->class == &html_class)
417 struct html_driver_ext *x = d->ext;
419 fputs ("</TABLE>\n", x->file);
425 free (cmd.v_variables);
428 /* Writes string STRING at the current position. If the text would
429 fall off the side of the page, then advance to the next line,
430 indenting by amount INDENT. */
432 write_varname (struct outp_driver *d, char *string, int indent)
434 struct outp_text text;
437 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
439 d->cp_y += d->font_height;
440 if (d->cp_y + d->font_height > d->length)
445 text.font = OUTP_FIXED;
446 text.justification = OUTP_LEFT;
447 text.string = ss_cstr (string);
450 text.h = text.v = INT_MAX;
451 d->class->text_draw (d, &text);
452 d->class->text_metrics (d, &text, &width, NULL);
456 /* When we can't fit all the values across the page, we write out all
457 the variable names just once. This is where we do it. */
459 write_fallback_headers (struct outp_driver *d)
461 const int max_width = n_chars_width(d) - 10;
467 const char *Line = _("Line");
468 char *leader = xmalloca (strlen (Line)
469 + INT_STRLEN_BOUND (line_number) + 1 + 1);
471 while (index < cmd.n_variables)
473 struct outp_text text;
476 /* Ensure that there is enough room for a line of text. */
477 if (d->cp_y + d->font_height > d->length)
480 /* The leader is a string like `Line 1: '. Write the leader. */
481 sprintf (leader, "%s %d:", Line, ++line_number);
482 text.font = OUTP_FIXED;
483 text.justification = OUTP_LEFT;
484 text.string = ss_cstr (leader);
487 text.h = text.v = INT_MAX;
488 d->class->text_draw (d, &text);
489 d->class->text_metrics (d, &text, &leader_width, NULL);
490 d->cp_x = leader_width;
499 int var_width = var_get_print_format (cmd.v_variables[index])->w;
500 if (width + var_width > max_width && width != 0)
504 d->cp_y += d->font_height;
511 char varname[VAR_NAME_LEN + 2];
512 snprintf (varname, sizeof varname,
513 " %s", var_get_name (cmd.v_variables[index]));
514 write_varname (d, varname, leader_width);
517 while (++index < cmd.n_variables);
521 d->cp_y += d->font_height;
526 /* There are three possible layouts for the LIST procedure:
528 1. If the values and their variables' name fit across the page,
529 then they are listed across the page in that way.
531 2. If the values can fit across the page, but not the variable
532 names, then as many variable names as necessary are printed
533 vertically to compensate.
535 3. If not even the values can fit across the page, the variable
536 names are listed just once, at the beginning, in a compact format,
537 and the values are listed with a variable name label at the
538 beginning of each line for easier reference.
540 This is complicated by the fact that we have to do all this for
541 every output driver, not just once. */
543 determine_layout (void)
545 struct outp_driver *d;
547 /* This is the largest page width of any driver, so we can tell what
548 size buffer to allocate. */
549 int largest_page_width = 0;
551 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
553 size_t column; /* Current column. */
554 int width; /* Accumulated width. */
555 int height; /* Height of vertical names. */
556 int max_width; /* Page width. */
558 struct list_ext *prc;
560 if (d->class == &html_class)
563 assert (d->class->special == 0);
567 max_width = n_chars_width (d);
568 largest_page_width = MAX (largest_page_width, max_width);
570 prc = d->prc = xmalloc (sizeof *prc);
576 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
578 const struct variable *v = cmd.v_variables[column];
579 int fmt_width = var_get_print_format (v)->w;
580 int name_len = strlen (var_get_name (v));
581 width += MAX (fmt_width, name_len);
583 if (width <= max_width)
585 prc->header_rows = 2;
590 for (width = cmd.n_variables - 1, height = 0, column = 0;
591 column < cmd.n_variables && width <= max_width;
594 const struct variable *v = cmd.v_variables[column];
595 int fmt_width = var_get_print_format (v)->w;
596 size_t name_len = strlen (var_get_name (v));
598 if (name_len > height)
602 /* If it fit then we need to determine how many labels can be
603 written horizontally. */
604 if (width <= max_width && height <= SHORT_NAME_LEN)
607 prc->n_vertical = SIZE_MAX;
609 for (column = cmd.n_variables; column-- != 0; )
611 const struct variable *v = cmd.v_variables[column];
612 int name_len = strlen (var_get_name (v));
613 int fmt_width = var_get_print_format (v)->w;
614 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
615 if (trial_width > max_width)
617 prc->n_vertical = column + 1;
622 assert (prc->n_vertical != SIZE_MAX);
624 prc->n_vertical = cmd.n_variables;
625 /* Finally determine the length of the headers. */
626 for (prc->header_rows = 0, column = 0;
627 column < prc->n_vertical;
630 const struct variable *var = cmd.v_variables[column];
631 size_t name_len = strlen (var_get_name (var));
632 prc->header_rows = MAX (prc->header_rows, name_len);
638 /* Otherwise use the ugly fallback listing format. */
640 prc->header_rows = 0;
642 d->cp_y += d->font_height;
643 write_fallback_headers (d);
644 d->cp_y += d->font_height;
647 ds_init_empty (&line_buffer);
650 /* Writes case C to output. */
652 list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
654 struct dictionary *dict = dataset_dict (ds);
655 struct outp_driver *d;
657 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
658 if (d->class->special == 0)
660 const struct list_ext *prc = d->prc;
661 const int max_width = n_chars_width (d);
664 if (!prc->header_rows)
666 ds_put_format(&line_buffer, "%8s: ",
667 var_get_name (cmd.v_variables[0]));
671 for (column = 0; column < cmd.n_variables; column++)
673 const struct variable *v = cmd.v_variables[column];
674 const struct fmt_spec *print = var_get_print_format (v);
677 if (prc->type == 0 && column >= prc->n_vertical)
679 int name_len = strlen (var_get_name (v));
680 width = MAX (name_len, print->w);
685 if (width + ds_length(&line_buffer) > max_width &&
686 ds_length(&line_buffer) != 0)
688 if (!n_lines_remaining (d))
694 write_line (d, ds_cstr (&line_buffer));
695 ds_clear(&line_buffer);
697 if (!prc->header_rows)
698 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
701 if (width > print->w)
702 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
704 if (fmt_is_string (print->type)
705 || dict_contains_var (dict, v))
707 data_out (case_data (c, v), print,
708 ds_put_uninit (&line_buffer, print->w));
712 union value case_idx_value;
713 case_idx_value.f = case_idx;
714 data_out (&case_idx_value, print,
715 ds_put_uninit (&line_buffer,print->w));
718 ds_put_char(&line_buffer, ' ');
721 if (!n_lines_remaining (d))
727 write_line (d, ds_cstr (&line_buffer));
728 ds_clear(&line_buffer);
730 else if (d->class == &html_class)
732 struct html_driver_ext *x = d->ext;
735 fputs (" <TR>\n", x->file);
737 for (column = 0; column < cmd.n_variables; column++)
739 const struct variable *v = cmd.v_variables[column];
740 const struct fmt_spec *print = var_get_print_format (v);
743 if (fmt_is_string (print->type)
744 || dict_contains_var (dict, v))
745 data_out (case_data (c, v), print, buf);
748 union value case_idx_value;
749 case_idx_value.f = case_idx;
750 data_out (&case_idx_value, print, buf);
753 fputs (" <TD>", x->file);
754 html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
755 fputs ("</TD>\n", x->file);
758 fputs (" </TR>\n", x->file);