X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Foutput.c;h=843b0d4e544996cebcd736b25c8225e185337d1f;hb=e9599a2f8e86d986fff1518682ba8942bd67d1c0;hp=a63f1171a446e0a42fb5c1f691a8adada0078f68;hpb=c708736bdd0fea4b79f3ee4a10e00c3abb95d9e3;p=pspp-builds.git diff --git a/src/output/output.c b/src/output/output.c index a63f1171..843b0d4e 100644 --- a/src/output/output.c +++ b/src/output/output.c @@ -1,36 +1,40 @@ -/* 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 -#include -#include + #include -#include +#include +#if HAVE_LC_PAPER +#include +#endif +#include +#include +#include + #include -#include "htmlP.h" -#include "intprops.h" -#include #include +#include #include +#include +#include + #include "error.h" +#include "intprops.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -70,12 +74,12 @@ static struct outp_names *outp_configure_vec; /* A list of driver classes. */ struct outp_driver_class_list { - struct outp_class *class; + const struct outp_class *class; struct outp_driver_class_list *next; }; -struct outp_driver_class_list *outp_class_list; -struct outp_driver *outp_driver_list; +static struct outp_driver_class_list *outp_class_list; +static struct outp_driver *outp_driver_list; char *outp_title; char *outp_subtitle; @@ -85,13 +89,12 @@ char *outp_subtitle; static int disabled_devices; static void destroy_driver (struct outp_driver *); -static void configure_driver_line (struct substring); 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); @@ -153,8 +156,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) { @@ -209,12 +212,12 @@ find_defn_value (const char *key) 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 @@ -227,7 +230,6 @@ outp_init (void) { extern struct outp_class ascii_class; extern struct outp_class postscript_class; - extern struct outp_class html_class; char def[] = "default"; @@ -254,7 +256,7 @@ delete_macros (void) } static void -init_default_drivers (void) +init_default_drivers (void) { error (0, 0, _("using default output driver configuration")); configure_driver (ss_cstr ("list"), @@ -279,8 +281,7 @@ outp_read_devices (void) init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE", "devices"), fn_getenv_default ("STAT_OUTPUT_INIT_PATH", - config_path), - NULL); + config_path)); ds_init_empty (&line); @@ -323,7 +324,7 @@ outp_read_devices (void) struct outp_names *n = search_names (cp, ep); if (n) { - configure_driver_line (ds_ss (&line)); + outp_configure_driver_line (ds_ss (&line)); delete_name (n); } } @@ -342,10 +343,10 @@ exit: ds_destroy (&line); delete_macros (); - if (result) + if (result) { - if (outp_driver_list == NULL) - error (0, 0, _("no active output drivers")); + if (outp_driver_list == NULL) + error (0, 0, _("no active output drivers")); } else error (0, 0, _("error reading device definition file")); @@ -405,7 +406,7 @@ outp_configure_macro (char *bp) free (d); return; } - + if (*ep == '=') ep++; while (isspace ((unsigned char) *ep)) @@ -440,10 +441,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); @@ -453,7 +455,7 @@ outp_done (void) free (outp_title); outp_title = NULL; - + free (outp_subtitle); outp_subtitle = NULL; } @@ -462,7 +464,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")); @@ -472,7 +474,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); @@ -492,7 +494,7 @@ get_option_token (struct substring *s, const char *driver_name, struct string *token) { int c; - + ds_clear (token); c = ss_get_char (s); if (c == EOF) @@ -510,7 +512,7 @@ get_option_token (struct substring *s, const char *driver_name, c = ss_get_char (s); if (c == quote) break; - else if (c == EOF) + else if (c == EOF) { error (0, 0, _("reached end of options inside quoted string " @@ -567,7 +569,7 @@ get_option_token (struct substring *s, const char *driver_name, case '7': out = c - '0'; while (ss_first (*s) >= '0' && ss_first (*s) <= '7') - out = c * 8 + (ss_get_char (s) - '0'); + out = out * 8 + (ss_get_char (s) - '0'); break; case 'x': case 'X': @@ -592,7 +594,7 @@ get_option_token (struct substring *s, const char *driver_name, } } } - else + else { for (;;) { @@ -604,7 +606,7 @@ get_option_token (struct substring *s, const char *driver_name, ss_advance (s, 1); } } - + return 1; } @@ -624,7 +626,7 @@ outp_parse_options (struct substring options, ss_ltrim (&left, ss_cstr (CC_SPACES)); if (ss_is_empty (left)) break; - + if (!get_option_token (&left, driver->name, &key)) break; @@ -644,7 +646,7 @@ outp_parse_options (struct substring options, ok = callback (driver, ds_cstr (&key), &value); } while (ok); - + ds_destroy (&key); ds_destroy (&value); @@ -686,7 +688,7 @@ configure_driver (struct substring driver_name, struct substring class_name, (int) ss_length (class_name), ss_data (class_name)); return; } - + /* Parse device type. */ device = 0; while (ss_tokenize (device_type, ss_cstr (CC_SPACES), &save_idx, &token)) @@ -706,7 +708,7 @@ configure_driver (struct substring driver_name, struct substring class_name, d->class = c->class; d->name = ss_xstrdup (driver_name); d->page_open = false; - d->device = OUTP_DEV_NONE; + d->device = device; d->cp_x = d->cp_y = 0; d->ext = NULL; d->prc = NULL; @@ -738,8 +740,8 @@ configure_driver (struct substring driver_name, struct substring class_name, DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS Adds a driver to outp_driver_list pursuant to the specification provided. */ -static void -configure_driver_line (struct substring line_) +void +outp_configure_driver_line (struct substring line_) { struct string line = DS_EMPTY_INITIALIZER; struct substring tokens[4]; @@ -749,7 +751,7 @@ configure_driver_line (struct substring line_) fn_interp_vars (line_, find_defn_value, &line); save_idx = 0; - for (i = 0; i < 4; i++) + for (i = 0; i < 4; i++) { struct substring *token = &tokens[i]; ds_separate (&line, ss_cstr (i < 3 ? ":" : ""), &save_idx, token); @@ -797,7 +799,7 @@ destroy_driver (struct outp_driver *d) code and stores subcategory in *SUBCAT on success. Returns -1 on failure. */ int -outp_match_keyword (const char *s, struct outp_option *tab, int *subcat) +outp_match_keyword (const char *s, const struct outp_option *tab, int *subcat) { for (; tab->keyword != NULL; tab++) if (!strcmp (s, tab->keyword)) @@ -808,276 +810,274 @@ outp_match_keyword (const char *s, struct outp_option *tab, int *subcat) return -1; } -/* Encapsulate two characters in a single int. */ -#define TWO_CHARS(A, B) \ - ((A) + ((B)<<8)) - -/* 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 */ -int -outp_evaluate_dimension (char *dimen, char **tail) +/* 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 *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 + struct unit { + char name[3]; double factor; + }; - /* 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: - error (0, 0, - _("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; - error (0, 0, _("bad dimension \"%s\""), dimen); + static const struct unit units[] = + { + {"pt", 72000 / 72}, + {"pc", 72000 / 72 * 12.0}, + {"in", 72000}, + {"cm", 72000 / 2.54}, + {"mm", 72000 / 25.4}, + {"", 72000 / 25.4}, + }; + + const struct unit *p; + + 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 are assumed to be + millimeters unless otherwise specified. Returns 0 on + error. */ +int +outp_evaluate_dimension (const char *dimen) +{ + double raw, factor; + char *tail; + + /* 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 true on - success. */ + 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 -internal_get_paper_size (char *size, int *h, int *v) +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) + /* Width. */ + raw_h = strtod (size, &tail); + if (raw_h <= 0.0) return false; - while (isspace ((unsigned char) *tail)) - tail++; - if (*tail == 'x') - tail++; - else if (*tail == 'b' && tail[1] == 'y') - tail += 2; - else - { - error (0, 0, _("`x' expected in paper size `%s'"), size); - return false; - } - *v = outp_evaluate_dimension (tail, &tail); - if (tail == NULL) - return 0; - while (isspace ((unsigned char) *tail)) - tail++; - if (*tail) - { - error (0, 0, _("trailing garbage `%s' on paper size `%s'"), tail, size); - return false; - } - - return true; -} -/* 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 true - on success. May modify SIZE. */ -/* Don't read further unless you've got a strong stomach. */ -bool -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,"); - FILE *f; - char *pprsz_fn; + /* Length. */ + raw_v = strtod (tail, &tail); + if (raw_v <= 0.0) + return false; - struct string line; - int line_number = 0; + /* Unit. */ + factor = parse_unit (tail); + if (factor == 0.0) + return false; - bool free_it = false; - bool result = false; - char *ep; + *h = raw_h * factor + .5; + *v = raw_v * factor + .5; + return true; +} - 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] = { - error (0, 0, _("paper size name cannot 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"}, + }; - pprsz_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_PAPERSIZE_FILE", - "papersize"), - fn_getenv_default ("STAT_OUTPUT_INIT_PATH", - config_path), - NULL); + size_t i; - ds_init_empty (&line); + for (i = 0; i < sizeof sizes / sizeof *sizes; i++) + if (ss_equals_case (ss_cstr (sizes[i][0]), name)) + { + 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; +} - if (pprsz_fn == NULL) - { - error (0, 0, _("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; - f = fopen (pprsz_fn, "r"); - if (!f) + file = fopen (file_name, "r"); + if (file == NULL) { - error (0, errno, _("error opening \"%s\""), pprsz_fn); - goto exit; + error (0, errno, _("error opening \"%s\""), file_name); + return false; } for (;;) { - struct substring p, name; + struct substring name; - if (!ds_read_config_line (&line, &line_number, f)) + if (!ds_read_config_line (&line, &line_number, file)) { - if (ferror (f)) - error (0, errno, _("error reading \"%s\""), pprsz_fn); + if (ferror (file)) + error (0, errno, _("error reading \"%s\""), file_name); break; } - p = ds_ss (&line); - ss_ltrim (&p, ss_cstr (CC_SPACES)); - if (!ss_match_char (&p, '"') || !ss_get_until (&p, '"', &name)) - goto lex_error; - if (ss_compare (name, ss_cstr (size))) - continue; + 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; + } + } - ss_ltrim (&p, ss_cstr (CC_SPACES)); - if (ss_match_char (&p, '=')) - { - if (free_it) - free (size); - ss_trim (&p, ss_cstr (CC_SPACES)); - size = ss_xstrdup (p); - free_it = true; - continue; - } - size = ss_data (p); - break; + fclose (file); + ds_destroy (&line); + error (0, 0, _("paper size file \"%s\" does not state a paper size"), + file_name); + return false; +} - lex_error: - error_at_line (0, 0, pprsz_fn, line_number, - _("syntax error in paper size definition")); - } +/* 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; +} - /* We found the one we want! */ - result = internal_get_paper_size (size, h, v); +/* 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; -exit: - ds_destroy (&line); - if (free_it) - free (size); + s = ss_cstr (size); + ss_trim (&s, ss_cstr (CC_SPACES)); - if (!result) - error (0, 0, _("error reading paper size definition file")); - - return result; + 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 + { + /* 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 @@ -1102,10 +1102,10 @@ outp_drivers (struct outp_driver *d) 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; @@ -1115,9 +1115,9 @@ outp_enable_device (int enable, int device) /* Opens a page on driver D (if one is not open). */ void -outp_open_page (struct outp_driver *d) +outp_open_page (struct outp_driver *d) { - if (!d->page_open) + if (!d->page_open) { d->cp_x = d->cp_y = 0; @@ -1129,9 +1129,9 @@ outp_open_page (struct outp_driver *d) /* Closes the page on driver D (if one is open). */ void -outp_close_page (struct outp_driver *d) +outp_close_page (struct outp_driver *d) { - if (d->page_open) + if (d->page_open) { if (d->class->close_page != NULL) d->class->close_page (d); @@ -1149,6 +1149,18 @@ outp_eject_page (struct outp_driver *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); + } +} + /* Returns the width of string S, in device units, when output on device D. */ int @@ -1156,7 +1168,7 @@ outp_string_width (struct outp_driver *d, const char *s, enum outp_font font) { struct outp_text text; int width; - + text.font = font; text.justification = OUTP_LEFT; text.string = ss_cstr (s);