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 struct outp_driver_class_list *outp_class_list;
72 struct outp_driver *outp_driver_list;
77 /* A set of OUTP_DEV_* bits indicating the devices that are
79 static int disabled_devices;
81 static void destroy_driver (struct outp_driver *);
82 static void configure_driver_line (struct string *);
83 static void configure_driver (const char *, const char *,
84 const char *, const char *);
86 /* Add a class to the class list. */
88 add_class (struct outp_class *class)
90 struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
92 new_list->class = class;
93 new_list->ref_count = 0;
97 outp_class_list = new_list;
98 new_list->next = NULL;
102 new_list->next = outp_class_list;
103 outp_class_list = new_list;
107 /* Finds the outp_names in outp_configure_vec with name between BP and
109 static struct outp_names *
110 search_names (char *bp, char *ep)
112 struct outp_names *n;
114 for (n = outp_configure_vec; n; n = n->next)
115 if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
120 /* Deletes outp_names NAME from outp_configure_vec. */
122 delete_name (struct outp_names * n)
126 n->prev->next = n->next;
128 n->next->prev = n->prev;
129 if (n == outp_configure_vec)
130 outp_configure_vec = n->next;
134 /* Adds the name between BP and EP exclusive to list
135 outp_configure_vec with source SOURCE. */
137 add_name (char *bp, char *ep, int source)
139 struct outp_names *n = xmalloc (sizeof *n);
140 n->name = xmalloc (ep - bp + 1);
141 memcpy (n->name, bp, ep - bp);
142 n->name[ep - bp] = 0;
144 n->next = outp_configure_vec;
146 if (outp_configure_vec)
147 outp_configure_vec->prev = n;
148 outp_configure_vec = n;
151 /* Checks that outp_configure_vec is empty, bitches & clears it if it
154 check_configure_vec (void)
156 struct outp_names *n;
158 for (n = outp_configure_vec; n; n = n->next)
159 if (n->source == OUTP_S_COMMAND_LINE)
160 msg (ME, _("Unknown output driver `%s'."), n->name);
162 msg (IE, _("Output driver `%s' referenced but never defined."), n->name);
163 outp_configure_clear ();
166 /* Searches outp_configure_vec for the name between BP and EP
167 exclusive. If found, it is deleted, then replaced by the names
168 given in EP+1, if any. */
170 expand_name (char *bp, char *ep)
172 struct outp_names *n = search_names (bp, ep);
180 while (isspace ((unsigned char) *bp))
183 while (*ep && !isspace ((unsigned char) *ep))
187 if (!search_names (bp, ep))
188 add_name (bp, ep, OUTP_S_INIT_FILE);
193 /* Looks for a macro with key KEY, and returns the corresponding value
194 if found, or NULL if not. */
196 find_defn_value (const char *key)
198 static char buf[INT_STRLEN_BOUND (int) + 1];
201 for (d = outp_macros; d; d = d->next)
202 if (!strcmp (key, d->key))
203 return ds_c_str(&d->value);
204 if (!strcmp (key, "viewwidth"))
206 sprintf (buf, "%d", get_viewwidth ());
209 else if (!strcmp (key, "viewlength"))
211 sprintf (buf, "%d", get_viewlength ());
218 /* Initializes global variables. */
222 extern struct outp_class ascii_class;
223 extern struct outp_class postscript_class;
224 extern struct outp_class epsf_class;
225 extern struct outp_class html_class;
227 char def[] = "default";
229 add_class (&html_class);
230 add_class (&epsf_class);
231 add_class (&postscript_class);
232 add_class (&ascii_class);
234 add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
237 /* Deletes all the output macros. */
241 struct outp_defn *d, *next;
243 for (d = outp_macros; d; d = next)
247 ds_destroy (&d->value);
253 init_default_drivers (void)
255 msg (MM, _("Using default output driver configuration."));
256 configure_driver ("list-ascii", "ascii", "listing",
257 "length=66 width=79 char-set=ascii "
258 "output-file=\"pspp.list\" "
259 "bold-on=\"\" italic-on=\"\" bold-italic-on=\"\"");
262 /* Reads the initialization file; initializes
265 outp_read_devices (void)
273 struct file_locator where;
275 init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
277 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
280 where.filename = init_fn;
281 where.line_number = 0;
282 err_push_file_locator (&where);
284 ds_init (&line, 128);
288 msg (IE, _("Cannot find output initialization file. "
289 "Use `-vvvvv' to view search path."));
293 msg (VM (1), _("%s: Opening device description file..."), init_fn);
294 f = fopen (init_fn, "r");
297 msg (IE, _("Opening %s: %s."), init_fn, strerror (errno));
305 if (!ds_get_config_line (f, &line, &where.line_number))
308 msg (ME, _("Reading %s: %s."), init_fn, strerror (errno));
311 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
312 if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
313 outp_configure_macro (&cp[7]);
317 for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
319 expand_name (cp, ep);
322 struct outp_names *n = search_names (cp, ep);
325 configure_driver_line (&line);
330 msg (IS, _("Syntax error."));
335 check_configure_vec ();
338 err_pop_file_locator (&where);
339 if (f && -1 == fclose (f))
340 msg (MW, _("Closing %s: %s."), init_fn, strerror (errno));
347 msg (VM (2), _("Device definition file read successfully."));
348 if (outp_driver_list == NULL)
349 msg (MW, _("No output drivers are active."));
352 msg (VM (1), _("Error reading device definition file."));
354 if (!result || outp_driver_list == NULL)
355 init_default_drivers ();
358 /* Clear the list of drivers to configure. */
360 outp_configure_clear (void)
362 struct outp_names *n, *next;
364 for (n = outp_configure_vec; n; n = next)
370 outp_configure_vec = NULL;
373 /* Adds the name BP to the list of drivers to configure into
376 outp_configure_add (char *bp)
378 char *ep = &bp[strlen (bp)];
379 if (!search_names (bp, ep))
380 add_name (bp, ep, OUTP_S_COMMAND_LINE);
383 /* Defines one configuration macro based on the text in BP, which
384 should be of the form `KEY=VALUE'. */
386 outp_configure_macro (char *bp)
391 while (isspace ((unsigned char) *bp))
394 while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
397 d = xmalloc (sizeof *d);
398 d->key = xmalloc (ep - bp + 1);
399 memcpy (d->key, bp, ep - bp);
402 /* Earlier definitions for a particular KEY override later ones. */
403 if (find_defn_value (d->key))
412 while (isspace ((unsigned char) *ep))
415 ds_create(&d->value, ep);
416 fn_interp_vars(&d->value, find_defn_value);
417 d->next = outp_macros;
420 outp_macros->prev = d;
424 /* Destroys all the drivers in driver list *DL and sets *DL to
427 destroy_list (struct outp_driver ** dl)
429 struct outp_driver *d, *next;
431 for (d = *dl; d; d = next)
440 /* Closes all the output drivers. */
444 struct outp_driver_class_list *n = outp_class_list ;
445 destroy_list (&outp_driver_list);
449 struct outp_driver_class_list *next = n->next;
453 outp_class_list = NULL;
458 free (outp_subtitle);
459 outp_subtitle = NULL;
462 /* Display on stdout a list of all registered driver classes. */
464 outp_list_classes (void)
466 int width = get_viewwidth();
467 struct outp_driver_class_list *c;
469 printf (_("Driver classes:\n\t"));
471 for (c = outp_class_list; c; c = c->next)
473 if ((int) strlen (c->class->name) + 1 > width)
476 width = get_viewwidth() - 8;
480 fputs (c->class->name, stdout);
485 static int op_token; /* `=', 'a', 0. */
486 static struct string op_tokstr;
487 static const char *prog;
489 /* Parses a token from prog into op_token, op_tokstr. Sets op_token
490 to '=' on an equals sign, to 'a' on a string or identifier token,
491 or to 0 at end of line. Returns the new op_token. */
497 msg (IS, _("Syntax error."));
501 while (isspace ((unsigned char) *prog))
513 ds_clear (&op_tokstr);
515 if (*prog == '\'' || *prog == '"')
519 while (*prog && *prog != quote)
522 ds_putc (&op_tokstr, *prog++);
528 assert ((int) *prog); /* How could a line end in `\'? */
577 while (*prog >= '0' && *prog <= '7')
578 c = c * 8 + *prog++ - '0';
585 while (isxdigit ((unsigned char) *prog))
588 if (isdigit ((unsigned char) *prog))
591 c += (tolower ((unsigned char) (*prog))
598 msg (IS, _("Syntax error in string constant."));
601 ds_putc (&op_tokstr, (unsigned char) c);
607 while (*prog && !isspace ((unsigned char) *prog) && *prog != '=')
608 ds_putc (&op_tokstr, *prog++);
615 /* Applies the user-specified options in string S to output driver D
616 (at configuration time). */
618 parse_options (const char *s, struct outp_driver * d)
623 ds_init (&op_tokstr, 64);
630 msg (IS, _("Syntax error in options."));
634 ds_truncate (&op_tokstr, 64);
635 strcpy (key, ds_c_str (&op_tokstr));
640 msg (IS, _("Syntax error in options (`=' expected)."));
647 msg (IS, _("Syntax error in options (value expected after `=')."));
650 d->class->option (d, key, &op_tokstr);
652 ds_destroy (&op_tokstr);
655 /* Find the driver in outp_driver_list with name NAME. */
656 static struct outp_driver *
657 find_driver (char *name)
659 struct outp_driver *d;
661 for (d = outp_driver_list; d; d = d->next)
662 if (!strcmp (d->name, name))
667 /* String S is in format:
668 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
669 Adds a driver to outp_driver_list pursuant to the specification
672 configure_driver (const char *driver_name, const char *class_name,
673 const char *device_type, const char *options)
675 struct outp_driver *d = NULL, *iter;
676 struct outp_driver_class_list *c = NULL;
678 d = xmalloc (sizeof *d);
680 d->name = xstrdup (driver_name);
683 d->next = d->prev = NULL;
684 d->device = OUTP_DEV_NONE;
687 for (c = outp_class_list; c; c = c->next)
688 if (!strcmp (c->class->name, class_name))
692 msg (IS, _("Unknown output driver class `%s'."), class_name);
697 if (!c->ref_count && !d->class->open_global (d->class))
699 msg (IS, _("Can't initialize output driver class `%s'."),
704 if (!d->class->preopen_driver (d))
706 msg (IS, _("Can't initialize output driver `%s' of class `%s'."),
707 d->name, d->class->name);
712 if (device_type != NULL)
714 char *copy = xstrdup (device_type);
717 for (type = strtok_r (copy, " \t\r\v", &sp); type;
718 type = strtok_r (NULL, " \t\r\v", &sp))
720 if (!strcmp (type, "listing"))
721 d->device |= OUTP_DEV_LISTING;
722 else if (!strcmp (type, "screen"))
723 d->device |= OUTP_DEV_SCREEN;
724 else if (!strcmp (type, "printer"))
725 d->device |= OUTP_DEV_PRINTER;
728 msg (IS, _("Unknown device type `%s'."), type);
738 parse_options (options, d);
739 if (!d->class->postopen_driver (d))
741 msg (IS, _("Can't complete initialization of output driver `%s' of "
742 "class `%s'."), d->name, d->class->name);
746 /* Find like-named driver and delete. */
747 iter = find_driver (d->name);
749 destroy_driver (iter);
752 d->next = outp_driver_list;
754 if (outp_driver_list)
755 outp_driver_list->prev = d;
756 outp_driver_list = d;
765 /* String LINE is in format:
766 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
767 Adds a driver to outp_driver_list pursuant to the specification
770 configure_driver_line (struct string *line)
772 struct string tokens[4];
776 fn_interp_vars (line, find_defn_value);
779 for (i = 0; i < 4; i++)
781 struct string *token = &tokens[i];
783 ds_separate (line, token, i < 3 ? ":" : "", &save_idx);
784 ds_trim_spaces (token);
787 if (!ds_is_empty (&tokens[0]) && !ds_is_empty (&tokens[1]))
788 configure_driver (ds_c_str (&tokens[0]), ds_c_str (&tokens[1]),
789 ds_c_str (&tokens[2]), ds_c_str (&tokens[3]));
791 msg (IS, _("Driver definition line missing driver name or class name"));
793 for (i = 0; i < 4; i++)
794 ds_destroy (&tokens[i]);
797 /* Destroys output driver D. */
799 destroy_driver (struct outp_driver *d)
802 d->class->close_page (d);
805 struct outp_driver_class_list *c;
808 d->class->close_driver (d);
810 for (c = outp_class_list; c; c = c->next)
811 if (c->class == d->class)
816 if (c->ref_count == 0)
818 if (!d->class->close_global (d->class))
819 msg (IS, _("Can't deinitialize output driver class `%s'."),
825 /* Remove this driver from the global driver list. */
827 d->prev->next = d->next;
829 d->next->prev = d->prev;
830 if (d == outp_driver_list)
831 outp_driver_list = d->next;
835 option_cmp (const void *a, const void *b)
837 const struct outp_option *o1 = a;
838 const struct outp_option *o2 = b;
839 return strcmp (o1->keyword, o2->keyword);
842 /* Tries to match S as one of the keywords in TAB, with corresponding
843 information structure INFO. Returns category code or 0 on failure;
844 if category code is negative then stores subcategory in *SUBCAT. */
846 outp_match_keyword (const char *s, struct outp_option *tab,
847 struct outp_option_info *info, int *subcat)
850 struct outp_option *oip;
852 /* Form hash table. */
853 if (NULL == info->initial)
858 struct outp_option *ptr[255], **oip;
860 for (count = 0; tab[count].keyword[0]; count++)
864 qsort (tab, count, sizeof *tab, option_cmp);
868 *cp = tab[0].keyword[0];
870 for (i = 0; i < count; i++)
871 if (tab[i].keyword[0] != *cp)
873 *++cp = tab[i].keyword[0];
878 info->initial = xstrdup (s);
879 info->options = xnmalloc (cp - s, sizeof *info->options);
880 memcpy (info->options, ptr, sizeof *info->options * (cp - s));
884 oip = *info->options;
888 cp = strchr (info->initial, s[0]);
892 printf (_("Trying to find keyword `%s'...\n"), s);
894 oip = info->options[cp - info->initial];
895 while (oip->keyword[0] == s[0])
898 printf ("- %s\n", oip->keyword);
900 if (!strcmp (s, oip->keyword))
903 *subcat = oip->subcat;
912 /* Encapsulate two characters in a single int. */
913 #define TWO_CHARS(A, B) \
916 /* Determines the size of a dimensional measurement and returns the
917 size in units of 1/72000". Units if not specified explicitly are
918 inches for values under 50, millimeters otherwise. Returns 0,
919 stores NULL to *TAIL on error; otherwise returns dimension, stores
922 outp_evaluate_dimension (char *dimen, char **tail)
928 value = strtod (s, &ptail);
935 b = strtod (s, &ptail);
936 if (b <= 0.0 || ptail == s)
941 c = strtod (s, &ptail);
942 if (c <= 0.0 || ptail == s)
952 else if (*ptail == '/')
956 b = strtod (s, &ptail);
957 if (b <= 0.0 || ptail == s)
964 if (*s == 0 || isspace ((unsigned char) *s))
969 value *= 72000 / 25.4;
975 /* Standard TeX units are supported. */
979 switch (TWO_CHARS (s[0], s[1]))
981 case TWO_CHARS ('p', 't'):
982 factor = 72000 / 72.27;
984 case TWO_CHARS ('p', 'c'):
985 factor = 72000 / 72.27 * 12.0;
987 case TWO_CHARS ('i', 'n'):
990 case TWO_CHARS ('b', 'p'):
991 factor = 72000 / 72.0;
993 case TWO_CHARS ('c', 'm'):
994 factor = 72000 / 2.54;
996 case TWO_CHARS ('m', 'm'):
997 factor = 72000 / 25.4;
999 case TWO_CHARS ('d', 'd'):
1000 factor = 72000 / 72.27 * 1.0700086;
1002 case TWO_CHARS ('c', 'c'):
1003 factor = 72000 / 72.27 * 12.840104;
1005 case TWO_CHARS ('s', 'p'):
1006 factor = 72000 / 72.27 / 65536.0;
1009 msg (SE, _("Unit \"%s\" is unknown in dimension \"%s\"."), s, dimen);
1024 msg (SE, _("Bad dimension \"%s\"."), dimen);
1028 /* Stores the dimensions in 1/72000" units of paper identified by
1029 SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each
1030 of HORZ and VERT are dimensions, into *H and *V. Return nonzero on
1033 internal_get_paper_size (char *size, int *h, int *v)
1037 while (isspace ((unsigned char) *size))
1039 *h = outp_evaluate_dimension (size, &tail);
1042 while (isspace ((unsigned char) *tail))
1046 else if (*tail == 'b' && tail[1] == 'y')
1050 msg (SE, _("`x' expected in paper size `%s'."), size);
1053 *v = outp_evaluate_dimension (tail, &tail);
1056 while (isspace ((unsigned char) *tail))
1060 msg (SE, _("Trailing garbage `%s' on paper size `%s'."), tail, size);
1067 /* Stores the dimensions, in 1/72000" units, of paper identified by
1068 SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x
1069 V', or it may be a case-insensitive paper identifier, which is
1070 looked up in the `papersize' configuration file. Returns nonzero
1071 on success. May modify SIZE. */
1072 /* Don't read further unless you've got a strong stomach. */
1074 outp_get_paper_size (char *size, int *h, int *v)
1083 static struct paper_size cache[4];
1090 struct file_locator where;
1094 int min_value, min_index;
1098 while (isspace ((unsigned char) *size))
1100 if (isdigit ((unsigned char) *size))
1101 return internal_get_paper_size (size, h, v);
1105 while (isspace ((unsigned char) *ep) && ep >= size)
1109 msg (SE, _("Paper size name must not be empty."));
1118 for (i = 0; i < 4; i++)
1119 if (cache[i].name != NULL && !strcasecmp (cache[i].name, size))
1127 pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE",
1129 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
1133 where.filename = pprsz_fn;
1134 where.line_number = 0;
1135 err_push_file_locator (&where);
1136 ds_init (&line, 128);
1138 if (pprsz_fn == NULL)
1140 msg (IE, _("Cannot find `papersize' configuration file."));
1144 msg (VM (1), _("%s: Opening paper size definition file..."), pprsz_fn);
1145 f = fopen (pprsz_fn, "r");
1148 msg (IE, _("Opening %s: %s."), pprsz_fn, strerror (errno));
1156 if (!ds_get_config_line (f, &line, &where.line_number))
1159 msg (ME, _("Reading %s: %s."), pprsz_fn, strerror (errno));
1162 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
1167 for (bp = ep = cp + 1; *ep && *ep != '"'; ep++);
1171 if (0 != strcasecmp (bp, size))
1174 for (cp = ep + 1; isspace ((unsigned char) *cp); cp++);
1177 size = xmalloc (ep - bp + 1);
1186 msg (IE, _("Syntax error in paper size definition."));
1189 /* We found the one we want! */
1190 result = internal_get_paper_size (size, h, v);
1193 min_value = cache[0].use;
1195 for (i = 1; i < 4; i++)
1196 if (cache[0].use < min_value)
1198 min_value = cache[i].use;
1201 free (cache[min_index].name);
1202 cache[min_index].name = xstrdup (size);
1203 cache[min_index].use = use;
1204 cache[min_index].h = *h;
1205 cache[min_index].v = *v;
1209 err_pop_file_locator (&where);
1215 msg (VM (2), _("Paper size definition file read successfully."));
1217 msg (VM (1), _("Error reading paper size definition file."));
1222 /* If D is NULL, returns the first enabled driver if any, NULL if
1223 none. Otherwise D must be the last driver returned by this
1224 function, in which case the next enabled driver is returned or NULL
1225 if that was the last. */
1226 struct outp_driver *
1227 outp_drivers (struct outp_driver *d)
1230 struct outp_driver *orig_d = d;
1236 d = outp_driver_list;
1243 || (d->device & disabled_devices) != d->device)))
1250 /* Enables (if ENABLE is nonzero) or disables (if ENABLE is zero) the
1251 device(s) given in mask DEVICE. */
1253 outp_enable_device (int enable, int device)
1256 disabled_devices &= ~device;
1258 disabled_devices |= device;
1261 /* Ejects the paper on device D, if the page is not blank. */
1263 outp_eject_page (struct outp_driver *d)
1265 if (d->page_open == 0)
1270 d->cp_x = d->cp_y = 0;
1272 if (d->class->close_page (d) == 0)
1273 msg (ME, _("Error closing page on %s device of %s class."),
1274 d->name, d->class->name);
1275 if (d->class->open_page (d) == 0)
1277 msg (ME, _("Error opening page on %s device of %s class."),
1278 d->name, d->class->name);
1285 /* Returns the width of string S, in device units, when output on
1288 outp_string_width (struct outp_driver *d, const char *s)
1290 struct outp_text text;
1292 text.options = OUTP_T_JUST_LEFT;
1293 ls_init (&text.s, (char *) s, strlen (s));
1294 d->class->text_metrics (d, &text);