Bug #21128. Reviewed by John Darrington.
[pspp-builds.git] / src / output / output.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <langinfo.h>
22 #include <locale.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include <data/file-name.h>
27 #include <data/settings.h>
28 #include <libpspp/alloc.h>
29 #include <libpspp/misc.h>
30 #include <libpspp/str.h>
31 #include <output/htmlP.h>
32 #include <output/output.h>
33
34 #include "error.h"
35 #include "intprops.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
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? */
43
44 /* FIXME: Have the reentrancy problems been solved? */
45
46 /* Where the output driver name came from. */
47 enum
48   {
49     OUTP_S_COMMAND_LINE,        /* Specified by the user. */
50     OUTP_S_INIT_FILE            /* `default' or the init file. */
51   };
52
53 /* Names the output drivers to be used. */
54 struct outp_names
55   {
56     char *name;                 /* Name of the output driver. */
57     int source;                 /* OUTP_S_* */
58     struct outp_names *next, *prev;
59   };
60
61 /* Defines an init file macro. */
62 struct outp_defn
63   {
64     char *key;
65     struct string value;
66     struct outp_defn *next, *prev;
67   };
68
69 static struct outp_defn *outp_macros;
70 static struct outp_names *outp_configure_vec;
71
72 /* A list of driver classes. */
73 struct outp_driver_class_list
74   {
75     const struct outp_class *class;
76     struct outp_driver_class_list *next;
77   };
78
79 static struct outp_driver_class_list *outp_class_list;
80 static struct outp_driver *outp_driver_list;
81
82 char *outp_title;
83 char *outp_subtitle;
84
85 /* A set of OUTP_DEV_* bits indicating the devices that are
86    disabled. */
87 static int disabled_devices;
88
89 static void destroy_driver (struct outp_driver *);
90 static void configure_driver (const struct substring, const struct substring,
91                               const struct substring, const struct substring);
92
93 /* Add a class to the class list. */
94 static void
95 add_class (const struct outp_class *class)
96 {
97   struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
98
99   new_list->class = class;
100
101   if (!outp_class_list)
102     {
103       outp_class_list = new_list;
104       new_list->next = NULL;
105     }
106   else
107     {
108       new_list->next = outp_class_list;
109       outp_class_list = new_list;
110     }
111 }
112
113 /* Finds the outp_names in outp_configure_vec with name between BP and
114    EP exclusive. */
115 static struct outp_names *
116 search_names (char *bp, char *ep)
117 {
118   struct outp_names *n;
119
120   for (n = outp_configure_vec; n; n = n->next)
121     if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
122       return n;
123   return NULL;
124 }
125
126 /* Deletes outp_names NAME from outp_configure_vec. */
127 static void
128 delete_name (struct outp_names * n)
129 {
130   free (n->name);
131   if (n->prev)
132     n->prev->next = n->next;
133   if (n->next)
134     n->next->prev = n->prev;
135   if (n == outp_configure_vec)
136     outp_configure_vec = n->next;
137   free (n);
138 }
139
140 /* Adds the name between BP and EP exclusive to list
141    outp_configure_vec with source SOURCE. */
142 static void
143 add_name (char *bp, char *ep, int source)
144 {
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;
149   n->source = source;
150   n->next = outp_configure_vec;
151   n->prev = NULL;
152   if (outp_configure_vec)
153     outp_configure_vec->prev = n;
154   outp_configure_vec = n;
155 }
156
157 /* Checks that outp_configure_vec is empty, complains and clears
158    it if it isn't. */
159 static void
160 check_configure_vec (void)
161 {
162   struct outp_names *n;
163
164   for (n = outp_configure_vec; n; n = n->next)
165     if (n->source == OUTP_S_COMMAND_LINE)
166       error (0, 0, _("unknown output driver `%s'"), n->name);
167     else
168       error (0, 0, _("output driver `%s' referenced but never defined"),
169              n->name);
170   outp_configure_clear ();
171 }
172
173 /* Searches outp_configure_vec for the name between BP and EP
174    exclusive.  If found, it is deleted, then replaced by the names
175    given in EP+1, if any. */
176 static void
177 expand_name (char *bp, char *ep)
178 {
179   struct outp_names *n = search_names (bp, ep);
180   if (!n)
181     return;
182   delete_name (n);
183
184   bp = ep + 1;
185   for (;;)
186     {
187       while (isspace ((unsigned char) *bp))
188         bp++;
189       ep = bp;
190       while (*ep && !isspace ((unsigned char) *ep))
191         ep++;
192       if (bp == ep)
193         return;
194       if (!search_names (bp, ep))
195         add_name (bp, ep, OUTP_S_INIT_FILE);
196       bp = ep;
197     }
198 }
199
200 /* Looks for a macro with key KEY, and returns the corresponding value
201    if found, or NULL if not. */
202 static const char *
203 find_defn_value (const char *key)
204 {
205   static char buf[INT_STRLEN_BOUND (int) + 1];
206   struct outp_defn *d;
207
208   for (d = outp_macros; d; d = d->next)
209     if (!strcmp (key, d->key))
210       return ds_cstr (&d->value);
211   if (!strcmp (key, "viewwidth"))
212     {
213       sprintf (buf, "%d", get_viewwidth ());
214       return buf;
215     }
216   else if (!strcmp (key, "viewlength"))
217     {
218       sprintf (buf, "%d", get_viewlength ());
219       return buf;
220     }
221   else
222     return getenv (key);
223 }
224
225 /* Initializes global variables. */
226 void
227 outp_init (void)
228 {
229   extern struct outp_class ascii_class;
230   extern struct outp_class postscript_class;
231
232   char def[] = "default";
233
234   add_class (&html_class);
235   add_class (&postscript_class);
236   add_class (&ascii_class);
237
238   add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
239 }
240
241 /* Deletes all the output macros. */
242 static void
243 delete_macros (void)
244 {
245   struct outp_defn *d, *next;
246
247   for (d = outp_macros; d; d = next)
248     {
249       next = d->next;
250       free (d->key);
251       ds_destroy (&d->value);
252       free (d);
253     }
254 }
255
256 static void
257 init_default_drivers (void)
258 {
259   error (0, 0, _("using default output driver configuration"));
260   configure_driver (ss_cstr ("list"),
261                     ss_cstr ("ascii"),
262                     ss_cstr ("listing"),
263                     ss_cstr ("length=66 width=79 output-file=\"pspp.list\""));
264 }
265
266 /* Reads the initialization file; initializes
267    outp_driver_list. */
268 void
269 outp_read_devices (void)
270 {
271   int result = 0;
272
273   char *init_fn;
274
275   FILE *f = NULL;
276   struct string line;
277   int line_number;
278
279   init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
280                                                "devices"),
281                             fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
282                                                config_path));
283
284   ds_init_empty (&line);
285
286   if (init_fn == NULL)
287     {
288       error (0, 0, _("cannot find output initialization file "
289                      "(use `-vv' to view search path)"));
290       goto exit;
291     }
292
293   f = fopen (init_fn, "r");
294   if (f == NULL)
295     {
296       error (0, errno, _("cannot open \"%s\""), init_fn);
297       goto exit;
298     }
299
300   line_number = 0;
301   for (;;)
302     {
303       char *cp;
304
305       if (!ds_read_config_line (&line, &line_number, f))
306         {
307           if (ferror (f))
308             error (0, errno, _("reading \"%s\""), init_fn);
309           break;
310         }
311       for (cp = ds_cstr (&line); isspace ((unsigned char) *cp); cp++);
312       if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
313         outp_configure_macro (&cp[7]);
314       else if (*cp)
315         {
316           char *ep;
317           for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
318           if (*ep == '=')
319             expand_name (cp, ep);
320           else if (*ep == ':')
321             {
322               struct outp_names *n = search_names (cp, ep);
323               if (n)
324                 {
325                   outp_configure_driver_line (ds_ss (&line));
326                   delete_name (n);
327                 }
328             }
329           else
330             error_at_line (0, 0, init_fn, line_number, _("syntax error"));
331         }
332     }
333   result = 1;
334
335   check_configure_vec ();
336
337 exit:
338   if (f && -1 == fclose (f))
339     error (0, errno, _("error closing \"%s\""), init_fn);
340   free (init_fn);
341   ds_destroy (&line);
342   delete_macros ();
343
344   if (result)
345     {
346       if (outp_driver_list == NULL)
347         error (0, 0, _("no active output drivers"));
348     }
349   else
350     error (0, 0, _("error reading device definition file"));
351
352   if (!result || outp_driver_list == NULL)
353     init_default_drivers ();
354 }
355
356 /* Clear the list of drivers to configure. */
357 void
358 outp_configure_clear (void)
359 {
360   struct outp_names *n, *next;
361
362   for (n = outp_configure_vec; n; n = next)
363     {
364       next = n->next;
365       free (n->name);
366       free (n);
367     }
368   outp_configure_vec = NULL;
369 }
370
371 /* Adds the name BP to the list of drivers to configure into
372    outp_driver_list. */
373 void
374 outp_configure_add (char *bp)
375 {
376   char *ep = &bp[strlen (bp)];
377   if (!search_names (bp, ep))
378     add_name (bp, ep, OUTP_S_COMMAND_LINE);
379 }
380
381 /* Defines one configuration macro based on the text in BP, which
382    should be of the form `KEY=VALUE'. */
383 void
384 outp_configure_macro (char *bp)
385 {
386   struct outp_defn *d;
387   char *ep;
388
389   while (isspace ((unsigned char) *bp))
390     bp++;
391   ep = bp;
392   while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
393     ep++;
394
395   d = xmalloc (sizeof *d);
396   d->key = xmalloc (ep - bp + 1);
397   memcpy (d->key, bp, ep - bp);
398   d->key[ep - bp] = 0;
399
400   /* Earlier definitions for a particular KEY override later ones. */
401   if (find_defn_value (d->key))
402     {
403       free (d->key);
404       free (d);
405       return;
406     }
407
408   if (*ep == '=')
409     ep++;
410   while (isspace ((unsigned char) *ep))
411     ep++;
412
413   ds_init_cstr (&d->value, ep);
414   fn_interp_vars (ds_ss (&d->value), find_defn_value, &d->value);
415   d->next = outp_macros;
416   d->prev = NULL;
417   if (outp_macros)
418     outp_macros->prev = d;
419   outp_macros = d;
420 }
421
422 /* Destroys all the drivers in driver list *DL and sets *DL to
423    NULL. */
424 static void
425 destroy_list (struct outp_driver ** dl)
426 {
427   struct outp_driver *d, *next;
428
429   for (d = *dl; d; d = next)
430     {
431       destroy_driver (d);
432       next = d->next;
433       free (d);
434     }
435   *dl = NULL;
436 }
437
438 /* Closes all the output drivers. */
439 void
440 outp_done (void)
441 {
442   struct outp_driver_class_list *n = outp_class_list ;
443   outp_configure_clear ();
444   destroy_list (&outp_driver_list);
445
446   while (n)
447     {
448       struct outp_driver_class_list *next = n->next;
449       free(n);
450       n = next;
451     }
452   outp_class_list = NULL;
453
454   free (outp_title);
455   outp_title = NULL;
456
457   free (outp_subtitle);
458   outp_subtitle = NULL;
459 }
460
461 /* Display on stdout a list of all registered driver classes. */
462 void
463 outp_list_classes (void)
464 {
465   int width = get_viewwidth();
466   struct outp_driver_class_list *c;
467
468   printf (_("Driver classes:\n\t"));
469   width -= 8;
470   for (c = outp_class_list; c; c = c->next)
471     {
472       if ((int) strlen (c->class->name) + 1 > width)
473         {
474           printf ("\n\t");
475           width = get_viewwidth() - 8;
476         }
477       else
478         putc (' ', stdout);
479       fputs (c->class->name, stdout);
480     }
481   putc('\n', stdout);
482 }
483
484 /* Obtains a token from S and advances its position.  Errors are
485    reported against the given DRIVER_NAME.
486    The token is stored in TOKEN.  Returns true if successful,
487    false on syntax error.
488
489    Caller is responsible for skipping leading spaces. */
490 static bool
491 get_option_token (struct substring *s, const char *driver_name,
492                   struct string *token)
493 {
494   int c;
495
496   ds_clear (token);
497   c = ss_get_char (s);
498   if (c == EOF)
499     {
500       error (0, 0, _("syntax error parsing options for \"%s\" driver"),
501              driver_name);
502       return false;
503     }
504   else if (c == '\'' || c == '"')
505     {
506       int quote = c;
507
508       for (;;)
509         {
510           c = ss_get_char (s);
511           if (c == quote)
512             break;
513           else if (c == EOF)
514             {
515               error (0, 0,
516                      _("reached end of options inside quoted string "
517                        "parsing options for \"%s\" driver"),
518                      driver_name);
519               return false;
520             }
521           else if (c != '\\')
522             ds_put_char (token, c);
523           else
524             {
525               int out;
526
527               c = ss_get_char (s);
528               switch (c)
529                 {
530                 case '\'':
531                   out = '\'';
532                   break;
533                 case '"':
534                   out = '"';
535                   break;
536                 case '\\':
537                   out = '\\';
538                   break;
539                 case 'a':
540                   out = '\a';
541                   break;
542                 case 'b':
543                   out = '\b';
544                   break;
545                 case 'f':
546                   out = '\f';
547                   break;
548                 case 'n':
549                   out = '\n';
550                   break;
551                 case 'r':
552                   out = '\r';
553                   break;
554                 case 't':
555                   out = '\t';
556                   break;
557                 case 'v':
558                   out = '\v';
559                   break;
560                 case '0':
561                 case '1':
562                 case '2':
563                 case '3':
564                 case '4':
565                 case '5':
566                 case '6':
567                 case '7':
568                   out = c - '0';
569                   while (ss_first (*s) >= '0' && ss_first (*s) <= '7')
570                     out = out * 8 + (ss_get_char (s) - '0');
571                   break;
572                 case 'x':
573                 case 'X':
574                   out = 0;
575                   while (isxdigit (ss_first (*s)))
576                     {
577                       c = ss_get_char (s);
578                       out *= 16;
579                       if (isdigit (c))
580                         out += c - '0';
581                       else
582                         out += tolower (c) - 'a' + 10;
583                     }
584                   break;
585                 default:
586                   error (0, 0, _("syntax error in string constant "
587                                  "parsing options for \"%s\" driver"),
588                          driver_name);
589                   return false;
590                 }
591               ds_put_char (token, out);
592             }
593         }
594     }
595   else
596     {
597       for (;;)
598         {
599           ds_put_char (token, c);
600
601           c = ss_first (*s);
602           if (c == EOF || c == '=' || isspace (c))
603             break;
604           ss_advance (s, 1);
605         }
606     }
607
608   return 1;
609 }
610
611 bool
612 outp_parse_options (struct substring options,
613                     bool (*callback) (struct outp_driver *, const char *key,
614                                       const struct string *value),
615                     struct outp_driver *driver)
616 {
617   struct string key = DS_EMPTY_INITIALIZER;
618   struct string value = DS_EMPTY_INITIALIZER;
619   struct substring left = options;
620   bool ok = true;
621
622   do
623     {
624       ss_ltrim (&left, ss_cstr (CC_SPACES));
625       if (ss_is_empty (left))
626         break;
627
628       if (!get_option_token (&left, driver->name, &key))
629         break;
630
631       ss_ltrim (&left, ss_cstr (CC_SPACES));
632       if (!ss_match_char (&left, '='))
633         {
634           error (0, 0, _("syntax error expecting `=' "
635                          "parsing options for driver \"%s\""),
636                  driver->name);
637           break;
638         }
639
640       ss_ltrim (&left, ss_cstr (CC_SPACES));
641       if (!get_option_token (&left, driver->name, &value))
642         break;
643
644       ok = callback (driver, ds_cstr (&key), &value);
645     }
646   while (ok);
647
648   ds_destroy (&key);
649   ds_destroy (&value);
650
651   return ok;
652 }
653
654 /* Find the driver in outp_driver_list with name NAME. */
655 static struct outp_driver *
656 find_driver (char *name)
657 {
658   struct outp_driver *d;
659
660   for (d = outp_driver_list; d; d = d->next)
661     if (!strcmp (d->name, name))
662       return d;
663   return NULL;
664 }
665
666 /* Adds a driver to outp_driver_list pursuant to the
667    specification provided.  */
668 static void
669 configure_driver (struct substring driver_name, struct substring class_name,
670                   struct substring device_type, struct substring options)
671 {
672   struct outp_driver *d, *iter;
673   struct outp_driver_class_list *c;
674
675   struct substring token;
676   size_t save_idx = 0;
677   int device;
678
679   /* Find class. */
680   for (c = outp_class_list; c; c = c->next)
681     if (!ss_compare (ss_cstr (c->class->name), class_name))
682       break;
683   if (c == NULL)
684     {
685       error (0, 0, _("unknown output driver class `%.*s'"),
686              (int) ss_length (class_name), ss_data (class_name));
687       return;
688     }
689
690   /* Parse device type. */
691   device = 0;
692   while (ss_tokenize (device_type, ss_cstr (CC_SPACES), &save_idx, &token))
693     if (!ss_compare (token, ss_cstr ("listing")))
694       device |= OUTP_DEV_LISTING;
695     else if (!ss_compare (token, ss_cstr ("screen")))
696       device |= OUTP_DEV_SCREEN;
697     else if (!ss_compare (token, ss_cstr ("printer")))
698       device |= OUTP_DEV_PRINTER;
699     else
700       error (0, 0, _("unknown device type `%.*s'"),
701              (int) ss_length (token), ss_data (token));
702
703   /* Open the device. */
704   d = xmalloc (sizeof *d);
705   d->next = d->prev = NULL;
706   d->class = c->class;
707   d->name = ss_xstrdup (driver_name);
708   d->page_open = false;
709   d->device = device;
710   d->cp_x = d->cp_y = 0;
711   d->ext = NULL;
712   d->prc = NULL;
713
714   /* Open driver. */
715   if (!d->class->open_driver (d, options))
716     {
717       error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
718              d->name, d->class->name);
719       free (d->name);
720       free (d);
721       return;
722     }
723
724   /* Find like-named driver and delete. */
725   iter = find_driver (d->name);
726   if (iter != NULL)
727     destroy_driver (iter);
728
729   /* Add to list. */
730   d->next = outp_driver_list;
731   d->prev = NULL;
732   if (outp_driver_list != NULL)
733     outp_driver_list->prev = d;
734   outp_driver_list = d;
735 }
736
737 /* String LINE is in format:
738    DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
739    Adds a driver to outp_driver_list pursuant to the specification
740    provided.  */
741 void
742 outp_configure_driver_line (struct substring line_)
743 {
744   struct string line = DS_EMPTY_INITIALIZER;
745   struct substring tokens[4];
746   size_t save_idx;
747   size_t i;
748
749   fn_interp_vars (line_, find_defn_value, &line);
750
751   save_idx = 0;
752   for (i = 0; i < 4; i++)
753     {
754       struct substring *token = &tokens[i];
755       ds_separate (&line, ss_cstr (i < 3 ? ":" : ""), &save_idx, token);
756       ss_trim (token, ss_cstr (CC_SPACES));
757     }
758
759   if (!ss_is_empty (tokens[0]) && !ss_is_empty (tokens[1]))
760     configure_driver (tokens[0], tokens[1], tokens[2], tokens[3]);
761   else
762     error (0, 0,
763            _("driver definition line missing driver name or class name"));
764
765   ds_destroy (&line);
766 }
767
768 /* Destroys output driver D. */
769 static void
770 destroy_driver (struct outp_driver *d)
771 {
772   outp_close_page (d);
773   if (d->class)
774     {
775       struct outp_driver_class_list *c;
776
777       d->class->close_driver (d);
778
779       for (c = outp_class_list; c; c = c->next)
780         if (c->class == d->class)
781           break;
782       assert (c != NULL);
783     }
784   free (d->name);
785
786   /* Remove this driver from the global driver list. */
787   if (d->prev)
788     d->prev->next = d->next;
789   if (d->next)
790     d->next->prev = d->prev;
791   if (d == outp_driver_list)
792     outp_driver_list = d->next;
793 }
794
795 /* Tries to match S as one of the keywords in TAB, with
796    corresponding information structure INFO.  Returns category
797    code and stores subcategory in *SUBCAT on success.  Returns -1
798    on failure. */
799 int
800 outp_match_keyword (const char *s, const struct outp_option *tab, int *subcat)
801 {
802   for (; tab->keyword != NULL; tab++)
803     if (!strcmp (s, tab->keyword))
804       {
805         *subcat = tab->subcat;
806         return tab->cat;
807       }
808   return -1;
809 }
810
811 /* Parses UNIT as a dimensional unit.  Returns the multiplicative
812    factor needed to change a quantity measured in that unit into
813    1/72000" units.  If UNIT is empty, it is treated as
814    millimeters.  If the unit is unrecognized, returns 0. */
815 static double
816 parse_unit (const char *unit)
817 {
818   struct unit
819     {
820       char name[3];
821       double factor;
822     };
823
824   static const struct unit units[] =
825     {
826       {"pt", 72000 / 72},
827       {"pc", 72000 / 72 * 12.0},
828       {"in", 72000},
829       {"cm", 72000 / 2.54},
830       {"mm", 72000 / 25.4},
831       {"", 72000 / 25.4},
832     };
833
834   const struct unit *p;
835
836   unit += strspn (unit, CC_SPACES);
837   for (p = units; p < units + sizeof units / sizeof *units; p++)
838     if (!strcasecmp (unit, p->name))
839       return p->factor;
840   return 0.0;
841 }
842
843 /* Determines the size of a dimensional measurement and returns
844    the size in units of 1/72000".  Units are assumed to be
845    millimeters unless otherwise specified.  Returns 0 on
846    error. */
847 int
848 outp_evaluate_dimension (const char *dimen)
849 {
850   double raw, factor;
851   char *tail;
852
853   /* Number. */
854   raw = strtod (dimen, &tail);
855   if (raw <= 0.0)
856     goto syntax_error;
857
858   /* Unit. */
859   factor = parse_unit (tail);
860   if (factor == 0.0)
861     goto syntax_error;
862
863   return raw * factor;
864
865 syntax_error:
866   error (0, 0, _("`%s' is not a valid length."), dimen);
867   return 0;
868 }
869
870 /* Stores the dimensions in 1/72000" units of paper identified by
871    SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and
872    VERT are numbers and UNIT is an optional unit of measurement,
873    into *H and *V.  Return true on success. */
874 static bool
875 parse_paper_size (const char *size, int *h, int *v)
876 {
877   double raw_h, raw_v, factor;
878   char *tail;
879
880   /* Width. */
881   raw_h = strtod (size, &tail);
882   if (raw_h <= 0.0)
883     return false;
884
885   /* Delimiter. */
886   tail += strspn (tail, CC_SPACES "x,");
887
888   /* Length. */
889   raw_v = strtod (tail, &tail);
890   if (raw_v <= 0.0)
891     return false;
892
893   /* Unit. */
894   factor = parse_unit (tail);
895   if (factor == 0.0)
896     return false;
897
898   *h = raw_h * factor + .5;
899   *v = raw_v * factor + .5;
900   return true;
901 }
902
903 static bool
904 get_standard_paper_size (struct substring name, int *h, int *v)
905 {
906   static const char *sizes[][2] =
907     {
908       {"a0", "841 x 1189 mm"},
909       {"a1", "594 x 841 mm"},
910       {"a2", "420 x 594 mm"},
911       {"a3", "297 x 420 mm"},
912       {"a4", "210 x 297 mm"},
913       {"a5", "148 x 210 mm"},
914       {"b5", "176 x 250 mm"},
915       {"a6", "105 x 148 mm"},
916       {"a7", "74 x 105 mm"},
917       {"a8", "52 x 74 mm"},
918       {"a9", "37 x 52 mm"},
919       {"a10", "26 x 37 mm"},
920       {"b0", "1000 x 1414 mm"},
921       {"b1", "707 x 1000 mm"},
922       {"b2", "500 x 707 mm"},
923       {"b3", "353 x 500 mm"},
924       {"b4", "250 x 353 mm"},
925       {"letter", "612 x 792 pt"},
926       {"legal", "612 x 1008 pt"},
927       {"executive", "522 x 756 pt"},
928       {"note", "612 x 792 pt"},
929       {"11x17", "792 x 1224 pt"},
930       {"tabloid", "792 x 1224 pt"},
931       {"statement", "396 x 612 pt"},
932       {"halfletter", "396 x 612 pt"},
933       {"halfexecutive", "378 x 522 pt"},
934       {"folio", "612 x 936 pt"},
935       {"quarto", "610 x 780 pt"},
936       {"ledger", "1224 x 792 pt"},
937       {"archA", "648 x 864 pt"},
938       {"archB", "864 x 1296 pt"},
939       {"archC", "1296 x 1728 pt"},
940       {"archD", "1728 x 2592 pt"},
941       {"archE", "2592 x 3456 pt"},
942       {"flsa", "612 x 936 pt"},
943       {"flse", "612 x 936 pt"},
944       {"csheet", "1224 x 1584 pt"},
945       {"dsheet", "1584 x 2448 pt"},
946       {"esheet", "2448 x 3168 pt"},
947     };
948
949   size_t i;
950
951   for (i = 0; i < sizeof sizes / sizeof *sizes; i++)
952     if (ss_equals_case (ss_cstr (sizes[i][0]), name))
953       {
954         bool ok = parse_paper_size (sizes[i][1], h, v);
955         assert (ok);
956         return ok;
957       }
958   error (0, 0, _("unknown paper type `%.*s'"),
959          (int) ss_length (name), ss_data (name));
960   return false;
961 }
962
963 /* Reads file FILE_NAME to find a paper size.  Stores the
964    dimensions, in 1/72000" units, into *H and *V.  Returns true
965    on success, false on failure. */
966 static bool
967 read_paper_conf (const char *file_name, int *h, int *v)
968 {
969   struct string line = DS_EMPTY_INITIALIZER;
970   int line_number = 0;
971   FILE *file;
972
973   file = fopen (file_name, "r");
974   if (file == NULL)
975     {
976       error (0, errno, _("error opening \"%s\""), file_name);
977       return false;
978     }
979
980   for (;;)
981     {
982       struct substring name;
983
984       if (!ds_read_config_line (&line, &line_number, file))
985         {
986           if (ferror (file))
987             error (0, errno, _("error reading \"%s\""), file_name);
988           break;
989         }
990
991       name = ds_ss (&line);
992       ss_trim (&name, ss_cstr (CC_SPACES));
993       if (!ss_is_empty (name))
994         {
995           bool ok = get_standard_paper_size (name, h, v);
996           fclose (file);
997           ds_destroy (&line);
998           return ok;
999         }
1000     }
1001
1002   fclose (file);
1003   ds_destroy (&line);
1004   error (0, 0, _("paper size file \"%s\" does not state a paper size"),
1005          file_name);
1006   return false;
1007 }
1008
1009 /* The user didn't specify a paper size, so let's choose a
1010    default based on his environment.  Stores the
1011    dimensions, in 1/72000" units, into *H and *V.  Returns true
1012    on success, false on failure. */
1013 static bool
1014 get_default_paper_size (int *h, int *v)
1015 {
1016   /* libpaper in Debian (and other distributions?) allows the
1017      paper size to be specified in $PAPERSIZE or in a file
1018      specified in $PAPERCONF. */
1019   if (getenv ("PAPERSIZE") != NULL)
1020     return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h, v);
1021   if (getenv ("PAPERCONF") != NULL)
1022     return read_paper_conf (getenv ("PAPERCONF"), h, v);
1023
1024 #if HAVE_LC_PAPER
1025   /* LC_PAPER is a non-standard glibc extension. */
1026   *h = (int) nl_langinfo(_NL_PAPER_WIDTH) * (72000 / 25.4);
1027   *v = (int) nl_langinfo(_NL_PAPER_HEIGHT) * (72000 / 25.4);
1028   if (*h > 0 && *v > 0)
1029      return true;
1030 #endif
1031
1032   /* libpaper defaults to /etc/papersize. */
1033   if (fn_exists ("/etc/papersize"))
1034     return read_paper_conf ("/etc/papersize", h, v);
1035
1036   /* Can't find a default. */
1037   return false;
1038 }
1039
1040 /* Stores the dimensions, in 1/72000" units, of paper identified
1041    by SIZE into *H and *V.  SIZE can be the name of a kind of
1042    paper ("a4", "letter", ...) or a pair of dimensions
1043    ("210x297", "8.5x11in", ...).  Returns true on success, false
1044    on failure.  On failure, *H and *V are set for A4 paper. */
1045 bool
1046 outp_get_paper_size (const char *size, int *h, int *v)
1047 {
1048   struct substring s;
1049   bool ok;
1050
1051   s = ss_cstr (size);
1052   ss_trim (&s, ss_cstr (CC_SPACES));
1053
1054   if (ss_is_empty (s))
1055     {
1056       /* Treat empty string as default paper size. */
1057       ok = get_default_paper_size (h, v);
1058     }
1059   else if (isdigit (ss_first (s)))
1060     {
1061       /* Treat string that starts with digit as explicit size. */
1062       ok = parse_paper_size (size, h, v);
1063       if (!ok)
1064         error (0, 0, _("syntax error in paper size `%s'"), size);
1065     }
1066   else
1067     {
1068       /* Check against standard paper sizes. */
1069       ok = get_standard_paper_size (s, h, v);
1070     }
1071
1072   /* Default to A4 on error. */
1073   if (!ok)
1074     {
1075       *h = 210 * (72000 / 25.4);
1076       *v = 297 * (72000 / 25.4);
1077     }
1078   return ok;
1079 }
1080
1081 /* If D is NULL, returns the first enabled driver if any, NULL if
1082    none.  Otherwise D must be the last driver returned by this
1083    function, in which case the next enabled driver is returned or NULL
1084    if that was the last. */
1085 struct outp_driver *
1086 outp_drivers (struct outp_driver *d)
1087 {
1088   for (;;)
1089     {
1090       if (d == NULL)
1091         d = outp_driver_list;
1092       else
1093         d = d->next;
1094
1095       if (d == NULL
1096           || (d->device == 0 || (d->device & disabled_devices) != d->device))
1097         break;
1098     }
1099
1100   return d;
1101 }
1102
1103 /* Enables (if ENABLE is true) or disables (if ENABLE is false) the
1104    device(s) given in mask DEVICE. */
1105 void
1106 outp_enable_device (bool enable, int device)
1107 {
1108   if (enable)
1109     disabled_devices &= ~device;
1110   else
1111     disabled_devices |= device;
1112 }
1113
1114 /* Opens a page on driver D (if one is not open). */
1115 void
1116 outp_open_page (struct outp_driver *d)
1117 {
1118   if (!d->page_open)
1119     {
1120       d->cp_x = d->cp_y = 0;
1121
1122       d->page_open = true;
1123       if (d->class->open_page != NULL)
1124         d->class->open_page (d);
1125     }
1126 }
1127
1128 /* Closes the page on driver D (if one is open). */
1129 void
1130 outp_close_page (struct outp_driver *d)
1131 {
1132   if (d->page_open)
1133     {
1134       if (d->class->close_page != NULL)
1135         d->class->close_page (d);
1136       d->page_open = false;
1137     }
1138 }
1139
1140 /* Ejects the page on device D, if a page is open and non-blank,
1141    and opens a new page.  */
1142 void
1143 outp_eject_page (struct outp_driver *d)
1144 {
1145   if (d->page_open && d->cp_y != 0)
1146     outp_close_page (d);
1147   outp_open_page (d);
1148 }
1149
1150 /* Flushes output to screen devices, so that the user can see
1151    output that doesn't fill up an entire page. */
1152 void
1153 outp_flush (struct outp_driver *d)
1154 {
1155   if (d->device & OUTP_DEV_SCREEN && d->class->flush != NULL)
1156     {
1157       outp_close_page (d);
1158       d->class->flush (d);
1159     }
1160 }
1161
1162 /* Returns the width of string S, in device units, when output on
1163    device D. */
1164 int
1165 outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
1166 {
1167   struct outp_text text;
1168   int width;
1169
1170   text.font = font;
1171   text.justification = OUTP_LEFT;
1172   text.string = ss_cstr (s);
1173   text.h = text.v = INT_MAX;
1174   d->class->text_metrics (d, &text, &width, NULL);
1175
1176   return width;
1177 }