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