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