X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Foutput.c;h=ab91ed6f5f0929d4d8d3d1ae455aa14886ea835b;hb=9b94efd7513afdb12a6023024e00e50801532fee;hp=3f79a6a02afb0bf7094b5c258aceee9de380b774;hpb=2048ef1bcb81fa766ccff3bb1cf30a886316983c;p=pspp-builds.git diff --git a/src/output/output.c b/src/output/output.c index 3f79a6a0..ab91ed6f 100644 --- a/src/output/output.c +++ b/src/output/output.c @@ -1,37 +1,38 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "output.h" -#include "message.h" -#include -#include -#include + #include -#include "alloc.h" -#include "message.h" -#include "filename.h" -#include "htmlP.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "error.h" #include "intprops.h" -#include "misc.h" -#include "settings.h" -#include "str.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -61,15 +62,22 @@ struct outp_names struct outp_defn { char *key; - char *value; + struct string value; struct outp_defn *next, *prev; }; static struct outp_defn *outp_macros; static struct outp_names *outp_configure_vec; -struct outp_driver_class_list *outp_class_list; -struct outp_driver *outp_driver_list; +/* A list of driver classes. */ +struct outp_driver_class_list + { + const struct outp_class *class; + struct outp_driver_class_list *next; + }; + +static struct outp_driver_class_list *outp_class_list; +static struct outp_driver *outp_driver_list; char *outp_title; char *outp_subtitle; @@ -79,18 +87,16 @@ char *outp_subtitle; static int disabled_devices; static void destroy_driver (struct outp_driver *); -static void configure_driver_line (char *); -static void configure_driver (const char *, const char *, - const char *, const char *); +static void configure_driver (const struct substring, const struct substring, + const struct substring, const struct substring); /* Add a class to the class list. */ static void -add_class (struct outp_class *class) +add_class (const struct outp_class *class) { struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list); new_list->class = class; - new_list->ref_count = 0; if (!outp_class_list) { @@ -148,8 +154,8 @@ add_name (char *bp, char *ep, int source) outp_configure_vec = n; } -/* Checks that outp_configure_vec is empty, bitches & clears it if it - isn't. */ +/* Checks that outp_configure_vec is empty, complains and clears + it if it isn't. */ static void check_configure_vec (void) { @@ -157,9 +163,10 @@ check_configure_vec (void) for (n = outp_configure_vec; n; n = n->next) if (n->source == OUTP_S_COMMAND_LINE) - msg (ME, _("Unknown output driver `%s'."), n->name); + error (0, 0, _("unknown output driver `%s'"), n->name); else - msg (IE, _("Output driver `%s' referenced but never defined."), n->name); + error (0, 0, _("output driver `%s' referenced but never defined"), + n->name); outp_configure_clear (); } @@ -200,15 +207,15 @@ find_defn_value (const char *key) for (d = outp_macros; d; d = d->next) if (!strcmp (key, d->key)) - return d->value; + return ds_cstr (&d->value); if (!strcmp (key, "viewwidth")) { - sprintf (buf, "%d", get_viewwidth ()); + sprintf (buf, "%d", settings_get_viewwidth ()); return buf; } else if (!strcmp (key, "viewlength")) { - sprintf (buf, "%d", get_viewlength ()); + sprintf (buf, "%d", settings_get_viewlength ()); return buf; } else @@ -220,23 +227,12 @@ void outp_init (void) { extern struct outp_class ascii_class; -#if !NO_POSTSCRIPT extern struct outp_class postscript_class; - extern struct outp_class epsf_class; -#endif -#if !NO_HTML - extern struct outp_class html_class; -#endif char def[] = "default"; -#if !NO_HTML add_class (&html_class); -#endif -#if !NO_POSTSCRIPT - add_class (&epsf_class); add_class (&postscript_class); -#endif add_class (&ascii_class); add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE); @@ -252,19 +248,19 @@ delete_macros (void) { next = d->next; free (d->key); - free (d->value); + ds_destroy (&d->value); free (d); } } static void -init_default_drivers (void) +init_default_drivers (void) { - msg (MM, _("Using default output driver configuration.")); - configure_driver ("list-ascii", "ascii", "listing", - "length=66 width=79 char-set=ascii " - "output-file=\"pspp.list\" " - "bold-on=\"\" italic-on=\"\" bold-italic-on=\"\""); + error (0, 0, _("using default output driver configuration")); + configure_driver (ss_cstr ("list"), + ss_cstr ("ascii"), + ss_cstr ("listing"), + ss_cstr ("length=66 width=79 output-file=\"pspp.list\"")); } /* Reads the initialization file; initializes @@ -278,45 +274,41 @@ outp_read_devices (void) FILE *f = NULL; struct string line; - struct file_locator where; + int line_number; init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE", "devices"), fn_getenv_default ("STAT_OUTPUT_INIT_PATH", - config_path), - NULL); - where.filename = init_fn; - where.line_number = 0; - err_push_file_locator (&where); + config_path)); - ds_init (&line, 128); + ds_init_empty (&line); if (init_fn == NULL) { - msg (IE, _("Cannot find output initialization file. " - "Use `-vvvvv' to view search path.")); + error (0, 0, _("cannot find output initialization file " + "(use `-vv' to view search path)")); goto exit; } - msg (VM (1), _("%s: Opening device description file..."), init_fn); f = fopen (init_fn, "r"); if (f == NULL) { - msg (IE, _("Opening %s: %s."), init_fn, strerror (errno)); + error (0, errno, _("cannot open \"%s\""), init_fn); goto exit; } + line_number = 0; for (;;) { char *cp; - if (!ds_get_config_line (f, &line, &where)) + if (!ds_read_config_line (&line, &line_number, f)) { if (ferror (f)) - msg (ME, _("Reading %s: %s."), init_fn, strerror (errno)); + error (0, errno, _("reading \"%s\""), init_fn); break; } - for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++); + for (cp = ds_cstr (&line); isspace ((unsigned char) *cp); cp++); if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6])) outp_configure_macro (&cp[7]); else if (*cp) @@ -330,12 +322,12 @@ outp_read_devices (void) struct outp_names *n = search_names (cp, ep); if (n) { - configure_driver_line (cp); + outp_configure_driver_line (ds_ss (&line)); delete_name (n); } } else - msg (IS, _("Syntax error.")); + error_at_line (0, 0, init_fn, line_number, _("syntax error")); } } result = 1; @@ -343,21 +335,19 @@ outp_read_devices (void) check_configure_vec (); exit: - err_pop_file_locator (&where); if (f && -1 == fclose (f)) - msg (MW, _("Closing %s: %s."), init_fn, strerror (errno)); + error (0, errno, _("error closing \"%s\""), init_fn); free (init_fn); ds_destroy (&line); delete_macros (); - if (result) + if (result) { - msg (VM (2), _("Device definition file read successfully.")); - if (outp_driver_list == NULL) - msg (MW, _("No output drivers are active.")); + if (outp_driver_list == NULL) + error (0, 0, _("no active output drivers")); } else - msg (VM (1), _("Error reading device definition file.")); + error (0, 0, _("error reading device definition file")); if (!result || outp_driver_list == NULL) init_default_drivers (); @@ -414,12 +404,14 @@ outp_configure_macro (char *bp) free (d); return; } - + if (*ep == '=') ep++; while (isspace ((unsigned char) *ep)) ep++; - d->value = fn_interp_vars (ep, find_defn_value); + + ds_init_cstr (&d->value, ep); + fn_interp_vars (ds_ss (&d->value), find_defn_value, &d->value); d->next = outp_macros; d->prev = NULL; if (outp_macros) @@ -447,10 +439,11 @@ destroy_list (struct outp_driver ** dl) void outp_done (void) { - struct outp_driver_class_list *n = outp_class_list ; + struct outp_driver_class_list *n = outp_class_list ; + outp_configure_clear (); destroy_list (&outp_driver_list); - while (n) + while (n) { struct outp_driver_class_list *next = n->next; free(n); @@ -460,7 +453,7 @@ outp_done (void) free (outp_title); outp_title = NULL; - + free (outp_subtitle); outp_subtitle = NULL; } @@ -469,7 +462,7 @@ outp_done (void) void outp_list_classes (void) { - int width = get_viewwidth(); + int width = settings_get_viewwidth (); struct outp_driver_class_list *c; printf (_("Driver classes:\n\t")); @@ -479,7 +472,7 @@ outp_list_classes (void) if ((int) strlen (c->class->name) + 1 > width) { printf ("\n\t"); - width = get_viewwidth() - 8; + width = settings_get_viewwidth () - 8; } else putc (' ', stdout); @@ -488,174 +481,174 @@ outp_list_classes (void) putc('\n', stdout); } -static int op_token; /* `=', 'a', 0. */ -static struct string op_tokstr; -static const char *prog; +/* Obtains a token from S and advances its position. Errors are + reported against the given DRIVER_NAME. + The token is stored in TOKEN. Returns true if successful, + false on syntax error. -/* Parses a token from prog into op_token, op_tokstr. Sets op_token - to '=' on an equals sign, to 'a' on a string or identifier token, - or to 0 at end of line. Returns the new op_token. */ -static int -tokener (void) + Caller is responsible for skipping leading spaces. */ +static bool +get_option_token (struct substring *s, const char *driver_name, + struct string *token) { - if (op_token == 0) + int c; + + ds_clear (token); + c = ss_get_char (s); + if (c == EOF) { - msg (IS, _("Syntax error.")); - return 0; + error (0, 0, _("syntax error parsing options for \"%s\" driver"), + driver_name); + return false; } - - while (isspace ((unsigned char) *prog)) - prog++; - if (!*prog) + else if (c == '\'' || c == '"') { - op_token = 0; - return 0; + int quote = c; + + for (;;) + { + c = ss_get_char (s); + if (c == quote) + break; + else if (c == EOF) + { + error (0, 0, + _("reached end of options inside quoted string " + "parsing options for \"%s\" driver"), + driver_name); + return false; + } + else if (c != '\\') + ds_put_char (token, c); + else + { + int out; + + c = ss_get_char (s); + switch (c) + { + case '\'': + out = '\''; + break; + case '"': + out = '"'; + break; + case '\\': + out = '\\'; + break; + case 'a': + out = '\a'; + break; + case 'b': + out = '\b'; + break; + case 'f': + out = '\f'; + break; + case 'n': + out = '\n'; + break; + case 'r': + out = '\r'; + break; + case 't': + out = '\t'; + break; + case 'v': + out = '\v'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + out = c - '0'; + while (ss_first (*s) >= '0' && ss_first (*s) <= '7') + out = out * 8 + (ss_get_char (s) - '0'); + break; + case 'x': + case 'X': + out = 0; + while (isxdigit (ss_first (*s))) + { + c = ss_get_char (s); + out *= 16; + if (isdigit (c)) + out += c - '0'; + else + out += tolower (c) - 'a' + 10; + } + break; + default: + error (0, 0, _("syntax error in string constant " + "parsing options for \"%s\" driver"), + driver_name); + return false; + } + ds_put_char (token, out); + } + } } - - if (*prog == '=') - op_token = *prog++; else { - ds_clear (&op_tokstr); - - if (*prog == '\'' || *prog == '"') - { - int quote = *prog++; - - while (*prog && *prog != quote) - { - if (*prog != '\\') - ds_putc (&op_tokstr, *prog++); - else - { - int c; - - prog++; - assert ((int) *prog); /* How could a line end in `\'? */ - switch (*prog++) - { - case '\'': - c = '\''; - break; - case '"': - c = '"'; - break; - case '?': - c = '?'; - break; - case '\\': - c = '\\'; - break; - case '}': - c = '}'; - break; - case 'a': - c = '\a'; - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - c = prog[-1] - '0'; - while (*prog >= '0' && *prog <= '7') - c = c * 8 + *prog++ - '0'; - } - break; - case 'x': - case 'X': - { - c = 0; - while (isxdigit ((unsigned char) *prog)) - { - c *= 16; - if (isdigit ((unsigned char) *prog)) - c += *prog - '0'; - else - c += (tolower ((unsigned char) (*prog)) - - 'a' + 10); - prog++; - } - } - break; - default: - msg (IS, _("Syntax error in string constant.")); - continue; - } - ds_putc (&op_tokstr, (unsigned char) c); - } - } - prog++; - } - else - while (*prog && !isspace ((unsigned char) *prog) && *prog != '=') - ds_putc (&op_tokstr, *prog++); - op_token = 'a'; + for (;;) + { + ds_put_char (token, c); + + c = ss_first (*s); + if (c == EOF || c == '=' || isspace (c)) + break; + ss_advance (s, 1); + } } return 1; } -/* Applies the user-specified options in string S to output driver D - (at configuration time). */ -static void -parse_options (const char *s, struct outp_driver * d) +bool +outp_parse_options (struct substring options, + bool (*callback) (struct outp_driver *, const char *key, + const struct string *value), + struct outp_driver *driver) { - prog = s; - op_token = -1; + struct string key = DS_EMPTY_INITIALIZER; + struct string value = DS_EMPTY_INITIALIZER; + struct substring left = options; + bool ok = true; - ds_init (&op_tokstr, 64); - while (tokener ()) + do { - char key[65]; + ss_ltrim (&left, ss_cstr (CC_SPACES)); + if (ss_is_empty (left)) + break; - if (op_token != 'a') - { - msg (IS, _("Syntax error in options.")); - break; - } + if (!get_option_token (&left, driver->name, &key)) + break; - ds_truncate (&op_tokstr, 64); - strcpy (key, ds_c_str (&op_tokstr)); - - tokener (); - if (op_token != '=') + ss_ltrim (&left, ss_cstr (CC_SPACES)); + if (!ss_match_char (&left, '=')) { - msg (IS, _("Syntax error in options (`=' expected).")); + error (0, 0, _("syntax error expecting `=' " + "parsing options for driver \"%s\""), + driver->name); break; } - tokener (); - if (op_token != 'a') - { - msg (IS, _("Syntax error in options (value expected after `=').")); - break; - } - d->class->option (d, key, &op_tokstr); + ss_ltrim (&left, ss_cstr (CC_SPACES)); + if (!get_option_token (&left, driver->name, &value)) + break; + + ok = callback (driver, ds_cstr (&key), &value); } - ds_destroy (&op_tokstr); + while (ok); + + ds_destroy (&key); + ds_destroy (&value); + + return ok; } /* Find the driver in outp_driver_list with name NAME. */ @@ -670,187 +663,123 @@ find_driver (char *name) return NULL; } -/* Tokenize string S into colon-separated fields, removing leading and - trailing whitespace on tokens. Returns a pointer to the - null-terminated token, which is formed by setting a NUL character - into the string. After the first call, subsequent calls should set - S to NULL. CP should be consistent across calls. Returns NULL - after all fields have been used up. - - FIXME: Should ignore colons inside double quotes. */ -static const char * -colon_tokenize (char *s, char **cp) -{ - char *token; - - if (!s) - { - s = *cp; - if (*s == 0) - return NULL; - } - token = s += strspn (s, " \t\v\r"); - *cp = strchr (s, ':'); - if (*cp == NULL) - s = *cp = strchr (s, 0); - else - s = (*cp)++; - while (s > token && strchr (" \t\v\r", s[-1])) - s--; - *s = 0; - return token; -} - -/* String S is in format: - DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS - Adds a driver to outp_driver_list pursuant to the specification - provided. */ +/* Adds a driver to outp_driver_list pursuant to the + specification provided. */ static void -configure_driver (const char *driver_name, const char *class_name, - const char *device_type, const char *options) +configure_driver (struct substring driver_name, struct substring class_name, + struct substring device_type, struct substring options) { - struct outp_driver *d = NULL, *iter; - struct outp_driver_class_list *c = NULL; + struct outp_driver *d, *iter; + struct outp_driver_class_list *c; - d = xmalloc (sizeof *d); - d->class = NULL; - d->name = xstrdup (driver_name); - d->driver_open = 0; - d->page_open = 0; - d->next = d->prev = NULL; - d->device = OUTP_DEV_NONE; - d->ext = NULL; + struct substring token; + size_t save_idx = 0; + int device; + /* Find class. */ for (c = outp_class_list; c; c = c->next) - if (!strcmp (c->class->name, class_name)) + if (!ss_compare (ss_cstr (c->class->name), class_name)) break; - if (!c) - { - msg (IS, _("Unknown output driver class `%s'."), class_name); - goto error; - } - - d->class = c->class; - if (!c->ref_count && !d->class->open_global (d->class)) - { - msg (IS, _("Can't initialize output driver class `%s'."), - d->class->name); - goto error; - } - c->ref_count++; - if (!d->class->preopen_driver (d)) + if (c == NULL) { - msg (IS, _("Can't initialize output driver `%s' of class `%s'."), - d->name, d->class->name); - goto error; + error (0, 0, _("unknown output driver class `%.*s'"), + (int) ss_length (class_name), ss_data (class_name)); + return; } - /* Device types. */ - if (device_type != NULL) - { - char *copy = xstrdup (device_type); - char *sp, *type; + /* Parse device type. */ + device = 0; + while (ss_tokenize (device_type, ss_cstr (CC_SPACES), &save_idx, &token)) + if (!ss_compare (token, ss_cstr ("listing"))) + device |= OUTP_DEV_LISTING; + else if (!ss_compare (token, ss_cstr ("screen"))) + device |= OUTP_DEV_SCREEN; + else if (!ss_compare (token, ss_cstr ("printer"))) + device |= OUTP_DEV_PRINTER; + else + error (0, 0, _("unknown device type `%.*s'"), + (int) ss_length (token), ss_data (token)); - for (type = strtok_r (copy, " \t\r\v", &sp); type; - type = strtok_r (NULL, " \t\r\v", &sp)) - { - if (!strcmp (type, "listing")) - d->device |= OUTP_DEV_LISTING; - else if (!strcmp (type, "screen")) - d->device |= OUTP_DEV_SCREEN; - else if (!strcmp (type, "printer")) - d->device |= OUTP_DEV_PRINTER; - else - { - msg (IS, _("Unknown device type `%s'."), type); - free (copy); - goto error; - } - } - free (copy); - } - - /* Options. */ - if (options != NULL) - parse_options (options, d); - if (!d->class->postopen_driver (d)) + /* Open the device. */ + d = xmalloc (sizeof *d); + d->next = d->prev = NULL; + d->class = c->class; + d->name = ss_xstrdup (driver_name); + d->page_open = false; + d->device = device; + d->cp_x = d->cp_y = 0; + d->ext = NULL; + d->prc = NULL; + + /* Open driver. */ + if (!d->class->open_driver (d, options)) { - msg (IS, _("Can't complete initialization of output driver `%s' of " - "class `%s'."), d->name, d->class->name); - goto error; + error (0, 0, _("cannot initialize output driver `%s' of class `%s'"), + d->name, d->class->name); + free (d->name); + free (d); + return; } /* Find like-named driver and delete. */ iter = find_driver (d->name); - if (iter) + if (iter != NULL) destroy_driver (iter); /* Add to list. */ d->next = outp_driver_list; d->prev = NULL; - if (outp_driver_list) + if (outp_driver_list != NULL) outp_driver_list->prev = d; outp_driver_list = d; - return; - -error: - if (d) - destroy_driver (d); - return; } -/* String S is in format: +/* String LINE is in format: DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS Adds a driver to outp_driver_list pursuant to the specification provided. */ -static void -configure_driver_line (char *s) +void +outp_configure_driver_line (struct substring line_) { - char *cp; - const char *driver_name, *class_name, *device_type, *options; + struct string line = DS_EMPTY_INITIALIZER; + struct substring tokens[4]; + size_t save_idx; + size_t i; - s = fn_interp_vars (s, find_defn_value); + fn_interp_vars (line_, find_defn_value, &line); - /* Driver name. */ - driver_name = colon_tokenize (s, &cp); - class_name = colon_tokenize (NULL, &cp); - device_type = colon_tokenize (NULL, &cp); - options = colon_tokenize (NULL, &cp); - if (driver_name == NULL || class_name == NULL) + save_idx = 0; + for (i = 0; i < 4; i++) { - msg (IS, _("Driver definition line contains fewer fields " - "than expected")); - return; + struct substring *token = &tokens[i]; + ds_separate (&line, ss_cstr (i < 3 ? ":" : ""), &save_idx, token); + ss_trim (token, ss_cstr (CC_SPACES)); } - configure_driver (driver_name, class_name, device_type, options); + if (!ss_is_empty (tokens[0]) && !ss_is_empty (tokens[1])) + configure_driver (tokens[0], tokens[1], tokens[2], tokens[3]); + else + error (0, 0, + _("driver definition line missing driver name or class name")); + + ds_destroy (&line); } /* Destroys output driver D. */ static void destroy_driver (struct outp_driver *d) { - if (d->page_open) - d->class->close_page (d); + outp_close_page (d); if (d->class) { struct outp_driver_class_list *c; - if (d->driver_open) - d->class->close_driver (d); + d->class->close_driver (d); for (c = outp_class_list; c; c = c->next) if (c->class == d->class) break; assert (c != NULL); - - c->ref_count--; - if (c->ref_count == 0) - { - if (!d->class->close_global (d->class)) - msg (IS, _("Can't deinitialize output driver class `%s'."), - d->class->name); - } } free (d->name); @@ -863,392 +792,290 @@ destroy_driver (struct outp_driver *d) outp_driver_list = d->next; } -static int -option_cmp (const void *a, const void *b) +/* Tries to match S as one of the keywords in TAB, with + corresponding information structure INFO. Returns category + code and stores subcategory in *SUBCAT on success. Returns -1 + on failure. */ +int +outp_match_keyword (const char *s, const struct outp_option *tab, int *subcat) { - const struct outp_option *o1 = a; - const struct outp_option *o2 = b; - return strcmp (o1->keyword, o2->keyword); + for (; tab->keyword != NULL; tab++) + if (!strcmp (s, tab->keyword)) + { + *subcat = tab->subcat; + return tab->cat; + } + return -1; } -/* Tries to match S as one of the keywords in TAB, with corresponding - information structure INFO. Returns category code or 0 on failure; - if category code is negative then stores subcategory in *SUBCAT. */ -int -outp_match_keyword (const char *s, struct outp_option *tab, - struct outp_option_info *info, int *subcat) +/* Parses UNIT as a dimensional unit. Returns the multiplicative + factor needed to change a quantity measured in that unit into + 1/72000" units. If UNIT is empty, it is treated as + millimeters. If the unit is unrecognized, returns 0. */ +static double +parse_unit (const char *unit) { - char *cp; - struct outp_option *oip; - - /* Form hash table. */ - if (NULL == info->initial) + struct unit { - /* Count items. */ - int count, i; - char s[256], *cp; - struct outp_option *ptr[255], **oip; - - for (count = 0; tab[count].keyword[0]; count++) - ; - - /* Sort items. */ - qsort (tab, count, sizeof *tab, option_cmp); - - cp = s; - oip = ptr; - *cp = tab[0].keyword[0]; - *oip++ = &tab[0]; - for (i = 0; i < count; i++) - if (tab[i].keyword[0] != *cp) - { - *++cp = tab[i].keyword[0]; - *oip++ = &tab[i]; - } - *++cp = 0; - - info->initial = xstrdup (s); - info->options = xnmalloc (cp - s, sizeof *info->options); - memcpy (info->options, ptr, sizeof *info->options * (cp - s)); - } - - cp = info->initial; - oip = *info->options; + char name[3]; + double factor; + }; - if (s[0] == 0) - return 0; - cp = strchr (info->initial, s[0]); - if (!cp) - return 0; -#if 0 - printf (_("Trying to find keyword `%s'...\n"), s); -#endif - oip = info->options[cp - info->initial]; - while (oip->keyword[0] == s[0]) + static const struct unit units[] = { -#if 0 - printf ("- %s\n", oip->keyword); -#endif - if (!strcmp (s, oip->keyword)) - { - if (oip->cat < 0) - *subcat = oip->subcat; - return oip->cat; - } - oip++; - } + {"pt", 72000 / 72}, + {"pc", 72000 / 72 * 12.0}, + {"in", 72000}, + {"cm", 72000 / 2.54}, + {"mm", 72000 / 25.4}, + {"", 72000 / 25.4}, + }; - return 0; -} + const struct unit *p; -/* Encapsulate two characters in a single int. */ -#define TWO_CHARS(A, B) \ - ((A) + ((B)<<8)) + unit += strspn (unit, CC_SPACES); + for (p = units; p < units + sizeof units / sizeof *units; p++) + if (!strcasecmp (unit, p->name)) + return p->factor; + return 0.0; +} -/* Determines the size of a dimensional measurement and returns the - size in units of 1/72000". Units if not specified explicitly are - inches for values under 50, millimeters otherwise. Returns 0, - stores NULL to *TAIL on error; otherwise returns dimension, stores - address of next */ +/* Determines the size of a dimensional measurement and returns + the size in units of 1/72000". Units are assumed to be + millimeters unless otherwise specified. Returns 0 on + error. */ int -outp_evaluate_dimension (char *dimen, char **tail) +outp_evaluate_dimension (const char *dimen) { - char *s = dimen; - char *ptail; - double value; - - value = strtod (s, &ptail); - if (ptail == s) - goto lossage; - if (*ptail == '-') - { - double b, c; - s = &ptail[1]; - b = strtod (s, &ptail); - if (b <= 0.0 || ptail == s) - goto lossage; - if (*ptail != '/') - goto lossage; - s = &ptail[1]; - c = strtod (s, &ptail); - if (c <= 0.0 || ptail == s) - goto lossage; - s = ptail; - if (c == 0.0) - goto lossage; - if (value > 0) - value += b / c; - else - value -= b / c; - } - else if (*ptail == '/') - { - double b; - s = &ptail[1]; - b = strtod (s, &ptail); - if (b <= 0.0 || ptail == s) - goto lossage; - s = ptail; - value /= b; - } - else - s = ptail; - if (*s == 0 || isspace ((unsigned char) *s)) - { - if (value < 50.0) - value *= 72000; - else - value *= 72000 / 25.4; - } - else - { - double factor; + double raw, factor; + char *tail; - /* Standard TeX units are supported. */ - if (*s == '"') - factor = 72000, s++; - else - switch (TWO_CHARS (s[0], s[1])) - { - case TWO_CHARS ('p', 't'): - factor = 72000 / 72.27; - break; - case TWO_CHARS ('p', 'c'): - factor = 72000 / 72.27 * 12.0; - break; - case TWO_CHARS ('i', 'n'): - factor = 72000; - break; - case TWO_CHARS ('b', 'p'): - factor = 72000 / 72.0; - break; - case TWO_CHARS ('c', 'm'): - factor = 72000 / 2.54; - break; - case TWO_CHARS ('m', 'm'): - factor = 72000 / 25.4; - break; - case TWO_CHARS ('d', 'd'): - factor = 72000 / 72.27 * 1.0700086; - break; - case TWO_CHARS ('c', 'c'): - factor = 72000 / 72.27 * 12.840104; - break; - case TWO_CHARS ('s', 'p'): - factor = 72000 / 72.27 / 65536.0; - break; - default: - msg (SE, _("Unit \"%s\" is unknown in dimension \"%s\"."), s, dimen); - *tail = NULL; - return 0; - } - ptail += 2; - value *= factor; - } - if (value <= 0.0) - goto lossage; - if (tail) - *tail = ptail; - return value + 0.5; - -lossage: - *tail = NULL; - msg (SE, _("Bad dimension \"%s\"."), dimen); + /* Number. */ + raw = strtod (dimen, &tail); + if (raw <= 0.0) + goto syntax_error; + + /* Unit. */ + factor = parse_unit (tail); + if (factor == 0.0) + goto syntax_error; + + return raw * factor; + +syntax_error: + error (0, 0, _("`%s' is not a valid length."), dimen); return 0; } /* Stores the dimensions in 1/72000" units of paper identified by - SIZE, which is of form `HORZ x VERT' or `HORZ by VERT' where each - of HORZ and VERT are dimensions, into *H and *V. Return nonzero on - success. */ -static int -internal_get_paper_size (char *size, int *h, int *v) + SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and + VERT are numbers and UNIT is an optional unit of measurement, + into *H and *V. Return true on success. */ +static bool +parse_paper_size (const char *size, int *h, int *v) { + double raw_h, raw_v, factor; char *tail; - while (isspace ((unsigned char) *size)) - size++; - *h = outp_evaluate_dimension (size, &tail); - if (tail == NULL) - return 0; - while (isspace ((unsigned char) *tail)) - tail++; - if (*tail == 'x') - tail++; - else if (*tail == 'b' && tail[1] == 'y') - tail += 2; - else - { - msg (SE, _("`x' expected in paper size `%s'."), size); - return 0; - } - *v = outp_evaluate_dimension (tail, &tail); - if (tail == NULL) - return 0; - while (isspace ((unsigned char) *tail)) - tail++; - if (*tail) - { - msg (SE, _("Trailing garbage `%s' on paper size `%s'."), tail, size); - return 0; - } - - return 1; -} + /* Width. */ + raw_h = strtod (size, &tail); + if (raw_h <= 0.0) + return false; -/* Stores the dimensions, in 1/72000" units, of paper identified by - SIZE into *H and *V. SIZE may be a pair of dimensions of form `H x - V', or it may be a case-insensitive paper identifier, which is - looked up in the `papersize' configuration file. Returns nonzero - on success. May modify SIZE. */ -/* Don't read further unless you've got a strong stomach. */ -int -outp_get_paper_size (char *size, int *h, int *v) -{ - struct paper_size - { - char *name; - int use; - int h, v; - }; + /* Delimiter. */ + tail += strspn (tail, CC_SPACES "x,"); - static struct paper_size cache[4]; - static int use; + /* Length. */ + raw_v = strtod (tail, &tail); + if (raw_v <= 0.0) + return false; - FILE *f; - char *pprsz_fn; + /* Unit. */ + factor = parse_unit (tail); + if (factor == 0.0) + return false; - struct string line; - struct file_locator where; + *h = raw_h * factor + .5; + *v = raw_v * factor + .5; + return true; +} - int free_it = 0; - int result = 0; - int min_value, min_index; - char *ep; - int i; - - while (isspace ((unsigned char) *size)) - size++; - if (isdigit ((unsigned char) *size)) - return internal_get_paper_size (size, h, v); - ep = size; - while (*ep) - ep++; - while (isspace ((unsigned char) *ep) && ep >= size) - ep--; - if (ep == size) +static bool +get_standard_paper_size (struct substring name, int *h, int *v) +{ + static const char *sizes[][2] = { - msg (SE, _("Paper size name must not be empty.")); - return 0; - } - - ep++; - if (*ep) - *ep = 0; + {"a0", "841 x 1189 mm"}, + {"a1", "594 x 841 mm"}, + {"a2", "420 x 594 mm"}, + {"a3", "297 x 420 mm"}, + {"a4", "210 x 297 mm"}, + {"a5", "148 x 210 mm"}, + {"b5", "176 x 250 mm"}, + {"a6", "105 x 148 mm"}, + {"a7", "74 x 105 mm"}, + {"a8", "52 x 74 mm"}, + {"a9", "37 x 52 mm"}, + {"a10", "26 x 37 mm"}, + {"b0", "1000 x 1414 mm"}, + {"b1", "707 x 1000 mm"}, + {"b2", "500 x 707 mm"}, + {"b3", "353 x 500 mm"}, + {"b4", "250 x 353 mm"}, + {"letter", "612 x 792 pt"}, + {"legal", "612 x 1008 pt"}, + {"executive", "522 x 756 pt"}, + {"note", "612 x 792 pt"}, + {"11x17", "792 x 1224 pt"}, + {"tabloid", "792 x 1224 pt"}, + {"statement", "396 x 612 pt"}, + {"halfletter", "396 x 612 pt"}, + {"halfexecutive", "378 x 522 pt"}, + {"folio", "612 x 936 pt"}, + {"quarto", "610 x 780 pt"}, + {"ledger", "1224 x 792 pt"}, + {"archA", "648 x 864 pt"}, + {"archB", "864 x 1296 pt"}, + {"archC", "1296 x 1728 pt"}, + {"archD", "1728 x 2592 pt"}, + {"archE", "2592 x 3456 pt"}, + {"flsa", "612 x 936 pt"}, + {"flse", "612 x 936 pt"}, + {"csheet", "1224 x 1584 pt"}, + {"dsheet", "1584 x 2448 pt"}, + {"esheet", "2448 x 3168 pt"}, + }; - use++; - for (i = 0; i < 4; i++) - if (cache[i].name != NULL && !strcasecmp (cache[i].name, size)) + size_t i; + + for (i = 0; i < sizeof sizes / sizeof *sizes; i++) + if (ss_equals_case (ss_cstr (sizes[i][0]), name)) { - *h = cache[i].h; - *v = cache[i].v; - cache[i].use = use; - return 1; + bool ok = parse_paper_size (sizes[i][1], h, v); + assert (ok); + return ok; } + error (0, 0, _("unknown paper type `%.*s'"), + (int) ss_length (name), ss_data (name)); + return false; +} - pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE", - "papersize"), - fn_getenv_default ("STAT_OUTPUT_INIT_PATH", - config_path), - NULL); - - where.filename = pprsz_fn; - where.line_number = 0; - err_push_file_locator (&where); - ds_init (&line, 128); - - if (pprsz_fn == NULL) - { - msg (IE, _("Cannot find `papersize' configuration file.")); - goto exit; - } +/* Reads file FILE_NAME to find a paper size. Stores the + dimensions, in 1/72000" units, into *H and *V. Returns true + on success, false on failure. */ +static bool +read_paper_conf (const char *file_name, int *h, int *v) +{ + struct string line = DS_EMPTY_INITIALIZER; + int line_number = 0; + FILE *file; - msg (VM (1), _("%s: Opening paper size definition file..."), pprsz_fn); - f = fopen (pprsz_fn, "r"); - if (!f) + file = fopen (file_name, "r"); + if (file == NULL) { - msg (IE, _("Opening %s: %s."), pprsz_fn, strerror (errno)); - goto exit; + error (0, errno, _("error opening \"%s\""), file_name); + return false; } for (;;) { - char *cp, *bp, *ep; + struct substring name; - if (!ds_get_config_line (f, &line, &where)) + if (!ds_read_config_line (&line, &line_number, file)) { - if (ferror (f)) - msg (ME, _("Reading %s: %s."), pprsz_fn, strerror (errno)); + if (ferror (file)) + error (0, errno, _("error reading \"%s\""), file_name); break; } - for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++); - if (*cp == 0) - continue; - if (*cp != '"') - goto lex_error; - for (bp = ep = cp + 1; *ep && *ep != '"'; ep++); - if (!*ep) - goto lex_error; - *ep = 0; - if (0 != strcasecmp (bp, size)) - continue; - - for (cp = ep + 1; isspace ((unsigned char) *cp); cp++); - if (*cp == '=') - { - size = xmalloc (ep - bp + 1); - strcpy (size, bp); - free_it = 1; - continue; - } - size = &ep[1]; - break; - lex_error: - msg (IE, _("Syntax error in paper size definition.")); + name = ds_ss (&line); + ss_trim (&name, ss_cstr (CC_SPACES)); + if (!ss_is_empty (name)) + { + bool ok = get_standard_paper_size (name, h, v); + fclose (file); + ds_destroy (&line); + return ok; + } } - /* We found the one we want! */ - result = internal_get_paper_size (size, h, v); - if (result) - { - min_value = cache[0].use; - min_index = 0; - for (i = 1; i < 4; i++) - if (cache[0].use < min_value) - { - min_value = cache[i].use; - min_index = i; - } - free (cache[min_index].name); - cache[min_index].name = xstrdup (size); - cache[min_index].use = use; - cache[min_index].h = *h; - cache[min_index].v = *v; - } - -exit: - err_pop_file_locator (&where); + fclose (file); ds_destroy (&line); - if (free_it) - free (size); + error (0, 0, _("paper size file \"%s\" does not state a paper size"), + file_name); + return false; +} - if (result) - msg (VM (2), _("Paper size definition file read successfully.")); +/* The user didn't specify a paper size, so let's choose a + default based on his environment. Stores the + dimensions, in 1/72000" units, into *H and *V. Returns true + on success, false on failure. */ +static bool +get_default_paper_size (int *h, int *v) +{ + /* libpaper in Debian (and other distributions?) allows the + paper size to be specified in $PAPERSIZE or in a file + specified in $PAPERCONF. */ + if (getenv ("PAPERSIZE") != NULL) + return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h, v); + if (getenv ("PAPERCONF") != NULL) + return read_paper_conf (getenv ("PAPERCONF"), h, v); + +#if HAVE_LC_PAPER + /* LC_PAPER is a non-standard glibc extension. */ + *h = (int) nl_langinfo(_NL_PAPER_WIDTH) * (72000 / 25.4); + *v = (int) nl_langinfo(_NL_PAPER_HEIGHT) * (72000 / 25.4); + if (*h > 0 && *v > 0) + return true; +#endif + + /* libpaper defaults to /etc/papersize. */ + if (fn_exists ("/etc/papersize")) + return read_paper_conf ("/etc/papersize", h, v); + + /* Can't find a default. */ + return false; +} + +/* Stores the dimensions, in 1/72000" units, of paper identified + by SIZE into *H and *V. SIZE can be the name of a kind of + paper ("a4", "letter", ...) or a pair of dimensions + ("210x297", "8.5x11in", ...). Returns true on success, false + on failure. On failure, *H and *V are set for A4 paper. */ +bool +outp_get_paper_size (const char *size, int *h, int *v) +{ + struct substring s; + bool ok; + + s = ss_cstr (size); + ss_trim (&s, ss_cstr (CC_SPACES)); + + if (ss_is_empty (s)) + { + /* Treat empty string as default paper size. */ + ok = get_default_paper_size (h, v); + } + else if (isdigit (ss_first (s))) + { + /* Treat string that starts with digit as explicit size. */ + ok = parse_paper_size (size, h, v); + if (!ok) + error (0, 0, _("syntax error in paper size `%s'"), size); + } else - msg (VM (1), _("Error reading paper size definition file.")); - - return result; + { + /* Check against standard paper sizes. */ + ok = get_standard_paper_size (s, h, v); + } + + /* Default to A4 on error. */ + if (!ok) + { + *h = 210 * (72000 / 25.4); + *v = 297 * (72000 / 25.4); + } + return ok; } /* If D is NULL, returns the first enabled driver if any, NULL if @@ -1258,10 +1085,6 @@ exit: struct outp_driver * outp_drivers (struct outp_driver *d) { -#if GLOBAL_DEBUGGING - struct outp_driver *orig_d = d; -#endif - for (;;) { if (d == NULL) @@ -1270,19 +1093,17 @@ outp_drivers (struct outp_driver *d) d = d->next; if (d == NULL - || (d->driver_open - && (d->device == 0 - || (d->device & disabled_devices) != d->device))) + || (d->device == 0 || (d->device & disabled_devices) != d->device)) break; } return d; } -/* Enables (if ENABLE is nonzero) or disables (if ENABLE is zero) the +/* Enables (if ENABLE is true) or disables (if ENABLE is false) the device(s) given in mask DEVICE. */ void -outp_enable_device (int enable, int device) +outp_enable_device (bool enable, int device) { if (enable) disabled_devices &= ~device; @@ -1290,40 +1111,67 @@ outp_enable_device (int enable, int device) disabled_devices |= device; } -/* Ejects the paper on device D, if the page is not blank. */ -int -outp_eject_page (struct outp_driver *d) +/* Opens a page on driver D (if one is not open). */ +void +outp_open_page (struct outp_driver *d) { - if (d->page_open == 0) - return 1; - - if (d->cp_y != 0) + if (!d->page_open) { d->cp_x = d->cp_y = 0; - if (d->class->close_page (d) == 0) - msg (ME, _("Error closing page on %s device of %s class."), - d->name, d->class->name); - if (d->class->open_page (d) == 0) - { - msg (ME, _("Error opening page on %s device of %s class."), - d->name, d->class->name); - return 0; - } + d->page_open = true; + if (d->class->open_page != NULL) + d->class->open_page (d); + } +} + +/* Closes the page on driver D (if one is open). */ +void +outp_close_page (struct outp_driver *d) +{ + if (d->page_open) + { + if (d->class->close_page != NULL) + d->class->close_page (d); + d->page_open = false; + } +} + +/* Ejects the page on device D, if a page is open and non-blank, + and opens a new page. */ +void +outp_eject_page (struct outp_driver *d) +{ + if (d->page_open && d->cp_y != 0) + outp_close_page (d); + outp_open_page (d); +} + +/* Flushes output to screen devices, so that the user can see + output that doesn't fill up an entire page. */ +void +outp_flush (struct outp_driver *d) +{ + if (d->device & OUTP_DEV_SCREEN && d->class->flush != NULL) + { + outp_close_page (d); + d->class->flush (d); } - return 1; } /* Returns the width of string S, in device units, when output on device D. */ int -outp_string_width (struct outp_driver *d, const char *s) +outp_string_width (struct outp_driver *d, const char *s, enum outp_font font) { struct outp_text text; + int width; - text.options = OUTP_T_JUST_LEFT; - ls_init (&text.s, (char *) s, strlen (s)); - d->class->text_metrics (d, &text); + text.font = font; + text.justification = OUTP_LEFT; + text.string = ss_cstr (s); + text.h = text.v = INT_MAX; + d->class->text_metrics (d, &text, &width, NULL); - return text.h; + return width; }