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