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
38 #define _(msgid) gettext (msgid)
40 /* FIXME? Should the output configuration format be changed to
41 drivername:classname:devicetype:options, where devicetype is zero
42 or more of screen, printer, listing? */
44 /* FIXME: Have the reentrancy problems been solved? */
46 /* Where the output driver name came from. */
49 OUTP_S_COMMAND_LINE, /* Specified by the user. */
50 OUTP_S_INIT_FILE /* `default' or the init file. */
53 /* Names the output drivers to be used. */
56 char *name; /* Name of the output driver. */
57 int source; /* OUTP_S_* */
58 struct outp_names *next, *prev;
61 /* Defines an init file macro. */
66 struct outp_defn *next, *prev;
69 static struct outp_defn *outp_macros;
70 static struct outp_names *outp_configure_vec;
72 struct outp_driver_class_list *outp_class_list;
73 struct outp_driver *outp_driver_list;
78 /* A set of OUTP_DEV_* bits indicating the devices that are
80 static int disabled_devices;
82 static void destroy_driver (struct outp_driver *);
83 static void configure_driver (char *);
86 /* This mechanism attempts to catch reentrant use of outp_driver_list. */
87 static int iterating_driver_list;
89 #define reentrancy() msg (FE, _("Attempt to iterate driver list reentrantly."))
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;
99 new_list->ref_count = 0;
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_DIGITS + 1];
207 for (d = outp_macros; d; d = d->next)
208 if (!strcmp (key, d->key))
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;
230 extern struct outp_class postscript_class;
231 extern struct outp_class epsf_class;
233 extern struct outp_class html_class;
234 extern struct outp_class devind_class;
236 char def[] = "default";
239 add_class (&html_class);
242 add_class (&epsf_class);
243 add_class (&postscript_class);
245 add_class (&devind_class);
246 add_class (&ascii_class);
248 add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
253 /* Deletes all the output macros. */
257 struct outp_defn *d, *next;
259 for (d = outp_macros; d; d = next)
268 /* Reads the initialization file; initializes outp_driver_list. */
270 outp_read_devices (void)
278 struct file_locator where;
281 if (iterating_driver_list)
285 init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
287 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
290 where.filename = init_fn;
291 where.line_number = 0;
292 err_push_file_locator (&where);
294 ds_init (&line, 128);
298 msg (IE, _("Cannot find output initialization file. "
299 "Use `-vvvv' to view search path."));
303 msg (VM (1), _("%s: Opening device description file..."), init_fn);
304 f = fopen (init_fn, "r");
307 msg (IE, _("Opening %s: %s."), init_fn, strerror (errno));
315 if (!ds_get_config_line (f, &line, &where))
318 msg (ME, _("Reading %s: %s."), init_fn, strerror (errno));
321 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
322 if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
323 outp_configure_macro (&cp[7]);
327 for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
329 expand_name (cp, ep);
332 struct outp_names *n = search_names (cp, ep);
335 configure_driver (cp);
340 msg (IS, _("Syntax error."));
345 check_configure_vec ();
348 err_pop_file_locator (&where);
349 if (f && -1 == fclose (f))
350 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 (2), _("Device definition file read successfully."));
360 msg (VM (1), _("Error reading device definition file."));
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))
420 d->value = fn_interp_vars (ep, find_defn_value);
421 d->next = outp_macros;
424 outp_macros->prev = d;
428 /* Destroys all the drivers in driver list *DL and sets *DL to
431 destroy_list (struct outp_driver ** dl)
433 struct outp_driver *d, *next;
435 for (d = *dl; d; d = next)
444 /* Closes all the output drivers. */
448 struct outp_driver_class_list *n = outp_class_list ;
450 if (iterating_driver_list)
453 destroy_list (&outp_driver_list);
457 struct outp_driver_class_list *next = n->next;
461 outp_class_list = NULL;
466 free (outp_subtitle);
467 outp_subtitle = NULL;
472 /* Display on stdout a list of all registered driver classes. */
474 outp_list_classes (void)
476 int width = get_viewwidth();
477 struct outp_driver_class_list *c;
479 printf (_("Driver classes:\n\t"));
481 for (c = outp_class_list; c; c = c->next)
483 if ((int) strlen (c->class->name) + 1 > width)
486 width = get_viewwidth() - 8;
490 fputs (c->class->name, stdout);
495 static int op_token; /* `=', 'a', 0. */
496 static struct string op_tokstr;
499 /* Parses a token from prog into op_token, op_tokstr. Sets op_token
500 to '=' on an equals sign, to 'a' on a string or identifier token,
501 or to 0 at end of line. Returns the new op_token. */
507 msg (IS, _("Syntax error."));
511 while (isspace ((unsigned char) *prog))
523 ds_clear (&op_tokstr);
525 if (*prog == '\'' || *prog == '"')
529 while (*prog && *prog != quote)
532 ds_putc (&op_tokstr, *prog++);
538 assert ((int) *prog); /* How could a line end in `\'? */
587 while (*prog >= '0' && *prog <= '7')
588 c = c * 8 + *prog++ - '0';
595 while (isxdigit ((unsigned char) *prog))
598 if (isdigit ((unsigned char) *prog))
601 c += (tolower ((unsigned char) (*prog))
608 msg (IS, _("Syntax error in string constant."));
611 ds_putc (&op_tokstr, (unsigned char) c);
617 while (*prog && !isspace ((unsigned char) *prog) && *prog != '=')
618 ds_putc (&op_tokstr, *prog++);
625 /* Applies the user-specified options in string S to output driver D
626 (at configuration time). */
628 parse_options (char *s, struct outp_driver * d)
633 ds_init (&op_tokstr, 64);
640 msg (IS, _("Syntax error in options."));
644 ds_truncate (&op_tokstr, 64);
645 strcpy (key, ds_c_str (&op_tokstr));
650 msg (IS, _("Syntax error in options (`=' expected)."));
657 msg (IS, _("Syntax error in options (value expected after `=')."));
660 d->class->option (d, key, &op_tokstr);
662 ds_destroy (&op_tokstr);
665 /* Find the driver in outp_driver_list with name NAME. */
666 static struct outp_driver *
667 find_driver (char *name)
669 struct outp_driver *d;
672 if (iterating_driver_list)
675 for (d = outp_driver_list; d; d = d->next)
676 if (!strcmp (d->name, name))
681 /* Tokenize string S into colon-separated fields, removing leading and
682 trailing whitespace on tokens. Returns a pointer to the
683 null-terminated token, which is formed by setting a NUL character
684 into the string. After the first call, subsequent calls should set
685 S to NULL. CP should be consistent across calls. Returns NULL
686 after all fields have been used up.
688 FIXME: Should ignore colons inside double quotes. */
690 colon_tokenize (char *s, char **cp)
700 token = s += strspn (s, " \t\v\r");
701 *cp = strchr (s, ':');
703 s = *cp = strchr (s, 0);
706 while (s > token && strchr (" \t\v\r", s[-1]))
712 /* String S is in format:
713 DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
714 Adds a driver to outp_driver_list pursuant to the specification
717 configure_driver (char *s)
720 struct outp_driver *d = NULL, *iter;
721 struct outp_driver_class_list *c = NULL;
723 s = fn_interp_vars (s, find_defn_value);
726 token = colon_tokenize (s, &cp);
729 msg (IS, _("Driver name expected."));
733 d = xmalloc (sizeof *d);
736 d->name = xstrdup (token);
740 d->next = d->prev = NULL;
742 d->device = OUTP_DEV_NONE;
747 token = colon_tokenize (NULL, &cp);
750 msg (IS, _("Class name expected."));
754 for (c = outp_class_list; c; c = c->next)
755 if (!strcmp (c->class->name, token))
759 msg (IS, _("Unknown output driver class `%s'."), token);
764 if (!c->ref_count && !d->class->open_global (d->class))
766 msg (IS, _("Can't initialize output driver class `%s'."),
771 if (!d->class->preopen_driver (d))
773 msg (IS, _("Can't initialize output driver `%s' of class `%s'."),
774 d->name, d->class->name);
779 token = colon_tokenize (NULL, &cp);
784 for (type = strtok_r (token, " \t\r\v", &sp); type;
785 type = strtok_r (NULL, " \t\r\v", &sp))
787 if (!strcmp (type, "listing"))
788 d->device |= OUTP_DEV_LISTING;
789 else if (!strcmp (type, "screen"))
790 d->device |= OUTP_DEV_SCREEN;
791 else if (!strcmp (type, "printer"))
792 d->device |= OUTP_DEV_PRINTER;
795 msg (IS, _("Unknown device type `%s'."), type);
802 token = colon_tokenize (NULL, &cp);
804 parse_options (token, d);
805 if (!d->class->postopen_driver (d))
807 msg (IS, _("Can't complete initialization of output driver `%s' of "
808 "class `%s'."), d->name, d->class->name);
812 /* Find like-named driver and delete. */
813 iter = find_driver (d->name);
815 destroy_driver (iter);
818 d->next = outp_driver_list;
820 if (outp_driver_list)
821 outp_driver_list->prev = d;
822 outp_driver_list = d;
832 /* Destroys output driver D. */
834 destroy_driver (struct outp_driver *d)
837 d->class->close_page (d);
840 struct outp_driver_class_list *c;
843 d->class->close_driver (d);
845 for (c = outp_class_list; c; c = c->next)
846 if (c->class == d->class)
851 if (c->ref_count == 0)
853 if (!d->class->close_global (d->class))
854 msg (IS, _("Can't deinitialize output driver class `%s'."),
860 /* Remove this driver from the global driver list. */
862 d->prev->next = d->next;
864 d->next->prev = d->prev;
865 if (d == outp_driver_list)
866 outp_driver_list = d->next;
870 option_cmp (const void *a, const void *b)
872 const struct outp_option *o1 = a;
873 const struct outp_option *o2 = b;
874 return strcmp (o1->keyword, o2->keyword);
877 /* Tries to match S as one of the keywords in TAB, with corresponding
878 information structure INFO. Returns category code or 0 on failure;
879 if category code is negative then stores subcategory in *SUBCAT. */
881 outp_match_keyword (const char *s, struct outp_option *tab,
882 struct outp_option_info *info, int *subcat)
885 struct outp_option *oip;
887 /* Form hash table. */
888 if (NULL == info->initial)
893 struct outp_option *ptr[255], **oip;
895 for (count = 0; tab[count].keyword[0]; count++)
899 qsort (tab, count, sizeof *tab, option_cmp);
903 *cp = tab[0].keyword[0];
905 for (i = 0; i < count; i++)
906 if (tab[i].keyword[0] != *cp)
908 *++cp = tab[i].keyword[0];
913 info->initial = xstrdup (s);
914 info->options = xmalloc (sizeof *info->options * (cp - s));
915 memcpy (info->options, ptr, sizeof *info->options * (cp - s));
919 oip = *info->options;
923 cp = strchr (info->initial, s[0]);
927 printf (_("Trying to find keyword `%s'...\n"), s);
929 oip = info->options[cp - info->initial];
930 while (oip->keyword[0] == s[0])
933 printf ("- %s\n", oip->keyword);
935 if (!strcmp (s, oip->keyword))
938 *subcat = oip->subcat;
947 /* Encapsulate two characters in a single int. */
948 #define TWO_CHARS(A, B) \
951 /* Determines the size of a dimensional measurement and returns the
952 size in units of 1/72000". Units if not specified explicitly are
953 inches for values under 50, millimeters otherwise. Returns 0,
954 stores NULL to *TAIL on error; otherwise returns dimension, stores
957 outp_evaluate_dimension (char *dimen, char **tail)
963 value = strtod (s, &ptail);
970 b = strtod (s, &ptail);
971 if (b <= 0.0 || ptail == s)
976 c = strtod (s, &ptail);
977 if (c <= 0.0 || ptail == s)
987 else if (*ptail == '/')
991 b = strtod (s, &ptail);
992 if (b <= 0.0 || ptail == s)
999 if (*s == 0 || isspace ((unsigned char) *s))
1004 value *= 72000 / 25.4;
1010 /* Standard TeX units are supported. */
1012 factor = 72000, s++;
1014 switch (TWO_CHARS (s[0], s[1]))
1016 case TWO_CHARS ('p', 't'):
1017 factor = 72000 / 72.27;
1019 case TWO_CHARS ('p', 'c'):
1020 factor = 72000 / 72.27 * 12.0;
1022 case TWO_CHARS ('i', 'n'):
1025 case TWO_CHARS ('b', 'p'):
1026 factor = 72000 / 72.0;
1028 case TWO_CHARS ('c', 'm'):
1029 factor = 72000 / 2.54;
1031 case TWO_CHARS ('m', 'm'):
1032 factor = 72000 / 25.4;
1034 case TWO_CHARS ('d', 'd'):
1035 factor = 72000 / 72.27 * 1.0700086;
1037 case TWO_CHARS ('c', 'c'):
1038 factor = 72000 / 72.27 * 12.840104;
1040 case TWO_CHARS ('s', 'p'):
1041 factor = 72000 / 72.27 / 65536.0;
1044 msg (SE, _("Unit \"%s\" is unknown in dimension \"%s\"."), s, dimen);
1059 msg (SE, _("Bad dimension \"%s\"."), dimen);
1063 /* Stores the dimensions in 1/72000" units of paper identified by
1064 SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each
1065 of HORZ and VERT are dimensions, into *H and *V. Return nonzero on
1068 internal_get_paper_size (char *size, int *h, int *v)
1072 while (isspace ((unsigned char) *size))
1074 *h = outp_evaluate_dimension (size, &tail);
1077 while (isspace ((unsigned char) *tail))
1081 else if (*tail == 'b' && tail[1] == 'y')
1085 msg (SE, _("`x' expected in paper size `%s'."), size);
1088 *v = outp_evaluate_dimension (tail, &tail);
1091 while (isspace ((unsigned char) *tail))
1095 msg (SE, _("Trailing garbage `%s' on paper size `%s'."), tail, size);
1102 /* Stores the dimensions, in 1/72000" units, of paper identified by
1103 SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x
1104 V', or it may be a case-insensitive paper identifier, which is
1105 looked up in the `papersize' configuration file. Returns nonzero
1106 on success. May modify SIZE. */
1107 /* Don't read further unless you've got a strong stomach. */
1109 outp_get_paper_size (char *size, int *h, int *v)
1118 static struct paper_size cache[4];
1125 struct file_locator where;
1129 int min_value, min_index;
1133 while (isspace ((unsigned char) *size))
1135 if (isdigit ((unsigned char) *size))
1136 return internal_get_paper_size (size, h, v);
1140 while (isspace ((unsigned char) *ep) && ep >= size)
1144 msg (SE, _("Paper size name must not be empty."));
1153 for (i = 0; i < 4; i++)
1154 if (cache[i].name != NULL && !strcasecmp (cache[i].name, size))
1162 pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE",
1164 fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
1168 where.filename = pprsz_fn;
1169 where.line_number = 0;
1170 err_push_file_locator (&where);
1171 ds_init (&line, 128);
1173 if (pprsz_fn == NULL)
1175 msg (IE, _("Cannot find `papersize' configuration file."));
1179 msg (VM (1), _("%s: Opening paper size definition file..."), pprsz_fn);
1180 f = fopen (pprsz_fn, "r");
1183 msg (IE, _("Opening %s: %s."), pprsz_fn, strerror (errno));
1191 if (!ds_get_config_line (f, &line, &where))
1194 msg (ME, _("Reading %s: %s."), pprsz_fn, strerror (errno));
1197 for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
1202 for (bp = ep = cp + 1; *ep && *ep != '"'; ep++);
1206 if (0 != strcasecmp (bp, size))
1209 for (cp = ep + 1; isspace ((unsigned char) *cp); cp++);
1212 size = xmalloc (ep - bp + 1);
1221 msg (IE, _("Syntax error in paper size definition."));
1224 /* We found the one we want! */
1225 result = internal_get_paper_size (size, h, v);
1228 min_value = cache[0].use;
1230 for (i = 1; i < 4; i++)
1231 if (cache[0].use < min_value)
1233 min_value = cache[i].use;
1236 free (cache[min_index].name);
1237 cache[min_index].name = xstrdup (size);
1238 cache[min_index].use = use;
1239 cache[min_index].h = *h;
1240 cache[min_index].v = *v;
1244 err_pop_file_locator (&where);
1250 msg (VM (2), _("Paper size definition file read successfully."));
1252 msg (VM (1), _("Error reading paper size definition file."));
1257 /* If D is NULL, returns the first enabled driver if any, NULL if
1258 none. Otherwise D must be the last driver returned by this
1259 function, in which case the next enabled driver is returned or NULL
1260 if that was the last. */
1261 struct outp_driver *
1262 outp_drivers (struct outp_driver *d)
1264 #if GLOBAL_DEBUGGING
1265 struct outp_driver *orig_d = d;
1271 d = outp_driver_list;
1278 || (d->device & disabled_devices) != d->device)))
1282 #if GLOBAL_DEBUGGING
1285 if (iterating_driver_list++)
1288 else if (orig_d && !d)
1290 assert (iterating_driver_list == 1);
1291 iterating_driver_list = 0;
1298 /* Enables (if ENABLE is nonzero) or disables (if ENABLE is zero) the
1299 device(s) given in mask DEVICE. */
1301 outp_enable_device (int enable, int device)
1304 disabled_devices &= ~device;
1306 disabled_devices |= device;
1309 /* Ejects the paper on device D, if the page is not blank. */
1311 outp_eject_page (struct outp_driver *d)
1313 if (d->page_open == 0)
1318 d->cp_x = d->cp_y = 0;
1320 if (d->class->close_page (d) == 0)
1321 msg (ME, _("Error closing page on %s device of %s class."),
1322 d->name, d->class->name);
1323 if (d->class->open_page (d) == 0)
1325 msg (ME, _("Error opening page on %s device of %s class."),
1326 d->name, d->class->name);
1333 /* Returns the width of string S, in device units, when output on
1336 outp_string_width (struct outp_driver *d, const char *s)
1338 struct outp_text text;
1340 text.options = OUTP_T_JUST_LEFT;
1341 ls_init (&text.s, (char *) s, strlen (s));
1342 d->class->text_metrics (d, &text);