Apply patch from Debian Security Team for CAN-2004-1184.
[enscript.git] / src / psgen.c
1 /*
2  * Convert ASCII to PostScript.
3  * Copyright (c) 1995-2002 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7
8 /*
9  * This file is part of GNU Enscript.
10  *
11  * Enscript is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Enscript is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include "gsint.h"
26 #include <libgen.h>
27
28 /*
29  * Types and definitions.
30  */
31
32 /* Values for token flags. */
33
34 /* EPSF. */
35 #define F_EPSF_CENTER                   0x01
36 #define F_EPSF_RIGHT                    0x02
37 #define M_EPSF_JUSTIFICATION            0x03
38
39 #define F_EPSF_NO_CPOINT_UPDATE_X       0x04
40 #define F_EPSF_NO_CPOINT_UPDATE_Y       0x08
41
42 #define F_EPSF_ABSOLUTE_X               0x10
43 #define F_EPSF_ABSOLUTE_Y               0x20
44
45 #define F_EPSF_SCALE_X                  0x40
46 #define F_EPSF_SCALE_Y                  0x80
47
48
49 /* Predicate to check if we are at the correct slice. */
50 #define CORRECT_SLICE() (slicing == 0 || current_slice == slice)
51
52 /* Predicates for the current body font. */
53
54 /* Is character <ch> printable. */
55 #define ISPRINT(ch) (font_ctype[(unsigned char) (ch)] != ' ')
56
57 /* Does character <ch> exist in current body font? */
58 #define EXISTS(ch) (font_ctype[(unsigned char) (ch)] == '*')
59
60
61 #define RESOURCE_LINE_WIDTH 75
62
63 /* Token types. */
64 typedef enum
65 {
66   tNONE,
67   tEOF,
68   tSTRING,
69   tFORMFEED,
70   tNEWLINE,
71   tCARRIAGE_RETURN,
72   tWRAPPED_NEWLINE,
73   tEPSF,
74   tSETFILENAME,
75   tSETPAGENUMBER,
76   tNEWPAGE,
77   tFONT,
78   tCOLOR,
79   tBGCOLOR,
80   tSAVEX,
81   tLOADX,
82   tPS
83 } TokenType;
84
85 /* Special escape tokens. */
86 typedef enum
87 {
88   ESC_COMMENT,
89   ESC_EPSF,
90   ESC_FONT,
91   ESC_COLOR,
92   ESC_BGCOLOR,
93   ESC_NEWPAGE,
94   ESC_SETFILENAME,
95   ESC_SETPAGENUMBER,
96   ESC_SHADE,
97   ESC_BGGRAY,
98   ESC_ESCAPE,
99   ESC_SAVEX,
100   ESC_LOADX,
101   ESC_PS
102 } SpecialEscape;
103
104 /* Token structure. */
105 struct gs_token_st
106 {
107   TokenType type;
108   unsigned int flags;
109   double new_x;                 /* Current point x after this token. */
110   double new_y;                 /* Current point y after this token. */
111   int new_col;                  /* Line column after this token. */
112
113   union
114     {
115       int i;
116       char *str;
117       struct
118         {
119           double x;             /* x-offset */
120           double y;             /* y-offset */
121           double w;             /* width */
122           double h;             /* height */
123           double xscale;
124           double yscale;
125           int llx, lly, urx, ury; /* Bounding box. */
126           char filename[512];
127           char *skipbuf;
128           unsigned int skipbuf_len;
129           unsigned int skipbuf_pos;
130           FILE *fp;             /* File from which eps image is read. */
131           int pipe;             /* Is <fp> opened to pipe?  */
132         } epsf;
133       Color color;
134       Color bgcolor;
135       struct
136         {
137           char name[512];
138           FontPoint size;
139           InputEncoding encoding;
140         } font;
141       char filename[512];
142     } u;
143 };
144
145 typedef struct gs_token_st Token;
146
147
148 /*
149  * Prototypes for static functions.
150  */
151
152 static void get_next_token ___P ((InputStream *is, double linestart,
153                                   double linepos, unsigned int col,
154                                   double linew, Token *token));
155
156 static void dump_ps_page_header ___P ((char *fname, int empty));
157
158 static void dump_ps_page_trailer ();
159
160 static void dump_empty_page ();
161
162 /*
163  * Recognize a EPS file described by <token>.  Returns 1 if file was a
164  * valid EPS file or 0 otherwise.  File is accepted if it starts with
165  * the PostScript magic `%!' and it has a valid `%%BoundingBox' DSC
166  * comment.
167  */
168 static int recognize_eps_file ___P ((Token *token));
169
170 /*
171  * Insert EPS file described by <token> to the output stream.
172  */
173 static void paste_epsf ___P ((Token *token));
174
175 /*
176  * Check if InputStream <is> contains a file which can be passed
177  * through without any modifications.  Returns 1 if file was passed or
178  * 0 otherwise.
179  */
180 static int do_pass_through ___P ((char *fname, InputStream *is));
181
182 /*
183  * Read one float dimension from InputStream <is>.  If <units> is
184  * true, number can be followed by an optional unit specifier.  If
185  * <horizontal> is true, dimension is horizontal, otherwise it is
186  * vertical (this is used to find out how big `line' units are).
187  */
188 static double read_float ___P ((InputStream *is, int units, int horizontal));
189
190 /*
191  * Print linenumber <linenum> to the beginning of the current line.
192  * Current line start is specified by point (x, y).
193  */
194 static void print_line_number ___P ((double x, double y, double space,
195                                      double margin, unsigned int linenum));
196
197 /* Send PostScript to the output file. */
198 #define OUTPUT(body)    \
199   do {                  \
200     if (cofp == NULL)   \
201       cofp = ofp;       \
202     if (do_print)       \
203       fprintf body;     \
204   } while (0)
205
206 /* Divert output to tmp file so the total page count can be counted. */
207 static void divert ();
208
209 /* Paste diverted data to the output and patch the total page counts. */
210 static void undivert ();
211
212 /*
213  * Handle two-side printing related binding options.  This function is
214  * called once for each even-numbered page.
215  */
216 static void handle_two_side_options ();
217
218 /*
219  * Global variables.
220  */
221
222 unsigned int current_pagenum = 0; /* The number of the current page. */
223 unsigned int total_pages_in_file;
224 unsigned int input_filenum = 0;
225 unsigned int current_file_linenum;
226 int first_pagenum_for_file;
227 char *fname = NULL;             /* The name of the current input file. */
228
229
230 /*
231  * Static variables
232  */
233
234 /* Have we dumped PS header? */
235 static int ps_header_dumped = 0;
236
237 /* Divert file. */
238 static FILE *divertfp = NULL;
239
240 /* Current output() file. */
241 static FILE *cofp = NULL;
242
243 /* To print or not to print, that's a question. */
244 static int do_print = 1;
245
246 /* Is ^@font{}-defined font active? */
247 static int user_fontp = 0;
248
249 /* The user ^@font{}-defined font. */
250 static char user_font_name[256];
251 static FontPoint user_font_pt;
252 static InputEncoding user_font_encoding;
253
254 /* Is ^@color{}-defined color active? */
255 static int user_colorp = 0;
256
257 /* The user ^@color{}-defined color. */
258 static Color user_color;
259
260 /* Is ^@bgcolor{}-defined color active? */
261 static int user_bgcolorp = 0;
262
263 /* The user ^@bgcolor{}-defined color. */
264 static Color user_bgcolor;
265
266 /* The last linenumber printed by print_line_number(). */
267 static unsigned int print_line_number_last;
268
269 /* Registers to store X-coordinates with the ^@savex{} escape.
270    Initially these are uninitialized. */
271 static double xstore[256];
272
273 /*
274  * Global functions.
275  */
276
277 void
278 dump_ps_header ()
279 {
280   char *cp, *cp2;
281   int i, j, got;
282
283   /* Dump PS header only once. */
284   if (ps_header_dumped)
285     return;
286   ps_header_dumped = 1;
287
288   /*
289    * Header.
290    */
291
292   OUTPUT ((cofp, "%s\n", output_first_line));
293   OUTPUT ((cofp, "%%%%BoundingBox: %d %d %d %d\n", media->llx, media->lly,
294            media->urx, media->ury));
295   OUTPUT ((cofp, "%%%%Title: %s\n", title));
296   OUTPUT ((cofp, "%%%%For: %s\n", passwd->pw_gecos));
297   OUTPUT ((cofp, "%%%%Creator: %s\n", version_string));
298   OUTPUT ((cofp, "%%%%CreationDate: %s\n", date_string));
299   OUTPUT ((cofp, "%%%%Orientation: %s\n",
300            ((nup > 1) && nup_landscape)
301            || ((nup == 1) && landscape) ? "Landscape" : "Portrait"));
302   OUTPUT ((cofp, "%%%%Pages: (atend)\n"));
303   OUTPUT ((cofp, "%%%%DocumentMedia: %s %d %d 0 () ()\n",
304            media->name, media->w, media->h));
305   OUTPUT ((cofp, "%%%%DocumentNeededResources: (atend)\n"));
306
307   if (count_key_value_set (pagedevice) > 0)
308     OUTPUT ((cofp, "%%%%LanguageLevel: 2\n"));
309
310   OUTPUT ((cofp, "%%%%EndComments\n"));
311
312
313   /*
314    * Procedure Definitions.
315    */
316
317   OUTPUT ((cofp, "%%%%BeginProlog\n"));
318
319   /* Prolog. */
320   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Prolog %s\n",
321            ps_version_string));
322   if (!paste_file ("enscript", ".pro"))
323     FATAL ((stderr, _("couldn't find prolog \"%s\": %s\n"), "enscript.pro",
324             strerror (errno)));
325   OUTPUT ((cofp, "%%%%EndResource\n"));
326
327   /* Encoding vector. */
328   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Encoding-%s %s\n",
329            encoding_name, ps_version_string));
330   if (!paste_file (encoding_name, ".enc"))
331     FATAL ((stderr, _("couldn't find encoding file \"%s.enc\": %s\n"),
332             encoding_name, strerror (errno)));
333   OUTPUT ((cofp, "%%%%EndResource\n"));
334
335   OUTPUT ((cofp, "%%%%EndProlog\n"));
336
337
338   /*
339    * Document Setup.
340    */
341
342   OUTPUT ((cofp, "%%%%BeginSetup\n"));
343
344   /* Download fonts. */
345   for (got = strhash_get_first (download_fonts, &cp, &j, (void **) &cp2); got;
346        got = strhash_get_next (download_fonts, &cp, &j, (void **) &cp2))
347     download_font (cp);
348
349   /* For each required font, emit %%IncludeResouce comment. */
350   for (got = strhash_get_first (res_fonts, &cp, &j, (void **) &cp2); got;
351        got = strhash_get_next (res_fonts, &cp, &j, (void **) &cp2))
352     OUTPUT ((cofp, "%%%%IncludeResource: font %s\n", cp));
353
354   OUTPUT ((cofp, "/HFpt_w %g def\n", HFpt.w));
355   OUTPUT ((cofp, "/HFpt_h %g def\n", HFpt.h));
356
357
358   /* Select our fonts. */
359
360   /* Header font HF. */
361   OUTPUT ((cofp, "/%s /HF-gs-font MF\n", HFname));
362   OUTPUT ((cofp,
363            "/HF /HF-gs-font findfont [HFpt_w 0 0 HFpt_h 0 0] makefont def\n"));
364
365   /* Our default typing font F. */
366   OUTPUT ((cofp, "/%s /F-gs-font MF\n", Fname));
367   OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
368
369   /* Underlay. */
370   if (underlay != NULL)
371     {
372       OUTPUT ((cofp, "/ul_str (%s) def\n", underlay));
373       OUTPUT ((cofp, "/ul_w_ptsize %g def\n", ul_ptsize.w));
374       OUTPUT ((cofp, "/ul_h_ptsize %g def\n", ul_ptsize.h));
375       OUTPUT ((cofp, "/ul_gray %g def\n", ul_gray));
376       OUTPUT ((cofp, "/ul_x %g def\n", ul_x));
377       OUTPUT ((cofp, "/ul_y %g def\n", ul_y));
378       OUTPUT ((cofp, "/ul_angle %g def\n", ul_angle));
379       OUTPUT ((cofp, "/ul_style %d def\n", ul_style));
380       OUTPUT ((cofp, "/%s /F-ul-font MF\n", ul_font));
381       OUTPUT ((cofp, "/ul_font /F-ul-font findfont \
382 [ul_w_ptsize 0 0 ul_h_ptsize 0 0] makefont def\n"));
383     }
384
385   /* Number of copies. */
386   OUTPUT ((cofp, "/#copies %d def\n", num_copies));
387
388   /* Page prefeed. */
389   if (page_prefeed)
390     OUTPUT ((cofp, "true page_prefeed\n"));
391
392   /* Statusdict definitions. */
393   if (count_key_value_set (statusdict) > 0)
394     {
395       OUTPUT ((cofp, "%% Statustdict definitions:\nstatusdict begin\n  "));
396       i = 2;
397       for (got = strhash_get_first (statusdict, &cp, &j, (void **) &cp2); got;
398            got = strhash_get_next (statusdict, &cp, &j, (void **) &cp2))
399         {
400           j = strlen (cp) + 1 + strlen (cp2) + 1;
401           if (i + j > RESOURCE_LINE_WIDTH)
402             {
403               OUTPUT ((cofp, "\n  "));
404               i = 2;
405             }
406           OUTPUT ((cofp, "%s %s ", cp2, cp));
407           i += j;
408         }
409       OUTPUT ((cofp, "\nend\n"));
410     }
411
412   /* Page device definitions. */
413   if (pslevel >= 2 &&
414       (count_key_value_set (pagedevice) > 0 || generate_PageSize))
415     {
416       OUTPUT ((cofp, "%% Pagedevice definitions:\n"));
417       OUTPUT ((cofp, "gs_languagelevel 1 gt {\n  <<\n    "));
418
419       i = 4;
420       for (got = strhash_get_first (pagedevice, &cp, &j, (void **) &cp2); got;
421            got = strhash_get_next (pagedevice, &cp, &j, (void **) &cp2))
422         {
423           j = strlen (cp2) + 1 + strlen (cp) + 2;
424           if (i + j > RESOURCE_LINE_WIDTH)
425             {
426               OUTPUT ((cofp, "\n    "));
427               i = 4;
428             }
429           OUTPUT ((cofp, "/%s %s ", cp, cp2));
430           i += j;
431         }
432
433       if (generate_PageSize)
434         {
435           if (i + 21 > RESOURCE_LINE_WIDTH)
436             {
437               OUTPUT ((cofp, "\n    "));
438               i = 4;
439             }
440           OUTPUT ((cofp, "/PageSize [%d %d] ", media->w, media->h));
441           i += 21;
442         }
443
444       OUTPUT ((cofp, "\n  >> setpagedevice\n} if\n"));
445     }
446
447   /*
448    * Dump header procset.  Header must come after all font inclusions
449    * and enscript's dynamic state definition.
450    */
451   if (header != HDR_NONE)
452     {
453       char *hdr;
454       if (header == HDR_SIMPLE)
455         hdr = "simple";
456       else
457         hdr = fancy_header_name;
458
459       OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Header-%s %s\n",
460                hdr, ps_version_string));
461       if (!paste_file (hdr, ".hdr"))
462         FATAL ((stderr,
463                 _("couldn't find header definition file \"%s.hdr\": %s\n"),
464                 hdr, strerror (errno)));
465       OUTPUT ((cofp, "%%%%EndResource\n"));
466     }
467
468   /*
469    * Count output width and height here; we can't do it earlier because
470    * header might have just allocated some extra space.
471    */
472   d_output_w = d_page_w;
473   d_output_h = d_page_h - d_header_h - d_footer_h;
474
475   /* Dump our current dynamic state. */
476   OUTPUT ((cofp, "/d_page_w %d def\n", d_page_w));
477   OUTPUT ((cofp, "/d_page_h %d def\n", d_page_h));
478
479   OUTPUT ((cofp, "/d_header_x %d def\n", 0));
480   OUTPUT ((cofp, "/d_header_y %d def\n", d_output_h + d_footer_h));
481   OUTPUT ((cofp, "/d_header_w %d def\n", d_header_w));
482   OUTPUT ((cofp, "/d_header_h %d def\n", d_header_h));
483
484   OUTPUT ((cofp, "/d_footer_x %d def\n", 0));
485   OUTPUT ((cofp, "/d_footer_y %d def\n", 0));
486   OUTPUT ((cofp, "/d_footer_w %d def\n", d_header_w));
487   OUTPUT ((cofp, "/d_footer_h %d def\n", d_footer_h));
488
489   OUTPUT ((cofp, "/d_output_w %d def\n", d_output_w));
490   OUTPUT ((cofp, "/d_output_h %d def\n", d_output_h));
491   OUTPUT ((cofp, "/cols %d def\n", num_columns));
492
493   OUTPUT ((cofp, "%%%%EndSetup\n"));
494 }
495
496
497 void
498 dump_ps_trailer ()
499 {
500   int i, j, got;
501   char *cp;
502   void *value;
503   unsigned int nup_subpage;
504
505   if (!ps_header_dumped)
506     /* No header, let's be consistent and forget trailer also. */
507     return;
508
509   /* The possible pending N-up showpage. */
510   nup_subpage = (total_pages - 1) % nup;
511   if (nup > 1 && nup_subpage + 1 != nup)
512     /* N-up showpage missing. */
513     OUTPUT ((cofp, "_R\nS\n"));
514
515   /* Trailer. */
516
517   OUTPUT ((cofp, "%%%%Trailer\n"));
518
519   if (page_prefeed)
520     OUTPUT ((cofp, "false page_prefeed\n"));
521
522   OUTPUT ((cofp, "%%%%Pages: %d\n", total_pages));
523
524   /* Document needed resources. */
525
526   /* fonts. */
527   OUTPUT ((cofp, "%%%%DocumentNeededResources: font "));
528   i = 32;                       /* length of the previous string. */
529   for (got = strhash_get_first (res_fonts, &cp, &j, &value); got;
530        got = strhash_get_next (res_fonts, &cp, &j, &value))
531     {
532       if (i + strlen (cp) + 1 > RESOURCE_LINE_WIDTH)
533         {
534           OUTPUT ((cofp, "\n%%%%+ font "));
535           i = 9;                /* length of the previous string. */
536         }
537       OUTPUT ((cofp, "%s ", cp));
538       i += strlen (cp) + 1;
539     }
540   OUTPUT ((cofp, "\n%%%%EOF\n"));
541 }
542
543
544 void
545 process_file (char *fname_arg, InputStream *is, int is_toc)
546 {
547   int col;
548   double x, y;
549   double lx, ly;
550   double linewidth;             /* Line width in points. */
551   double lineend;
552   int done = 0;
553   int page_clear = 1;
554   unsigned int line_column;
555   unsigned int current_linenum;
556   double linenumber_space = 0;
557   double linenumber_margin = 0;
558   Token token;
559   int reuse_last_token = 0;
560   unsigned int current_slice = 1;
561   int last_wrapped_line = -1;
562   int last_spaced_file_linenum = -1;
563   int save_current_pagenum;
564   int toc_pagenum = 0;
565
566   /* Save filename. */
567   xfree (fname);
568   fname = xstrdup (fname_arg);
569
570   /* Init page number and line counters. */
571   if (!continuous_page_numbers)
572     current_pagenum = 0;
573   total_pages_in_file = 0;
574   current_file_linenum = start_line_number;
575
576   /*
577    * Count possible line number spaces.  This should be enought for 99999
578    * lines
579    */
580   linenumber_space = CHAR_WIDTH ('0') * 5 + 1.0;
581   linenumber_margin = CHAR_WIDTH (':') + CHAR_WIDTH ('m');
582
583   /* We got a new input file. */
584   input_filenum++;
585
586   /* We haven't printed any line numbers yet. */
587   print_line_number_last = (unsigned int) -1;
588
589   if (pass_through || output_language_pass_through)
590     if (do_pass_through (fname, is))
591       /* All done. */
592       return;
593
594   /* We have work to do, let's give header a chance to dump itself. */
595   dump_ps_header ();
596
597   /*
598    * Align files to the file_align boundary, this is handy for two-side
599    * printing.
600    */
601   while ((total_pages % file_align) != 0)
602     {
603       total_pages++;
604       dump_empty_page ();
605     }
606
607   MESSAGE (1, (stderr, _("processing file \"%s\"...\n"), fname));
608
609   linewidth = d_output_w / num_columns - 2 * d_output_x_margin
610     - line_indent;
611
612   /* Save the current running page number for possible toc usage. */
613   first_pagenum_for_file = total_pages + 1;
614
615   /*
616    * Divert our output to a temp file.  We will re-process it
617    * afterwards to patch, for example, the number of pages in the
618    * document.
619    */
620   divert ();
621
622   /* Process this input file. */
623   while (!done)
624     {
625       /* Start a new page. */
626       page_clear = 1;
627
628       for (col = 0; !done && col < num_columns; col++)
629         {
630           /* Move to the beginning of the column <col>. */
631           lx = x = col * d_output_w / (float) num_columns + d_output_x_margin
632             + line_indent;
633           lineend = lx + linewidth;
634
635           ly = y = d_footer_h + d_output_h - d_output_y_margin - LINESKIP;
636           current_linenum = 0;
637           line_column = 0;
638
639           while (1)
640             {
641               if (line_numbers && line_column == 0
642                   && (current_file_linenum != last_spaced_file_linenum))
643                 {
644                   /* Forward x by the amount needed by our line numbers. */
645                   x += linenumber_space + linenumber_margin;
646                   last_spaced_file_linenum = current_file_linenum;
647                 }
648
649               /* Get token. */
650               if (!reuse_last_token)
651                 get_next_token (is, lx, x, line_column, lineend, &token);
652               reuse_last_token = 0;
653
654               /*
655                * Page header printing is delayed to this point because
656                * we want to handle files ending with a newline character
657                * with care.  If the last newline would cause a pagebreak,
658                * otherwise we would print page header to the non-existent
659                * next page and that would be ugly ;)
660                */
661
662               if (token.type == tEOF)
663                 {
664                   done = 1;
665                   goto end_of_page;
666                 }
667
668               /*
669                * Now we know that we are going to make marks to this page
670                * => print page header.
671                */
672
673               if (page_clear)
674                 {
675                   PageRange *pr;
676
677                   current_pagenum++;
678                   total_pages_in_file++;
679
680                   /* Check page ranges. */
681                   if (page_ranges == NULL)
682                     do_print = 1;
683                   else
684                     {
685                       do_print = 0;
686                       for (pr = page_ranges; pr; pr = pr->next)
687                         {
688                           if (pr->odd || pr->even)
689                             {
690                               if ((pr->odd && (current_pagenum % 2) == 1)
691                                   || (pr->even && (current_pagenum % 2) == 0))
692                                 {
693                                   do_print = 1;
694                                   break;
695                                 }
696                             }
697                           else
698                             {
699                               if (pr->start <= current_pagenum
700                                   && current_pagenum <= pr->end)
701                                 {
702                                   do_print = 1;
703                                   break;
704                                 }
705                             }
706                         }
707                     }
708
709                   if (do_print)
710                     total_pages++;
711
712                   if (is_toc)
713                     {
714                       save_current_pagenum = current_pagenum;
715                       toc_pagenum--;
716                       current_pagenum = toc_pagenum;
717                     }
718
719                   dump_ps_page_header (fname, 0);
720                   page_clear = 0;
721
722                   if (is_toc)
723                     current_pagenum = save_current_pagenum;
724                 }
725
726               /* Print line highlight. */
727               if (line_column == 0 && line_highlight_gray < 1.0)
728                 OUTPUT ((cofp, "%g %g %g %g %g line_highlight\n",
729                          lx, (y - baselineskip
730                               + (font_bbox_lly * Fpt.h / UNITS_PER_POINT)),
731                          linewidth, Fpt.h + baselineskip,
732                          line_highlight_gray));
733
734               /* Print line numbers if needed. */
735               if (line_numbers && line_column == 0 && token.type != tFORMFEED)
736                 print_line_number (lx, y, linenumber_space, linenumber_margin,
737                                    current_file_linenum);
738
739               /* Check rest of tokens. */
740               switch (token.type)
741                 {
742                 case tFORMFEED:
743                   switch (formfeed_type)
744                     {
745                     case FORMFEED_COLUMN:
746                       goto end_of_column;
747                       break;
748
749                     case FORMFEED_PAGE:
750                       goto end_of_page;
751                       break;
752
753                     case FORMFEED_HCOLUMN:
754                       /*
755                        * Advance y-coordinate to the next even
756                        * `horizontal_column_height' position.
757                        */
758                       {
759                         int current_row;
760
761                         current_row = (ly - y) / horizontal_column_height;
762                         y = ly - (current_row + 1) * horizontal_column_height;
763
764                         /* Check the end of the page. */
765                         if (y < d_footer_h + d_output_y_margin)
766                           goto end_of_column;
767                       }
768                       break;
769                     }
770                   break;
771
772                 case tSTRING:
773                   if (CORRECT_SLICE ())
774                     {
775                       if (bggray < 1.0)
776                         {
777                           OUTPUT ((cofp, "%g %g %g %g %g (%s) bgs\n", x, y,
778                                    Fpt.h + baselineskip,
779                                    baselineskip
780                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
781                                    bggray,
782                                    token.u.str));
783                         }
784                       else if (user_bgcolorp)
785                         {
786                           OUTPUT ((cofp, "%g %g %g %g %g %g %g (%s) bgcs\n",
787                                    x, y, Fpt.h + baselineskip,
788                                    baselineskip
789                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
790                                    user_bgcolor.r,
791                                    user_bgcolor.g,
792                                    user_bgcolor.b,
793                                    token.u.str));
794                         }
795                       else
796                         {
797                           OUTPUT ((cofp, "%g %g M\n(%s) s\n", x, y,
798                                    token.u.str));
799                         }
800                     }
801                   x = token.new_x;
802                   line_column = token.new_col;
803                   break;
804
805                 case tCARRIAGE_RETURN:
806                   /* Just reset the x-coordinate. */
807                   x = col * d_output_w / (float) num_columns
808                     + d_output_x_margin + line_indent;
809                   line_column = 0;
810                   break;
811
812                 case tNEWLINE:
813                 case tWRAPPED_NEWLINE:
814                   if (token.type == tNEWLINE)
815                     {
816                       current_file_linenum++;
817                       current_slice = 1;
818                       y -= LINESKIP;
819                     }
820                   else
821                     {
822                       current_slice++;
823                       if (!slicing)
824                         {
825                           /* Mark wrapped line marks. */
826                           switch (mark_wrapped_lines_style)
827                             {
828                             case MWLS_NONE:
829                               /* nothing */
830                               break;
831
832                             case MWLS_PLUS:
833                               OUTPUT ((cofp, "%g %g M (+) s\n", x, y));
834                               break;
835
836                             default:
837                               /* Print some fancy graphics. */
838                               OUTPUT ((cofp,
839                                        "%g %g %g %g %d wrapped_line_mark\n",
840                                        x, y, Fpt.w, Fpt.h,
841                                        mark_wrapped_lines_style));
842                               break;
843                             }
844
845                           /*
846                            * For wrapped newlines, decrement y only if
847                            * we are not slicing the input.
848                            */
849                           y -= LINESKIP;
850                         }
851
852                       /* Count the wrapped lines here. */
853                       if (!slicing || current_slice > slice)
854                         if (current_file_linenum != last_wrapped_line)
855                           {
856                             if (do_print)
857                               num_truncated_lines++;
858                             last_wrapped_line = current_file_linenum;
859                           }
860                     }
861
862                   current_linenum++;
863                   if (current_linenum >= lines_per_page
864                       || y < d_footer_h + d_output_y_margin)
865                     goto end_of_column;
866
867                   x = col * d_output_w / (float) num_columns
868                     + d_output_x_margin + line_indent;
869                   line_column = 0;
870                   break;
871
872                 case tEPSF:
873                   /* Count current point movement. */
874
875                   if (token.flags & F_EPSF_ABSOLUTE_Y)
876                     token.new_y = ly;
877                   else
878                     token.new_y = y;
879                   token.new_y += token.u.epsf.y - token.u.epsf.h;
880
881                   if (token.flags & F_EPSF_ABSOLUTE_X)
882                     token.new_x = lx;
883                   else
884                     token.new_x = x;
885                   token.new_x += token.u.epsf.x;
886
887                   /* Check flags. */
888
889                   /* Justification flags overwrite <x_ofs>. */
890                   if (token.flags & F_EPSF_CENTER)
891                     token.new_x = lx + (linewidth - token.u.epsf.w) / 2;
892                   if (token.flags & F_EPSF_RIGHT)
893                     token.new_x = lx + (linewidth - token.u.epsf.w);
894
895                   /* Check if eps file does not fit to this column. */
896                   if ((token.flags & F_EPSF_NO_CPOINT_UPDATE_Y) == 0
897                       && token.new_y < d_footer_h + d_output_y_margin)
898                     {
899                       if (current_linenum == 0)
900                         {
901                           /*
902                            * At the beginning of the column, warn user
903                            * and print image.
904                            */
905                           MESSAGE (0, (stderr, _("EPS file \"%s\" is too \
906 large for page\n"),
907                                        token.u.epsf.filename));
908                         }
909                       else
910                         {
911                           /* Must start a new column. */
912                           reuse_last_token = 1;
913                           goto end_of_column;
914                         }
915                     }
916
917                   /* Do paste. */
918                   if (CORRECT_SLICE ())
919                     paste_epsf (&token);
920
921                   /* Update current point? */
922                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_Y))
923                     y = token.new_y;
924                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_X))
925                     x = token.new_x + token.u.epsf.w;
926
927                   if (y < d_footer_h + d_output_y_margin)
928                     goto end_of_column;
929                   break;
930
931                 case tFONT:
932                   /* Select a new current font. */
933                   if (line_column == 0)
934                     {
935                       double newh;
936
937                       /* Check for possible line skip change. */
938                       if (token.u.font.name[0] == '\0')
939                         newh = default_Fpt.h;
940                       else
941                         newh = token.u.font.size.h;
942
943                       if (newh != Fpt.h)
944                         {
945                           /* We need a different line skip value. */
946                           y -= (newh - Fpt.h);
947                         }
948                       /*
949                        * We must check for page overflow after we have
950                        * set the new font.
951                        */
952                     }
953
954                   MESSAGE (2, (stderr, "^@font="));
955                   if (token.u.font.name[0] == '\0')
956                     {
957                       /* Select the default font. */
958                       Fpt.w = default_Fpt.w;
959                       Fpt.h = default_Fpt.h;
960                       Fname = default_Fname;
961                       encoding = default_Fencoding;
962                       OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
963                       user_fontp = 0;
964                     }
965                   else
966                     {
967                       strhash_put (res_fonts, token.u.font.name,
968                                    strlen (token.u.font.name) + 1,
969                                    NULL, NULL);
970                       if (token.u.font.encoding == default_Fencoding)
971                         OUTPUT ((cofp, "/%s %g %g SUF\n", token.u.font.name,
972                                  token.u.font.size.w, token.u.font.size.h));
973                       else if (token.u.font.encoding == ENC_PS)
974                         OUTPUT ((cofp, "/%s %g %g SUF_PS\n", token.u.font.name,
975                                  token.u.font.size.w, token.u.font.size.h));
976                       else
977                         FATAL ((stderr,
978                                 _("user font encoding can be only the system's default or `ps'")));
979
980                       strcpy (user_font_name, token.u.font.name);
981                       user_font_pt.w = token.u.font.size.w;
982                       user_font_pt.h = token.u.font.size.h;
983                       user_font_encoding = token.u.font.encoding;
984                       user_fontp = 1;
985
986                       Fpt.w = user_font_pt.w;
987                       Fpt.h = user_font_pt.h;
988                       Fname = user_font_name;
989                       encoding = user_font_encoding;
990                     }
991                   MESSAGE (2, (stderr, "%s %g/%gpt\n", Fname, Fpt.w, Fpt.h));
992                   read_font_info ();
993
994                   /*
995                    * Check for page overflow in that case that we were
996                    * at the first column and font were changed to a bigger
997                    * one.
998                    */
999                   if (y < d_footer_h + d_output_y_margin)
1000                     goto end_of_column;
1001                   break;
1002
1003                 case tCOLOR:
1004                   /* Select a new color. */
1005                   MESSAGE (2, (stderr, "^@color{%f %f %f}\n",
1006                                token.u.color.r,
1007                                token.u.color.g,
1008                                token.u.color.b));
1009                   if (token.u.color.r == token.u.color.g
1010                       && token.u.color.g == token.u.color.b
1011                       && token.u.color.b == 0.0)
1012                     {
1013                       /* Select the default color (black). */
1014                       OUTPUT ((cofp, "0 setgray\n"));
1015                       user_colorp = 0;
1016                     }
1017                   else
1018                     {
1019                       OUTPUT ((cofp, "%g %g %g setrgbcolor\n",
1020                                token.u.color.r,
1021                                token.u.color.g,
1022                                token.u.color.b));
1023
1024                       user_color.r = token.u.color.r;
1025                       user_color.g = token.u.color.g;
1026                       user_color.b = token.u.color.b;
1027                       user_colorp = 1;
1028                     }
1029                   break;
1030
1031                 case tBGCOLOR:
1032                   /* Select a new background color. */
1033                   MESSAGE (2, (stderr, "^@bgcolor{%f %f %f}\n",
1034                                token.u.color.r,
1035                                token.u.color.g,
1036                                token.u.color.b));
1037
1038                   if (token.u.color.r == token.u.color.g
1039                       && token.u.color.g == token.u.color.b
1040                       && token.u.color.b == 1.0)
1041                     {
1042                       /* Select the default bgcolor (white). */
1043                       user_bgcolorp = 0;
1044                     }
1045                   else
1046                     {
1047                       user_bgcolor.r = token.u.color.r;
1048                       user_bgcolor.g = token.u.color.g;
1049                       user_bgcolor.b = token.u.color.b;
1050                       user_bgcolorp = 1;
1051                     }
1052                   break;
1053
1054                 case tSETFILENAME:
1055                   xfree (fname);
1056                   fname = xstrdup (token.u.filename);
1057                   break;
1058
1059                 case tSETPAGENUMBER:
1060                   current_pagenum = token.u.i - 1;
1061                   break;
1062
1063                 case tNEWPAGE:
1064                   if (current_linenum >= token.u.i)
1065                     goto end_of_page;
1066                   break;
1067
1068                 case tSAVEX:
1069                   xstore[(unsigned char) token.u.i] = x;
1070                   break;
1071
1072                 case tLOADX:
1073                   x = xstore[(unsigned char) token.u.i];
1074                   break;
1075
1076                 case tPS:
1077                   OUTPUT ((cofp, "%g %g M\n%s\n", x, y, token.u.str));
1078                   xfree (token.u.str);
1079                   break;
1080
1081                 case tNONE:
1082                 default:
1083                   FATAL ((stderr, "process_file(): got illegal token %d",
1084                           token.type));
1085                   break;
1086                 }
1087             }
1088         end_of_column:
1089           ;                     /* ULTRIX's cc needs this line. */
1090         }
1091
1092     end_of_page:
1093       if (!page_clear)
1094         dump_ps_page_trailer ();
1095     }
1096
1097   /*
1098    * Reset print flag to true so all the required document trailers
1099    * etc. get printed properly.
1100    */
1101   do_print = 1;
1102
1103   /* Undivert our output from the temp file to our output stream. */
1104   undivert ();
1105
1106   /* Table of contents? */
1107   if (toc)
1108     {
1109       char *cp;
1110       int save_total_pages = total_pages;
1111
1112       /* use first pagenum in file for toc */
1113       total_pages = first_pagenum_for_file;
1114
1115       cp = format_user_string ("TOC", toc_fmt_string);
1116       fprintf (toc_fp, "%s\n", cp);
1117       xfree (cp);
1118
1119       total_pages = save_total_pages;
1120     }
1121 }
1122
1123 \f
1124 /*
1125  * Static functions.
1126  */
1127
1128 /* Help macros. */
1129
1130 /* Check if character <ch> fits to current line. */
1131 #define FITS_ON_LINE(ch) ((linepos + CHAR_WIDTH (ch) < linew) || col == 0)
1132
1133 /* Is line buffer empty? */
1134 #define BUFFER_EMPTY() (bufpos == 0)
1135
1136 /* Unconditionally append character <ch> to the line buffer. */
1137 #define APPEND_CHAR(ch)                                 \
1138   do {                                                  \
1139     if (bufpos >= buflen)                               \
1140       {                                                 \
1141         buflen += 4096;                                 \
1142         buffer = xrealloc (buffer, buflen);             \
1143       }                                                 \
1144     buffer[bufpos++] = ch;                              \
1145   } while (0)
1146
1147 /*
1148  * Copy character <ch> (it fits to this line) to output buffer and
1149  * update current point counters.
1150  */
1151 #define EMIT(ch)                \
1152   do {                          \
1153     APPEND_CHAR (ch);           \
1154     linepos += CHAR_WIDTH (ch); \
1155     col++;                      \
1156   } while (0)
1157
1158 #define UNEMIT(ch)              \
1159   do {                          \
1160     linepos -= CHAR_WIDTH (ch); \
1161     col--;                      \
1162   } while (0)
1163
1164 #define ISSPACE(ch) ((ch) == ' ' || (ch) == '\t')
1165 #define ISOCTAL(ch) ('0' <= (ch) && (ch) <= '7')
1166
1167 /* Read one special escape from input <fp>. */
1168
1169 static struct
1170 {
1171   char *name;
1172   SpecialEscape escape;
1173 } escapes[] =
1174   {
1175     {"comment",         ESC_COMMENT},
1176     {"epsf",            ESC_EPSF},
1177     {"font",            ESC_FONT},
1178     {"color",           ESC_COLOR},
1179     {"bgcolor",         ESC_BGCOLOR},
1180     {"newpage",         ESC_NEWPAGE},
1181     {"ps",              ESC_PS},
1182     {"setfilename",     ESC_SETFILENAME},
1183     {"setpagenumber",   ESC_SETPAGENUMBER},
1184     {"shade",           ESC_SHADE},
1185     {"bggray",          ESC_BGGRAY},
1186     {"escape",          ESC_ESCAPE},
1187     {"savex",           ESC_SAVEX},
1188     {"loadx",           ESC_LOADX},
1189     {NULL, 0},
1190   };
1191
1192
1193 static void
1194 read_special_escape (InputStream *is, Token *token)
1195 {
1196   char escname[256];
1197   char buf[4096];
1198   int i, e;
1199   int ch;
1200
1201   /* Get escape name. */
1202   for (i = 0; i < sizeof (escname) - 1 && (ch = is_getc (is)) != EOF; i++)
1203     {
1204       if (!isalnum (ch))
1205         {
1206           is_ungetc (ch, is);
1207           break;
1208         }
1209       else
1210         escname[i] = ch;
1211     }
1212   escname[i] = '\0';
1213
1214   /* Lookup escape. */
1215   for (e = 0; escapes[e].name; e++)
1216     if (strcmp (escname, escapes[e].name) == 0)
1217       break;
1218   if (escapes[e].name == NULL)
1219     FATAL ((stderr, _("unknown special escape: %s"), escname));
1220
1221   /*
1222    * The epsf escape takes optional arguments so it must be handled
1223    * differently.
1224    */
1225   if (escapes[e].escape == ESC_EPSF)
1226     {
1227       int i;
1228       int pw, ph;
1229       double scale;
1230
1231       token->flags = 0;
1232       token->u.epsf.x = 0.0;
1233       token->u.epsf.y = 0.0;
1234       token->u.epsf.h = 0.0;
1235       token->u.epsf.pipe = 0;
1236
1237       ch = is_getc (is);
1238       if (ch == '[')
1239         {
1240           /* Read options. */
1241           while ((ch = is_getc (is)) != EOF && ch != ']')
1242             {
1243               switch (ch)
1244                 {
1245                 case 'c':       /* center justification */
1246                   token->flags &= ~M_EPSF_JUSTIFICATION;
1247                   token->flags |= F_EPSF_CENTER;
1248                   break;
1249
1250                 case 'n':       /* no current point update */
1251                   /* Check the next character. */
1252                   ch = is_getc (is);
1253                   switch (ch)
1254                     {
1255                     case 'x':
1256                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1257                       break;
1258
1259                     case 'y':
1260                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1261                       break;
1262
1263                     default:
1264                       is_ungetc (ch, is);
1265                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1266                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1267                       break;
1268                     }
1269                   break;
1270
1271                 case 'r':       /* right justification */
1272                   token->flags &= ~M_EPSF_JUSTIFICATION;
1273                   token->flags |= F_EPSF_RIGHT;
1274                   break;
1275
1276
1277                 case 's':       /* scale */
1278                   /* Check the next character. */
1279                   ch = is_getc (is);
1280                   switch (ch)
1281                     {
1282                     case 'x':
1283                       token->flags |= F_EPSF_SCALE_X;
1284                       token->u.epsf.xscale = read_float (is, 0, 1);
1285                       break;
1286
1287                     case 'y':
1288                       token->flags |= F_EPSF_SCALE_Y;
1289                       token->u.epsf.yscale = read_float (is, 0, 0);
1290                       break;
1291
1292                     default:
1293                       is_ungetc (ch, is);
1294                       token->flags |= F_EPSF_SCALE_X;
1295                       token->flags |= F_EPSF_SCALE_Y;
1296                       token->u.epsf.xscale = token->u.epsf.yscale
1297                         = read_float (is, 0, 1);
1298                       break;
1299                     }
1300                   break;
1301
1302                 case 'x':       /* x-position */
1303                   token->u.epsf.x = read_float (is, 1, 1);
1304
1305                   /* Check the next character. */
1306                   ch = is_getc (is);
1307                   switch (ch)
1308                     {
1309                     case 'a':
1310                       token->flags |= F_EPSF_ABSOLUTE_X;
1311                       break;
1312
1313                     default:
1314                       is_ungetc (ch, is);
1315                       break;
1316                     }
1317                   break;
1318
1319                 case 'y':       /* y-position */
1320                   token->u.epsf.y = - read_float (is, 1, 0);
1321
1322                   /* Check the next character. */
1323                   ch = is_getc (is);
1324                   switch (ch)
1325                     {
1326                     case 'a':
1327                       token->flags |= F_EPSF_ABSOLUTE_Y;
1328                       break;
1329
1330                     default:
1331                       is_ungetc (ch, is);
1332                       break;
1333                     }
1334                   break;
1335
1336                 case 'h':       /* height */
1337                   token->u.epsf.h = read_float (is, 1, 0);
1338                   break;
1339
1340                 case ' ':
1341                 case '\t':
1342                   break;
1343
1344                 default:
1345                   FATAL ((stderr, _("illegal option %c for ^@epsf escape"),
1346                           ch));
1347                 }
1348             }
1349           if (ch != ']')
1350             FATAL ((stderr,
1351                     _("malformed ^@epsf escape: no ']' after options")));
1352
1353           ch = is_getc (is);
1354         }
1355       if (ch == '{')
1356         {
1357           /* Read filename. */
1358           for (i = 0; (ch = is_getc (is)) != EOF && ch != '}'; i++)
1359             {
1360               token->u.epsf.filename[i] = ch;
1361               if (i + 1 >= sizeof (token->u.epsf.filename))
1362                 FATAL ((stderr,
1363                         _("too long file name for ^@epsf escape:\n%.*s"),
1364                         i, token->u.epsf.filename));
1365             }
1366           if (ch == EOF)
1367             FATAL ((stderr, _("unexpected EOF while scanning ^@epsf escape")));
1368
1369           token->u.epsf.filename[i] = '\0';
1370           token->type = tEPSF;
1371         }
1372       else
1373         FATAL ((stderr, _("malformed ^@epsf escape: no '{' found")));
1374
1375       /*
1376        * Now we have a valid epsf-token in <token>.  Let's read BoundingBox
1377        * and do some calculations.
1378        */
1379       if (!recognize_eps_file (token))
1380         /* Recognize eps has already printed error message so we are done. */
1381         token->type = tNONE;
1382       else
1383         {
1384           /* Some fixups for x and y dimensions. */
1385           token->u.epsf.y += LINESKIP - 1;
1386           if (token->u.epsf.h != 0.0)
1387             token->u.epsf.h -= 1.0;
1388
1389           /* Count picture's width and height. */
1390
1391           pw = token->u.epsf.urx - token->u.epsf.llx;
1392           ph = token->u.epsf.ury - token->u.epsf.lly;
1393
1394           /* The default scale. */
1395           if (token->u.epsf.h == 0.0)
1396             scale = 1.0;
1397           else
1398             scale = token->u.epsf.h / ph;
1399
1400           if ((token->flags & F_EPSF_SCALE_X) == 0)
1401             token->u.epsf.xscale = scale;
1402           if ((token->flags & F_EPSF_SCALE_Y) == 0)
1403             token->u.epsf.yscale = scale;
1404
1405           pw *= token->u.epsf.xscale;
1406           ph *= token->u.epsf.yscale;
1407
1408           token->u.epsf.w = pw;
1409           token->u.epsf.h = ph;
1410         }
1411     }
1412   else if (escapes[e].escape == ESC_COMMENT)
1413     {
1414       /* Comment the rest of this line. */
1415       while ((ch = is_getc (is)) != EOF && ch != nl)
1416         ;
1417       token->type = tNONE;
1418     }
1419   else
1420     {
1421       char *cp;
1422       int parenlevel;
1423
1424       /*
1425        * Handle the rest of the escapes.
1426        */
1427
1428       /* Read argument. */
1429       ch = is_getc (is);
1430       if (ch != '{')
1431         FATAL ((stderr, _("malformed %s escape: no '{' found"),
1432                 escapes[e].name));
1433
1434       parenlevel = 0;
1435       for (i = 0;
1436            (ch = is_getc (is)) != EOF && (parenlevel > 0 || ch != '}'); i++)
1437         {
1438           if (ch == '{')
1439             parenlevel++;
1440           else if (ch == '}')
1441             parenlevel--;
1442
1443           buf[i] = ch;
1444           if (i + 1 >= sizeof (buf))
1445             FATAL ((stderr, _("too long argument for %s escape:\n%.*s"),
1446                     escapes[i].name, i, buf));
1447         }
1448       buf[i] = '\0';
1449
1450       /* And now handle the escape. */
1451       switch (escapes[e].escape)
1452         {
1453         case ESC_FONT:
1454           strcpy (token->u.font.name, buf);
1455
1456           /* Check for the default font. */
1457           if (strcmp (token->u.font.name, "default") == 0)
1458             token->u.font.name[0] = '\0';
1459           else
1460             {
1461               if (!parse_font_spec (token->u.font.name, &cp,
1462                                     &token->u.font.size,
1463                                     &token->u.font.encoding))
1464                 FATAL ((stderr, _("malformed font spec for ^@font escape: %s"),
1465                         token->u.font.name));
1466
1467               strcpy (token->u.font.name, cp);
1468               xfree (cp);
1469             }
1470           token->type = tFONT;
1471           break;
1472
1473         case ESC_COLOR:
1474         case ESC_BGCOLOR:
1475           /* Check for the default color. */
1476           if (strcmp (buf, "default") == 0)
1477             {
1478               double val = 0;
1479
1480               if (escapes[e].escape == ESC_BGCOLOR)
1481                 val = 1;
1482
1483               token->u.color.r = val;
1484               token->u.color.g = val;
1485               token->u.color.b = val;
1486             }
1487           else
1488             {
1489               int got;
1490
1491               got = sscanf (buf, "%g %g %g",
1492                             &token->u.color.r,
1493                             &token->u.color.g,
1494                             &token->u.color.b);
1495               switch (got)
1496                 {
1497                 case 0:
1498                 case 2:
1499                   FATAL ((stderr,
1500                           _("malformed color spec for ^@%s escape: %s"),
1501                           escapes[e].escape == ESC_COLOR
1502                           ? "color" : "bgcolor",
1503                           buf));
1504                   break;
1505
1506                 case 1:
1507                   token->u.color.g = token->u.color.b = token->u.color.r;
1508                   break;
1509
1510                 default:
1511                   /* Got all three components. */
1512                   break;
1513                 }
1514             }
1515           if (escapes[e].escape == ESC_COLOR)
1516             token->type = tCOLOR;
1517           else
1518             token->type = tBGCOLOR;
1519           break;
1520
1521         case ESC_SHADE:
1522           line_highlight_gray = atof (buf);
1523           if (line_highlight_gray < 0.0 || line_highlight_gray > 1.0)
1524             FATAL ((stderr, _("invalid value for ^@shade escape: %s"), buf));
1525
1526           token->type = tNONE;
1527           break;
1528
1529         case ESC_BGGRAY:
1530           bggray = atof (buf);
1531           if (bggray < 0.0 || bggray > 1.0)
1532             FATAL ((stderr, _("invalid value for ^@bggray escape: %s"), buf));
1533
1534           token->type = tNONE;
1535           break;
1536
1537         case ESC_ESCAPE:
1538           if (strcmp (buf, "default") == 0)
1539             escape_char = default_escape_char;
1540           else
1541             escape_char = atoi (buf);
1542           token->type = tNONE;
1543           break;
1544
1545         case ESC_SETFILENAME:
1546           strcpy (token->u.filename, buf);
1547           token->type = tSETFILENAME;
1548           break;
1549
1550         case ESC_SETPAGENUMBER:
1551           token->u.i = atoi (buf);
1552           token->type = tSETPAGENUMBER;
1553           break;
1554
1555         case ESC_NEWPAGE:
1556           if (i == 0)
1557             token->u.i = 1;     /* The default is the first line. */
1558           else
1559             token->u.i = atoi (buf);
1560           token->type = tNEWPAGE;
1561           break;
1562
1563         case ESC_SAVEX:
1564           token->type = tSAVEX;
1565           token->u.i = atoi (buf);
1566           break;
1567
1568         case ESC_LOADX:
1569           token->type = tLOADX;
1570           token->u.i = atoi (buf);
1571           break;
1572
1573         case ESC_PS:
1574           token->u.str = xstrdup (buf);
1575           token->type = tPS;
1576           break;
1577
1578         default:
1579           /* NOTREACHED */
1580           abort ();
1581           break;
1582         }
1583     }
1584 }
1585
1586
1587 /* Get next token from input file <fp>. */
1588 static void
1589 get_next_token (InputStream *is, double linestart, double linepos,
1590                 unsigned int col, double linew, Token *token)
1591 {
1592   static unsigned char *buffer = NULL; /* output buffer */
1593   static unsigned int buflen = 0; /* output buffer's length */
1594   unsigned int bufpos = 0;      /* current position in output buffer */
1595   int ch = 0;
1596   int done = 0;
1597   int i;
1598   static int pending_token = tNONE;
1599   unsigned int original_col = col;
1600
1601   if (pending_token != tNONE)
1602     {
1603       token->type = pending_token;
1604       pending_token = tNONE;
1605       return;
1606     }
1607
1608 #define DONE_DONE 1
1609 #define DONE_WRAP 2
1610
1611   while (!done)
1612     {
1613       ch = is_getc (is);
1614       switch (ch)
1615         {
1616         case EOF:
1617           if (BUFFER_EMPTY ())
1618             {
1619               token->type = tEOF;
1620               return;
1621             }
1622
1623           done = DONE_DONE;
1624           break;
1625
1626         case '\r':
1627         case '\n':
1628           /*
1629            * One of these is the newline character and the other one
1630            * is carriage return.
1631            */
1632           if (ch == nl)
1633             {
1634               /* The newline character. */
1635               if (BUFFER_EMPTY ())
1636                 {
1637                   token->type = tNEWLINE;
1638                   return;
1639                 }
1640               else
1641                 {
1642                   is_ungetc (ch, is);
1643                   done = DONE_DONE;
1644                 }
1645             }
1646           else
1647             {
1648               /* The carriage return character. */
1649               if (BUFFER_EMPTY ())
1650                 {
1651                   token->type = tCARRIAGE_RETURN;
1652                   return;
1653                 }
1654               else
1655                 {
1656                   is_ungetc (ch, is);
1657                   done = DONE_DONE;
1658                 }
1659             }
1660           break;
1661
1662         case '\t':
1663           if (font_is_fixed)
1664             {
1665               i = tabsize - (col % tabsize);
1666               for (; i > 0; i--)
1667                 {
1668                   if (FITS_ON_LINE (' '))
1669                     EMIT (' ');
1670                   else
1671                     {
1672                       done = DONE_WRAP;
1673                       break;
1674                     }
1675                 }
1676             }
1677           else
1678             {
1679               /* Proportional font. */
1680
1681               double grid = tabsize * CHAR_WIDTH (' ');
1682               col++;
1683
1684               /* Move linepos to the next multiple of <grid>. */
1685               linepos = (((int) ((linepos - linestart) / grid) + 1) * grid
1686                          + linestart);
1687               if (linepos >= linew)
1688                 done = DONE_WRAP;
1689               else
1690                 done = DONE_DONE;
1691             }
1692           break;
1693
1694         case '\f':
1695           if (BUFFER_EMPTY ())
1696             {
1697               if (interpret_formfeed)
1698                 token->type = tFORMFEED;
1699               else
1700                 token->type = tNEWLINE;
1701               return;
1702             }
1703           else
1704             {
1705               is_ungetc (ch, is);
1706               done = DONE_DONE;
1707             }
1708           break;
1709
1710         default:
1711           /* Handle special escapes. */
1712           if (special_escapes && ch == escape_char)
1713             {
1714               if (BUFFER_EMPTY ())
1715                 {
1716                   /* Interpret special escapes. */
1717                   read_special_escape (is, token);
1718                   if (token->type != tNONE)
1719                     return;
1720
1721                   /*
1722                    * Got tNONE special escape => read_special_escape()
1723                    * has already done what was needed.  Just read more.
1724                    */
1725                   break;
1726                 }
1727               else
1728                 {
1729                   is_ungetc (ch, is);
1730                   done = DONE_DONE;
1731                   break;
1732                 }
1733             }
1734
1735           /* Handle backspace character. */
1736           if (ch == bs)
1737             {
1738               if (BUFFER_EMPTY () || !EXISTS (buffer[bufpos - 1]))
1739                 linepos -= CHAR_WIDTH ('m');
1740               else
1741                 linepos -= CHAR_WIDTH (buffer[bufpos - 1]);
1742
1743               done = DONE_DONE;
1744               break;
1745             }
1746
1747           /* Check normal characters. */
1748           if (EXISTS (ch))
1749             {
1750               if (FITS_ON_LINE (ch))
1751                 {
1752                   /*
1753                    * Print control characters (and optionally
1754                    * characters greater than 127) in the escaped form
1755                    * so PostScript interpreter will not hang on them.
1756                    */
1757                   if (ch < 040 || (clean_7bit && ch >= 0200))
1758                     {
1759                       char buf[10];
1760
1761                       sprintf (buf, "\\%03o", ch);
1762                       for (i = 0; buf[i]; i++)
1763                         APPEND_CHAR (buf[i]);
1764
1765                       /* Update current point counters manually. */
1766                       linepos += CHAR_WIDTH (ch);
1767                       col++;
1768                     }
1769                   else if (ch == '(' || ch == ')' || ch == '\\')
1770                     {
1771                       /* These must be quoted in PostScript strings. */
1772                       APPEND_CHAR ('\\');
1773                       EMIT (ch);
1774                     }
1775                   else
1776                     EMIT (ch);
1777                 }
1778               else
1779                 {
1780                   is_ungetc (ch, is);
1781                   done = DONE_WRAP;
1782                 }
1783             }
1784           else if (ISPRINT (ch))
1785             {
1786               /* Printable, but do not exists in this font. */
1787               if (FITS_ON_LINE ('?'))
1788                 {
1789                   EMIT ('?');
1790                   if (missing_chars[ch]++ == 0)
1791                     num_missing_chars++;
1792                 }
1793               else
1794                 {
1795                   is_ungetc (ch, is);
1796                   done = DONE_WRAP;
1797                 }
1798             }
1799           else
1800             {
1801               char buf[20];
1802               double len = 0.0;
1803
1804               /*
1805                * Non-printable and does not exist in current font, print
1806                * it in the format specified by non_printable_format.
1807                */
1808
1809               if (non_printable_chars[ch]++ == 0)
1810                 num_non_printable_chars++;
1811
1812               switch (non_printable_format)
1813                 {
1814                 case NPF_SPACE:
1815                   strcpy (buf, " ");
1816                   break;
1817
1818                 case NPF_QUESTIONMARK:
1819                   strcpy (buf, "?");
1820                   break;
1821
1822                 case NPF_CARET:
1823                   if (ch < 0x20)
1824                     {
1825                       buf[0] = '^';
1826                       buf[1] = '@' + ch;
1827                       buf[2] = '\0';
1828                       break;
1829                     }
1830                   /* FALLTHROUGH */
1831
1832                 case NPF_OCTAL:
1833                   sprintf (buf, "\\%03o", ch);
1834                   break;
1835                 }
1836
1837               /* Count length. */
1838               for (i = 0; buf[i]; i++)
1839                 len += CHAR_WIDTH (buf[i]);
1840
1841               if (linepos + len < linew || col == 0)
1842                 {
1843                   /* Print it. */
1844                   for (i = 0; buf[i]; i++)
1845                     {
1846                       if (buf[i] == '\\')
1847                         APPEND_CHAR ('\\'); /* Escape '\\' characters. */
1848                       EMIT (buf[i]);
1849                     }
1850                 }
1851               else
1852                 {
1853                   is_ungetc (ch, is);
1854                   done = DONE_WRAP;
1855                 }
1856             }
1857           break;
1858         }
1859     }
1860
1861   /* Got a string. */
1862
1863   /* Check for wrapped line. */
1864   if (done == DONE_WRAP)
1865     {
1866       /* This line is too long. */
1867       ch = nl;
1868       if (line_end == LE_TRUNCATE)
1869         {
1870           /* Truncate this line. */
1871           while ((ch = is_getc (is)) != EOF && ch != nl)
1872             ;
1873         }
1874       else if (!BUFFER_EMPTY () && line_end == LE_WORD_WRAP)
1875         {
1876           int w;
1877
1878           if (ISSPACE (buffer[bufpos - 1]))
1879             {
1880               /* Skip all whitespace from the end of the wrapped line. */
1881               while ((w = is_getc (is)) != EOF && ISSPACE (w))
1882                 ;
1883               is_ungetc (w, is);
1884             }
1885           else
1886             {
1887               /* Find the previous word boundary for the wrap. */
1888               for (w = bufpos - 1; w >= 0 && !ISSPACE (buffer[w]); w--)
1889                 ;
1890               w++;
1891               if (w > 0 || original_col > 0)
1892                 {
1893                   /*
1894                    * Ok, we found a word boundary.  Now we must unemit
1895                    * characters from the buffer to the intput stream.
1896                    *
1897                    * Note:
1898                    *  - bufpos is unsigned integer variable
1899                    *  - some characters are escaped with '\\'
1900                    *  - some characters are printed in octal notation
1901                    */
1902                   do
1903                     {
1904                       bufpos--;
1905
1906                       /* Check for '(', ')' and '\\'. */
1907                       if (bufpos > w
1908                           && (buffer[bufpos] == '('
1909                               || buffer[bufpos] ==  ')'
1910                               || buffer[bufpos] == '\\')
1911                           && buffer[bufpos - 1] == '\\')
1912                         {
1913                           is_ungetc (buffer[bufpos], is);
1914                           UNEMIT (buffer[bufpos]);
1915                           bufpos--;
1916                         }
1917                       /* Check the octal notations "\\%03o". */
1918                       else if (bufpos - 2 > w
1919                                && ISOCTAL (buffer[bufpos])
1920                                && ISOCTAL (buffer[bufpos - 1])
1921                                && ISOCTAL (buffer[bufpos - 2])
1922                                && buffer[bufpos - 3] == '\\')
1923                         {
1924                           unsigned int ti;
1925
1926                           /*
1927                            * It is a potential octal character.  Now we
1928                            * must process the buffer from the beginning
1929                            * and see if `bufpos - 3' really starts a character.
1930                            */
1931                           for (ti = w; ti < bufpos - 3; ti++)
1932                             {
1933                               if (buffer[ti] == '\\')
1934                                 {
1935                                   if (ISOCTAL (buffer[ti + 1]))
1936                                     {
1937                                       unsigned int tti;
1938
1939                                       for (tti = 0;
1940                                            tti < 3 && ISOCTAL (buffer[ti + 1]);
1941                                            tti++, ti++)
1942                                         ;
1943                                     }
1944                                   else
1945                                     /* Simple escape. */
1946                                     ti++;
1947                                 }
1948                             }
1949
1950                           /*
1951                            * If <ti> is equal to <bufpos - 3>, we found
1952                            * an octal character, otherwise the leading
1953                            * backslash at <bufpos - 3> belongs to the
1954                            * previous character.
1955                            */
1956                           if (ti == bufpos - 3)
1957                             {
1958                               int tch;
1959
1960                               tch = (((buffer[bufpos - 2] - '0') << 6)
1961                                      + ((buffer[bufpos - 1] - '0') << 3)
1962                                      + (buffer[bufpos] - '0'));
1963                               is_ungetc (tch, is);
1964                               UNEMIT (tch);
1965                               bufpos -= 3;
1966                             }
1967                           else
1968                             /* Normal character. */
1969                             goto unemit_normal;
1970                         }
1971                       else
1972                         {
1973                           /* Normal character, just unget it. */
1974                         unemit_normal:
1975                           is_ungetc (buffer[bufpos], is);
1976                           UNEMIT (buffer[bufpos]);
1977                         }
1978                     }
1979                   while (bufpos > w);
1980                 }
1981             }
1982         }
1983
1984       if (ch == nl)
1985         {
1986           if (line_end == LE_TRUNCATE)
1987             {
1988               if (do_print)
1989                 num_truncated_lines++;
1990               pending_token = tNEWLINE;
1991             }
1992           else
1993             pending_token = tWRAPPED_NEWLINE;
1994         }
1995       else
1996         pending_token = tEOF;
1997     }
1998
1999   APPEND_CHAR ('\0');
2000   token->type = tSTRING;
2001   token->u.str = (char *) buffer;
2002   token->new_x = linepos;
2003   token->new_col = col;
2004 }
2005
2006
2007 static void
2008 dump_ps_page_header (char *fname, int empty)
2009 {
2010   char *dirc, *basec, *fdir, *ftail;
2011   int got, i;
2012   char *cp, *cp2;
2013   char *cstr = "%%";
2014   unsigned int nup_subpage;
2015
2016   /* The N-up printing sub-page. */
2017   nup_subpage = (total_pages - 1) % nup;
2018
2019   /* Split fname into fdir and ftail. */
2020   dirc = strdup(fname);
2021   basec = strdup(fname);
2022   fdir = dirname(dirc);
2023   ftail = basename(basec);
2024
2025   if (nup > 1)
2026     {
2027       /* N-up printing is active. */
2028       cstr = "%";
2029
2030       if (nup_subpage == 0)
2031         {
2032           /* This is a real page start. */
2033
2034           switch (page_label)
2035             {
2036             case LABEL_SHORT:
2037               OUTPUT ((cofp, "%%%%Page: (%d-%d) %d\n", current_pagenum,
2038                        current_pagenum + nup - 1, total_pages / nup + 1));
2039               break;
2040
2041             case LABEL_LONG:
2042               OUTPUT ((cofp, "%%%%Page: (%s:%3d-%3d) %d\n", ftail,
2043                        current_pagenum, current_pagenum + nup - 1,
2044                        total_pages / nup + 1));
2045               break;
2046             }
2047
2048           /* Page setup. */
2049           OUTPUT ((cofp, "%%%%BeginPageSetup\n_S\n"));
2050
2051           if ((total_pages / nup + 1) % 2 == 0)
2052             /* Two-side binding options for the even pages. */
2053             handle_two_side_options ();
2054
2055 #define PRINT_BOUNDING_BOXES 0
2056
2057 #if PRINT_BOUNDING_BOXES
2058           OUTPUT ((cofp,
2059                    "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
2060                    media->llx, media->lly, media->llx, media->ury,
2061                    media->urx, media->ury, media->urx, media->lly));
2062 #endif
2063
2064           if (landscape)
2065             {
2066               if (nup_landscape)
2067                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2068                          media->lly, -media->urx));
2069               else
2070                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2071             }
2072           else
2073             {
2074               if (nup_landscape)
2075                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2076                          media->lly, -media->llx));
2077               else
2078                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->ury));
2079             }
2080         }
2081     }
2082
2083   /* Page start comment. */
2084   switch (page_label)
2085     {
2086     case LABEL_SHORT:
2087       OUTPUT ((cofp, "%sPage: (%d) %d\n", cstr, current_pagenum, total_pages));
2088       break;
2089
2090     case LABEL_LONG:
2091       OUTPUT ((cofp, "%sPage: (%s:%3d) %d\n", cstr, ftail, current_pagenum,
2092                total_pages));
2093       break;
2094     }
2095
2096   /*
2097    * Page Setup.
2098    */
2099
2100   OUTPUT ((cofp, "%sBeginPageSetup\n_S\n", cstr));
2101
2102   if (nup > 1)
2103     {
2104       int xm, ym;
2105
2106       OUTPUT ((cofp, "%% N-up sub-page %d/%d\n", nup_subpage + 1, nup));
2107       if (landscape)
2108         {
2109           if (nup_columnwise)
2110             {
2111               xm = nup_subpage % nup_columns;
2112               ym = nup_subpage / nup_columns;
2113             }
2114           else
2115             {
2116               xm = nup_subpage / nup_rows;
2117               ym = nup_subpage % nup_rows;
2118             }
2119
2120           OUTPUT ((cofp, "%d %d translate\n",
2121                    xm * (nup_width + nup_xpad),
2122                    ym * (nup_height + nup_ypad)));
2123         }
2124       else
2125         {
2126           if (nup_columnwise)
2127             {
2128               xm = nup_subpage / nup_rows;
2129               ym = nup_subpage % nup_rows;
2130             }
2131           else
2132             {
2133               xm = nup_subpage % nup_columns;
2134               ym = nup_subpage / nup_columns;
2135             }
2136
2137           OUTPUT ((cofp, "%d %d translate\n",
2138                    xm * (nup_width + nup_xpad),
2139                    -((int) (ym * (nup_height + nup_ypad) + nup_height))));
2140         }
2141       OUTPUT ((cofp, "%g dup scale\n", nup_scale));
2142
2143       /* And finally, the real page setup. */
2144       if (landscape)
2145         OUTPUT ((cofp, "90 rotate\n%d %d translate\n", 0, -d_page_h));
2146     }
2147   else
2148     {
2149       /* No N-up printing. */
2150
2151       if (total_pages % 2 == 0)
2152         /* Two-side binding options for the even pages. */
2153         handle_two_side_options ();
2154
2155       if (landscape)
2156         OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2157                  media->lly, -media->urx));
2158       else
2159         OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2160     }
2161
2162   /* Some constants etc. */
2163   OUTPUT ((cofp, "/pagenum %d def\n", current_pagenum));
2164
2165   cp = escape_string (fname);
2166   OUTPUT ((cofp, "/fname (%s) def\n", cp));
2167   xfree (cp);
2168
2169   cp = escape_string (fdir);
2170   OUTPUT ((cofp, "/fdir (%s) def\n", cp));
2171   xfree (cp);
2172   xfree (dirc);
2173
2174   cp = escape_string (ftail);
2175   OUTPUT ((cofp, "/ftail (%s) def\n", cp));
2176   xfree (cp);
2177   xfree (basec);
2178
2179   /* Do we have a pending ^@font{} font? */
2180   if (user_fontp)
2181     {
2182       if (encoding == default_Fencoding)
2183         OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
2184       else
2185         /* This must be the case. */
2186         OUTPUT ((cofp, "/%s %g %g SUF_PS\n", Fname, Fpt.w, Fpt.h));
2187     }
2188
2189   /* Dump user defined strings. */
2190   if (count_key_value_set (user_strings) > 0)
2191     {
2192       OUTPUT ((cofp, "%% User defined strings:\n"));
2193       for (got = strhash_get_first (user_strings, &cp, &i, (void **) &cp2);
2194            got;
2195            got = strhash_get_next (user_strings, &cp, &i, (void **) &cp2))
2196         {
2197           cp2 = format_user_string ("%Format", cp2);
2198           OUTPUT ((cofp, "/%s (%s) def\n", cp, cp2));
2199           xfree (cp2);
2200         }
2201     }
2202
2203   /* User supplied header? */
2204   if (page_header)
2205     {
2206       char *h_left;
2207       char *h_center;
2208       char *h_right = NULL;
2209
2210       h_left = format_user_string ("page header", page_header);
2211       h_center = strchr (h_left, '|');
2212       if (h_center)
2213         {
2214           *h_center = '\0';
2215           h_center++;
2216
2217           h_right = strchr (h_center, '|');
2218           if (h_right)
2219             {
2220               *h_right = '\0';
2221               h_right++;
2222             }
2223         }
2224
2225       OUTPUT ((cofp, "/user_header_p true def\n"));
2226       OUTPUT ((cofp, "/user_header_left_str (%s) def\n", h_left));
2227       OUTPUT ((cofp, "/user_header_center_str (%s) def\n",
2228                h_center ? h_center : ""));
2229       OUTPUT ((cofp, "/user_header_right_str (%s) def\n",
2230                h_right ? h_right : ""));
2231       xfree (h_left);
2232     }
2233   else
2234     OUTPUT ((cofp, "/user_header_p false def\n"));
2235
2236   /* User supplied footer? */
2237   if (page_footer)
2238     {
2239       char *f_left;
2240       char *f_center;
2241       char *f_right = NULL;
2242
2243       f_left = format_user_string ("page footer", page_footer);
2244       f_center = strchr (f_left, '|');
2245       if (f_center)
2246         {
2247           *f_center = '\0';
2248           f_center++;
2249
2250           f_right = strchr (f_center, '|');
2251           if (f_right)
2252             {
2253               *f_right = '\0';
2254               f_right++;
2255             }
2256         }
2257
2258       OUTPUT ((cofp, "/user_footer_p true def\n"));
2259       OUTPUT ((cofp, "/user_footer_left_str (%s) def\n", f_left));
2260       OUTPUT ((cofp, "/user_footer_center_str (%s) def\n",
2261                f_center ? f_center : ""));
2262       OUTPUT ((cofp, "/user_footer_right_str (%s) def\n",
2263                f_right ? f_right : ""));
2264       xfree (f_left);
2265     }
2266   else
2267     OUTPUT ((cofp, "/user_footer_p false def\n"));
2268
2269   OUTPUT ((cofp, "%%%%EndPageSetup\n"));
2270
2271   /*
2272    * Mark standard page decorations.
2273    */
2274
2275   if (!empty)
2276     {
2277       /* Highlight bars. */
2278       if (highlight_bars)
2279         OUTPUT ((cofp, "%d %f %d %f highlight_bars\n", highlight_bars,
2280                  LINESKIP, d_output_y_margin, highlight_bar_gray));
2281
2282       /* Underlay. */
2283       if (underlay != NULL)
2284         {
2285           if (ul_position_p || ul_angle_p)
2286             OUTPUT ((cofp, "user_underlay\n"));
2287           else
2288             OUTPUT ((cofp, "underlay\n"));
2289         }
2290
2291       /* Column lines. */
2292       if (num_columns > 1 && (header == HDR_FANCY || borders))
2293         OUTPUT ((cofp, "column_lines\n"));
2294
2295       /* Borders around columns. */
2296       if (borders)
2297         OUTPUT ((cofp, "column_borders\n"));
2298
2299       /* Header. */
2300       switch (header)
2301         {
2302         case HDR_NONE:
2303           break;
2304
2305         case HDR_SIMPLE:
2306         case HDR_FANCY:
2307           OUTPUT ((cofp, "do_header\n"));
2308           break;
2309         }
2310     }
2311
2312   /* Do we have a pending ^@color{} color? */
2313   if (user_colorp)
2314     OUTPUT ((cofp, "%g %g %g setrgbcolor\n", user_color.r, user_color.g,
2315              user_color.b));
2316 }
2317
2318
2319 static void
2320 dump_ps_page_trailer ()
2321 {
2322   unsigned int nup_subpage = (total_pages - 1) % nup;
2323
2324   OUTPUT ((cofp, "_R\n"));
2325
2326   if (nup > 1)
2327     {
2328       if (nup_subpage + 1 == nup)
2329         /* Real end of page. */
2330         OUTPUT ((cofp, "_R\nS\n"));
2331     }
2332   else
2333     OUTPUT ((cofp, "S\n"));
2334 }
2335
2336
2337 static void
2338 dump_empty_page ()
2339 {
2340   if (nup > 1)
2341     {
2342       unsigned int nup_subpage = (total_pages - 1) % nup;
2343
2344       if (nup_subpage == 0)
2345         {
2346           /* Real start of the page, must do it the harder way. */
2347           dump_ps_page_header ("", 1);
2348           OUTPUT ((cofp, "_R\n"));
2349         }
2350       else
2351         OUTPUT ((cofp, "%%Page: (-) %d\n", total_pages));
2352
2353       if (nup_subpage + 1 == nup)
2354         /* This is the last page on this sheet, dump us. */
2355         OUTPUT ((cofp, "_R\nS\n"));
2356     }
2357   else
2358     OUTPUT ((cofp, "%%%%Page: (-) %d\nS\n", total_pages));
2359 }
2360
2361
2362 static int
2363 recognize_eps_file (Token *token)
2364 {
2365   int i;
2366   char buf[4096];
2367   char *filename;
2368   int line;
2369   int valid_epsf;
2370   float llx, lly, urx, ury;
2371
2372   MESSAGE (2, (stderr, "^@epsf=\"%s\"\n", token->u.epsf.filename));
2373
2374   i = strlen (token->u.epsf.filename);
2375
2376   /* Read EPS data from file. */
2377   filename = tilde_subst (token->u.epsf.filename);
2378
2379   token->u.epsf.fp = fopen (filename, "rb");
2380   xfree (filename);
2381
2382   if (token->u.epsf.fp == NULL)
2383     {
2384       if (token->u.epsf.filename[0] != '/')
2385         {
2386           /* Name is not absolute, let's lookup path. */
2387           FileLookupCtx ctx;
2388
2389           ctx.name = token->u.epsf.filename;
2390           ctx.suffix = "";
2391           ctx.fullname = buffer_alloc ();
2392
2393           if (pathwalk (libpath, file_lookup, &ctx))
2394             token->u.epsf.fp = fopen (buffer_ptr (ctx.fullname), "rb");
2395
2396           buffer_free (ctx.fullname);
2397         }
2398       if (token->u.epsf.fp == NULL)
2399         {
2400           MESSAGE (0, (stderr, _("couldn't open EPS file \"%s\": %s\n"),
2401                        token->u.epsf.filename, strerror (errno)));
2402           return 0;
2403         }
2404     }
2405
2406   /* Find BoundingBox DSC comment. */
2407
2408   line = 0;
2409   valid_epsf = 0;
2410   token->u.epsf.skipbuf = NULL;
2411   token->u.epsf.skipbuf_len = 0;
2412   token->u.epsf.skipbuf_pos = 0;
2413
2414   while (fgets (buf, sizeof (buf), token->u.epsf.fp))
2415     {
2416       line++;
2417
2418       /* Append data to the skip buffer. */
2419       i = strlen (buf);
2420       if (i + token->u.epsf.skipbuf_pos >= token->u.epsf.skipbuf_len)
2421         {
2422           token->u.epsf.skipbuf_len += 8192;
2423           token->u.epsf.skipbuf = xrealloc (token->u.epsf.skipbuf,
2424                                             token->u.epsf.skipbuf_len);
2425         }
2426       memcpy (token->u.epsf.skipbuf + token->u.epsf.skipbuf_pos, buf, i);
2427       token->u.epsf.skipbuf_pos += i;
2428
2429       /* Check the "%!" magic cookie. */
2430       if (line == 1)
2431         {
2432           if (buf[0] != '%' || buf[1] != '!')
2433             {
2434               MESSAGE (0,
2435                        (stderr,
2436                         _("EPS file \"%s\" does not start with \"%%!\" magic\n"),
2437                         token->u.epsf.filename));
2438               break;
2439             }
2440         }
2441
2442 #define BB_DSC "%%BoundingBox:"
2443
2444       if (strncmp (buf, BB_DSC, strlen (BB_DSC)) == 0)
2445         {
2446           i = sscanf (buf + strlen (BB_DSC), "%f %f %f %f",
2447                       &llx, &lly, &urx, &ury);
2448           if (i != 4)
2449             {
2450               /* (atend) ? */
2451
2452               /* Skip possible whitespace. */
2453               for (i = strlen (BB_DSC);
2454                    buf[i] && (buf[i] == ' ' || buf[i] == '\t');
2455                    i++)
2456                 ;
2457 #define BB_DSC_ATEND "(atend)"
2458               if (strncmp (buf + i, BB_DSC_ATEND, strlen (BB_DSC_ATEND)) != 0)
2459                 {
2460                   /* No, this BoundingBox comment is corrupted. */
2461                   MESSAGE (0, (stderr, _("EPS file \"%s\" contains malformed \
2462 %%%%BoundingBox row:\n\"%.*s\"\n"),
2463                                token->u.epsf.filename, strlen (buf) - 1, buf));
2464                   break;
2465                 }
2466             }
2467           else
2468             {
2469               /* It was a valid EPS file. */
2470
2471               /* We store bounding box in int format. */
2472               token->u.epsf.llx = llx;
2473               token->u.epsf.lly = lly;
2474               token->u.epsf.urx = urx;
2475               token->u.epsf.ury = ury;
2476
2477               valid_epsf = 1;
2478               break;
2479             }
2480         }
2481     }
2482
2483   /* Check that we found the BoundingBox comment. */
2484   if (!valid_epsf)
2485     {
2486       MESSAGE (0, (stderr, _("EPS file \"%s\" is not a valid EPS file\n"),
2487                    token->u.epsf.filename));
2488       if (token->u.epsf.pipe)
2489         pclose (token->u.epsf.fp);
2490       else
2491         fclose (token->u.epsf.fp);
2492       xfree (token->u.epsf.skipbuf);
2493       return 0;
2494     }
2495
2496   MESSAGE (2, (stderr, "BoundingBox: %d %d %d %d\n",
2497                token->u.epsf.llx, token->u.epsf.lly,
2498                token->u.epsf.urx, token->u.epsf.ury));
2499
2500   return 1;
2501 }
2502
2503
2504 static void
2505 paste_epsf (Token *token)
2506 {
2507   char buf[4096];
2508   int i;
2509
2510   /* EPSF import header. */
2511   OUTPUT ((cofp, "BeginEPSF\n"));
2512   OUTPUT ((cofp, "%g %g translate\n", token->new_x, token->new_y));
2513   OUTPUT ((cofp, "%g %g scale\n", token->u.epsf.xscale, token->u.epsf.yscale));
2514   OUTPUT ((cofp, "%d %d translate\n", -token->u.epsf.llx,
2515            -token->u.epsf.lly));
2516   OUTPUT ((cofp, "%d %d %d %d Box clip newpath\n",
2517            token->u.epsf.llx - 1,
2518            token->u.epsf.lly - 1,
2519            token->u.epsf.urx - token->u.epsf.llx + 2,
2520            token->u.epsf.ury - token->u.epsf.lly + 2));
2521   OUTPUT ((cofp, "%%%%BeginDocument: %s%s\n", token->u.epsf.filename,
2522            token->u.epsf.pipe ? "|" : ""));
2523
2524   if (do_print)
2525     {
2526       /* Dump skip buffer. */
2527       fwrite (token->u.epsf.skipbuf, 1, token->u.epsf.skipbuf_pos, cofp);
2528
2529       /* Dump file. */
2530       while ((i = fread (buf, 1, sizeof (buf), token->u.epsf.fp)) != 0)
2531         fwrite (buf, 1, i, cofp);
2532     }
2533
2534   /* Add a newline to keep comments correct */
2535   OUTPUT ((cofp, "\n"));
2536
2537   /* EPSF import trailer. */
2538   OUTPUT ((cofp, "%%%%EndDocument\nEndEPSF\n"));
2539
2540   /* Cleanup. */
2541   if (token->u.epsf.pipe)
2542     pclose (token->u.epsf.fp);
2543   else
2544     fclose (token->u.epsf.fp);
2545   xfree (token->u.epsf.skipbuf);
2546 }
2547
2548
2549 static double
2550 read_float (InputStream *is, int units, int horizontal)
2551 {
2552   char buf[256];
2553   int i, ch;
2554   double val;
2555
2556   for (i = 0; (i < sizeof (buf) - 1
2557                && (ch = is_getc (is)) != EOF
2558                && ISNUMBERDIGIT (ch));
2559        i++)
2560     buf[i] = ch;
2561   buf[i] = '\0';
2562   if (ch != EOF)
2563     is_ungetc (ch, is);
2564
2565   val = atof (buf);
2566
2567   if (units)
2568     {
2569       /* Get unit. */
2570       ch = is_getc (is);
2571       switch (ch)
2572         {
2573         case 'c':               /* centimeters */
2574           val *= 72 / 2.54;
2575           break;
2576
2577         case 'p':               /* PostScript points */
2578           break;
2579
2580         case 'i':               /* inches */
2581           val *= 72;
2582           break;
2583
2584         default:
2585           is_ungetc (ch, is);
2586           /* FALLTHROUGH */
2587
2588         case 'l':               /* lines or characters */
2589           if (horizontal)
2590             val *= CHAR_WIDTH ('m');
2591           else
2592             val *= LINESKIP;
2593           break;
2594         }
2595     }
2596
2597   return val;
2598 }
2599
2600
2601 /* Magics used to recognize different pass-through files. */
2602 static struct
2603 {
2604   char *magic;
2605   unsigned int magiclen;
2606   char *name;
2607   int revert_delta;
2608 } pass_through_magics[] =
2609   {
2610     {"%!",      2, "PostScript",        -2},
2611     {"\004%!",  3, "PostScript",        -2},
2612     {"\033E",   2, "PCL",               -2},
2613     {"\033%",   2, "PCL",               -2},
2614     {NULL, 0, NULL, 0},
2615   };
2616
2617
2618 static int
2619 do_pass_through (char *fname, InputStream *is)
2620 {
2621   int ch;
2622   unsigned long saved_pos = is->bufpos;
2623   int i, j;
2624
2625   if (output_language_pass_through)
2626     MESSAGE (1,
2627              (stderr,
2628               _("passing through all input files for output language `%s'\n"),
2629               output_language));
2630   else
2631     {
2632       /*
2633        * Try to recognize pass-through files.
2634        */
2635
2636       for (i = 0; pass_through_magics[i].magic; i++)
2637         {
2638           for (j = 0; j < pass_through_magics[i].magiclen; j++)
2639             {
2640               ch = is_getc (is);
2641               if (ch == EOF
2642                   || ch != (unsigned char) pass_through_magics[i].magic[j])
2643                 break;
2644             }
2645
2646           if (j >= pass_through_magics[i].magiclen)
2647             /* The <i>th one matched. */
2648             break;
2649
2650           /*
2651            * Try the next one, but first, seek the input stream to its
2652            * start.
2653            */
2654           is->bufpos = saved_pos;
2655         }
2656
2657       /* Did we find any? */
2658       if (pass_through_magics[i].magic == NULL)
2659         /* No we didn't. */
2660         return 0;
2661
2662       /* Yes, it really is a pass-through file.  Now do the pass through. */
2663
2664       is->bufpos += pass_through_magics[i].revert_delta;
2665
2666       if (ps_header_dumped)
2667         {
2668           /* A pass-through file between normal ASCII files, obey DSC. */
2669
2670           /*
2671            * XXX I don't know how to handle PCL files... Let's hope none
2672            * mixes them with the normal ASCII files.
2673            */
2674
2675           OUTPUT ((cofp,
2676                    "%%%%Page: (%s) -1\n_S\n%%%%BeginDocument: %s\n",
2677                    fname, fname));
2678         }
2679
2680       MESSAGE (1, (stderr, _("passing through %s file \"%s\"\n"),
2681                    pass_through_magics[i].name, fname));
2682     }
2683
2684   /* And now, do the actual pass-through. */
2685   do
2686     {
2687       /* Note: this will be written directly to the <ofp>. */
2688       fwrite (is->buf + is->bufpos, 1, is->data_in_buf - is->bufpos, ofp);
2689       is->bufpos = is->data_in_buf;
2690
2691       /* Read more data to the input buffer. */
2692       ch = is_getc (is);
2693       is->bufpos = 0;
2694     }
2695   while (ch != EOF);
2696
2697   if (!output_language_pass_through)
2698     {
2699       if (ps_header_dumped)
2700         /*
2701          * XXX How to end a PCL file mixed between ASCII files?
2702          */
2703         OUTPUT ((cofp, "%%%%EndDocument\n_R\n"));
2704     }
2705
2706   return 1;
2707 }
2708
2709
2710 static void
2711 print_line_number (double x, double y, double space, double margin,
2712                    unsigned int linenum)
2713 {
2714   double len = 0.0;
2715   char buf[20];
2716   int i;
2717   char *saved_Fname = "";
2718   FontPoint saved_Fpt;
2719   InputEncoding saved_Fencoding;
2720
2721   saved_Fpt.w = 0.0;
2722   saved_Fpt.h = 0.0;
2723
2724   /* Do not print linenumbers for wrapped lines. */
2725   if (linenum == print_line_number_last)
2726     return;
2727   print_line_number_last = linenum;
2728
2729   if (user_fontp)
2730     {
2731       /* Re-select our default typing font. */
2732       saved_Fname = Fname;
2733       saved_Fpt.w = Fpt.w;
2734       saved_Fpt.h = Fpt.h;
2735       saved_Fencoding = encoding;
2736
2737       Fname = default_Fname;
2738       Fpt.w = default_Fpt.w;
2739       Fpt.h = default_Fpt.h;
2740       encoding = default_Fencoding;
2741
2742       OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
2743       read_font_info ();
2744     }
2745
2746   /* Count linenumber string length. */
2747   sprintf (buf, "%d", linenum);
2748   for (i = 0; buf[i]; i++)
2749     len += CHAR_WIDTH (buf[i]);
2750
2751   /* Print line numbers. */
2752   OUTPUT ((cofp, "%g %g M (%s:) s\n", x + space - len, y, buf));
2753
2754   if (user_fontp)
2755     {
2756       /* Switch back to the user font. */
2757       Fname = saved_Fname;
2758       Fpt.w = saved_Fpt.w;
2759       Fpt.h = saved_Fpt.h;
2760       encoding = saved_Fencoding;
2761
2762       OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
2763       read_font_info ();
2764     }
2765 }
2766
2767 \f
2768 /*
2769  * The name of the divert file, shared between divert() and undivert()
2770  * functions.
2771  */
2772 static char divertfname[512];
2773
2774 static void
2775 divert ()
2776 {
2777   assert (divertfp == NULL);
2778
2779   /* Open divert file. */
2780
2781   divertfp = tmpfile ();
2782   if (divertfp == NULL)
2783     FATAL ((stderr, _("couldn't create temporary divert file: %s"),
2784             strerror (errno)));
2785
2786   cofp = divertfp;
2787 }
2788
2789
2790 static void
2791 undivert ()
2792 {
2793   char buf[1024];
2794   int doc_level = 0;
2795   char *cp;
2796
2797   assert (divertfp != NULL);
2798
2799   if (fseek (divertfp, 0, SEEK_SET) != 0)
2800     FATAL ((stderr, _("couldn't rewind divert file: %s"), strerror (errno)));
2801
2802   while (fgets (buf, sizeof (buf), divertfp))
2803     {
2804       if (strncmp (buf, "%%BeginDocument", 15) == 0)
2805         doc_level++;
2806       else if (strncmp (buf, "%%EndDocument", 13) == 0)
2807         doc_level--;
2808
2809       if (doc_level == 0)
2810         {
2811           if (strncmp (buf, "% User defined strings", 22) == 0)
2812             {
2813               fputs (buf, ofp);
2814               while (fgets (buf, sizeof (buf), divertfp))
2815                 {
2816                   if (strncmp (buf, "%%EndPageSetup", 14) == 0)
2817                     break;
2818
2819                   /* Patch total pages to the user defined strings. */
2820                   cp = strchr (buf, '\001');
2821                   if (cp)
2822                     {
2823                       *cp = '\0';
2824                       fputs (buf, ofp);
2825                       fprintf (ofp, "%d", total_pages_in_file);
2826                       fputs (cp + 1, ofp);
2827                     }
2828                   else
2829                     fputs (buf, ofp);
2830                 }
2831             }
2832         }
2833
2834       fputs (buf, ofp);
2835     }
2836
2837   fclose (divertfp);
2838   divertfp = NULL;
2839
2840   cofp = ofp;
2841 }
2842
2843
2844 static void
2845 handle_two_side_options ()
2846 {
2847   if (rotate_even_pages)
2848     /* Rotate page 180 degrees. */
2849     OUTPUT ((cofp, "180 rotate\n%d %d translate\n",
2850              -media->w, -media->h));
2851
2852   if (swap_even_page_margins)
2853     OUTPUT ((cofp, "%d 0 translate\n",
2854              -(media->llx - (media->w - media->urx))));
2855 }