data_out function to dynamically allocate return value.
[pspp-builds.git] / src / language / data-io / list.q
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "intprops.h"
24 #include "xmalloca.h"
25
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>
45
46 #include "minmax.h"
47 #include "xalloc.h"
48
49 #include "gettext.h"
50 #define _(msgid) gettext (msgid)
51
52 /* (headers) */
53
54 /* (specification)
55    list (lst_):
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,
59              wrap:!wrap/single,
60              weight:weight/!noweight.
61 */
62 /* (declarations) */
63 /* (functions) */
64
65 /* Layout for one output driver. */
66 struct list_ext
67   {
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. */
72   };
73
74 /* Parsed command. */
75 static struct cmd_list cmd;
76
77 /* Line buffer. */
78 static struct string line_buffer;
79
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);
84
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*);
92
93 /* Returns the number of text lines that can fit on the remainder of
94    the page. */
95 static inline unsigned
96 n_lines_remaining (struct outp_driver *d)
97 {
98   int diff;
99
100   diff = d->length - d->cp_y;
101   return (diff > 0) ? (diff / d->font_height) : 0;
102 }
103
104 /* Returns the number of fixed-width character that can fit across the
105    page. */
106 static inline unsigned
107 n_chars_width (struct outp_driver *d)
108 {
109   return d->width / d->fixed_width;
110 }
111
112 /* Writes the line S at the current position and advances to the next
113    line.  */
114 static void
115 write_line (struct outp_driver *d, const char *s)
116 {
117   struct outp_text text;
118
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);
123   text.x = d->cp_x;
124   text.y = d->cp_y;
125   text.h = text.v = INT_MAX;
126   d->class->text_draw (d, &text);
127   d->cp_x = 0;
128   d->cp_y += d->font_height;
129 }
130
131 /* Parses and executes the LIST procedure. */
132 int
133 cmd_list (struct lexer *lexer, struct dataset *ds)
134 {
135   struct dictionary *dict = dataset_dict (ds);
136   struct variable *casenum_var = NULL;
137   struct casegrouper *grouper;
138   struct casereader *group;
139   casenumber case_idx;
140   bool ok;
141
142   if (!parse_list (lexer, ds, &cmd, NULL))
143     return CMD_FAILURE;
144
145   /* Fill in defaults. */
146   if (cmd.step == LONG_MIN)
147     cmd.step = 1;
148   if (cmd.first == LONG_MIN)
149     cmd.first = 1;
150   if (cmd.last == LONG_MIN)
151     cmd.last = LONG_MAX;
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)
156     {
157       msg (SE, _("No variables specified."));
158       return CMD_FAILURE;
159     }
160
161   /* Verify arguments. */
162   if (cmd.first > cmd.last)
163     {
164       int t;
165       msg (SW, _("The first case (%ld) specified precedes the last case (%ld) "
166            "specified.  The values will be swapped."), cmd.first, cmd.last);
167       t = cmd.first;
168       cmd.first = cmd.last;
169       cmd.last = t;
170     }
171   if (cmd.first < 1)
172     {
173       msg (SW, _("The first case (%ld) to list is less than 1.  The value is "
174            "being reset to 1."), cmd.first);
175       cmd.first = 1;
176     }
177   if (cmd.last < 1)
178     {
179       msg (SW, _("The last case (%ld) to list is less than 1.  The value is "
180            "being reset to 1."), cmd.last);
181       cmd.last = 1;
182     }
183   if (cmd.step < 1)
184     {
185       msg (SW, _("The step value %ld is less than 1.  The value is being "
186            "reset to 1."), cmd.step);
187       cmd.step = 1;
188     }
189
190   /* Weighting variable. */
191   if (cmd.weight == LST_WEIGHT)
192     {
193       if (dict_get_weight (dict) != NULL)
194         {
195           size_t i;
196
197           for (i = 0; i < cmd.n_variables; i++)
198             if (cmd.v_variables[i] == dict_get_weight (dict))
199               break;
200           if (i >= cmd.n_variables)
201             {
202               /* Add the weight variable to the end of the variable list. */
203               cmd.n_variables++;
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);
208             }
209         }
210       else
211         msg (SW, _("`/FORMAT WEIGHT' specified, but weighting is not on."));
212     }
213
214   /* Case number. */
215   if (cmd.numbering == LST_NUMBERED)
216     {
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);
222
223       /* Add the weight variable at the beginning of the variable list. */
224       cmd.n_variables++;
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;
230     }
231
232   determine_layout ();
233
234   case_idx = 0;
235   for (grouper = casegrouper_create_splits (proc_open (ds), dict);
236        casegrouper_get_next_group (grouper, &group);
237        casereader_destroy (group))
238     {
239       struct ccase *c;
240
241       write_all_headers (group, ds);
242       for (; (c = casereader_read (group)) != NULL; case_unref (c))
243         {
244           case_idx++;
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);
248         }
249     }
250   ok = casegrouper_destroy (grouper);
251   ok = proc_commit (ds) && ok;
252
253   ds_destroy(&line_buffer);
254
255   clean_up ();
256
257   var_destroy (casenum_var);
258
259   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
260 }
261
262 /* Writes headers to all devices.  This is done at the beginning of
263    each SPLIT FILE group. */
264 static void
265 write_all_headers (struct casereader *input, const struct dataset *ds)
266 {
267   struct outp_driver *d;
268   struct ccase *c;
269
270   c = casereader_peek (input, 0);
271   if (c == NULL)
272     return;
273   output_split_file_values (ds, c);
274   case_unref (c);
275
276   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
277     {
278       if (!d->class->special)
279         {
280           d->cp_y += d->font_height;            /* Blank line. */
281           write_header (d);
282         }
283       else if (d->class == &html_class)
284         {
285           struct html_driver_ext *x = d->ext;
286
287           fputs ("<TABLE BORDER=1>\n  <TR>\n", x->file);
288
289           {
290             size_t i;
291
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]));
295           }
296
297           fputs ("  </TR>\n", x->file);
298         }
299       else
300         NOT_REACHED ();
301     }
302 }
303
304 /* Writes the headers.  Some of them might be vertical; most are
305    probably horizontal. */
306 static void
307 write_header (struct outp_driver *d)
308 {
309   struct list_ext *prc = d->prc;
310
311   if (!prc->header_rows)
312     return;
313
314   if (n_lines_remaining (d) < prc->header_rows + 1)
315     {
316       outp_eject_page (d);
317       assert (n_lines_remaining (d) >= prc->header_rows + 1);
318     }
319
320   /* Design the header. */
321   if (!prc->header)
322     {
323       size_t i;
324       size_t x;
325
326       /* Allocate, initialize header. */
327       prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
328       {
329         int w = n_chars_width (d);
330         for (i = 0; i < prc->header_rows; i++)
331           {
332             prc->header[i] = xmalloc (w + 1);
333             memset (prc->header[i], ' ', w);
334           }
335       }
336
337       /* Put in vertical names. */
338       for (i = x = 0; i < prc->n_vertical; i++)
339         {
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);
344           size_t j;
345
346           memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
347           x += print->w - 1;
348           for (j = 0; j < name_len; j++)
349             prc->header[name_len - j - 1][x] = name[j];
350           x += 2;
351         }
352
353       /* Put in horizontal names. */
354       for (; i < cmd.n_variables; i++)
355         {
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);
360
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);
366           x += name_len + 1;
367         }
368
369       /* Add null bytes. */
370       for (i = 0; i < prc->header_rows; i++)
371         {
372           for (x = n_chars_width (d); x >= 1; x--)
373             if (prc->header[i][x - 1] != ' ')
374               {
375                 prc->header[i][x] = 0;
376                 break;
377               }
378           assert (x);
379         }
380     }
381
382   /* Write out the header, in back-to-front order except for the last line. */
383   if (prc->header_rows >= 2)
384     {
385       size_t i;
386
387       for (i = prc->header_rows - 1; i-- != 0; )
388         write_line (d, prc->header[i]);
389     }
390   write_line (d, prc->header[prc->header_rows - 1]);
391 }
392
393
394 /* Frees up all the memory we've allocated. */
395 static void
396 clean_up (void)
397 {
398   struct outp_driver *d;
399
400   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
401     if (d->class->special == 0)
402       {
403         struct list_ext *prc = d->prc;
404         size_t i;
405
406         if (prc->header)
407           {
408             for (i = 0; i < prc->header_rows; i++)
409               free (prc->header[i]);
410             free (prc->header);
411           }
412         free (prc);
413       }
414     else if (d->class == &html_class)
415       {
416         if (d->page_open)
417           {
418             struct html_driver_ext *x = d->ext;
419
420             fputs ("</TABLE>\n", x->file);
421           }
422       }
423     else
424       NOT_REACHED ();
425
426   free (cmd.v_variables);
427 }
428
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. */
432 static void
433 write_varname (struct outp_driver *d, char *string, int indent)
434 {
435   struct outp_text text;
436   int width;
437
438   if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
439     {
440       d->cp_y += d->font_height;
441       if (d->cp_y + d->font_height > d->length)
442         outp_eject_page (d);
443       d->cp_x = indent;
444     }
445
446   text.font = OUTP_FIXED;
447   text.justification = OUTP_LEFT;
448   text.string = ss_cstr (string);
449   text.x = d->cp_x;
450   text.y = d->cp_y;
451   text.h = text.v = INT_MAX;
452   d->class->text_draw (d, &text);
453   d->class->text_metrics (d, &text, &width, NULL);
454   d->cp_x += width;
455 }
456
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. */
459 static void
460 write_fallback_headers (struct outp_driver *d)
461 {
462   const int max_width = n_chars_width(d) - 10;
463
464   int index = 0;
465   int width = 0;
466   int line_number = 0;
467
468   const char *Line = _("Line");
469   char *leader = xmalloca (strlen (Line)
470                            + INT_STRLEN_BOUND (line_number) + 1 + 1);
471
472   while (index < cmd.n_variables)
473     {
474       struct outp_text text;
475       int leader_width;
476
477       /* Ensure that there is enough room for a line of text. */
478       if (d->cp_y + d->font_height > d->length)
479         outp_eject_page (d);
480
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);
486       text.x = 0;
487       text.y = d->cp_y;
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;
492
493       goto entry;
494       do
495         {
496           width++;
497
498         entry:
499           {
500             int var_width = var_get_print_format (cmd.v_variables[index])->w;
501             if (width + var_width > max_width && width != 0)
502               {
503                 width = 0;
504                 d->cp_x = 0;
505                 d->cp_y += d->font_height;
506                 break;
507               }
508             width += var_width;
509           }
510
511           {
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);
516           }
517         }
518       while (++index < cmd.n_variables);
519
520     }
521   d->cp_x = 0;
522   d->cp_y += d->font_height;
523
524   freea (leader);
525 }
526
527 /* There are three possible layouts for the LIST procedure:
528
529    1. If the values and their variables' name fit across the page,
530    then they are listed across the page in that way.
531
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.
535
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.
540
541    This is complicated by the fact that we have to do all this for
542    every output driver, not just once.  */
543 static void
544 determine_layout (void)
545 {
546   struct outp_driver *d;
547
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;
551
552   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
553     {
554       size_t column;    /* Current column. */
555       int width;        /* Accumulated width. */
556       int height;       /* Height of vertical names. */
557       int max_width;    /* Page width. */
558
559       struct list_ext *prc;
560
561       if (d->class == &html_class)
562         continue;
563
564       assert (d->class->special == 0);
565
566       outp_open_page (d);
567
568       max_width = n_chars_width (d);
569       largest_page_width = MAX (largest_page_width, max_width);
570
571       prc = d->prc = xmalloc (sizeof *prc);
572       prc->type = 0;
573       prc->n_vertical = 0;
574       prc->header = NULL;
575
576       /* Try layout #1. */
577       for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
578         {
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);
583         }
584       if (width <= max_width)
585         {
586           prc->header_rows = 2;
587           continue;
588         }
589
590       /* Try layout #2. */
591       for (width = cmd.n_variables - 1, height = 0, column = 0;
592            column < cmd.n_variables && width <= max_width;
593            column++)
594         {
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));
598           width += fmt_width;
599           if (name_len > height)
600             height = name_len;
601         }
602
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)
606         {
607 #ifndef NDEBUG
608           prc->n_vertical = SIZE_MAX;
609 #endif
610           for (column = cmd.n_variables; column-- != 0; )
611             {
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)
617                 {
618                   prc->n_vertical = column + 1;
619                   break;
620                 }
621               width = trial_width;
622             }
623           assert (prc->n_vertical != SIZE_MAX);
624
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;
629                column++)
630             {
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);
634             }
635           prc->header_rows++;
636           continue;
637         }
638
639       /* Otherwise use the ugly fallback listing format. */
640       prc->type = 1;
641       prc->header_rows = 0;
642
643       d->cp_y += d->font_height;
644       write_fallback_headers (d);
645       d->cp_y += d->font_height;
646     }
647
648   ds_init_empty (&line_buffer);
649 }
650
651 /* Writes case C to output. */
652 static void
653 list_case (const struct ccase *c, casenumber case_idx,
654            const struct dataset *ds)
655 {
656   struct dictionary *dict = dataset_dict (ds);
657   struct outp_driver *d;
658
659   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
660     if (d->class->special == 0)
661       {
662         const struct list_ext *prc = d->prc;
663         const int max_width = n_chars_width (d);
664         int column;
665
666         if (!prc->header_rows)
667           {
668             ds_put_format(&line_buffer, "%8s: ",
669                           var_get_name (cmd.v_variables[0]));
670           }
671
672
673         for (column = 0; column < cmd.n_variables; column++)
674           {
675             const struct variable *v = cmd.v_variables[column];
676             const struct fmt_spec *print = var_get_print_format (v);
677             int width;
678
679             if (prc->type == 0 && column >= prc->n_vertical)
680               {
681                 int name_len = strlen (var_get_name (v));
682                 width = MAX (name_len, print->w);
683               }
684             else
685               width = print->w;
686
687             if (width + ds_length(&line_buffer) > max_width &&
688                 ds_length(&line_buffer) != 0)
689               {
690                 if (!n_lines_remaining (d))
691                   {
692                     outp_eject_page (d);
693                     write_header (d);
694                   }
695
696                 write_line (d, ds_cstr (&line_buffer));
697                 ds_clear(&line_buffer);
698
699                 if (!prc->header_rows)
700                   ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
701               }
702
703             if (width > print->w)
704               ds_put_char_multiple(&line_buffer, ' ', width - print->w);
705
706             if (fmt_is_string (print->type)
707                 || dict_contains_var (dict, v))
708               {
709                 char *s = data_out (case_data (c, v), print);
710                 ds_put_cstr (&line_buffer, s);
711                 free (s);
712               }
713             else
714               {
715                 char *s;
716                 union value case_idx_value;
717                 case_idx_value.f = case_idx;
718                 s = data_out (&case_idx_value, print);
719                 ds_put_cstr (&line_buffer, s);
720                 free (s);
721               }
722
723             ds_put_char (&line_buffer, ' ');
724           }
725
726         if (!n_lines_remaining (d))
727           {
728             outp_eject_page (d);
729             write_header (d);
730           }
731
732         write_line (d, ds_cstr (&line_buffer));
733         ds_clear(&line_buffer);
734       }
735     else if (d->class == &html_class)
736       {
737         struct html_driver_ext *x = d->ext;
738         int column;
739
740         fputs ("  <TR>\n", x->file);
741
742         for (column = 0; column < cmd.n_variables; column++)
743           {
744             const struct variable *v = cmd.v_variables[column];
745             const struct fmt_spec *print = var_get_print_format (v);
746             char *s = NULL;
747
748             if (fmt_is_string (print->type)
749                 || dict_contains_var (dict, v))
750               s = data_out (case_data (c, v), print);
751             else
752               {
753                 union value case_idx_value;
754                 case_idx_value.f = case_idx;
755                 s = data_out (&case_idx_value, print);
756               }
757
758             fputs ("    <TD>", x->file);
759             html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
760             free (s);
761             fputs ("</TD>\n", x->file);
762           }
763
764         fputs ("  </TR>\n", x->file);
765       }
766     else
767       NOT_REACHED ();
768 }
769
770 /*
771    Local Variables:
772    mode: c
773    End:
774 */