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