Merge from vendor branch GROFF:
[dragonfly.git] / contrib / groff-1.19 / src / preproc / html / pre-html.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3      Written by Gaius Mulley (gaius@glam.ac.uk).
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #define PREHTMLC
22
23 #include "lib.h"
24
25 #include <signal.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include "errarg.h"
31 #include "error.h"
32 #include "stringclass.h"
33 #include "posix.h"
34 #include "defs.h"
35 #include "searchpath.h"
36 #include "paper.h"
37 #include "font.h"
38
39 #include <errno.h>
40 #include <sys/types.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #ifdef _POSIX_VERSION
46 # include <sys/wait.h>
47 # define PID_T pid_t
48 #else /* not _POSIX_VERSION */
49 # define PID_T int
50 #endif /* not _POSIX_VERSION */
51
52 #include <stdarg.h>
53
54 #include "nonposix.h"
55
56 /* Establish some definitions to facilitate discrimination between
57    differing runtime environments. */
58
59 #undef MAY_FORK_CHILD_PROCESS
60 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
61
62 #if defined(__MSDOS__) || defined(_WIN32)
63
64 // Most MS-DOS and Win32 environments will be missing the `fork' capability
65 // (some like Cygwin have it, but it is best avoided).
66
67 # define MAY_FORK_CHILD_PROCESS 0
68
69 // On these systems, we use `spawn...', instead of `fork' ... `exec...'.
70 # include <process.h>   // for `spawn...'
71 # include <fcntl.h>     // for attributes of pipes
72
73 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
74
75 // These Win32 implementations allow parent and `spawn...'ed child to
76 // multitask asynchronously.
77
78 #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
79
80 # else
81
82 // Others may adopt MS-DOS behaviour where parent must sleep,
83 // from `spawn...' until child terminates.
84
85 #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
86
87 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
88
89 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
90 /* When we are building a DEBUGGING version we need to tell pre-grohtml
91    where to put intermediate files (the DEBUGGING version will preserve
92    these on exit).
93
94    On a UNIX host, we might simply use `/tmp', but MS-DOS and Win32 will
95    probably not have this on all disk drives, so default to using
96    `c:/temp' instead.  (Note that user may choose to override this by
97    supplying a definition such as
98
99      -DDEBUG_FILE_DIR=d:/path/to/debug/files
100
101    in the CPPFLAGS to `make'.) */
102
103 #  define DEBUG_FILE_DIR c:/temp
104 # endif
105
106 #else /* not __MSDOS__ or _WIN32 */
107
108 // For non-Microsoft environments assume UNIX conventions,
109 // so `fork' is required and child processes are asynchronous.
110 # define MAY_FORK_CHILD_PROCESS 1
111 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
112
113 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
114 /* For a DEBUGGING version, on the UNIX host, we can also usually rely
115    on being able to use `/tmp' for temporary file storage.  (Note that,
116    as in the __MSDOS__ or _WIN32 case above, the user may override this
117    by defining
118
119      -DDEBUG_FILE_DIR=/path/to/debug/files
120
121    in the CPPFLAGS.) */
122
123 #  define DEBUG_FILE_DIR /tmp
124 # endif
125
126 #endif /* not __MSDOS__ or _WIN32 */
127
128 #ifdef DEBUGGING
129 // For a DEBUGGING version, we need some additional macros,
130 // to direct the captured debug mode output to appropriately named files
131 // in the specified DEBUG_FILE_DIR.
132
133 # define DEBUG_TEXT(text) #text
134 # define DEBUG_NAME(text) DEBUG_TEXT(text)
135 # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
136 #endif
137
138 extern "C" const char *Version_string;
139
140 #include "pre-html.h"
141 #include "pushback.h"
142 #include "html-strings.h"
143
144 #define DEFAULT_LINE_LENGTH 7   // inches wide
145 #define DEFAULT_IMAGE_RES 100   // number of pixels per inch resolution
146 #define IMAGE_BOARDER_PIXELS 0
147 #define INLINE_LEADER_CHAR '\\'
148
149 // Don't use colour names here!  Otherwise there is a dependency on
150 // a file called `rgb.txt' which maps names to colours.
151 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
152 #define MIN_ALPHA_BITS 0
153 #define MAX_ALPHA_BITS 4
154
155 #define PAGE_TEMPLATE_SHORT "pg"
156 #define PAGE_TEMPLATE_LONG "-page-"
157 #define PS_TEMPLATE_SHORT "ps"
158 #define PS_TEMPLATE_LONG "-ps-"
159 #define REGION_TEMPLATE_SHORT "rg"
160 #define REGION_TEMPLATE_LONG "-regions-"
161
162 #if 0
163 # define DEBUGGING
164 #endif
165
166 #if !defined(TRUE)
167 # define TRUE (1==1)
168 #endif
169 #if !defined(FALSE)
170 # define FALSE (1==0)
171 #endif
172
173 typedef enum {
174   CENTERED, LEFT, RIGHT, INLINE
175 } IMAGE_ALIGNMENT;
176
177 static int postscriptRes = -1;          // postscript resolution,
178                                         // dots per inch
179 static int stdoutfd = 1;                // output file descriptor -
180                                         // normally 1 but might move
181                                         // -1 means closed
182 static char *psFileName = NULL;         // name of postscript file
183 static char *psPageName = NULL;         // name of file containing
184                                         // postscript current page
185 static char *regionFileName = NULL;     // name of file containing all
186                                         // image regions
187 static char *imagePageName = NULL;      // name of bitmap image containing
188                                         // current page
189 static const char *image_device = "pnmraw";
190 static int image_res = DEFAULT_IMAGE_RES;
191 static int vertical_offset = 0;
192 static char *image_template = NULL;     // image template filename
193 static char *macroset_template= NULL;   // image template passed to troff
194                                         // by -D
195 static int troff_arg = 0;               // troff arg index
196 static char *image_dir = NULL;          // user specified image directory
197 static int textAlphaBits = MAX_ALPHA_BITS;
198 static int graphicAlphaBits = MAX_ALPHA_BITS;
199 static char *antiAlias = NULL;          // antialias arguments we pass to gs
200 static int show_progress = FALSE;       // should we display page numbers as
201                                         // they are processed?
202 static int currentPageNo = -1;          // current image page number
203 #if defined(DEBUGGING)
204 static int debug = FALSE;
205 static char *troffFileName = NULL;      // output of pre-html output which
206                                         // is sent to troff -Tps
207 static char *htmlFileName = NULL;       // output of pre-html output which
208                                         // is sent to troff -Thtml
209 #endif
210
211 static char *linebuf = NULL;            // for scanning devps/DESC
212 static int linebufsize = 0;
213
214 const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
215 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
216
217
218 /*
219  *  Images are generated via postscript, gs, and the pnm utilities.
220  */
221 #define IMAGE_DEVICE "-Tps"
222
223
224 static int do_file(const char *filename);
225
226
227 /*
228  *  sys_fatal - Write a fatal error message.
229  *              Taken from src/roff/groff/pipeline.c.
230  */
231
232 void sys_fatal(const char *s)
233 {
234   fatal("%1: %2", s, strerror(errno));
235 }
236
237 /*
238  *  get_line - Copy a line (w/o newline) from a file to the
239  *             global line buffer.
240  */
241
242 int get_line(FILE *f)
243 {
244   if (f == 0)
245     return 0;
246   if (linebuf == 0) {
247     linebuf = new char[128];
248     linebufsize = 128;
249   }
250   int i = 0;
251   // skip leading whitespace
252   for (;;) {
253     int c = getc(f);
254     if (c == EOF)
255       return 0;
256     if (c != ' ' && c != '\t') {
257       ungetc(c, f);
258       break;
259     }
260   }
261   for (;;) {
262     int c = getc(f);
263     if (c == EOF)
264       break;
265     if (i + 1 >= linebufsize) {
266       char *old_linebuf = linebuf;
267       linebuf = new char[linebufsize * 2];
268       memcpy(linebuf, old_linebuf, linebufsize);
269       a_delete old_linebuf;
270       linebufsize *= 2;
271     }
272     linebuf[i++] = c;
273     if (c == '\n') {
274       i--;
275       break;
276     }
277   }
278   linebuf[i] = '\0';
279   return 1;
280 }
281
282 /*
283  *  get_resolution - Return the postscript resolution from devps/DESC.
284  */
285
286 static unsigned int get_resolution(void)
287 {
288   char *pathp;
289   FILE *f;
290   unsigned int res;
291   f = font_path.open_file("devps/DESC", &pathp);
292   a_delete pathp;
293   if (f == 0)
294     fatal("can't open devps/DESC");
295   while (get_line(f)) {
296     int n = sscanf(linebuf, "res %u", &res);
297     if (n >= 1) {
298       fclose(f);
299       return res;
300     }
301   }
302   fatal("can't find `res' keyword in devps/DESC");
303   return 0;
304 }
305
306 /*
307  *  html_system - A wrapper for system().
308  */
309
310 void html_system(const char *s, int redirect_stdout)
311 {
312   // Redirect standard error to the null device.  This is more
313   // portable than using "2> /dev/null", since it doesn't require a
314   // Unixy shell.
315   int save_stderr = dup(2);
316   int save_stdout = dup(1);
317   int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
318   if (save_stderr > 2 && fdnull > 2)
319     dup2(fdnull, 2);
320   if (redirect_stdout && save_stdout > 1 && fdnull > 1)
321     dup2(fdnull, 1);
322   if (fdnull >= 0)
323     close(fdnull);
324   int status = system(s);
325   dup2(save_stderr, 2);
326   if (redirect_stdout)
327     dup2(save_stdout, 1);
328   if (status == -1)
329     fprintf(stderr, "Calling `%s' failed\n", s);
330   else if (status)
331     fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
332   close(save_stderr);
333   close(save_stdout);
334 }
335
336 /*
337  *  make_message - Create a string via malloc and place the result of the
338  *                 va args into string.  Finally the new string is returned.
339  *                 Taken from man page of printf(3).
340  */
341
342 char *make_message(const char *fmt, ...)
343 {
344   /* Guess we need no more than 100 bytes. */
345   int n, size = 100;
346   char *p;
347   char *np;
348   va_list ap;
349   if ((p = (char *)malloc(size)) == NULL)
350     return NULL;
351   while (1) {
352     /* Try to print in the allocated space. */
353     va_start(ap, fmt);
354     n = vsnprintf(p, size, fmt, ap);
355     va_end(ap);
356     /* If that worked, return the string. */
357     if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
358       if (size > n + 1) {
359         np = strsave(p);
360         free(p);
361         return np;
362       }
363       return p;
364     }
365     /* Else try again with more space. */
366     else                /* glibc 2.0 */
367       size *= 2;        /* twice the old size */
368     if ((np = (char *)realloc(p, size)) == NULL) {
369       free(p);          /* realloc failed, free old, p. */
370       return NULL;
371     }
372     p = np;             /* use realloc'ed, p */
373   }
374 }
375
376 /*
377  *  the class and methods for retaining ascii text
378  */
379
380 struct char_block {
381   enum { SIZE = 256 };
382   char buffer[SIZE];
383   int used;
384   char_block *next;
385
386   char_block();
387 };
388
389 /*
390  *  char_block - Constructor.  Set the, used, and, next, fields to zero.
391  */
392
393 char_block::char_block()
394 : used(0), next(0)
395 {
396   for (int i = 0; i < SIZE; i++)
397     buffer[i] = 0;
398 }
399
400 class char_buffer {
401 public:
402   char_buffer();
403   ~char_buffer();
404   int read_file(FILE *fp);
405   int do_html(int argc, char *argv[]);
406   int do_image(int argc, char *argv[]);
407   void emit_troff_output(int device_format_selector);
408   void write_upto_newline(char_block **t, int *i, int is_html);
409   int can_see(char_block **t, int *i, const char *string);
410   int skip_spaces(char_block **t, int *i);
411   void skip_until_newline(char_block **t, int *i);
412 private:
413   char_block *head;
414   char_block *tail;
415   int run_output_filter(int device_format_selector, int argc, char *argv[]);
416 };
417
418 /*
419  *  char_buffer - Constructor.
420  */
421
422 char_buffer::char_buffer()
423 : head(0), tail(0)
424 {
425 }
426
427 /*
428  *  char_buffer - Destructor.  Throw away the whole buffer list.
429  */
430
431 char_buffer::~char_buffer()
432 {
433   while (head != NULL) {
434     char_block *temp = head;
435     head = head->next;
436     delete temp;
437   }
438 }
439
440 /*
441  *  read_file - Read in a complete file, fp, placing the contents inside
442  *              char_blocks.
443  */
444
445 int char_buffer::read_file(FILE *fp)
446 {
447   int n;
448   while (!feof(fp)) {
449     if (tail == NULL) {
450       tail = new char_block;
451       head = tail;
452     }
453     else {
454       if (tail->used == char_block::SIZE) {
455         tail->next = new char_block;
456         tail = tail->next;
457       }
458     }
459     // at this point we have a tail which is ready for the next SIZE
460     // bytes of the file
461     n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
462     if (n <= 0)
463       // error
464       return 0;
465     else
466       tail->used += n * sizeof(char);
467   }
468   return 1;
469 }
470
471 /*
472  *  writeNbytes - Write n bytes to stdout.
473  */
474
475 static void writeNbytes(const char *s, int l)
476 {
477   int n = 0;
478   int r;
479
480   while (n < l) {
481     r = write(stdoutfd, s, l - n);
482     if (r < 0)
483       sys_fatal("write");
484     n += r;
485     s += r;
486   }
487 }
488
489 /*
490  *  writeString - Write a string to stdout.
491  */
492
493 static void writeString(const char *s)
494 {
495   writeNbytes(s, strlen(s));
496 }
497
498 /*
499  *  makeFileName - Create the image filename template
500  *                 and the macroset image template.
501  */
502
503 static void makeFileName(void)
504 {
505   if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
506     error("cannot use a `%%' within the image directory name");
507     exit(1);
508   }
509
510   if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
511     error("cannot use a `%%' within the image template");
512     exit(1);
513   }
514
515   if (image_dir == NULL)
516     image_dir = (char *)"";
517   else if (strlen(image_dir) > 0
518            && image_dir[strlen(image_dir) - 1] != '/') {
519     image_dir = make_message("%s/", image_dir);
520     if (image_dir == NULL)
521       sys_fatal("make_message");
522   }
523
524   if (image_template == NULL)
525     macroset_template = make_message("%sgrohtml-%d", image_dir,
526                                      (int)getpid());
527   else
528     macroset_template = make_message("%s%s", image_dir, image_template);
529
530   if (macroset_template == NULL)
531     sys_fatal("make_message");
532
533   image_template =
534     (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
535   if (image_template == NULL)
536     sys_fatal("malloc");
537   strcpy(image_template, macroset_template);
538   strcat(image_template, "-%d");
539 }
540
541 /*
542  *  setupAntiAlias - Set up the antialias string, used when we call gs.
543  */
544
545 static void setupAntiAlias(void)
546 {
547   if (textAlphaBits == 0 && graphicAlphaBits == 0)
548     antiAlias = make_message(" ");
549   else if (textAlphaBits == 0)
550     antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
551   else if (graphicAlphaBits == 0)
552     antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
553   else
554     antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
555                              textAlphaBits, graphicAlphaBits);
556 }
557
558 /*
559  *  checkImageDir - Check whether the image directory is available.
560  */
561
562 static void checkImageDir(void)
563 {
564   if (image_dir != NULL && strcmp(image_dir, "") != 0)
565     if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
566       error("cannot create directory `%1'", image_dir);
567       exit(1);
568     }
569 }
570
571 /*
572  *  write_end_image - End the image.  Write out the image extents if we
573  *                    are using -Tps.
574  */
575
576 static void write_end_image(int is_html)
577 {
578   /*
579    *  if we are producing html then these
580    *    emit image name and enable output
581    *  else
582    *    we are producing images
583    *    in which case these generate image
584    *    boundaries
585    */
586   writeString("\\O[4]\\O[2]");
587   if (is_html)
588     writeString("\\O[1]");
589   else
590     writeString("\\O[0]");
591 }
592
593 /*
594  *  write_start_image - Write troff code which will:
595  *
596  *                      (i)  disable html output for the following image
597  *                      (ii) reset the max/min x/y registers during postscript
598  *                           rendering.
599  */
600
601 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
602 {
603   writeString("\\O[5");
604   switch (pos) {
605   case INLINE:
606     writeString("i");
607     break;
608   case LEFT:
609     writeString("l");
610     break;
611   case RIGHT:
612     writeString("r");
613     break;
614   case CENTERED:
615   default:
616     writeString("c");
617     break;
618   }
619   writeString(image_template);
620   writeString(".png]");
621   if (is_html)
622     writeString("\\O[0]\\O[3]");
623   else
624     // reset min/max registers
625     writeString("\\O[1]\\O[3]");
626 }
627
628 /*
629  *  write_upto_newline - Write the contents of the buffer until a newline
630  *                       is seen.  Check for HTML_IMAGE_INLINE_BEGIN and
631  *                       HTML_IMAGE_INLINE_END; process them if they are
632  *                       present.
633  */
634
635 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
636 {
637   int j = *i;
638
639   if (*t) {
640     while (j < (*t)->used
641            && (*t)->buffer[j] != '\n'
642            && (*t)->buffer[j] != INLINE_LEADER_CHAR)
643       j++;
644     if (j < (*t)->used
645         && (*t)->buffer[j] == '\n')
646       j++;
647     writeNbytes((*t)->buffer + (*i), j - (*i));
648     if ((*t)->buffer[j] == INLINE_LEADER_CHAR) {
649       if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
650         write_start_image(INLINE, is_html);
651       else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
652         write_end_image(is_html);
653       else {
654         if (j < (*t)->used) {
655           *i = j;
656           j++;
657           writeNbytes((*t)->buffer + (*i), j - (*i));
658         }
659       }
660     }
661     if (j == (*t)->used) {
662       *i = 0;
663       *t = (*t)->next;
664       if (*t && (*t)->buffer[j - 1] != '\n')
665         write_upto_newline(t, i, is_html);
666     }
667     else
668       // newline was seen
669       *i = j;
670   }
671 }
672
673 /*
674  *  can_see - Return TRUE if we can see string in t->buffer[i] onwards.
675  */
676
677 int char_buffer::can_see(char_block **t, int *i, const char *str)
678 {
679   int j = 0;
680   int l = strlen(str);
681   int k = *i;
682   char_block *s = *t;
683
684   while (s) {
685     while (k < s->used && j < l && s->buffer[k] == str[j]) {
686       j++;
687       k++;
688     }
689     if (j == l) {
690       *i = k;
691       *t = s;
692       return TRUE;
693     }
694     else if (k < s->used && s->buffer[k] != str[j])
695       return( FALSE );
696     s = s->next;
697     k = 0;
698   }
699   return FALSE;
700 }
701
702 /*
703  *  skip_spaces - Return TRUE if we have not run out of data.
704  *                Consume spaces also.
705  */
706
707 int char_buffer::skip_spaces(char_block **t, int *i)
708 {
709   char_block *s = *t;
710   int k = *i;
711
712   while (s) {
713     while (k < s->used && isspace(s->buffer[k]))
714       k++;
715     if (k == s->used) {
716       k = 0;
717       s = s->next;
718     }
719     else {
720       *i = k;
721       return TRUE;
722     }
723   }
724   return FALSE;
725 }
726
727 /*
728  *  skip_until_newline - Skip all characters until a newline is seen.
729  *                       The newline is not consumed.
730  */
731
732 void char_buffer::skip_until_newline(char_block **t, int *i)
733 {
734   int j = *i;
735
736   if (*t) {
737     while (j < (*t)->used && (*t)->buffer[j] != '\n')
738       j++;
739     if (j == (*t)->used) {
740       *i = 0;
741       *t = (*t)->next;
742       skip_until_newline(t, i);
743     }
744     else
745       // newline was seen
746       *i = j;
747   }
748 }
749
750 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
751 #define HTML_OUTPUT_FILTER     0
752 #define IMAGE_OUTPUT_FILTER    1
753 #define OUTPUT_STREAM(name)   creat((name), S_IWUSR | S_IRUSR)
754 #define PS_OUTPUT_STREAM      OUTPUT_STREAM(psFileName)
755 #define REGION_OUTPUT_STREAM  OUTPUT_STREAM(regionFileName)
756
757 /*
758  *  emit_troff_output - Write formatted buffer content to the troff
759  *                      post-processor data pipeline.
760  */
761
762 void char_buffer::emit_troff_output(int device_format_selector)
763 {
764   // Handle output for BOTH html and image device formats
765   // if `device_format_selector' is passed as
766   //
767   //   HTML_FORMAT(HTML_OUTPUT_FILTER)
768   //     Buffer data is written to the output stream
769   //     with template image names translated to actual image names.
770   //
771   //   HTML_FORMAT(IMAGE_OUTPUT_FILTER)
772   //     Buffer data is written to the output stream
773   //     with no translation, for image file creation in the post-processor.
774
775   int idx = 0;
776   char_block *element = head;
777
778   while (element != NULL)
779     write_upto_newline(&element, &idx, device_format_selector);
780
781 #if 0
782   if (close(stdoutfd) < 0)
783     sys_fatal ("close");
784
785   // now we grab fd=1 so that the next pipe cannot use fd=1
786   if (stdoutfd == 1) {
787     if (dup(2) != stdoutfd)
788       sys_fatal ("dup failed to use fd=1");
789   }
790 #endif /* 0 */
791 }
792
793 /*
794  *  The image class remembers the position of all images in the
795  *  postscript file and assigns names for each image.
796  */
797
798 struct imageItem {
799   imageItem *next;
800   int X1;
801   int Y1;
802   int X2;
803   int Y2;
804   char *imageName;
805   int resolution;
806   int maxx;
807   int pageNo;
808
809   imageItem(int x1, int y1, int x2, int y2,
810             int page, int res, int max_width, char *name);
811   ~imageItem();
812 };
813
814 /*
815  *  imageItem - Constructor.
816  */
817
818 imageItem::imageItem(int x1, int y1, int x2, int y2,
819                      int page, int res, int max_width, char *name)
820 {
821   X1 = x1;
822   Y1 = y1;
823   X2 = x2;
824   Y2 = y2;
825   pageNo = page;
826   resolution = res;
827   maxx = max_width;
828   imageName = name;
829   next = NULL;
830 }
831
832 /*
833  *  imageItem - Destructor.
834  */
835
836 imageItem::~imageItem()
837 {
838   if (imageName)
839     free(imageName);
840 }
841
842 /*
843  *  imageList - A class containing a list of imageItems.
844  */
845
846 class imageList {
847 private:
848   imageItem *head;
849   imageItem *tail;
850   int count;
851 public:
852   imageList();
853   ~imageList();
854   void add(int x1, int y1, int x2, int y2,
855            int page, int res, int maxx, char *name);
856   void createImages(void);
857   int createPage(int pageno);
858   void createImage(imageItem *i);
859   int getMaxX(int pageno);
860 };
861
862 /*
863  *  imageList - Constructor.
864  */
865
866 imageList::imageList()
867 : head(0), tail(0), count(0)
868 {
869 }
870
871 /*
872  *  imageList - Destructor.
873  */
874
875 imageList::~imageList()
876 {
877   while (head != NULL) {
878     imageItem *i = head;
879     head = head->next;
880     delete i;
881   }
882 }
883
884 /*
885  *  createPage - Create one image of, page pageno, from the postscript file.
886  */
887
888 int imageList::createPage(int pageno)
889 {
890   char *s;
891
892   if (currentPageNo == pageno)
893     return 0;
894
895   if (currentPageNo >= 1) {
896     /*
897      *  We need to unlink the files which change each time a new page is
898      *  processed.  The final unlink is done by xtmpfile when pre-grohtml
899      *  exits.
900      */
901     unlink(imagePageName);
902     unlink(psPageName);
903   }
904
905   if (show_progress) {
906     fprintf(stderr, "[%d] ", pageno);
907     fflush(stderr);
908   }
909
910 #if defined(DEBUGGING)
911   if (debug)
912     fprintf(stderr, "creating page %d\n", pageno);
913 #endif
914
915   s = make_message("psselect -q -p%d %s %s\n",
916                    pageno, psFileName, psPageName);
917
918   if (s == NULL)
919     sys_fatal("make_message");
920 #if defined(DEBUGGING)
921   if (debug) {
922     fwrite(s, sizeof(char), strlen(s), stderr);
923     fflush(stderr);
924   }
925 #endif
926   html_system(s, 1);
927
928   s = make_message("echo showpage | "
929                    "gs%s -q -dBATCH -dSAFER "
930                    "-dDEVICEHEIGHTPOINTS=792 "
931                    "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
932                    "-sDEVICE=%s -r%d %s "
933                    "-sOutputFile=%s %s -\n",
934                    EXE_EXT,
935                    (getMaxX(pageno) * image_res) / postscriptRes,
936                    image_device,
937                    image_res,
938                    antiAlias,
939                    imagePageName,
940                    psPageName);
941   if (s == NULL)
942     sys_fatal("make_message");
943 #if defined(DEBUGGING)
944   if (debug) {
945     fwrite(s, sizeof(char), strlen(s), stderr);
946     fflush(stderr);
947   }
948 #endif
949   html_system(s, 1);
950   free(s);
951   currentPageNo = pageno;
952   return 0;
953 }
954
955 /*
956  *  min - Return the minimum of two numbers.
957  */
958
959 int min(int x, int y)
960 {
961   if (x < y)
962     return x;
963   else
964     return y;
965 }
966
967 /*
968  *  max - Return the maximum of two numbers.
969  */
970
971 int max(int x, int y)
972 {
973   if (x > y)
974     return x;
975   else
976     return y;
977 }
978
979 /*
980  *  getMaxX - Return the largest right-hand position for any image
981  *            on, pageno.
982  */
983
984 int imageList::getMaxX(int pageno)
985 {
986   imageItem *h = head;
987   int x = postscriptRes * DEFAULT_LINE_LENGTH;
988
989   while (h != NULL) {
990     if (h->pageNo == pageno)
991       x = max(h->X2, x);
992     h = h->next;
993   }
994   return x;
995 }
996
997 /*
998  *  createImage - Generate a minimal png file from the set of page images.
999  */
1000
1001 void imageList::createImage(imageItem *i)
1002 {
1003   if (i->X1 != -1) {
1004     char *s;
1005     int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1006                    - IMAGE_BOARDER_PIXELS,
1007                  0);
1008     int y1 = max(image_res * vertical_offset / 72
1009                    + min(i->Y1, i->Y2) * image_res / postscriptRes
1010                    - IMAGE_BOARDER_PIXELS,
1011                  0);
1012     int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1013              + IMAGE_BOARDER_PIXELS;
1014     int y2 = image_res * vertical_offset / 72
1015              + max(i->Y1, i->Y2) * image_res / postscriptRes
1016              + 1 + IMAGE_BOARDER_PIXELS;
1017     if (createPage(i->pageNo) == 0) {
1018       s = make_message("pnmcut%s %d %d %d %d < %s "
1019                        "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1020                        EXE_EXT,
1021                        x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1022                        imagePageName,
1023                        EXE_EXT,
1024                        TRANSPARENT,
1025                        i->imageName);
1026       if (s == NULL)
1027         sys_fatal("make_message");
1028
1029 #if defined(DEBUGGING)
1030       if (debug) {
1031         fprintf(stderr, s);
1032         fflush(stderr);
1033       }
1034 #endif
1035       html_system(s, 0);
1036       free(s);
1037     }
1038     else {
1039       fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1040       fflush(stderr);
1041     }
1042 #if defined(DEBUGGING)
1043   }
1044   else {
1045     if (debug) {
1046       fprintf(stderr, "ignoring image as x1 coord is -1\n");
1047       fflush(stderr);
1048     }
1049 #endif
1050   }
1051 }
1052
1053 /*
1054  *  add - Add an image description to the imageList.
1055  */
1056
1057 void imageList::add(int x1, int y1, int x2, int y2,
1058                     int page, int res, int maxx, char *name)
1059 {
1060   imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1061
1062   if (head == NULL) {
1063     head = i;
1064     tail = i;
1065   }
1066   else {
1067     tail->next = i;
1068     tail = i;
1069   }
1070 }
1071
1072 /*
1073  *  createImages - For each image descriptor on the imageList,
1074  *                 create the actual image.
1075  */
1076
1077 void imageList::createImages(void)
1078 {
1079   imageItem *h = head;
1080
1081   while (h != NULL) {
1082     createImage(h);
1083     h = h->next;
1084   }
1085 }
1086
1087 static imageList listOfImages;  // List of images defined by the region file.
1088
1089 /*
1090  *  generateImages - Parse the region file and generate images
1091  *                   from the postscript file.  The region file
1092  *                   contains the x1,y1--x2,y2 extents of each
1093  *                   image.
1094  */
1095
1096 static void generateImages(char *region_file_name)
1097 {
1098   pushBackBuffer *f=new pushBackBuffer(region_file_name);
1099
1100   while (f->putPB(f->getPB()) != eof) {
1101     if (f->isString("grohtml-info:page")) {
1102       int page = f->readInt();
1103       int x1 = f->readInt();
1104       int y1 = f->readInt();
1105       int x2 = f->readInt();
1106       int y2 = f->readInt();
1107       int maxx = f->readInt();
1108       char *name = f->readString();
1109       int res = postscriptRes;
1110       listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1111       while (f->putPB(f->getPB()) != '\n'
1112              && f->putPB(f->getPB()) != eof)
1113         (void)f->getPB();
1114       if (f->putPB(f->getPB()) == '\n')
1115         (void)f->getPB();
1116     }
1117     else {
1118       /* Write any error messages out to the user. */
1119       fputc(f->getPB(), stderr);
1120     }
1121   }
1122
1123   listOfImages.createImages();
1124   if (show_progress) {
1125     fprintf(stderr, "done\n");
1126     fflush(stderr);
1127   }
1128   delete f;
1129 }
1130
1131 /*
1132  *  set_redirection - Set up I/O Redirection for handle, was, to refer to
1133  *                    stream on handle, willbe.
1134  */
1135
1136 static void set_redirection(int was, int willbe)
1137 {
1138   // Nothing to do if `was' and `willbe' already have same handle.
1139   if (was != willbe) {
1140     // Otherwise attempt the specified redirection.
1141     if (dup2 (willbe, was) < 0) {
1142       // Redirection failed, so issue diagnostic and bail out.
1143       fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1144       if (willbe == STDOUT_FILENO)
1145         fprintf(stderr,
1146                 "likely that stdout should be opened before %d\n", was);
1147       sys_fatal("dup2");
1148     }
1149
1150     // When redirection has been successfully completed assume redundant
1151     // handle `willbe' is no longer required, so close it.
1152     if (close(willbe) < 0)
1153       // Issue diagnostic if `close' fails.
1154       sys_fatal("close");
1155   }
1156 }
1157
1158 /*
1159  *  save_and_redirect - Get duplicate handle for stream, was, then
1160  *                      redirect, was, to refer to, willbe.
1161  */
1162
1163 static int save_and_redirect(int was, int willbe)
1164 {
1165   if (was == willbe)
1166     // No redirection specified so don't do anything but silently bailing out.
1167     return (was);
1168
1169   // Proceeding with redirection so first save and verify our duplicate
1170   // handle for `was'.
1171   int saved = dup(was);
1172   if (saved < 0) {
1173     fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1174     sys_fatal("dup");
1175   }
1176
1177   // Duplicate handle safely established so complete redirection.
1178   set_redirection(was, willbe);
1179
1180   // Finally return the saved duplicate descriptor for the
1181   // original `was' stream.
1182   return saved;
1183 }
1184
1185 /*
1186  *  alterDeviceTo - If, toImage, is set
1187  *                     the argument list is altered to include
1188  *                     IMAGE_DEVICE and we invoke groff rather than troff.
1189  *                  Else
1190  *                     set -Thtml and groff.
1191  */
1192
1193 static void alterDeviceTo(int argc, char *argv[], int toImage)
1194 {
1195   int i = 0;
1196
1197   if (toImage) {
1198     while (i < argc) {
1199       if (strcmp(argv[i], "-Thtml") == 0)
1200         argv[i] = (char *)IMAGE_DEVICE;
1201       i++;
1202     }
1203     argv[troff_arg] = (char *)"groff";  /* rather than troff */
1204   }
1205   else {
1206     while (i < argc) {
1207       if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1208         argv[i] = (char *)"-Thtml";
1209       i++;
1210     }
1211     argv[troff_arg] = (char *)"groff";  /* use groff -Z */
1212   }
1213 }
1214
1215 /*
1216  *  addZ - Append -Z onto the command list for groff.
1217  */
1218
1219 char **addZ(int argc, char *argv[])
1220 {
1221   char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1222   int i = 0;
1223
1224   if (new_argv == NULL)
1225     sys_fatal("malloc");
1226
1227   if (argc > 0) {
1228     new_argv[i] = argv[i];
1229     i++;
1230   }
1231   new_argv[i] = (char *)"-Z";
1232   while (i < argc) {
1233     new_argv[i + 1] = argv[i];
1234     i++;
1235   }
1236   argc++;
1237   new_argv[argc] = NULL;
1238   return new_argv;
1239 }
1240
1241 /*
1242  *  addRegDef - Append a defined register or string onto the command
1243  *              list for troff.
1244  */
1245
1246 char **addRegDef(int argc, char *argv[], const char *numReg)
1247 {
1248   char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1249   int i = 0;
1250
1251   if (new_argv == NULL)
1252     sys_fatal("malloc");
1253
1254   while (i < argc) {
1255     new_argv[i] = argv[i];
1256     i++;
1257   }
1258   new_argv[argc] = strsave(numReg);
1259   argc++;
1260   new_argv[argc] = NULL;
1261   return new_argv;
1262 }
1263
1264 /*
1265  *  dump_args - Display the argument list.
1266  */
1267
1268 void dump_args(int argc, char *argv[])
1269 {
1270   fprintf(stderr, "  %d arguments:", argc);
1271   for (int i = 0; i < argc; i++)
1272     fprintf(stderr, " %s", argv[i]);
1273   fprintf(stderr, "\n");
1274 }
1275
1276 int char_buffer::run_output_filter(int filter, int /* argc */, char **argv)
1277 {
1278   int pipedes[2];
1279   PID_T child_pid;
1280   int status;
1281
1282   if (pipe(pipedes) < 0)
1283     sys_fatal("pipe");
1284
1285 #if MAY_FORK_CHILD_PROCESS
1286   // This is the UNIX process model.  To invoke our post-processor,
1287   // we must `fork' the current process.
1288
1289   if ((child_pid = fork()) < 0)
1290     sys_fatal("fork");
1291
1292   else if (child_pid == 0) {
1293     // This is the child process fork.  We redirect its `stdin' stream
1294     // to read data emerging from our pipe.  There is no point in saving,
1295     // since we won't be able to restore later!
1296
1297     set_redirection(STDIN_FILENO, pipedes[0]);
1298
1299     // The parent process will be writing this data, so we should release
1300     // the child's writeable handle on the pipe, since we have no use for it.
1301
1302     if (close(pipedes[1]) < 0)
1303       sys_fatal("close");
1304
1305     // The IMAGE_OUTPUT_FILTER needs special output redirection...
1306
1307     if (filter == IMAGE_OUTPUT_FILTER) {
1308       // with BOTH `stdout' AND `stderr' diverted to files.
1309
1310       set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1311       set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1312     }
1313
1314     // Now we are ready to launch the output filter.
1315
1316     execvp(argv[0], argv);
1317
1318     // If we get to here then the `exec...' request for the output filter
1319     // failed.  Diagnose it and bail out.
1320
1321     error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1322     fflush(stderr);     // just in case error() didn't
1323     exit(1);
1324   }
1325
1326   else {
1327     // This is the parent process fork.  We will be writing data to the
1328     // filter pipeline, and the child will be reading it.  We have no further
1329     // use for our read handle on the pipe, and should close it.
1330
1331     if (close(pipedes[0]) < 0)
1332       sys_fatal("close");
1333
1334     // Now we redirect the `stdout' stream to the inlet end of the pipe,
1335     // and push out the appropiately formatted data to the filter.
1336
1337     pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1338     emit_troff_output(DEVICE_FORMAT(filter));
1339
1340     // After emitting all the data we close our connection to the inlet
1341     // end of the pipe so the child process will detect end of data.
1342
1343     set_redirection(STDOUT_FILENO, pipedes[1]);
1344
1345     // Finally, we must wait for the child process to complete.
1346
1347     if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1348       sys_fatal("wait");
1349   }
1350
1351 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1352
1353   // We do not have `fork', (or we prefer not to use it),
1354   // but asynchronous processes are allowed, passing data through pipes.
1355   // This should be ok for most Win32 systems and is preferred to `fork'
1356   // for starting child processes under Cygwin.
1357
1358   // Before we start the post-processor we bind its inherited `stdin'
1359   // stream to the readable end of our pipe, saving our own `stdin' stream
1360   // in `pipedes[0]'.
1361
1362   pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1363
1364   // for the Win32 model,
1365   // we need special provision for saving BOTH `stdout' and `stderr'.
1366
1367   int saved_stdout = dup(STDOUT_FILENO);
1368   int saved_stderr = STDERR_FILENO;
1369
1370   // The IMAGE_OUTPUT_FILTER needs special output redirection...
1371
1372   if (filter == IMAGE_OUTPUT_FILTER) {
1373     // with BOTH `stdout' AND `stderr' diverted to files while saving a
1374     // duplicate handle for `stderr'.
1375
1376     set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1377     saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1378   }
1379
1380   // We then use an asynchronous spawn request to start the post-processor.
1381
1382   if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1383     // Should the spawn request fail we issue a diagnostic and bail out.
1384
1385     error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1386     exit(1);
1387   }
1388
1389   // Once the post-processor has been started we revert our `stdin'
1390   // to its original saved source, which also closes the readable handle
1391   // for the pipe.
1392
1393   set_redirection(STDIN_FILENO, pipedes[0]);
1394
1395   // if we redirected `stderr', for use by the image post-processor,
1396   // then we also need to reinstate its original assignment.
1397
1398   if (filter == IMAGE_OUTPUT_FILTER)
1399     set_redirection(STDERR_FILENO, saved_stderr);
1400
1401   // Now we redirect the `stdout' stream to the inlet end of the pipe,
1402   // and push out the appropiately formatted data to the filter.
1403
1404   set_redirection(STDOUT_FILENO, pipedes[1]);
1405   emit_troff_output(DEVICE_FORMAT(filter));
1406
1407   // After emitting all the data we close our connection to the inlet
1408   // end of the pipe so the child process will detect end of data.
1409
1410   set_redirection(STDOUT_FILENO, saved_stdout);
1411
1412   // And finally, we must wait for the child process to complete.
1413
1414   if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1415     sys_fatal("wait");
1416
1417 #else /* can't do asynchronous pipes! */
1418
1419   // TODO: code to support an MS-DOS style process model
1420   //        should go here
1421
1422 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1423
1424   return 0;
1425 }
1426
1427 /*
1428  *  do_html - Set the troff number htmlflip and
1429  *            write out the buffer to troff -Thtml.
1430  */
1431
1432 int char_buffer::do_html(int argc, char *argv[])
1433 {
1434   string s;
1435
1436   alterDeviceTo(argc, argv, 0);
1437   argv += troff_arg;            // skip all arguments up to groff
1438   argc -= troff_arg;
1439   argv = addZ(argc, argv);
1440   argc++;
1441
1442   s = "-dwww-image-template=";
1443   s += macroset_template;       // do not combine these statements,
1444                                 // otherwise they will not work
1445   s += '\0';                    // the trailing `\0' is ignored
1446   argv = addRegDef(argc, argv, s.contents());
1447   argc++;
1448
1449 #if defined(DEBUGGING)
1450 # define HTML_DEBUG_STREAM  OUTPUT_STREAM(htmlFileName)
1451   // slight security risk so only enabled if compiled with defined(DEBUGGING)
1452   if (debug) {
1453     int saved_stdout = save_and_redirect(STDOUT_FILENO, HTML_DEBUG_STREAM);
1454     emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
1455     set_redirection(STDOUT_FILENO, saved_stdout);
1456   }
1457 #endif
1458
1459   return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1460 }
1461
1462 /*
1463  *  do_image - Write out the buffer to troff -Tps.
1464  */
1465
1466 int char_buffer::do_image(int argc, char *argv[])
1467 {
1468   string s;
1469
1470   alterDeviceTo(argc, argv, 1);
1471   argv += troff_arg;            // skip all arguments up to troff/groff
1472   argc -= troff_arg;
1473   argv = addRegDef(argc, argv, "-rps4html=1");
1474   argc++;
1475
1476   s = "-dwww-image-template=";
1477   s += macroset_template;
1478   s += '\0';
1479   argv = addRegDef(argc, argv, s.contents());
1480   argc++;
1481
1482   // override local settings and produce a page size letter postscript file
1483   argv = addRegDef(argc, argv, "-P-pletter");
1484   argc++;
1485
1486 #if defined(DEBUGGING)
1487 # define IMAGE_DEBUG_STREAM  OUTPUT_STREAM(troffFileName)
1488   // slight security risk so only enabled if compiled with defined(DEBUGGING)
1489   if (debug) {
1490     int saved_stdout = save_and_redirect(STDOUT_FILENO, IMAGE_DEBUG_STREAM);
1491     emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
1492     set_redirection(STDOUT_FILENO, saved_stdout);
1493   }
1494 #endif
1495
1496   return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1497 }
1498
1499 static char_buffer inputFile;
1500
1501 /*
1502  *  usage - Emit usage arguments.
1503  */
1504
1505 static void usage(FILE *stream)
1506 {
1507   fprintf(stream,
1508           "usage: %s troffname [-Iimage_name] [-Dimage_directory]\n"
1509           "       [-P-o vertical_image_offset] [-P-i image_resolution]\n"
1510           "       [troff flags]\n",
1511           program_name);
1512   fprintf(stream,
1513           "    vertical_image_offset (default %d/72 of an inch)\n",
1514           vertical_offset);
1515   fprintf(stream,
1516           "    image_resolution (default %d) pixels per inch\n",
1517           image_res);
1518   fprintf(stream,
1519           "    image_name is the name of the stem for all images\n"
1520           "    (default is grohtml-<pid>)\n");
1521   fprintf(stream,
1522           "    place all png files into image_directory\n");
1523 }
1524
1525 /*
1526  *  scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1527  *                  and -P-I.  Return the argument index of the first
1528  *                  non-option.
1529  */
1530
1531 static int scanArguments(int argc, char **argv)
1532 {
1533   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
1534   if (!command_prefix)
1535     command_prefix = PROG_PREFIX;
1536   char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1];
1537   strcpy(troff_name, command_prefix);
1538   strcat(troff_name, "troff");
1539   int c, i;
1540   static const struct option long_options[] = {
1541     { "help", no_argument, 0, CHAR_MAX + 1 },
1542     { "version", no_argument, 0, 'v' },
1543     { NULL, 0, 0, 0 }
1544   };
1545   while ((c = getopt_long(argc, argv,
1546                           "+a:g:o:i:I:j:D:F:vbdhlrnp", long_options, NULL))
1547          != EOF)
1548     switch(c) {
1549     case 'v':
1550       printf("GNU pre-grohtml (groff) version %s\n", Version_string);
1551       exit(0);
1552     case 'a':
1553       textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1554                           MAX_ALPHA_BITS);
1555       if (textAlphaBits == 3) {
1556         error("cannot use 3 bits of antialiasing information");
1557         exit(1);
1558       }
1559       break;
1560     case 'g':
1561       graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1562                              MAX_ALPHA_BITS);
1563       if (graphicAlphaBits == 3) {
1564         error("cannot use 3 bits of antialiasing information");
1565         exit(1);
1566       }
1567       break;
1568     case 'b':
1569       // handled by post-grohtml (set background color to white)
1570       break;
1571     case 'D':
1572       image_dir = optarg;
1573       break;
1574     case 'I':
1575       image_template = optarg;
1576       break;
1577     case 'i':
1578       image_res = atoi(optarg);
1579       break;
1580     case 'F':
1581       font_path.command_line_dir(optarg);
1582       break;
1583     case 'j':
1584       // handled by post-grohtml (set job name for multiple file output)
1585       break;
1586     case 'o':
1587       vertical_offset = atoi(optarg);
1588       break;
1589     case 'p':
1590       show_progress = TRUE;
1591       break;
1592     case 'd':
1593 #if defined(DEBUGGING)
1594       debug = TRUE;
1595 #endif
1596       break;
1597     case 'h':
1598       // handled by post-grohtml
1599       break;
1600     case CHAR_MAX + 1: // --help
1601       usage(stdout);
1602       exit(0);
1603       break;
1604     case '?':
1605       usage(stderr);
1606       exit(1);
1607       break;
1608     default:
1609       break;
1610     }
1611
1612   i = optind;
1613   while (i < argc) {
1614     if (strcmp(argv[i], troff_name) == 0)
1615       troff_arg = i;
1616     else if (argv[i][0] != '-')
1617       return i;
1618     i++;
1619   }
1620   a_delete troff_name;
1621
1622   return argc;
1623 }
1624
1625 /*
1626  *  makeTempFiles - Name the temporary files.
1627  */
1628
1629 static int makeTempFiles(void)
1630 {
1631 #if defined(DEBUGGING)
1632   psFileName = DEBUG_FILE("prehtml-ps");
1633   regionFileName = DEBUG_FILE("prehtml-region");
1634   imagePageName = DEBUG_FILE("prehtml-page");
1635   psPageName = DEBUG_FILE("prehtml-psn");
1636   troffFileName = DEBUG_FILE("prehtml-troff");
1637   htmlFileName = DEBUG_FILE("prehtml-html");
1638 #else /* not DEBUGGING */
1639   FILE *f;
1640
1641   /* psPageName contains a single page of postscript */
1642   f = xtmpfile(&psPageName,
1643                PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1644                TRUE);
1645   if (f == NULL) {
1646     sys_fatal("xtmpfile");
1647     return -1;
1648   }
1649   fclose(f);
1650
1651   /* imagePageName contains a bitmap image of the single postscript page */
1652   f = xtmpfile(&imagePageName,
1653                PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1654                TRUE);
1655   if (f == NULL) {
1656     sys_fatal("xtmpfile");
1657     return -1;
1658   }
1659   fclose(f);
1660
1661   /* psFileName contains a postscript file of the complete document */
1662   f = xtmpfile(&psFileName,
1663                PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1664                TRUE);
1665   if (f == NULL) {
1666     sys_fatal("xtmpfile");
1667     return -1;
1668   }
1669   fclose(f);
1670
1671   /* regionFileName contains a list of the images and their boxed coordinates */
1672   f = xtmpfile(&regionFileName,
1673                REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1674                TRUE);
1675   if (f == NULL) {
1676     sys_fatal("xtmpfile");
1677     return -1;
1678   }
1679   fclose(f);
1680
1681 #endif /* not DEBUGGING */
1682   return 0;
1683 }
1684
1685 int main(int argc, char **argv)
1686 {
1687   program_name = argv[0];
1688   int i;
1689   int found = 0;
1690   int ok = 1;
1691
1692 #ifdef CAPTURE_MODE
1693   FILE *dump;
1694   fprintf(stderr, "%s: invoked with %d arguments ...\n", argv[0], argc);
1695   for (i = 0; i < argc; i++)
1696     fprintf(stderr, "%2d: %s\n", i, argv[i]);
1697   if ((dump = fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL) {
1698     while((i = fgetc(stdin)) >= 0)
1699       fputc(i, dump);
1700     fclose(dump);
1701   }
1702   exit(1);
1703 #endif /* CAPTURE_MODE */
1704   postscriptRes = get_resolution();
1705   i = scanArguments(argc, argv);
1706   setupAntiAlias();
1707   checkImageDir();
1708   makeFileName();
1709   while (i < argc) {
1710     if (argv[i][0] != '-') {
1711       /* found source file */
1712       ok = do_file(argv[i]);
1713       if (!ok)
1714         return 0;
1715       found = 1;
1716     }
1717     i++;
1718   }
1719
1720   if (!found)
1721     do_file("-");
1722   if (makeTempFiles())
1723     return 1;
1724   ok = inputFile.do_image(argc, argv);
1725   if (ok == 0) {
1726     generateImages(regionFileName);
1727     ok = inputFile.do_html(argc, argv);
1728   }
1729   return ok;
1730 }
1731
1732 static int do_file(const char *filename)
1733 {
1734   FILE *fp;
1735
1736   current_filename = filename;
1737   if (strcmp(filename, "-") == 0)
1738     fp = stdin;
1739   else {
1740     fp = fopen(filename, "r");
1741     if (fp == 0) {
1742       error("can't open `%1': %2", filename, strerror(errno));
1743       return 0;
1744     }
1745   }
1746
1747   if (inputFile.read_file(fp)) {
1748     // XXX
1749   }
1750
1751   if (fp != stdin)
1752     fclose(fp);
1753   current_filename = NULL;
1754   return 1;
1755 }