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/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 (const 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 (; (c = casereader_read (group)) != NULL; case_unref (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 c = casereader_peek (input, 0);
273 output_split_file_values (ds, c);
276 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
278 if (!d->class->special)
280 d->cp_y += d->font_height; /* Blank line. */
283 else if (d->class == &html_class)
285 struct html_driver_ext *x = d->ext;
287 fputs ("<TABLE BORDER=1>\n <TR>\n", x->file);
292 for (i = 0; i < cmd.n_variables; i++)
293 fprintf (x->file, " <TH><EM>%s</EM></TH>\n",
294 var_get_name (cmd.v_variables[i]));
297 fputs (" </TR>\n", x->file);
304 /* Writes the headers. Some of them might be vertical; most are
305 probably horizontal. */
307 write_header (struct outp_driver *d)
309 struct list_ext *prc = d->prc;
311 if (!prc->header_rows)
314 if (n_lines_remaining (d) < prc->header_rows + 1)
317 assert (n_lines_remaining (d) >= prc->header_rows + 1);
320 /* Design the header. */
326 /* Allocate, initialize header. */
327 prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
329 int w = n_chars_width (d);
330 for (i = 0; i < prc->header_rows; i++)
332 prc->header[i] = xmalloc (w + 1);
333 memset (prc->header[i], ' ', w);
337 /* Put in vertical names. */
338 for (i = x = 0; i < prc->n_vertical; i++)
340 const struct variable *v = cmd.v_variables[i];
341 const char *name = var_get_name (v);
342 size_t name_len = strlen (name);
343 const struct fmt_spec *print = var_get_print_format (v);
346 memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
348 for (j = 0; j < name_len; j++)
349 prc->header[name_len - j - 1][x] = name[j];
353 /* Put in horizontal names. */
354 for (; i < cmd.n_variables; i++)
356 const struct variable *v = cmd.v_variables[i];
357 const char *name = var_get_name (v);
358 size_t name_len = strlen (name);
359 const struct fmt_spec *print = var_get_print_format (v);
361 memset (&prc->header[prc->header_rows - 1][x], '-',
362 MAX (print->w, (int) name_len));
363 if ((int) name_len < print->w)
364 x += print->w - name_len;
365 memcpy (&prc->header[0][x], name, name_len);
369 /* Add null bytes. */
370 for (i = 0; i < prc->header_rows; i++)
372 for (x = n_chars_width (d); x >= 1; x--)
373 if (prc->header[i][x - 1] != ' ')
375 prc->header[i][x] = 0;
382 /* Write out the header, in back-to-front order except for the last line. */
383 if (prc->header_rows >= 2)
387 for (i = prc->header_rows - 1; i-- != 0; )
388 write_line (d, prc->header[i]);
390 write_line (d, prc->header[prc->header_rows - 1]);
394 /* Frees up all the memory we've allocated. */
398 struct outp_driver *d;
400 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
401 if (d->class->special == 0)
403 struct list_ext *prc = d->prc;
408 for (i = 0; i < prc->header_rows; i++)
409 free (prc->header[i]);
414 else if (d->class == &html_class)
418 struct html_driver_ext *x = d->ext;
420 fputs ("</TABLE>\n", x->file);
426 free (cmd.v_variables);
429 /* Writes string STRING at the current position. If the text would
430 fall off the side of the page, then advance to the next line,
431 indenting by amount INDENT. */
433 write_varname (struct outp_driver *d, char *string, int indent)
435 struct outp_text text;
438 if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
440 d->cp_y += d->font_height;
441 if (d->cp_y + d->font_height > d->length)
446 text.font = OUTP_FIXED;
447 text.justification = OUTP_LEFT;
448 text.string = ss_cstr (string);
451 text.h = text.v = INT_MAX;
452 d->class->text_draw (d, &text);
453 d->class->text_metrics (d, &text, &width, NULL);
457 /* When we can't fit all the values across the page, we write out all
458 the variable names just once. This is where we do it. */
460 write_fallback_headers (struct outp_driver *d)
462 const int max_width = n_chars_width(d) - 10;
468 const char *Line = _("Line");
469 char *leader = xmalloca (strlen (Line)
470 + INT_STRLEN_BOUND (line_number) + 1 + 1);
472 while (index < cmd.n_variables)
474 struct outp_text text;
477 /* Ensure that there is enough room for a line of text. */
478 if (d->cp_y + d->font_height > d->length)
481 /* The leader is a string like `Line 1: '. Write the leader. */
482 sprintf (leader, "%s %d:", Line, ++line_number);
483 text.font = OUTP_FIXED;
484 text.justification = OUTP_LEFT;
485 text.string = ss_cstr (leader);
488 text.h = text.v = INT_MAX;
489 d->class->text_draw (d, &text);
490 d->class->text_metrics (d, &text, &leader_width, NULL);
491 d->cp_x = leader_width;
500 int var_width = var_get_print_format (cmd.v_variables[index])->w;
501 if (width + var_width > max_width && width != 0)
505 d->cp_y += d->font_height;
512 char varname[VAR_NAME_LEN + 2];
513 snprintf (varname, sizeof varname,
514 " %s", var_get_name (cmd.v_variables[index]));
515 write_varname (d, varname, leader_width);
518 while (++index < cmd.n_variables);
522 d->cp_y += d->font_height;
527 /* There are three possible layouts for the LIST procedure:
529 1. If the values and their variables' name fit across the page,
530 then they are listed across the page in that way.
532 2. If the values can fit across the page, but not the variable
533 names, then as many variable names as necessary are printed
534 vertically to compensate.
536 3. If not even the values can fit across the page, the variable
537 names are listed just once, at the beginning, in a compact format,
538 and the values are listed with a variable name label at the
539 beginning of each line for easier reference.
541 This is complicated by the fact that we have to do all this for
542 every output driver, not just once. */
544 determine_layout (void)
546 struct outp_driver *d;
548 /* This is the largest page width of any driver, so we can tell what
549 size buffer to allocate. */
550 int largest_page_width = 0;
552 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
554 size_t column; /* Current column. */
555 int width; /* Accumulated width. */
556 int height; /* Height of vertical names. */
557 int max_width; /* Page width. */
559 struct list_ext *prc;
561 if (d->class == &html_class)
564 assert (d->class->special == 0);
568 max_width = n_chars_width (d);
569 largest_page_width = MAX (largest_page_width, max_width);
571 prc = d->prc = xmalloc (sizeof *prc);
577 for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
579 const struct variable *v = cmd.v_variables[column];
580 int fmt_width = var_get_print_format (v)->w;
581 int name_len = strlen (var_get_name (v));
582 width += MAX (fmt_width, name_len);
584 if (width <= max_width)
586 prc->header_rows = 2;
591 for (width = cmd.n_variables - 1, height = 0, column = 0;
592 column < cmd.n_variables && width <= max_width;
595 const struct variable *v = cmd.v_variables[column];
596 int fmt_width = var_get_print_format (v)->w;
597 size_t name_len = strlen (var_get_name (v));
599 if (name_len > height)
603 /* If it fit then we need to determine how many labels can be
604 written horizontally. */
605 if (width <= max_width && height <= SHORT_NAME_LEN)
608 prc->n_vertical = SIZE_MAX;
610 for (column = cmd.n_variables; column-- != 0; )
612 const struct variable *v = cmd.v_variables[column];
613 int name_len = strlen (var_get_name (v));
614 int fmt_width = var_get_print_format (v)->w;
615 int trial_width = width - fmt_width + MAX (fmt_width, name_len);
616 if (trial_width > max_width)
618 prc->n_vertical = column + 1;
623 assert (prc->n_vertical != SIZE_MAX);
625 prc->n_vertical = cmd.n_variables;
626 /* Finally determine the length of the headers. */
627 for (prc->header_rows = 0, column = 0;
628 column < prc->n_vertical;
631 const struct variable *var = cmd.v_variables[column];
632 size_t name_len = strlen (var_get_name (var));
633 prc->header_rows = MAX (prc->header_rows, name_len);
639 /* Otherwise use the ugly fallback listing format. */
641 prc->header_rows = 0;
643 d->cp_y += d->font_height;
644 write_fallback_headers (d);
645 d->cp_y += d->font_height;
648 ds_init_empty (&line_buffer);
651 /* Writes case C to output. */
653 list_case (const struct ccase *c, casenumber case_idx,
654 const struct dataset *ds)
656 struct dictionary *dict = dataset_dict (ds);
657 struct outp_driver *d;
659 for (d = outp_drivers (NULL); d; d = outp_drivers (d))
660 if (d->class->special == 0)
662 const struct list_ext *prc = d->prc;
663 const int max_width = n_chars_width (d);
666 if (!prc->header_rows)
668 ds_put_format(&line_buffer, "%8s: ",
669 var_get_name (cmd.v_variables[0]));
673 for (column = 0; column < cmd.n_variables; column++)
675 const struct variable *v = cmd.v_variables[column];
676 const struct fmt_spec *print = var_get_print_format (v);
679 if (prc->type == 0 && column >= prc->n_vertical)
681 int name_len = strlen (var_get_name (v));
682 width = MAX (name_len, print->w);
687 if (width + ds_length(&line_buffer) > max_width &&
688 ds_length(&line_buffer) != 0)
690 if (!n_lines_remaining (d))
696 write_line (d, ds_cstr (&line_buffer));
697 ds_clear(&line_buffer);
699 if (!prc->header_rows)
700 ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
703 if (width > print->w)
704 ds_put_char_multiple(&line_buffer, ' ', width - print->w);
706 if (fmt_is_string (print->type)
707 || dict_contains_var (dict, v))
709 char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
710 ds_put_cstr (&line_buffer, s);
716 union value case_idx_value;
717 case_idx_value.f = case_idx;
718 s = data_out (&case_idx_value, dict_get_encoding (dict), print);
719 ds_put_cstr (&line_buffer, s);
723 ds_put_char (&line_buffer, ' ');
726 if (!n_lines_remaining (d))
732 write_line (d, ds_cstr (&line_buffer));
733 ds_clear(&line_buffer);
735 else if (d->class == &html_class)
737 struct html_driver_ext *x = d->ext;
740 fputs (" <TR>\n", x->file);
742 for (column = 0; column < cmd.n_variables; column++)
744 const struct variable *v = cmd.v_variables[column];
745 const struct fmt_spec *print = var_get_print_format (v);
748 if (fmt_is_string (print->type)
749 || dict_contains_var (dict, v))
750 s = data_out (case_data (c, v), dict_get_encoding (dict), print);
753 union value case_idx_value;
754 case_idx_value.f = case_idx;
755 s = data_out (&case_idx_value, dict_get_encoding (dict), print);
758 fputs (" <TD>", x->file);
759 html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
761 fputs ("</TD>\n", x->file);
764 fputs (" </TR>\n", x->file);