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
22 #include <libpspp/message.h>
27 #include <libpspp/alloc.h>
28 #include <libpspp/message.h>
29 #include <data/filename.h>
32 #include <libpspp/misc.h>
33 #include <data/settings.h>
34 #include <libpspp/str.h>
37 #define _(msgid) gettext (msgid)
39 /* FIXME? Should the output configuration format be changed to
40 drivername:classname:devicetype:options, where devicetype is zero
41 or more of screen, printer, listing? */
43 /* FIXME: Have the reentrancy problems been solved? */
45 /* Where the output driver name came from. */
48 OUTP_S_COMMAND_LINE, /* Specified by the user. */
49 OUTP_S_INIT_FILE /* `default' or the init file. */
52 /* Names the output drivers to be used. */
55 char *name; /* Name of the output driver. */
56 int source; /* OUTP_S_* */
57 struct outp_names *next, *prev;
60 /* Defines an init file macro. */
65 struct outp_defn *next, *prev;
68 static struct outp_defn *outp_macros;
69 static struct outp_names *outp_configure_vec;
71 /* A list of driver classes. */
72 struct outp_driver_class_list
74 struct outp_class *class;
75 struct outp_driver_class_list *next;
78 struct outp_driver_class_list *outp_class_list;
79 struct outp_driver *outp_driver_list;
84 /* A set of OUTP_DEV_* bits indicating the devices that are
86 static int disabled_devices;
88 static void destroy_driver (struct outp_driver *);
89 static void configure_driver_line (struct string *);
90 static void configure_driver (const struct string *, const struct string *,
91 const struct string *, const struct string *);
93 /* Add a class to the class list. */
95 add_class (struct outp_class *class)
97 struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
99 new_list->class = class;
101 if (!outp_class_list)
103 outp_class_list = new_list;
104 new_list->next = NULL;
108 new_list->next = outp_class_list;
109 outp_class_list = new_list;
113 /* Finds the outp_names in outp_configure_vec with name between BP and
115 static struct outp_names *
116 search_names (char *bp, char *ep)
118 struct outp_names *n;
120 for (n = outp_configure_vec; n; n = n->next)
121 if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
126 /* Deletes outp_names NAME from outp_configure_vec. */
128 delete_name (struct outp_names * n)
132 n->prev->next = n->next;
134 n->next->prev = n->prev;
135 if (n == outp_configure_vec)
136 outp_configure_vec = n->next;
140 /* Adds the name between BP and EP exclusive to list
141 outp_configure_vec with source SOURCE. */
143 add_name (char *bp, char *ep, int source)
145 struct outp_names *n = xmalloc (sizeof *n);
146 n->name = xmalloc (ep - bp + 1);
147 memcpy (n->name, bp, ep - bp);
148 n->name[ep - bp] = 0;
150 n->next = outp_configure_vec;
152 if (outp_configure_vec)
153 outp_configure_vec->prev = n;
154 outp_configure_vec = n;
157 /* Checks that outp_configure_vec is empty, bitches & clears it if it
160 check_configure_vec (void)
162 struct outp_names *n;
164 for (n = outp_configure_vec; n; n = n->next)
165 if (n->source == OUTP_S_COMMAND_LINE)
166 msg (ME, _("Unknown output driver `%s'."), n->name);
168 msg (IE, _("Output driver `%s' referenced but never defined."), n->name);
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 msg (MM, _("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)
281 struct file_locator where;
283 init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
285 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
288 where.filename = init_fn;
289 where.line_number = 0;
290 err_push_file_locator (&where);
292 ds_init (&line, 128);
296 msg (IE, _("Cannot find output initialization file. "
297 "Use `-vvvvv' to view search path."));
301 f = fopen (init_fn, "r");
304 msg (IE, _("Opening %s: %s."), init_fn, strerror (errno));
312 if (!ds_get_config_line (f, &line, &where.line_number))
315 msg (ME, _("Reading %s: %s."), init_fn, strerror (errno));
318 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
319 if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
320 outp_configure_macro (&cp[7]);
324 for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
326 expand_name (cp, ep);
329 struct outp_names *n = search_names (cp, ep);
332 configure_driver_line (&line);
337 msg (IS, _("Syntax error."));
342 check_configure_vec ();
345 err_pop_file_locator (&where);
346 if (f && -1 == fclose (f))
347 msg (MW, _("Closing %s: %s."), init_fn, strerror (errno));
354 if (outp_driver_list == NULL)
355 msg (MW, _("No output drivers are active."));
358 msg (VM (1), _("Error reading device definition file."));
360 if (!result || outp_driver_list == NULL)
361 init_default_drivers ();
364 /* Clear the list of drivers to configure. */
366 outp_configure_clear (void)
368 struct outp_names *n, *next;
370 for (n = outp_configure_vec; n; n = next)
376 outp_configure_vec = NULL;
379 /* Adds the name BP to the list of drivers to configure into
382 outp_configure_add (char *bp)
384 char *ep = &bp[strlen (bp)];
385 if (!search_names (bp, ep))
386 add_name (bp, ep, OUTP_S_COMMAND_LINE);
389 /* Defines one configuration macro based on the text in BP, which
390 should be of the form `KEY=VALUE'. */
392 outp_configure_macro (char *bp)
397 while (isspace ((unsigned char) *bp))
400 while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
403 d = xmalloc (sizeof *d);
404 d->key = xmalloc (ep - bp + 1);
405 memcpy (d->key, bp, ep - bp);
408 /* Earlier definitions for a particular KEY override later ones. */
409 if (find_defn_value (d->key))
418 while (isspace ((unsigned char) *ep))
421 ds_create(&d->value, ep);
422 fn_interp_vars(&d->value, find_defn_value);
423 d->next = outp_macros;
426 outp_macros->prev = d;
430 /* Destroys all the drivers in driver list *DL and sets *DL to
433 destroy_list (struct outp_driver ** dl)
435 struct outp_driver *d, *next;
437 for (d = *dl; d; d = next)
446 /* Closes all the output drivers. */
450 struct outp_driver_class_list *n = outp_class_list ;
451 destroy_list (&outp_driver_list);
455 struct outp_driver_class_list *next = n->next;
459 outp_class_list = NULL;
464 free (outp_subtitle);
465 outp_subtitle = NULL;
468 /* Display on stdout a list of all registered driver classes. */
470 outp_list_classes (void)
472 int width = get_viewwidth();
473 struct outp_driver_class_list *c;
475 printf (_("Driver classes:\n\t"));
477 for (c = outp_class_list; c; c = c->next)
479 if ((int) strlen (c->class->name) + 1 > width)
482 width = get_viewwidth() - 8;
486 fputs (c->class->name, stdout);
491 static int op_token; /* `=', 'a', 0. */
492 static struct string op_tokstr;
493 static const char *prog;
495 /* Parses a token from prog into op_token, op_tokstr. Sets op_token
496 to '=' on an equals sign, to 'a' on a string or identifier token,
497 or to 0 at end of line. Returns the new op_token. */
503 msg (IS, _("Syntax error."));
507 while (isspace ((unsigned char) *prog))
519 ds_clear (&op_tokstr);
521 if (*prog == '\'' || *prog == '"')
525 while (*prog && *prog != quote)
528 ds_putc (&op_tokstr, *prog++);
534 assert ((int) *prog); /* How could a line end in `\'? */
583 while (*prog >= '0' && *prog <= '7')
584 c = c * 8 + *prog++ - '0';
591 while (isxdigit ((unsigned char) *prog))
594 if (isdigit ((unsigned char) *prog))
597 c += (tolower ((unsigned char) (*prog))
604 msg (IS, _("Syntax error in string constant."));
607 ds_putc (&op_tokstr, (unsigned char) c);
613 while (*prog && !isspace ((unsigned char) *prog) && *prog != '=')
614 ds_putc (&op_tokstr, *prog++);
622 outp_parse_options (const char *options,
623 bool (*callback) (struct outp_driver *, const char *key,
624 const struct string *value),
625 struct outp_driver *driver)
632 ds_init (&op_tokstr, 64);
633 while (ok && tokener ())
639 msg (IS, _("Syntax error in options."));
643 ds_truncate (&op_tokstr, 64);
644 strcpy (key, ds_c_str (&op_tokstr));
649 msg (IS, _("Syntax error in options (`=' expected)."));
656 msg (IS, _("Syntax error in options (value expected after `=')."));
659 ok = callback (driver, key, &op_tokstr);
661 ds_destroy (&op_tokstr);
666 /* Find the driver in outp_driver_list with name NAME. */
667 static struct outp_driver *
668 find_driver (char *name)
670 struct outp_driver *d;
672 for (d = outp_driver_list; d; d = d->next)
673 if (!strcmp (d->name, name))
678 /* String S is in format:
679 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
680 Adds a driver to outp_driver_list pursuant to the specification
683 configure_driver (const struct string *driver_name,
684 const struct string *class_name,
685 const struct string *device_type,
686 const struct string *options)
688 struct outp_driver *d, *iter;
689 struct outp_driver_class_list *c;
693 for (c = outp_class_list; c; c = c->next)
694 if (!strcmp (c->class->name, ds_c_str (class_name)))
698 msg (IS, _("Unknown output driver class `%s'."), ds_c_str (class_name));
702 /* Parse device type. */
704 if (device_type != NULL)
706 struct string token = DS_INITIALIZER;
709 while (ds_tokenize (device_type, &token, " \t\r\v", &save_idx))
711 const char *type = ds_c_str (&token);
712 if (!strcmp (type, "listing"))
713 device |= OUTP_DEV_LISTING;
714 else if (!strcmp (type, "screen"))
715 device |= OUTP_DEV_SCREEN;
716 else if (!strcmp (type, "printer"))
717 device |= OUTP_DEV_PRINTER;
719 msg (IS, _("Unknown device type `%s'."), type);
724 /* Open the device. */
725 d = xmalloc (sizeof *d);
726 d->next = d->prev = NULL;
728 d->name = xstrdup (ds_c_str (driver_name));
729 d->page_open = false;
730 d->device = OUTP_DEV_NONE;
731 d->cp_x = d->cp_y = 0;
736 if (!d->class->open_driver (d, ds_c_str (options)))
738 msg (IS, _("Can't initialize output driver `%s' of class `%s'."),
739 d->name, d->class->name);
745 /* Find like-named driver and delete. */
746 iter = find_driver (d->name);
748 destroy_driver (iter);
751 d->next = outp_driver_list;
753 if (outp_driver_list != NULL)
754 outp_driver_list->prev = d;
755 outp_driver_list = d;
758 /* String LINE is in format:
759 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
760 Adds a driver to outp_driver_list pursuant to the specification
763 configure_driver_line (struct string *line)
765 struct string tokens[4];
769 fn_interp_vars (line, find_defn_value);
772 for (i = 0; i < 4; i++)
774 struct string *token = &tokens[i];
776 ds_separate (line, token, i < 3 ? ":" : "", &save_idx);
777 ds_trim_spaces (token);
780 if (!ds_is_empty (&tokens[0]) && !ds_is_empty (&tokens[1]))
781 configure_driver (&tokens[0], &tokens[1], &tokens[2], &tokens[3]);
783 msg (IS, _("Driver definition line missing driver name or class name"));
785 for (i = 0; i < 4; i++)
786 ds_destroy (&tokens[i]);
789 /* Destroys output driver D. */
791 destroy_driver (struct outp_driver *d)
796 struct outp_driver_class_list *c;
798 d->class->close_driver (d);
800 for (c = outp_class_list; c; c = c->next)
801 if (c->class == d->class)
807 /* Remove this driver from the global driver list. */
809 d->prev->next = d->next;
811 d->next->prev = d->prev;
812 if (d == outp_driver_list)
813 outp_driver_list = d->next;
816 /* Tries to match S as one of the keywords in TAB, with
817 corresponding information structure INFO. Returns category
818 code and stores subcategory in *SUBCAT on success. Returns -1
821 outp_match_keyword (const char *s, struct outp_option *tab, int *subcat)
823 for (; tab->keyword != NULL; tab++)
824 if (!strcmp (s, tab->keyword))
826 *subcat = tab->subcat;
832 /* Encapsulate two characters in a single int. */
833 #define TWO_CHARS(A, B) \
836 /* Determines the size of a dimensional measurement and returns the
837 size in units of 1/72000". Units if not specified explicitly are
838 inches for values under 50, millimeters otherwise. Returns 0,
839 stores NULL to *TAIL on error; otherwise returns dimension, stores
842 outp_evaluate_dimension (char *dimen, char **tail)
848 value = strtod (s, &ptail);
855 b = strtod (s, &ptail);
856 if (b <= 0.0 || ptail == s)
861 c = strtod (s, &ptail);
862 if (c <= 0.0 || ptail == s)
872 else if (*ptail == '/')
876 b = strtod (s, &ptail);
877 if (b <= 0.0 || ptail == s)
884 if (*s == 0 || isspace ((unsigned char) *s))
889 value *= 72000 / 25.4;
895 /* Standard TeX units are supported. */
899 switch (TWO_CHARS (s[0], s[1]))
901 case TWO_CHARS ('p', 't'):
902 factor = 72000 / 72.27;
904 case TWO_CHARS ('p', 'c'):
905 factor = 72000 / 72.27 * 12.0;
907 case TWO_CHARS ('i', 'n'):
910 case TWO_CHARS ('b', 'p'):
911 factor = 72000 / 72.0;
913 case TWO_CHARS ('c', 'm'):
914 factor = 72000 / 2.54;
916 case TWO_CHARS ('m', 'm'):
917 factor = 72000 / 25.4;
919 case TWO_CHARS ('d', 'd'):
920 factor = 72000 / 72.27 * 1.0700086;
922 case TWO_CHARS ('c', 'c'):
923 factor = 72000 / 72.27 * 12.840104;
925 case TWO_CHARS ('s', 'p'):
926 factor = 72000 / 72.27 / 65536.0;
929 msg (SE, _("Unit \"%s\" is unknown in dimension \"%s\"."), s, dimen);
944 msg (SE, _("Bad dimension \"%s\"."), dimen);
948 /* Stores the dimensions in 1/72000" units of paper identified by
949 SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each
950 of HORZ and VERT are dimensions, into *H and *V. Return nonzero on
953 internal_get_paper_size (char *size, int *h, int *v)
957 while (isspace ((unsigned char) *size))
959 *h = outp_evaluate_dimension (size, &tail);
962 while (isspace ((unsigned char) *tail))
966 else if (*tail == 'b' && tail[1] == 'y')
970 msg (SE, _("`x' expected in paper size `%s'."), size);
973 *v = outp_evaluate_dimension (tail, &tail);
976 while (isspace ((unsigned char) *tail))
980 msg (SE, _("Trailing garbage `%s' on paper size `%s'."), tail, size);
987 /* Stores the dimensions, in 1/72000" units, of paper identified by
988 SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x
989 V', or it may be a case-insensitive paper identifier, which is
990 looked up in the `papersize' configuration file. Returns nonzero
991 on success. May modify SIZE. */
992 /* Don't read further unless you've got a strong stomach. */
994 outp_get_paper_size (char *size, int *h, int *v)
1003 static struct paper_size cache[4];
1010 struct file_locator where;
1014 int min_value, min_index;
1018 while (isspace ((unsigned char) *size))
1020 if (isdigit ((unsigned char) *size))
1021 return internal_get_paper_size (size, h, v);
1025 while (isspace ((unsigned char) *ep) && ep >= size)
1029 msg (SE, _("Paper size name must not be empty."));
1038 for (i = 0; i < 4; i++)
1039 if (cache[i].name != NULL && !strcasecmp (cache[i].name, size))
1047 pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE",
1049 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
1053 where.filename = pprsz_fn;
1054 where.line_number = 0;
1055 err_push_file_locator (&where);
1056 ds_init (&line, 128);
1058 if (pprsz_fn == NULL)
1060 msg (IE, _("Cannot find `papersize' configuration file."));
1064 f = fopen (pprsz_fn, "r");
1067 msg (IE, _("Opening %s: %s."), pprsz_fn, strerror (errno));
1075 if (!ds_get_config_line (f, &line, &where.line_number))
1078 msg (ME, _("Reading %s: %s."), pprsz_fn, strerror (errno));
1081 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
1086 for (bp = ep = cp + 1; *ep && *ep != '"'; ep++);
1090 if (0 != strcasecmp (bp, size))
1093 for (cp = ep + 1; isspace ((unsigned char) *cp); cp++);
1096 size = xmalloc (ep - bp + 1);
1105 msg (IE, _("Syntax error in paper size definition."));
1108 /* We found the one we want! */
1109 result = internal_get_paper_size (size, h, v);
1112 min_value = cache[0].use;
1114 for (i = 1; i < 4; i++)
1115 if (cache[0].use < min_value)
1117 min_value = cache[i].use;
1120 free (cache[min_index].name);
1121 cache[min_index].name = xstrdup (size);
1122 cache[min_index].use = use;
1123 cache[min_index].h = *h;
1124 cache[min_index].v = *v;
1128 err_pop_file_locator (&where);
1134 msg (VM (1), _("Error reading paper size definition file."));
1139 /* If D is NULL, returns the first enabled driver if any, NULL if
1140 none. Otherwise D must be the last driver returned by this
1141 function, in which case the next enabled driver is returned or NULL
1142 if that was the last. */
1143 struct outp_driver *
1144 outp_drivers (struct outp_driver *d)
1147 struct outp_driver *orig_d = d;
1153 d = outp_driver_list;
1158 || (d->device == 0 || (d->device & disabled_devices) != d->device))
1165 /* Enables (if ENABLE is nonzero) or disables (if ENABLE is zero) the
1166 device(s) given in mask DEVICE. */
1168 outp_enable_device (int enable, int device)
1171 disabled_devices &= ~device;
1173 disabled_devices |= device;
1176 /* Opens a page on driver D (if one is not open). */
1178 outp_open_page (struct outp_driver *d)
1182 d->cp_x = d->cp_y = 0;
1184 d->page_open = true;
1185 if (d->class->open_page != NULL)
1186 d->class->open_page (d);
1190 /* Closes the page on driver D (if one is open). */
1192 outp_close_page (struct outp_driver *d)
1196 if (d->class->close_page != NULL)
1197 d->class->close_page (d);
1198 d->page_open = false;
1202 /* Ejects the paper on device D, if a page is open and is not
1205 outp_eject_page (struct outp_driver *d)
1207 if (d->page_open && d->cp_y != 0)
1209 outp_close_page (d);
1214 /* Returns the width of string S, in device units, when output on
1217 outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
1219 struct outp_text text;
1223 text.justification = OUTP_LEFT;
1224 ls_init (&text.string, (char *) s, strlen (s));
1225 text.h = text.v = INT_MAX;
1226 d->class->text_metrics (d, &text, &width, NULL);