Rewrite PSPP output engine.
[pspp-builds.git] / src / output / driver.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 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 <output/driver.h>
20 #include <output/driver-provider.h>
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <data/file-name.h>
29 #include <data/settings.h>
30 #include <libpspp/array.h>
31 #include <libpspp/assertion.h>
32 #include <libpspp/string-map.h>
33 #include <libpspp/string-set.h>
34 #include <libpspp/str.h>
35 #include <output/output-item.h>
36 #include <output/text-item.h>
37
38 #include "error.h"
39 #include "xalloc.h"
40 #include "xmemdup0.h"
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 static const struct output_driver_class *driver_classes[];
46
47 static struct output_driver **drivers;
48 static size_t n_drivers, allocated_drivers;
49
50 static unsigned int enabled_device_types = ((1u << OUTPUT_DEVICE_UNKNOWN)
51                                             | (1u << OUTPUT_DEVICE_LISTING)
52                                             | (1u << OUTPUT_DEVICE_SCREEN)
53                                             | (1u << OUTPUT_DEVICE_PRINTER));
54
55 static struct output_item *deferred_syntax;
56 static bool in_command;
57
58 void
59 output_close (void)
60 {
61   while (n_drivers > 0)
62     {
63       struct output_driver *d = drivers[--n_drivers];
64       output_driver_destroy (d);
65     }
66 }
67
68 static void
69 expand_macro (const char *name, struct string *dst, void *macros_)
70 {
71   const struct string_map *macros = macros_;
72
73   if (!strcmp (name, "viewwidth"))
74     ds_put_format (dst, "%d", settings_get_viewwidth ());
75   else if (!strcmp (name, "viewlength"))
76     ds_put_format (dst, "%d", settings_get_viewlength ());
77   else
78     {
79       const char *value = string_map_find (macros, name);
80       if (value != NULL)
81         ds_put_cstr (dst, value);
82     }
83 }
84
85 /* Defines one configuration macro based on the text in BP, which
86    should be of the form `KEY=VALUE'.  Returns true if
87    successful, false if S is not in the proper form. */
88 bool
89 output_define_macro (const char *s, struct string_map *macros)
90 {
91   const char *key_start, *value;
92   size_t key_len;
93   char *key;
94
95   s += strspn (s, CC_SPACES);
96
97   key_start = s;
98   key_len = strcspn (s, "=" CC_SPACES);
99   if (key_len == 0)
100     return false;
101   s += key_len;
102
103   s += strspn (s, CC_SPACES);
104   if (*s == '=')
105     s++;
106
107   s += strspn (s, CC_SPACES);
108   value = s;
109
110   key = xmemdup0 (key_start, key_len);
111   if (!string_map_contains (macros, key))
112     {
113       struct string expanded_value = DS_EMPTY_INITIALIZER;
114
115       fn_interp_vars (ss_cstr (value), expand_macro, &macros, &expanded_value);
116       string_map_insert_nocopy (macros, key, ds_steal_cstr (&expanded_value));
117     }
118   else
119     free (key);
120
121   return true;
122 }
123
124 static void
125 add_driver_names (char *to, struct string_set *names)
126 {
127   char *save_ptr = NULL;
128   char *name;
129
130   for (name = strtok_r (to, CC_SPACES, &save_ptr); name != NULL;
131        name = strtok_r (NULL, CC_SPACES, &save_ptr))
132     string_set_insert (names, name);
133 }
134
135 static void
136 init_default_drivers (void)
137 {
138   error (0, 0, _("using default output driver configuration"));
139   output_configure_driver ("list:ascii:listing:"
140                            "length=66 width=79 output-file=\"pspp.list\"");
141 }
142
143 static void
144 warn_unused_drivers (const struct string_set *unused_drivers,
145                      const struct string_set *requested_drivers)
146 {
147   const struct string_set_node *node;
148   const char *name;
149
150   STRING_SET_FOR_EACH (name, node, unused_drivers)
151     if (string_set_contains (requested_drivers, name))
152       error (0, 0, _("unknown output driver `%s'"), name);
153     else
154       error (0, 0, _("output driver `%s' referenced but never defined"), name);
155 }
156
157 void
158 output_read_configuration (const struct string_map *macros_,
159                            const struct string_set *driver_names_)
160 {
161   struct string_map macros = STRING_MAP_INITIALIZER (macros);
162   struct string_set driver_names = STRING_SET_INITIALIZER (driver_names);
163   char *devices_file_name = NULL;
164   FILE *devices_file = NULL;
165   struct string line = DS_EMPTY_INITIALIZER;
166   int line_number;
167
168   ds_init_empty (&line);
169
170   devices_file_name = fn_search_path ("devices", config_path);
171   if (devices_file_name == NULL)
172     {
173       error (0, 0, _("cannot find output initialization file "
174                      "(use `-vv' to view search path)"));
175       goto exit;
176     }
177   devices_file = fopen (devices_file_name, "r");
178   if (devices_file == NULL)
179     {
180       error (0, errno, _("cannot open \"%s\""), devices_file_name);
181       goto exit;
182     }
183
184   string_map_replace_map (&macros, macros_);
185   string_set_union (&driver_names, driver_names_);
186   if (string_set_is_empty (&driver_names))
187     string_set_insert (&driver_names, "default");
188
189   line_number = 0;
190   for (;;)
191     {
192       char *cp, *delimiter, *name;
193
194       if (!ds_read_config_line (&line, &line_number, devices_file))
195         {
196           if (ferror (devices_file))
197             error (0, errno, _("reading \"%s\""), devices_file_name);
198           break;
199         }
200
201       cp = ds_cstr (&line);
202       cp += strspn (cp, CC_SPACES);
203
204       if (*cp == '\0')
205         continue;
206       else if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
207         {
208           if (!output_define_macro (&cp[7], &macros))
209             error_at_line (0, 0, devices_file_name, line_number,
210                            _("\"%s\" is not a valid macro definition"),
211                            &cp[7]);
212           continue;
213         }
214
215       delimiter = cp + strcspn (cp, ":=");
216       name = xmemdup0 (cp, delimiter - cp);
217       if (*delimiter == '=')
218         {
219           if (string_set_delete (&driver_names, name))
220             add_driver_names (delimiter + 1, &driver_names);
221         }
222       else if (*delimiter == ':')
223         {
224           if (string_set_delete (&driver_names, name))
225             {
226               fn_interp_vars (ds_ss (&line), expand_macro, &macros, &line);
227               output_configure_driver (ds_cstr (&line));
228             }
229         }
230       else
231         error_at_line (0, 0, devices_file_name, line_number,
232                        _("syntax error"));
233       free (name);
234     }
235
236   warn_unused_drivers (&driver_names, driver_names_);
237
238 exit:
239   if (devices_file != NULL)
240     fclose (devices_file);
241   free (devices_file_name);
242   ds_destroy (&line);
243   string_set_destroy (&driver_names);
244   string_map_destroy (&macros);
245
246   if (n_drivers == 0)
247     {
248       error (0, 0, _("no active output drivers"));
249       init_default_drivers ();
250     }
251 }
252
253 /* Obtains a token from S and advances its position.  Errors are
254    reported against the given DRIVER_NAME.
255    The token is stored in TOKEN.  Returns true if successful,
256    false on syntax error.
257
258    Caller is responsible for skipping leading spaces. */
259 static bool
260 get_option_token (char **s_, const char *driver_name,
261                   struct string *token)
262 {
263   struct substring s = ss_cstr (*s_);
264   int c;
265
266   ds_clear (token);
267   c = ss_get_char (&s);
268   if (c == EOF)
269     {
270       error (0, 0, _("syntax error parsing options for \"%s\" driver"),
271              driver_name);
272       return false;
273     }
274   else if (c == '\'' || c == '"')
275     {
276       int quote = c;
277
278       for (;;)
279         {
280           c = ss_get_char (&s);
281           if (c == quote)
282             break;
283           else if (c == EOF)
284             {
285               error (0, 0,
286                      _("reached end of options inside quoted string "
287                        "parsing options for \"%s\" driver"),
288                      driver_name);
289               return false;
290             }
291           else if (c != '\\')
292             ds_put_char (token, c);
293           else
294             {
295               int out;
296
297               c = ss_get_char (&s);
298               switch (c)
299                 {
300                 case '\'':
301                   out = '\'';
302                   break;
303                 case '"':
304                   out = '"';
305                   break;
306                 case '\\':
307                   out = '\\';
308                   break;
309                 case 'a':
310                   out = '\a';
311                   break;
312                 case 'b':
313                   out = '\b';
314                   break;
315                 case 'f':
316                   out = '\f';
317                   break;
318                 case 'n':
319                   out = '\n';
320                   break;
321                 case 'r':
322                   out = '\r';
323                   break;
324                 case 't':
325                   out = '\t';
326                   break;
327                 case 'v':
328                   out = '\v';
329                   break;
330                 case '0':
331                 case '1':
332                 case '2':
333                 case '3':
334                 case '4':
335                 case '5':
336                 case '6':
337                 case '7':
338                   out = c - '0';
339                   while (ss_first (s) >= '0' && ss_first (s) <= '7')
340                     out = out * 8 + (ss_get_char (&s) - '0');
341                   break;
342                 case 'x':
343                 case 'X':
344                   out = 0;
345                   while (isxdigit (ss_first (s)))
346                     {
347                       c = ss_get_char (&s);
348                       out *= 16;
349                       if (isdigit (c))
350                         out += c - '0';
351                       else
352                         out += tolower (c) - 'a' + 10;
353                     }
354                   break;
355                 default:
356                   error (0, 0, _("syntax error in string constant "
357                                  "parsing options for \"%s\" driver"),
358                          driver_name);
359                   return false;
360                 }
361               ds_put_char (token, out);
362             }
363         }
364     }
365   else
366     {
367       for (;;)
368         {
369           ds_put_char (token, c);
370
371           c = ss_first (s);
372           if (c == EOF || c == '=' || isspace (c))
373             break;
374           ss_advance (&s, 1);
375         }
376     }
377
378   *s_ = s.string;
379   return 1;
380 }
381
382 static void
383 parse_options (const char *driver_name, char *options,
384                struct string_map *option_map)
385 {
386   struct string key = DS_EMPTY_INITIALIZER;
387   struct string value = DS_EMPTY_INITIALIZER;
388
389   for (;;)
390     {
391       options += strspn (options, CC_SPACES);
392       if (*options == '\0')
393         break;
394
395       if (!get_option_token (&options, driver_name, &key))
396         break;
397
398       options += strspn (options, CC_SPACES);
399       if (*options != '=')
400         {
401           error (0, 0, _("syntax error expecting `=' "
402                          "parsing options for driver \"%s\""),
403                  driver_name);
404           break;
405         }
406       options++;
407
408       options += strspn (options, CC_SPACES);
409       if (!get_option_token (&options, driver_name, &value))
410         break;
411
412       if (string_map_contains (option_map, ds_cstr (&key)))
413         error (0, 0, _("driver \"%s\" defines option \"%s\" multiple times"),
414                driver_name, ds_cstr (&key));
415       else
416         string_map_insert (option_map, ds_cstr (&key), ds_cstr (&value));
417     }
418
419   ds_destroy (&key);
420   ds_destroy (&value);
421 }
422
423 static char *
424 trim_token (char *token)
425 {
426   if (token != NULL)
427     {
428       char *end;
429
430       /* Trim leading spaces. */
431       while (isspace ((unsigned char) *token))
432         token++;
433
434       /* Trim trailing spaces. */
435       end = strchr (token, '\0');
436       while (end > token && isspace ((unsigned char) end[-1]))
437         *--end = '\0';
438
439       /* An empty token is a null token. */
440       if (*token == '\0')
441         return NULL;
442     }
443   return token;
444 }
445
446 static const struct output_driver_class *
447 find_output_class (const char *name)
448 {
449   const struct output_driver_class **classp;
450
451   for (classp = driver_classes; *classp != NULL; classp++)
452     if (!strcmp ((*classp)->name, name))
453       break;
454
455   return *classp;
456 }
457
458 static struct output_driver *
459 create_driver (const char *driver_name, const char *class_name,
460                const char *device_type, struct string_map *options)
461 {
462   const struct output_driver_class *class;
463   enum output_device_type type;
464   struct output_driver *driver;
465
466   type = OUTPUT_DEVICE_UNKNOWN;
467   if (device_type != NULL && device_type[0] != '\0')
468     {
469       if (!strcmp (device_type, "listing"))
470         type = OUTPUT_DEVICE_LISTING;
471       else if (!strcmp (device_type, "screen"))
472         type = OUTPUT_DEVICE_SCREEN;
473       else if (!strcmp (device_type, "printer"))
474         type = OUTPUT_DEVICE_PRINTER;
475       else
476         error (0, 0, _("unknown device type `%s'"), device_type);
477     }
478
479   class = find_output_class (class_name);
480   if (class != NULL)
481     driver = class->create (driver_name, type, options);
482   else
483     {
484       error (0, 0, _("unknown output driver class `%s'"), class_name);
485       driver = NULL;
486     }
487
488   string_map_destroy (options);
489
490   return driver;
491 }
492
493 struct output_driver *
494 output_driver_create (const char *class_name, struct string_map *options)
495 {
496   return create_driver (class_name, class_name, NULL, options);
497 }
498
499 /* String LINE is in format:
500    DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
501 */
502 void
503 output_configure_driver (const char *line_)
504 {
505   char *save_ptr = NULL;
506   char *line = xstrdup (line_);
507   char *driver_name = trim_token (strtok_r (line, ":", &save_ptr));
508   char *class_name = trim_token (strtok_r (NULL, ":", &save_ptr));
509   char *device_type = trim_token (strtok_r (NULL, ":", &save_ptr));
510   char *options = trim_token (strtok_r (NULL, "", &save_ptr));
511
512   if (driver_name && class_name)
513     {
514       struct string_map option_map;
515       struct output_driver *driver;
516
517       string_map_init (&option_map);
518       if (options != NULL)
519         parse_options (driver_name, options, &option_map);
520
521       driver = create_driver (driver_name, class_name,
522                               device_type, &option_map);
523       if (driver != NULL)
524         output_driver_register (driver);
525     }
526   else
527     error (0, 0,
528            _("driver definition line missing driver name or class name"));
529
530   free (line);
531 }
532
533 /* Display on stdout a list of all registered driver classes. */
534 void
535 output_list_classes (void)
536 {
537   const struct output_driver_class **classp;
538
539   printf (_("Driver classes:"));
540   for (classp = driver_classes; *classp != NULL; classp++)
541     printf (" %s", (*classp)->name);
542   putc ('\n', stdout);
543 }
544
545 static bool
546 driver_is_enabled (const struct output_driver *d)
547 {
548   return (1u << d->device_type) & enabled_device_types;
549 }
550
551 static void
552 output_submit__ (struct output_item *item)
553 {
554   size_t i;
555
556   for (i = 0; i < n_drivers; i++)
557     {
558       struct output_driver *d = drivers[i];
559       if (driver_is_enabled (d))
560         d->class->submit (d, item);
561     }
562
563   output_item_unref (item);
564 }
565
566 /* Submits ITEM to the configured output drivers, and transfers ownership to
567    the output subsystem. */
568 void
569 output_submit (struct output_item *item)
570 {
571   if (is_text_item (item))
572     {
573       struct text_item *text = to_text_item (item);
574       switch (text_item_get_type (text))
575         {
576         case TEXT_ITEM_SYNTAX:
577           if (!in_command)
578             {
579               if (deferred_syntax != NULL)
580                 output_submit__ (deferred_syntax);
581               deferred_syntax = item;
582               return;
583             }
584           break;
585
586         case TEXT_ITEM_COMMAND_OPEN:
587           output_submit__ (item);
588           if (deferred_syntax != NULL)
589             {
590               output_submit__ (deferred_syntax);
591               deferred_syntax = NULL;
592             }
593           in_command = true;
594           return;
595
596         case TEXT_ITEM_COMMAND_CLOSE:
597           in_command = false;
598           break;
599
600         default:
601           break;
602         }
603     }
604
605   output_submit__ (item);
606 }
607
608 /* Flushes output to screen devices, so that the user can see
609    output that doesn't fill up an entire page. */
610 void
611 output_flush (void)
612 {
613   size_t i;
614
615   for (i = 0; i < n_drivers; i++)
616     {
617       struct output_driver *d = drivers[i];
618       if (driver_is_enabled (d) && d->class->flush != NULL)
619         d->class->flush (d);
620     }
621 }
622
623 unsigned int
624 output_get_enabled_types (void)
625 {
626   return enabled_device_types;
627 }
628
629 void
630 output_set_enabled_types (unsigned int types)
631 {
632   enabled_device_types = types;
633 }
634
635 void
636 output_set_type_enabled (bool enable, enum output_device_type type)
637 {
638   unsigned int bit = 1u << type;
639   if (enable)
640     enabled_device_types |= bit;
641   else
642     enabled_device_types |= ~bit;
643 }
644 \f
645 void
646 output_driver_init (struct output_driver *driver,
647                     const struct output_driver_class *class,
648                     const char *name, enum output_device_type type)
649 {
650   driver->class = class;
651   driver->name = xstrdup (name);
652   driver->device_type = type;
653 }
654
655 void
656 output_driver_destroy (struct output_driver *driver)
657 {
658   if (driver != NULL)
659     {
660       char *name = driver->name;
661       if (output_driver_is_registered (driver))
662         output_driver_unregister (driver);
663       if (driver->class->destroy)
664         driver->class->destroy (driver);
665       free (name);
666     }
667 }
668
669 const char *
670 output_driver_get_name (const struct output_driver *driver)
671 {
672   return driver->name;
673 }
674 \f
675 void
676 output_driver_register (struct output_driver *driver)
677 {
678   assert (!output_driver_is_registered (driver));
679   if (n_drivers >= allocated_drivers)
680     drivers = x2nrealloc (drivers, &allocated_drivers, sizeof *drivers);
681   drivers[n_drivers++] = driver;
682 }
683
684 void
685 output_driver_unregister (struct output_driver *driver)
686 {
687   size_t i;
688
689   for (i = 0; i < n_drivers; i++)
690     if (drivers[i] == driver)
691       {
692         remove_element (drivers, n_drivers, sizeof *drivers, i);
693         return;
694       }
695   NOT_REACHED ();
696 }
697
698 bool
699 output_driver_is_registered (const struct output_driver *driver)
700 {
701   size_t i;
702
703   for (i = 0; i < n_drivers; i++)
704     if (drivers[i] == driver)
705       return true;
706   return false;
707 }
708 \f
709 /* Known driver classes. */
710
711 static const struct output_driver_class *driver_classes[] =
712   {
713     &ascii_class,
714 #ifdef HAVE_CAIRO
715     &cairo_class,
716 #endif
717     &html_class,
718     &odt_class,
719     &csv_class,
720     NULL,
721   };
722