1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <libpspp/alloc.h>
27 #include <data/file-name.h>
30 #include <libpspp/misc.h>
31 #include <data/settings.h>
32 #include <libpspp/str.h>
36 #define _(msgid) gettext (msgid)
38 /* FIXME? Should the output configuration format be changed to
39 drivername:classname:devicetype:options, where devicetype is zero
40 or more of screen, printer, listing? */
42 /* FIXME: Have the reentrancy problems been solved? */
44 /* Where the output driver name came from. */
47 OUTP_S_COMMAND_LINE, /* Specified by the user. */
48 OUTP_S_INIT_FILE /* `default' or the init file. */
51 /* Names the output drivers to be used. */
54 char *name; /* Name of the output driver. */
55 int source; /* OUTP_S_* */
56 struct outp_names *next, *prev;
59 /* Defines an init file macro. */
64 struct outp_defn *next, *prev;
67 static struct outp_defn *outp_macros;
68 static struct outp_names *outp_configure_vec;
70 /* A list of driver classes. */
71 struct outp_driver_class_list
73 struct outp_class *class;
74 struct outp_driver_class_list *next;
77 struct outp_driver_class_list *outp_class_list;
78 struct outp_driver *outp_driver_list;
83 /* A set of OUTP_DEV_* bits indicating the devices that are
85 static int disabled_devices;
87 static void destroy_driver (struct outp_driver *);
88 static void configure_driver_line (struct string *);
89 static void configure_driver (const struct string *, const struct string *,
90 const struct string *, const struct string *);
92 /* Add a class to the class list. */
94 add_class (struct outp_class *class)
96 struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
98 new_list->class = class;
100 if (!outp_class_list)
102 outp_class_list = new_list;
103 new_list->next = NULL;
107 new_list->next = outp_class_list;
108 outp_class_list = new_list;
112 /* Finds the outp_names in outp_configure_vec with name between BP and
114 static struct outp_names *
115 search_names (char *bp, char *ep)
117 struct outp_names *n;
119 for (n = outp_configure_vec; n; n = n->next)
120 if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
125 /* Deletes outp_names NAME from outp_configure_vec. */
127 delete_name (struct outp_names * n)
131 n->prev->next = n->next;
133 n->next->prev = n->prev;
134 if (n == outp_configure_vec)
135 outp_configure_vec = n->next;
139 /* Adds the name between BP and EP exclusive to list
140 outp_configure_vec with source SOURCE. */
142 add_name (char *bp, char *ep, int source)
144 struct outp_names *n = xmalloc (sizeof *n);
145 n->name = xmalloc (ep - bp + 1);
146 memcpy (n->name, bp, ep - bp);
147 n->name[ep - bp] = 0;
149 n->next = outp_configure_vec;
151 if (outp_configure_vec)
152 outp_configure_vec->prev = n;
153 outp_configure_vec = n;
156 /* Checks that outp_configure_vec is empty, bitches & clears it if it
159 check_configure_vec (void)
161 struct outp_names *n;
163 for (n = outp_configure_vec; n; n = n->next)
164 if (n->source == OUTP_S_COMMAND_LINE)
165 error (0, 0, _("unknown output driver `%s'"), n->name);
167 error (0, 0, _("output driver `%s' referenced but never defined"),
169 outp_configure_clear ();
172 /* Searches outp_configure_vec for the name between BP and EP
173 exclusive. If found, it is deleted, then replaced by the names
174 given in EP+1, if any. */
176 expand_name (char *bp, char *ep)
178 struct outp_names *n = search_names (bp, ep);
186 while (isspace ((unsigned char) *bp))
189 while (*ep && !isspace ((unsigned char) *ep))
193 if (!search_names (bp, ep))
194 add_name (bp, ep, OUTP_S_INIT_FILE);
199 /* Looks for a macro with key KEY, and returns the corresponding value
200 if found, or NULL if not. */
202 find_defn_value (const char *key)
204 static char buf[INT_STRLEN_BOUND (int) + 1];
207 for (d = outp_macros; d; d = d->next)
208 if (!strcmp (key, d->key))
209 return ds_c_str(&d->value);
210 if (!strcmp (key, "viewwidth"))
212 sprintf (buf, "%d", get_viewwidth ());
215 else if (!strcmp (key, "viewlength"))
217 sprintf (buf, "%d", get_viewlength ());
224 /* Initializes global variables. */
228 extern struct outp_class ascii_class;
229 extern struct outp_class postscript_class;
230 extern struct outp_class html_class;
232 char def[] = "default";
234 add_class (&html_class);
235 add_class (&postscript_class);
236 add_class (&ascii_class);
238 add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
241 /* Deletes all the output macros. */
245 struct outp_defn *d, *next;
247 for (d = outp_macros; d; d = next)
251 ds_destroy (&d->value);
257 init_default_drivers (void)
261 error (0, 0, _("using default output driver configuration"));
264 "list:ascii:listing:"
265 "length=66 width=79 output-file=\"pspp.list\"");
266 configure_driver_line (&s);
270 /* Reads the initialization file; initializes
273 outp_read_devices (void)
283 init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
285 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
293 error (0, 0, _("cannot find output initialization file "
294 "(use `-vv' to view search path)"));
298 f = fopen (init_fn, "r");
301 error (0, errno, _("cannot open \"%s\""), init_fn);
310 if (!ds_get_config_line (f, &line, &line_number))
313 error (0, errno, _("reading \"%s\""), init_fn);
316 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
317 if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
318 outp_configure_macro (&cp[7]);
322 for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
324 expand_name (cp, ep);
327 struct outp_names *n = search_names (cp, ep);
330 configure_driver_line (&line);
335 error_at_line (0, 0, init_fn, line_number, _("syntax error"));
340 check_configure_vec ();
343 if (f && -1 == fclose (f))
344 error (0, errno, _("error closing \"%s\""), init_fn);
351 if (outp_driver_list == NULL)
352 error (0, 0, _("no active output drivers"));
355 error (0, 0, _("error reading device definition file"));
357 if (!result || outp_driver_list == NULL)
358 init_default_drivers ();
361 /* Clear the list of drivers to configure. */
363 outp_configure_clear (void)
365 struct outp_names *n, *next;
367 for (n = outp_configure_vec; n; n = next)
373 outp_configure_vec = NULL;
376 /* Adds the name BP to the list of drivers to configure into
379 outp_configure_add (char *bp)
381 char *ep = &bp[strlen (bp)];
382 if (!search_names (bp, ep))
383 add_name (bp, ep, OUTP_S_COMMAND_LINE);
386 /* Defines one configuration macro based on the text in BP, which
387 should be of the form `KEY=VALUE'. */
389 outp_configure_macro (char *bp)
394 while (isspace ((unsigned char) *bp))
397 while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
400 d = xmalloc (sizeof *d);
401 d->key = xmalloc (ep - bp + 1);
402 memcpy (d->key, bp, ep - bp);
405 /* Earlier definitions for a particular KEY override later ones. */
406 if (find_defn_value (d->key))
415 while (isspace ((unsigned char) *ep))
418 ds_create(&d->value, ep);
419 fn_interp_vars(&d->value, find_defn_value);
420 d->next = outp_macros;
423 outp_macros->prev = d;
427 /* Destroys all the drivers in driver list *DL and sets *DL to
430 destroy_list (struct outp_driver ** dl)
432 struct outp_driver *d, *next;
434 for (d = *dl; d; d = next)
443 /* Closes all the output drivers. */
447 struct outp_driver_class_list *n = outp_class_list ;
448 destroy_list (&outp_driver_list);
452 struct outp_driver_class_list *next = n->next;
456 outp_class_list = NULL;
461 free (outp_subtitle);
462 outp_subtitle = NULL;
465 /* Display on stdout a list of all registered driver classes. */
467 outp_list_classes (void)
469 int width = get_viewwidth();
470 struct outp_driver_class_list *c;
472 printf (_("Driver classes:\n\t"));
474 for (c = outp_class_list; c; c = c->next)
476 if ((int) strlen (c->class->name) + 1 > width)
479 width = get_viewwidth() - 8;
483 fputs (c->class->name, stdout);
488 /* Obtains a token from S starting at position *POS, which is
489 updated. Errors are reported against the given DRIVER_NAME.
490 The token is stored in TOKEN. Returns true if successful,
491 false on syntax error.
493 Caller is responsible for skipping leading spaces. */
495 get_option_token (const struct string *s, const char *driver_name,
496 size_t *pos, struct string *token)
504 error (0, 0, _("syntax error parsing options for \"%s\" driver"),
508 else if (c == '\'' || c == '"')
515 c = ds_at (s, (*pos)++);
521 _("reached end of options inside quoted string "
522 "parsing options for \"%s\" driver"),
532 switch (ds_at (s, *pos))
573 while (ds_at (s, *pos) >= '0' && ds_at (s, *pos) <= '7')
574 out = c * 8 + ds_at (s, (*pos)++) - '0';
579 while (isxdigit (ds_at (s, *pos)))
590 out += tolower (c) - 'a' + 10;
594 error (0, 0, _("syntax error in string constant "
595 "parsing options for \"%s\" driver"),
599 ds_putc (token, out);
608 c = ds_at (s, ++*pos);
610 while (c != EOF && c != '=' && !isspace (c));
617 outp_parse_options (const struct string *options,
618 bool (*callback) (struct outp_driver *, const char *key,
619 const struct string *value),
620 struct outp_driver *driver)
622 struct string key = DS_INITIALIZER;
623 struct string value = DS_INITIALIZER;
629 pos += ds_span (options, pos, " \t");
630 if (ds_at (options, pos) == EOF)
633 if (!get_option_token (options, driver->name, &pos, &key))
636 pos += ds_span (options, pos, " \t");
637 if (ds_at (options, pos) != '=')
639 error (0, 0, _("syntax error expecting `=' "
640 "parsing options for driver \"%s\""),
646 pos += ds_span (options, pos, " \t");
647 if (!get_option_token (options, driver->name, &pos, &value))
650 ok = callback (driver, ds_c_str (&key), &value);
660 /* Find the driver in outp_driver_list with name NAME. */
661 static struct outp_driver *
662 find_driver (char *name)
664 struct outp_driver *d;
666 for (d = outp_driver_list; d; d = d->next)
667 if (!strcmp (d->name, name))
672 /* String S is in format:
673 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
674 Adds a driver to outp_driver_list pursuant to the specification
677 configure_driver (const struct string *driver_name,
678 const struct string *class_name,
679 const struct string *device_type,
680 const struct string *options)
682 struct outp_driver *d, *iter;
683 struct outp_driver_class_list *c;
687 for (c = outp_class_list; c; c = c->next)
688 if (!strcmp (c->class->name, ds_c_str (class_name)))
692 error (0, 0, _("unknown output driver class `%s'"),
693 ds_c_str (class_name));
697 /* Parse device type. */
699 if (device_type != NULL)
701 struct string token = DS_INITIALIZER;
704 while (ds_tokenize (device_type, &token, " \t\r\v", &save_idx))
706 const char *type = ds_c_str (&token);
707 if (!strcmp (type, "listing"))
708 device |= OUTP_DEV_LISTING;
709 else if (!strcmp (type, "screen"))
710 device |= OUTP_DEV_SCREEN;
711 else if (!strcmp (type, "printer"))
712 device |= OUTP_DEV_PRINTER;
714 error (0, 0, _("unknown device type `%s'"), type);
719 /* Open the device. */
720 d = xmalloc (sizeof *d);
721 d->next = d->prev = NULL;
723 d->name = xstrdup (ds_c_str (driver_name));
724 d->page_open = false;
725 d->device = OUTP_DEV_NONE;
726 d->cp_x = d->cp_y = 0;
731 if (!d->class->open_driver (d, options))
733 error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
734 d->name, d->class->name);
740 /* Find like-named driver and delete. */
741 iter = find_driver (d->name);
743 destroy_driver (iter);
746 d->next = outp_driver_list;
748 if (outp_driver_list != NULL)
749 outp_driver_list->prev = d;
750 outp_driver_list = d;
753 /* String LINE is in format:
754 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
755 Adds a driver to outp_driver_list pursuant to the specification
758 configure_driver_line (struct string *line)
760 struct string tokens[4];
764 fn_interp_vars (line, find_defn_value);
767 for (i = 0; i < 4; i++)
769 struct string *token = &tokens[i];
771 ds_separate (line, token, i < 3 ? ":" : "", &save_idx);
772 ds_trim_spaces (token);
775 if (!ds_is_empty (&tokens[0]) && !ds_is_empty (&tokens[1]))
776 configure_driver (&tokens[0], &tokens[1], &tokens[2], &tokens[3]);
779 _("driver definition line missing driver name or class name"));
781 for (i = 0; i < 4; i++)
782 ds_destroy (&tokens[i]);
785 /* Destroys output driver D. */
787 destroy_driver (struct outp_driver *d)
792 struct outp_driver_class_list *c;
794 d->class->close_driver (d);
796 for (c = outp_class_list; c; c = c->next)
797 if (c->class == d->class)
803 /* Remove this driver from the global driver list. */
805 d->prev->next = d->next;
807 d->next->prev = d->prev;
808 if (d == outp_driver_list)
809 outp_driver_list = d->next;
812 /* Tries to match S as one of the keywords in TAB, with
813 corresponding information structure INFO. Returns category
814 code and stores subcategory in *SUBCAT on success. Returns -1
817 outp_match_keyword (const char *s, struct outp_option *tab, int *subcat)
819 for (; tab->keyword != NULL; tab++)
820 if (!strcmp (s, tab->keyword))
822 *subcat = tab->subcat;
828 /* Encapsulate two characters in a single int. */
829 #define TWO_CHARS(A, B) \
832 /* Determines the size of a dimensional measurement and returns the
833 size in units of 1/72000". Units if not specified explicitly are
834 inches for values under 50, millimeters otherwise. Returns 0,
835 stores NULL to *TAIL on error; otherwise returns dimension, stores
838 outp_evaluate_dimension (char *dimen, char **tail)
844 value = strtod (s, &ptail);
851 b = strtod (s, &ptail);
852 if (b <= 0.0 || ptail == s)
857 c = strtod (s, &ptail);
858 if (c <= 0.0 || ptail == s)
868 else if (*ptail == '/')
872 b = strtod (s, &ptail);
873 if (b <= 0.0 || ptail == s)
880 if (*s == 0 || isspace ((unsigned char) *s))
885 value *= 72000 / 25.4;
891 /* Standard TeX units are supported. */
895 switch (TWO_CHARS (s[0], s[1]))
897 case TWO_CHARS ('p', 't'):
898 factor = 72000 / 72.27;
900 case TWO_CHARS ('p', 'c'):
901 factor = 72000 / 72.27 * 12.0;
903 case TWO_CHARS ('i', 'n'):
906 case TWO_CHARS ('b', 'p'):
907 factor = 72000 / 72.0;
909 case TWO_CHARS ('c', 'm'):
910 factor = 72000 / 2.54;
912 case TWO_CHARS ('m', 'm'):
913 factor = 72000 / 25.4;
915 case TWO_CHARS ('d', 'd'):
916 factor = 72000 / 72.27 * 1.0700086;
918 case TWO_CHARS ('c', 'c'):
919 factor = 72000 / 72.27 * 12.840104;
921 case TWO_CHARS ('s', 'p'):
922 factor = 72000 / 72.27 / 65536.0;
926 _("unit \"%s\" is unknown in dimension \"%s\""), s, dimen);
941 error (0, 0, _("bad dimension \"%s\""), dimen);
945 /* Stores the dimensions in 1/72000" units of paper identified by
946 SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each
947 of HORZ and VERT are dimensions, into *H and *V. Return nonzero on
950 internal_get_paper_size (char *size, int *h, int *v)
954 while (isspace ((unsigned char) *size))
956 *h = outp_evaluate_dimension (size, &tail);
959 while (isspace ((unsigned char) *tail))
963 else if (*tail == 'b' && tail[1] == 'y')
967 error (0, 0, _("`x' expected in paper size `%s'"), size);
970 *v = outp_evaluate_dimension (tail, &tail);
973 while (isspace ((unsigned char) *tail))
977 error (0, 0, _("trailing garbage `%s' on paper size `%s'"), tail, size);
984 /* Stores the dimensions, in 1/72000" units, of paper identified by
985 SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x
986 V', or it may be a case-insensitive paper identifier, which is
987 looked up in the `papersize' configuration file. Returns nonzero
988 on success. May modify SIZE. */
989 /* Don't read further unless you've got a strong stomach. */
991 outp_get_paper_size (char *size, int *h, int *v)
1004 int line_number = 0;
1010 while (isspace ((unsigned char) *size))
1012 if (isdigit ((unsigned char) *size))
1013 return internal_get_paper_size (size, h, v);
1017 while (isspace ((unsigned char) *ep) && ep >= size)
1021 error (0, 0, _("paper size name cannot be empty"));
1029 pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE",
1031 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
1037 if (pprsz_fn == NULL)
1039 error (0, 0, _("cannot find `papersize' configuration file"));
1043 f = fopen (pprsz_fn, "r");
1046 error (0, errno, _("error opening \"%s\""), pprsz_fn);
1054 if (!ds_get_config_line (f, &line, &line_number))
1057 error (0, errno, _("error reading \"%s\""), pprsz_fn);
1060 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
1065 for (bp = ep = cp + 1; *ep && *ep != '"'; ep++);
1069 if (0 != strcasecmp (bp, size))
1072 for (cp = ep + 1; isspace ((unsigned char) *cp); cp++);
1075 size = xmalloc (ep - bp + 1);
1084 error_at_line (0, 0, pprsz_fn, line_number,
1085 _("syntax error in paper size definition"));
1088 /* We found the one we want! */
1089 result = internal_get_paper_size (size, h, v);
1097 error (0, 0, _("error reading paper size definition file"));
1102 /* If D is NULL, returns the first enabled driver if any, NULL if
1103 none. Otherwise D must be the last driver returned by this
1104 function, in which case the next enabled driver is returned or NULL
1105 if that was the last. */
1106 struct outp_driver *
1107 outp_drivers (struct outp_driver *d)
1110 struct outp_driver *orig_d = d;
1116 d = outp_driver_list;
1121 || (d->device == 0 || (d->device & disabled_devices) != d->device))
1128 /* Enables (if ENABLE is nonzero) or disables (if ENABLE is zero) the
1129 device(s) given in mask DEVICE. */
1131 outp_enable_device (int enable, int device)
1134 disabled_devices &= ~device;
1136 disabled_devices |= device;
1139 /* Opens a page on driver D (if one is not open). */
1141 outp_open_page (struct outp_driver *d)
1145 d->cp_x = d->cp_y = 0;
1147 d->page_open = true;
1148 if (d->class->open_page != NULL)
1149 d->class->open_page (d);
1153 /* Closes the page on driver D (if one is open). */
1155 outp_close_page (struct outp_driver *d)
1159 if (d->class->close_page != NULL)
1160 d->class->close_page (d);
1161 d->page_open = false;
1165 /* Ejects the page on device D, if a page is open and non-blank,
1166 and opens a new page. */
1168 outp_eject_page (struct outp_driver *d)
1170 if (d->page_open && d->cp_y != 0)
1171 outp_close_page (d);
1175 /* Returns the width of string S, in device units, when output on
1178 outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
1180 struct outp_text text;
1184 text.justification = OUTP_LEFT;
1185 ls_init (&text.string, (char *) s, strlen (s));
1186 text.h = text.v = INT_MAX;
1187 d->class->text_metrics (d, &text, &width, NULL);