1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007 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/>. */
23 #include <libpspp/alloc.h>
24 #include <data/file-name.h>
27 #include <libpspp/misc.h>
28 #include <data/settings.h>
29 #include <libpspp/str.h>
33 #define _(msgid) gettext (msgid)
35 /* FIXME? Should the output configuration format be changed to
36 drivername:classname:devicetype:options, where devicetype is zero
37 or more of screen, printer, listing? */
39 /* FIXME: Have the reentrancy problems been solved? */
41 /* Where the output driver name came from. */
44 OUTP_S_COMMAND_LINE, /* Specified by the user. */
45 OUTP_S_INIT_FILE /* `default' or the init file. */
48 /* Names the output drivers to be used. */
51 char *name; /* Name of the output driver. */
52 int source; /* OUTP_S_* */
53 struct outp_names *next, *prev;
56 /* Defines an init file macro. */
61 struct outp_defn *next, *prev;
64 static struct outp_defn *outp_macros;
65 static struct outp_names *outp_configure_vec;
67 /* A list of driver classes. */
68 struct outp_driver_class_list
70 const struct outp_class *class;
71 struct outp_driver_class_list *next;
74 static struct outp_driver_class_list *outp_class_list;
75 static struct outp_driver *outp_driver_list;
80 /* A set of OUTP_DEV_* bits indicating the devices that are
82 static int disabled_devices;
84 static void destroy_driver (struct outp_driver *);
85 static void configure_driver (const struct substring, const struct substring,
86 const struct substring, const struct substring);
88 /* Add a class to the class list. */
90 add_class (const struct outp_class *class)
92 struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
94 new_list->class = class;
98 outp_class_list = new_list;
99 new_list->next = NULL;
103 new_list->next = outp_class_list;
104 outp_class_list = new_list;
108 /* Finds the outp_names in outp_configure_vec with name between BP and
110 static struct outp_names *
111 search_names (char *bp, char *ep)
113 struct outp_names *n;
115 for (n = outp_configure_vec; n; n = n->next)
116 if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
121 /* Deletes outp_names NAME from outp_configure_vec. */
123 delete_name (struct outp_names * n)
127 n->prev->next = n->next;
129 n->next->prev = n->prev;
130 if (n == outp_configure_vec)
131 outp_configure_vec = n->next;
135 /* Adds the name between BP and EP exclusive to list
136 outp_configure_vec with source SOURCE. */
138 add_name (char *bp, char *ep, int source)
140 struct outp_names *n = xmalloc (sizeof *n);
141 n->name = xmalloc (ep - bp + 1);
142 memcpy (n->name, bp, ep - bp);
143 n->name[ep - bp] = 0;
145 n->next = outp_configure_vec;
147 if (outp_configure_vec)
148 outp_configure_vec->prev = n;
149 outp_configure_vec = n;
152 /* Checks that outp_configure_vec is empty, complains and clears
155 check_configure_vec (void)
157 struct outp_names *n;
159 for (n = outp_configure_vec; n; n = n->next)
160 if (n->source == OUTP_S_COMMAND_LINE)
161 error (0, 0, _("unknown output driver `%s'"), n->name);
163 error (0, 0, _("output driver `%s' referenced but never defined"),
165 outp_configure_clear ();
168 /* Searches outp_configure_vec for the name between BP and EP
169 exclusive. If found, it is deleted, then replaced by the names
170 given in EP+1, if any. */
172 expand_name (char *bp, char *ep)
174 struct outp_names *n = search_names (bp, ep);
182 while (isspace ((unsigned char) *bp))
185 while (*ep && !isspace ((unsigned char) *ep))
189 if (!search_names (bp, ep))
190 add_name (bp, ep, OUTP_S_INIT_FILE);
195 /* Looks for a macro with key KEY, and returns the corresponding value
196 if found, or NULL if not. */
198 find_defn_value (const char *key)
200 static char buf[INT_STRLEN_BOUND (int) + 1];
203 for (d = outp_macros; d; d = d->next)
204 if (!strcmp (key, d->key))
205 return ds_cstr (&d->value);
206 if (!strcmp (key, "viewwidth"))
208 sprintf (buf, "%d", get_viewwidth ());
211 else if (!strcmp (key, "viewlength"))
213 sprintf (buf, "%d", get_viewlength ());
220 /* Initializes global variables. */
224 extern struct outp_class ascii_class;
225 extern struct outp_class postscript_class;
227 char def[] = "default";
229 add_class (&html_class);
230 add_class (&postscript_class);
231 add_class (&ascii_class);
233 add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
236 /* Deletes all the output macros. */
240 struct outp_defn *d, *next;
242 for (d = outp_macros; d; d = next)
246 ds_destroy (&d->value);
252 init_default_drivers (void)
254 error (0, 0, _("using default output driver configuration"));
255 configure_driver (ss_cstr ("list"),
258 ss_cstr ("length=66 width=79 output-file=\"pspp.list\""));
261 /* Reads the initialization file; initializes
264 outp_read_devices (void)
274 init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
276 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
279 ds_init_empty (&line);
283 error (0, 0, _("cannot find output initialization file "
284 "(use `-vv' to view search path)"));
288 f = fopen (init_fn, "r");
291 error (0, errno, _("cannot open \"%s\""), init_fn);
300 if (!ds_read_config_line (&line, &line_number, f))
303 error (0, errno, _("reading \"%s\""), init_fn);
306 for (cp = ds_cstr (&line); isspace ((unsigned char) *cp); cp++);
307 if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
308 outp_configure_macro (&cp[7]);
312 for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
314 expand_name (cp, ep);
317 struct outp_names *n = search_names (cp, ep);
320 outp_configure_driver_line (ds_ss (&line));
325 error_at_line (0, 0, init_fn, line_number, _("syntax error"));
330 check_configure_vec ();
333 if (f && -1 == fclose (f))
334 error (0, errno, _("error closing \"%s\""), init_fn);
341 if (outp_driver_list == NULL)
342 error (0, 0, _("no active output drivers"));
345 error (0, 0, _("error reading device definition file"));
347 if (!result || outp_driver_list == NULL)
348 init_default_drivers ();
351 /* Clear the list of drivers to configure. */
353 outp_configure_clear (void)
355 struct outp_names *n, *next;
357 for (n = outp_configure_vec; n; n = next)
363 outp_configure_vec = NULL;
366 /* Adds the name BP to the list of drivers to configure into
369 outp_configure_add (char *bp)
371 char *ep = &bp[strlen (bp)];
372 if (!search_names (bp, ep))
373 add_name (bp, ep, OUTP_S_COMMAND_LINE);
376 /* Defines one configuration macro based on the text in BP, which
377 should be of the form `KEY=VALUE'. */
379 outp_configure_macro (char *bp)
384 while (isspace ((unsigned char) *bp))
387 while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
390 d = xmalloc (sizeof *d);
391 d->key = xmalloc (ep - bp + 1);
392 memcpy (d->key, bp, ep - bp);
395 /* Earlier definitions for a particular KEY override later ones. */
396 if (find_defn_value (d->key))
405 while (isspace ((unsigned char) *ep))
408 ds_init_cstr (&d->value, ep);
409 fn_interp_vars (ds_ss (&d->value), find_defn_value, &d->value);
410 d->next = outp_macros;
413 outp_macros->prev = d;
417 /* Destroys all the drivers in driver list *DL and sets *DL to
420 destroy_list (struct outp_driver ** dl)
422 struct outp_driver *d, *next;
424 for (d = *dl; d; d = next)
433 /* Closes all the output drivers. */
437 struct outp_driver_class_list *n = outp_class_list ;
438 destroy_list (&outp_driver_list);
442 struct outp_driver_class_list *next = n->next;
446 outp_class_list = NULL;
451 free (outp_subtitle);
452 outp_subtitle = NULL;
455 /* Display on stdout a list of all registered driver classes. */
457 outp_list_classes (void)
459 int width = get_viewwidth();
460 struct outp_driver_class_list *c;
462 printf (_("Driver classes:\n\t"));
464 for (c = outp_class_list; c; c = c->next)
466 if ((int) strlen (c->class->name) + 1 > width)
469 width = get_viewwidth() - 8;
473 fputs (c->class->name, stdout);
478 /* Obtains a token from S and advances its position. Errors are
479 reported against the given DRIVER_NAME.
480 The token is stored in TOKEN. Returns true if successful,
481 false on syntax error.
483 Caller is responsible for skipping leading spaces. */
485 get_option_token (struct substring *s, const char *driver_name,
486 struct string *token)
494 error (0, 0, _("syntax error parsing options for \"%s\" driver"),
498 else if (c == '\'' || c == '"')
510 _("reached end of options inside quoted string "
511 "parsing options for \"%s\" driver"),
516 ds_put_char (token, c);
563 while (ss_first (*s) >= '0' && ss_first (*s) <= '7')
564 out = out * 8 + (ss_get_char (s) - '0');
569 while (isxdigit (ss_first (*s)))
576 out += tolower (c) - 'a' + 10;
580 error (0, 0, _("syntax error in string constant "
581 "parsing options for \"%s\" driver"),
585 ds_put_char (token, out);
593 ds_put_char (token, c);
596 if (c == EOF || c == '=' || isspace (c))
606 outp_parse_options (struct substring options,
607 bool (*callback) (struct outp_driver *, const char *key,
608 const struct string *value),
609 struct outp_driver *driver)
611 struct string key = DS_EMPTY_INITIALIZER;
612 struct string value = DS_EMPTY_INITIALIZER;
613 struct substring left = options;
618 ss_ltrim (&left, ss_cstr (CC_SPACES));
619 if (ss_is_empty (left))
622 if (!get_option_token (&left, driver->name, &key))
625 ss_ltrim (&left, ss_cstr (CC_SPACES));
626 if (!ss_match_char (&left, '='))
628 error (0, 0, _("syntax error expecting `=' "
629 "parsing options for driver \"%s\""),
634 ss_ltrim (&left, ss_cstr (CC_SPACES));
635 if (!get_option_token (&left, driver->name, &value))
638 ok = callback (driver, ds_cstr (&key), &value);
648 /* Find the driver in outp_driver_list with name NAME. */
649 static struct outp_driver *
650 find_driver (char *name)
652 struct outp_driver *d;
654 for (d = outp_driver_list; d; d = d->next)
655 if (!strcmp (d->name, name))
660 /* Adds a driver to outp_driver_list pursuant to the
661 specification provided. */
663 configure_driver (struct substring driver_name, struct substring class_name,
664 struct substring device_type, struct substring options)
666 struct outp_driver *d, *iter;
667 struct outp_driver_class_list *c;
669 struct substring token;
674 for (c = outp_class_list; c; c = c->next)
675 if (!ss_compare (ss_cstr (c->class->name), class_name))
679 error (0, 0, _("unknown output driver class `%.*s'"),
680 (int) ss_length (class_name), ss_data (class_name));
684 /* Parse device type. */
686 while (ss_tokenize (device_type, ss_cstr (CC_SPACES), &save_idx, &token))
687 if (!ss_compare (token, ss_cstr ("listing")))
688 device |= OUTP_DEV_LISTING;
689 else if (!ss_compare (token, ss_cstr ("screen")))
690 device |= OUTP_DEV_SCREEN;
691 else if (!ss_compare (token, ss_cstr ("printer")))
692 device |= OUTP_DEV_PRINTER;
694 error (0, 0, _("unknown device type `%.*s'"),
695 (int) ss_length (token), ss_data (token));
697 /* Open the device. */
698 d = xmalloc (sizeof *d);
699 d->next = d->prev = NULL;
701 d->name = ss_xstrdup (driver_name);
702 d->page_open = false;
704 d->cp_x = d->cp_y = 0;
709 if (!d->class->open_driver (d, options))
711 error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
712 d->name, d->class->name);
718 /* Find like-named driver and delete. */
719 iter = find_driver (d->name);
721 destroy_driver (iter);
724 d->next = outp_driver_list;
726 if (outp_driver_list != NULL)
727 outp_driver_list->prev = d;
728 outp_driver_list = d;
731 /* String LINE is in format:
732 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
733 Adds a driver to outp_driver_list pursuant to the specification
736 outp_configure_driver_line (struct substring line_)
738 struct string line = DS_EMPTY_INITIALIZER;
739 struct substring tokens[4];
743 fn_interp_vars (line_, find_defn_value, &line);
746 for (i = 0; i < 4; i++)
748 struct substring *token = &tokens[i];
749 ds_separate (&line, ss_cstr (i < 3 ? ":" : ""), &save_idx, token);
750 ss_trim (token, ss_cstr (CC_SPACES));
753 if (!ss_is_empty (tokens[0]) && !ss_is_empty (tokens[1]))
754 configure_driver (tokens[0], tokens[1], tokens[2], tokens[3]);
757 _("driver definition line missing driver name or class name"));
762 /* Destroys output driver D. */
764 destroy_driver (struct outp_driver *d)
769 struct outp_driver_class_list *c;
771 d->class->close_driver (d);
773 for (c = outp_class_list; c; c = c->next)
774 if (c->class == d->class)
780 /* Remove this driver from the global driver list. */
782 d->prev->next = d->next;
784 d->next->prev = d->prev;
785 if (d == outp_driver_list)
786 outp_driver_list = d->next;
789 /* Tries to match S as one of the keywords in TAB, with
790 corresponding information structure INFO. Returns category
791 code and stores subcategory in *SUBCAT on success. Returns -1
794 outp_match_keyword (const char *s, const struct outp_option *tab, int *subcat)
796 for (; tab->keyword != NULL; tab++)
797 if (!strcmp (s, tab->keyword))
799 *subcat = tab->subcat;
805 /* Encapsulate two characters in a single int. */
806 #define TWO_CHARS(A, B) \
809 /* Determines the size of a dimensional measurement and returns the
810 size in units of 1/72000". Units if not specified explicitly are
811 inches for values under 50, millimeters otherwise. Returns 0,
812 stores NULL to *TAIL on error; otherwise returns dimension, stores
815 outp_evaluate_dimension (char *dimen, char **tail)
821 value = strtod (s, &ptail);
828 b = strtod (s, &ptail);
829 if (b <= 0.0 || ptail == s)
834 c = strtod (s, &ptail);
835 if (c <= 0.0 || ptail == s)
845 else if (*ptail == '/')
849 b = strtod (s, &ptail);
850 if (b <= 0.0 || ptail == s)
857 if (*s == 0 || isspace ((unsigned char) *s))
862 value *= 72000 / 25.4;
868 /* Standard TeX units are supported. */
872 switch (TWO_CHARS (s[0], s[1]))
874 case TWO_CHARS ('p', 't'):
875 factor = 72000 / 72.27;
877 case TWO_CHARS ('p', 'c'):
878 factor = 72000 / 72.27 * 12.0;
880 case TWO_CHARS ('i', 'n'):
883 case TWO_CHARS ('b', 'p'):
884 factor = 72000 / 72.0;
886 case TWO_CHARS ('c', 'm'):
887 factor = 72000 / 2.54;
889 case TWO_CHARS ('m', 'm'):
890 factor = 72000 / 25.4;
892 case TWO_CHARS ('d', 'd'):
893 factor = 72000 / 72.27 * 1.0700086;
895 case TWO_CHARS ('c', 'c'):
896 factor = 72000 / 72.27 * 12.840104;
898 case TWO_CHARS ('s', 'p'):
899 factor = 72000 / 72.27 / 65536.0;
903 _("unit \"%s\" is unknown in dimension \"%s\""), s, dimen);
918 error (0, 0, _("bad dimension \"%s\""), dimen);
922 /* Stores the dimensions in 1/72000" units of paper identified by
923 SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each
924 of HORZ and VERT are dimensions, into *H and *V. Return true on
927 internal_get_paper_size (char *size, int *h, int *v)
931 while (isspace ((unsigned char) *size))
933 *h = outp_evaluate_dimension (size, &tail);
936 while (isspace ((unsigned char) *tail))
940 else if (*tail == 'b' && tail[1] == 'y')
944 error (0, 0, _("`x' expected in paper size `%s'"), size);
947 *v = outp_evaluate_dimension (tail, &tail);
950 while (isspace ((unsigned char) *tail))
954 error (0, 0, _("trailing garbage `%s' on paper size `%s'"), tail, size);
961 /* Stores the dimensions, in 1/72000" units, of paper identified by
962 SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x
963 V', or it may be a case-insensitive paper identifier, which is
964 looked up in the `papersize' configuration file. Returns true
965 on success. May modify SIZE. */
966 /* Don't read further unless you've got a strong stomach. */
968 outp_get_paper_size (char *size, int *h, int *v)
983 bool free_it = false;
987 while (isspace ((unsigned char) *size))
989 if (isdigit ((unsigned char) *size))
990 return internal_get_paper_size (size, h, v);
994 while (isspace ((unsigned char) *ep) && ep >= size)
998 error (0, 0, _("paper size name cannot be empty"));
1006 pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE",
1008 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
1011 ds_init_empty (&line);
1013 if (pprsz_fn == NULL)
1015 error (0, 0, _("cannot find `papersize' configuration file"));
1019 f = fopen (pprsz_fn, "r");
1022 error (0, errno, _("error opening \"%s\""), pprsz_fn);
1028 struct substring p, name;
1030 if (!ds_read_config_line (&line, &line_number, f))
1033 error (0, errno, _("error reading \"%s\""), pprsz_fn);
1038 ss_ltrim (&p, ss_cstr (CC_SPACES));
1039 if (!ss_match_char (&p, '"') || !ss_get_until (&p, '"', &name))
1041 if (ss_compare (name, ss_cstr (size)))
1044 ss_ltrim (&p, ss_cstr (CC_SPACES));
1045 if (ss_match_char (&p, '='))
1049 ss_trim (&p, ss_cstr (CC_SPACES));
1050 size = ss_xstrdup (p);
1058 error_at_line (0, 0, pprsz_fn, line_number,
1059 _("syntax error in paper size definition"));
1062 /* We found the one we want! */
1063 result = internal_get_paper_size (size, h, v);
1071 error (0, 0, _("error reading paper size definition file"));
1076 /* If D is NULL, returns the first enabled driver if any, NULL if
1077 none. Otherwise D must be the last driver returned by this
1078 function, in which case the next enabled driver is returned or NULL
1079 if that was the last. */
1080 struct outp_driver *
1081 outp_drivers (struct outp_driver *d)
1086 d = outp_driver_list;
1091 || (d->device == 0 || (d->device & disabled_devices) != d->device))
1098 /* Enables (if ENABLE is true) or disables (if ENABLE is false) the
1099 device(s) given in mask DEVICE. */
1101 outp_enable_device (bool enable, int device)
1104 disabled_devices &= ~device;
1106 disabled_devices |= device;
1109 /* Opens a page on driver D (if one is not open). */
1111 outp_open_page (struct outp_driver *d)
1115 d->cp_x = d->cp_y = 0;
1117 d->page_open = true;
1118 if (d->class->open_page != NULL)
1119 d->class->open_page (d);
1123 /* Closes the page on driver D (if one is open). */
1125 outp_close_page (struct outp_driver *d)
1129 if (d->class->close_page != NULL)
1130 d->class->close_page (d);
1131 d->page_open = false;
1135 /* Ejects the page on device D, if a page is open and non-blank,
1136 and opens a new page. */
1138 outp_eject_page (struct outp_driver *d)
1140 if (d->page_open && d->cp_y != 0)
1141 outp_close_page (d);
1145 /* Flushes output to screen devices, so that the user can see
1146 output that doesn't fill up an entire page. */
1148 outp_flush (struct outp_driver *d)
1150 if (d->device & OUTP_DEV_SCREEN && d->class->flush != NULL)
1152 outp_close_page (d);
1153 d->class->flush (d);
1157 /* Returns the width of string S, in device units, when output on
1160 outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
1162 struct outp_text text;
1166 text.justification = OUTP_LEFT;
1167 text.string = ss_cstr (s);
1168 text.h = text.v = INT_MAX;
1169 d->class->text_metrics (d, &text, &width, NULL);