Imported Upstream version 1.22.4
[platform/upstream/groff.git] / src / preproc / html / pre-html.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2000-2018 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 3 of the License, or
10  * (at your option) any later 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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. 
20  */
21
22 #define PREHTMLC
23
24 #include "lib.h"
25
26 #include <signal.h>
27 #include <ctype.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include "errarg.h"
32 #include "error.h"
33 #include "stringclass.h"
34 #include "posix.h"
35 #include "defs.h"
36 #include "searchpath.h"
37 #include "paper.h"
38 #include "device.h"
39 #include "font.h"
40
41 #include <errno.h>
42 #include <sys/types.h>
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif
46
47 #ifdef _POSIX_VERSION
48 # include <sys/wait.h>
49 # define PID_T pid_t
50 #else /* not _POSIX_VERSION */
51 # define PID_T int
52 #endif /* not _POSIX_VERSION */
53
54 #include <stdarg.h>
55
56 #include "nonposix.h"
57
58 #if 0
59 # define DEBUGGING
60 #endif
61
62 /* Establish some definitions to facilitate discrimination between
63    differing runtime environments. */
64
65 #undef MAY_FORK_CHILD_PROCESS
66 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
67
68 #if defined(__MSDOS__) || defined(_WIN32)
69
70 // Most MS-DOS and Win32 environments will be missing the 'fork' capability
71 // (some like Cygwin have it, but it is best avoided).
72
73 # define MAY_FORK_CHILD_PROCESS 0
74
75 // On these systems, we use 'spawn...', instead of 'fork' ... 'exec...'.
76 # include <process.h>   // for 'spawn...'
77 # include <fcntl.h>     // for attributes of pipes
78
79 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
80
81 // These Win32 implementations allow parent and 'spawn...'ed child to
82 // multitask asynchronously.
83
84 #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
85
86 # else
87
88 // Others may adopt MS-DOS behaviour where parent must sleep,
89 // from 'spawn...' until child terminates.
90
91 #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
92
93 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
94
95 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
96 /* When we are building a DEBUGGING version we need to tell pre-grohtml
97    where to put intermediate files (the DEBUGGING version will preserve
98    these on exit).
99
100    On a Unix host, we might simply use '/tmp', but MS-DOS and Win32 will
101    probably not have this on all disk drives, so default to using
102    'c:/temp' instead.  (Note that user may choose to override this by
103    supplying a definition such as
104
105      -DDEBUG_FILE_DIR=d:/path/to/debug/files
106
107    in the CPPFLAGS to 'make'.) */
108
109 #  define DEBUG_FILE_DIR c:/temp
110 # endif
111
112 #else /* not __MSDOS__ or _WIN32 */
113
114 // For non-Microsoft environments assume Unix conventions,
115 // so 'fork' is required and child processes are asynchronous.
116 # define MAY_FORK_CHILD_PROCESS 1
117 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
118
119 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
120 /* For a DEBUGGING version, on the Unix host, we can also usually rely
121    on being able to use '/tmp' for temporary file storage.  (Note that,
122    as in the __MSDOS__ or _WIN32 case above, the user may override this
123    by defining
124
125      -DDEBUG_FILE_DIR=/path/to/debug/files
126
127    in the CPPFLAGS.) */
128
129 #  define DEBUG_FILE_DIR /tmp
130 # endif
131
132 #endif /* not __MSDOS__ or _WIN32 */
133
134 #ifdef DEBUGGING
135 // For a DEBUGGING version, we need some additional macros,
136 // to direct the captured debug mode output to appropriately named files
137 // in the specified DEBUG_FILE_DIR.
138
139 # define DEBUG_TEXT(text) #text
140 # define DEBUG_NAME(text) DEBUG_TEXT(text)
141 # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
142 #endif
143
144 extern "C" const char *Version_string;
145
146 #include "pre-html.h"
147 #include "pushback.h"
148 #include "html-strings.h"
149
150 #define DEFAULT_LINE_LENGTH 7   // inches wide
151 #define DEFAULT_IMAGE_RES 100   // number of pixels per inch resolution
152 #define IMAGE_BOARDER_PIXELS 0
153 #define INLINE_LEADER_CHAR '\\'
154
155 // Don't use colour names here!  Otherwise there is a dependency on
156 // a file called 'rgb.txt' which maps names to colours.
157 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
158 #define MIN_ALPHA_BITS 0
159 #define MAX_ALPHA_BITS 4
160
161 #define PAGE_TEMPLATE_SHORT "pg"
162 #define PAGE_TEMPLATE_LONG "-page-"
163 #define PS_TEMPLATE_SHORT "ps"
164 #define PS_TEMPLATE_LONG "-ps-"
165 #define REGION_TEMPLATE_SHORT "rg"
166 #define REGION_TEMPLATE_LONG "-regions-"
167
168 #if !defined(TRUE)
169 # define TRUE (1==1)
170 #endif
171 #if !defined(FALSE)
172 # define FALSE (1==0)
173 #endif
174
175 typedef enum {
176   CENTERED, LEFT, RIGHT, INLINE
177 } IMAGE_ALIGNMENT;
178
179 typedef enum {xhtml, html4} html_dialect;
180
181 static int postscriptRes = -1;          // postscript resolution,
182                                         // dots per inch
183 static int stdoutfd = 1;                // output file descriptor -
184                                         // normally 1 but might move
185                                         // -1 means closed
186 static char *psFileName = NULL;         // name of postscript file
187 static char *psPageName = NULL;         // name of file containing
188                                         // postscript current page
189 static char *regionFileName = NULL;     // name of file containing all
190                                         // image regions
191 static char *imagePageName = NULL;      // name of bitmap image containing
192                                         // current page
193 static const char *image_device = "pnmraw";
194 static int image_res = DEFAULT_IMAGE_RES;
195 static int vertical_offset = 0;
196 static char *image_template = NULL;     // image template filename
197 static char *macroset_template= NULL;   // image template passed to troff
198                                         // by -D
199 static int troff_arg = 0;               // troff arg index
200 static char *image_dir = NULL;          // user specified image directory
201 static int textAlphaBits = MAX_ALPHA_BITS;
202 static int graphicAlphaBits = MAX_ALPHA_BITS;
203 static char *antiAlias = NULL;          // antialias arguments we pass to gs
204 static int show_progress = FALSE;       // should we display page numbers as
205                                         // they are processed?
206 static int currentPageNo = -1;          // current image page number
207 #if defined(DEBUGGING)
208 static int debug = FALSE;
209 static char *troffFileName = NULL;      // output of pre-html output which
210                                         // is sent to troff -Tps
211 static char *htmlFileName = NULL;       // output of pre-html output which
212                                         // is sent to troff -Thtml
213 #endif
214 static int eqn_flag = FALSE;            // must we preprocess via eqn?
215
216 static char *linebuf = NULL;            // for scanning devps/DESC
217 static int linebufsize = 0;
218 static const char *image_gen = NULL;    // the 'gs' program
219
220 const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
221 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
222 static html_dialect dialect = html4;
223
224
225 /*
226  *  Images are generated via postscript, gs, and the pnm utilities.
227  */
228 #define IMAGE_DEVICE "-Tps"
229
230
231 static int do_file(const char *filename);
232
233
234 /*
235  *  sys_fatal - Write a fatal error message.
236  *              Taken from src/roff/groff/pipeline.c.
237  */
238
239 void sys_fatal(const char *s)
240 {
241   fatal("%1: %2", s, strerror(errno));
242 }
243
244 /*
245  *  get_line - Copy a line (w/o newline) from a file to the
246  *             global line buffer.
247  */
248
249 int get_line(FILE *f)
250 {
251   if (f == 0)
252     return 0;
253   if (linebuf == 0) {
254     linebuf = new char[128];
255     linebufsize = 128;
256   }
257   int i = 0;
258   // skip leading whitespace
259   for (;;) {
260     int c = getc(f);
261     if (c == EOF)
262       return 0;
263     if (c != ' ' && c != '\t') {
264       ungetc(c, f);
265       break;
266     }
267   }
268   for (;;) {
269     int c = getc(f);
270     if (c == EOF)
271       break;
272     if (i + 1 >= linebufsize) {
273       char *old_linebuf = linebuf;
274       linebuf = new char[linebufsize * 2];
275       memcpy(linebuf, old_linebuf, linebufsize);
276       a_delete old_linebuf;
277       linebufsize *= 2;
278     }
279     linebuf[i++] = c;
280     if (c == '\n') {
281       i--;
282       break;
283     }
284   }
285   linebuf[i] = '\0';
286   return 1;
287 }
288
289 /*
290  *  get_resolution - Return the postscript resolution from devps/DESC.
291  */
292
293 static unsigned int get_resolution(void)
294 {
295   char *pathp;
296   FILE *f;
297   unsigned int res;
298   f = font_path.open_file("devps/DESC", &pathp);
299   free(pathp);
300   if (f == 0)
301     fatal("can't open devps/DESC");
302   while (get_line(f)) {
303     int n = sscanf(linebuf, "res %u", &res);
304     if (n >= 1) {
305       fclose(f);
306       return res;
307     }
308   }
309   fatal("can't find 'res' keyword in devps/DESC");
310   return 0;
311 }
312
313 /*
314  *  html_system - A wrapper for system().
315  */
316
317 void html_system(const char *s, int redirect_stdout)
318 {
319 #if defined(DEBUGGING)
320   if (debug) {
321     fprintf(stderr, "executing: ");
322     fwrite(s, sizeof(char), strlen(s), stderr);
323     fflush(stderr);
324   }
325 #endif
326   {
327     // Redirect standard error to the null device.  This is more
328     // portable than using "2> /dev/null", since it doesn't require a
329     // Unixy shell.
330     int save_stderr = dup(2);
331     int save_stdout = dup(1);
332     int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
333     if (save_stderr > 2 && fdnull > 2)
334       dup2(fdnull, 2);
335     if (redirect_stdout && save_stdout > 1 && fdnull > 1)
336       dup2(fdnull, 1);
337     if (fdnull >= 0)
338       close(fdnull);
339     int status = system(s);
340     dup2(save_stderr, 2);
341     if (redirect_stdout)
342       dup2(save_stdout, 1);
343     if (status == -1)
344       fprintf(stderr, "Calling '%s' failed\n", s);
345     else if (status)
346       fprintf(stderr, "Calling '%s' returned status %d\n", s, status);
347     close(save_stderr);
348     close(save_stdout);
349   }
350 }
351
352 /*
353  *  make_message - Create a string via malloc and place the result of the
354  *                 va args into string.  Finally the new string is returned.
355  *                 Taken from man page of printf(3).
356  */
357
358 char *make_message(const char *fmt, ...)
359 {
360   /* Guess we need no more than 100 bytes. */
361   int n, size = 100;
362   char *p;
363   char *np;
364   va_list ap;
365   if ((p = (char *)malloc(size)) == NULL)
366     return NULL;
367   while (1) {
368     /* Try to print in the allocated space. */
369     va_start(ap, fmt);
370     n = vsnprintf(p, size, fmt, ap);
371     va_end(ap);
372     /* If that worked, return the string. */
373     if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
374       if (size > n + 1) {
375         np = strsave(p);
376         free(p);
377         return np;
378       }
379       return p;
380     }
381     /* Else try again with more space. */
382     else                /* glibc 2.0 */
383       size *= 2;        /* twice the old size */
384     if ((np = (char *)realloc(p, size)) == NULL) {
385       free(p);          /* realloc failed, free old, p. */
386       return NULL;
387     }
388     p = np;             /* use realloc'ed, p */
389   }
390 }
391
392 /*
393  *  the class and methods for retaining ascii text
394  */
395
396 struct char_block {
397   enum { SIZE = 256 };
398   char buffer[SIZE];
399   int used;
400   char_block *next;
401
402   char_block();
403 };
404
405 /*
406  *  char_block - Constructor.  Set the, used, and, next, fields to zero.
407  */
408
409 char_block::char_block()
410 : used(0), next(0)
411 {
412   for (int i = 0; i < SIZE; i++)
413     buffer[i] = 0;
414 }
415
416 class char_buffer {
417 public:
418   char_buffer();
419   ~char_buffer();
420   int read_file(FILE *fp);
421   int do_html(int argc, char *argv[]);
422   int do_image(int argc, char *argv[]);
423   void emit_troff_output(int device_format_selector);
424   void write_upto_newline(char_block **t, int *i, int is_html);
425   int can_see(char_block **t, int *i, const char *string);
426   int skip_spaces(char_block **t, int *i);
427   void skip_until_newline(char_block **t, int *i);
428 private:
429   char_block *head;
430   char_block *tail;
431   int run_output_filter(int device_format_selector, int argc, char *argv[]);
432 };
433
434 /*
435  *  char_buffer - Constructor.
436  */
437
438 char_buffer::char_buffer()
439 : head(0), tail(0)
440 {
441 }
442
443 /*
444  *  char_buffer - Destructor.  Throw away the whole buffer list.
445  */
446
447 char_buffer::~char_buffer()
448 {
449   while (head != NULL) {
450     char_block *temp = head;
451     head = head->next;
452     delete temp;
453   }
454 }
455
456 /*
457  *  read_file - Read in a complete file, fp, placing the contents inside
458  *              char_blocks.
459  */
460
461 int char_buffer::read_file(FILE *fp)
462 {
463   int n;
464   while (!feof(fp)) {
465     if (tail == NULL) {
466       tail = new char_block;
467       head = tail;
468     }
469     else {
470       if (tail->used == char_block::SIZE) {
471         tail->next = new char_block;
472         tail = tail->next;
473       }
474     }
475     // at this point we have a tail which is ready for the next SIZE
476     // bytes of the file
477     n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
478     if (n <= 0)
479       // error
480       return 0;
481     else
482       tail->used += n * sizeof(char);
483   }
484   return 1;
485 }
486
487 /*
488  *  writeNbytes - Write n bytes to stdout.
489  */
490
491 static void writeNbytes(const char *s, int l)
492 {
493   int n = 0;
494   int r;
495
496   while (n < l) {
497     r = write(stdoutfd, s, l - n);
498     if (r < 0)
499       sys_fatal("write");
500     n += r;
501     s += r;
502   }
503 }
504
505 /*
506  *  writeString - Write a string to stdout.
507  */
508
509 static void writeString(const char *s)
510 {
511   writeNbytes(s, strlen(s));
512 }
513
514 /*
515  *  makeFileName - Create the image filename template
516  *                 and the macroset image template.
517  */
518
519 static void makeFileName(void)
520 {
521   if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
522     error("cannot use a '%%' within the image directory name");
523     exit(1);
524   }
525
526   if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
527     error("cannot use a '%%' within the image template");
528     exit(1);
529   }
530
531   if (image_dir == NULL)
532     image_dir = (char *)"";
533   else if (strlen(image_dir) > 0
534            && image_dir[strlen(image_dir) - 1] != '/') {
535     image_dir = make_message("%s/", image_dir);
536     if (image_dir == NULL)
537       sys_fatal("make_message");
538   }
539
540   if (image_template == NULL)
541     macroset_template = make_message("%sgrohtml-%d", image_dir,
542                                      (int)getpid());
543   else
544     macroset_template = make_message("%s%s", image_dir, image_template);
545
546   if (macroset_template == NULL)
547     sys_fatal("make_message");
548
549   image_template =
550     (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
551   if (image_template == NULL)
552     sys_fatal("malloc");
553   strcpy(image_template, macroset_template);
554   strcat(image_template, "-%d");
555 }
556
557 /*
558  *  setupAntiAlias - Set up the antialias string, used when we call gs.
559  */
560
561 static void setupAntiAlias(void)
562 {
563   if (textAlphaBits == 0 && graphicAlphaBits == 0)
564     antiAlias = make_message(" ");
565   else if (textAlphaBits == 0)
566     antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
567   else if (graphicAlphaBits == 0)
568     antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
569   else
570     antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
571                              textAlphaBits, graphicAlphaBits);
572 }
573
574 /*
575  *  checkImageDir - Check whether the image directory is available.
576  */
577
578 static void checkImageDir(void)
579 {
580   if (image_dir != NULL && strcmp(image_dir, "") != 0)
581     if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
582       error("cannot create directory '%1'", image_dir);
583       exit(1);
584     }
585 }
586
587 /*
588  *  write_end_image - End the image.  Write out the image extents if we
589  *                    are using -Tps.
590  */
591
592 static void write_end_image(int is_html)
593 {
594   /*
595    *  if we are producing html then these
596    *    emit image name and enable output
597    *  else
598    *    we are producing images
599    *    in which case these generate image
600    *    boundaries
601    */
602   writeString("\\O[4]\\O[2]");
603   if (is_html)
604     writeString("\\O[1]");
605   else
606     writeString("\\O[0]");
607 }
608
609 /*
610  *  write_start_image - Write troff code which will:
611  *
612  *                      (i)  disable html output for the following image
613  *                      (ii) reset the max/min x/y registers during postscript
614  *                           rendering.
615  */
616
617 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
618 {
619   writeString("\\O[5");
620   switch (pos) {
621   case INLINE:
622     writeString("i");
623     break;
624   case LEFT:
625     writeString("l");
626     break;
627   case RIGHT:
628     writeString("r");
629     break;
630   case CENTERED:
631   default:
632     writeString("c");
633     break;
634   }
635   writeString(image_template);
636   writeString(".png]");
637   if (is_html)
638     writeString("\\O[0]\\O[3]");
639   else
640     // reset min/max registers
641     writeString("\\O[1]\\O[3]");
642 }
643
644 /*
645  *  write_upto_newline - Write the contents of the buffer until a newline
646  *                       is seen.  Check for HTML_IMAGE_INLINE_BEGIN and
647  *                       HTML_IMAGE_INLINE_END; process them if they are
648  *                       present.
649  */
650
651 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
652 {
653   int j = *i;
654
655   if (*t) {
656     while (j < (*t)->used
657            && (*t)->buffer[j] != '\n'
658            && (*t)->buffer[j] != INLINE_LEADER_CHAR)
659       j++;
660     if (j < (*t)->used
661         && (*t)->buffer[j] == '\n')
662       j++;
663     writeNbytes((*t)->buffer + (*i), j - (*i));
664     if (j < char_block::SIZE && (*t)->buffer[j] == INLINE_LEADER_CHAR) {
665       if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
666         write_start_image(INLINE, is_html);
667       else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
668         write_end_image(is_html);
669       else {
670         if (j < (*t)->used) {
671           *i = j;
672           j++;
673           writeNbytes((*t)->buffer + (*i), j - (*i));
674         }
675       }
676     }
677     if (j == (*t)->used) {
678       *i = 0;
679       *t = (*t)->next;
680       if (*t && (*t)->buffer[j - 1] != '\n')
681         write_upto_newline(t, i, is_html);
682     }
683     else
684       // newline was seen
685       *i = j;
686   }
687 }
688
689 /*
690  *  can_see - Return TRUE if we can see string in t->buffer[i] onwards.
691  */
692
693 int char_buffer::can_see(char_block **t, int *i, const char *str)
694 {
695   int j = 0;
696   int l = strlen(str);
697   int k = *i;
698   char_block *s = *t;
699
700   while (s) {
701     while (k < s->used && j < l && s->buffer[k] == str[j]) {
702       j++;
703       k++;
704     }
705     if (j == l) {
706       *i = k;
707       *t = s;
708       return TRUE;
709     }
710     else if (k < s->used && s->buffer[k] != str[j])
711       return( FALSE );
712     s = s->next;
713     k = 0;
714   }
715   return FALSE;
716 }
717
718 /*
719  *  skip_spaces - Return TRUE if we have not run out of data.
720  *                Consume spaces also.
721  */
722
723 int char_buffer::skip_spaces(char_block **t, int *i)
724 {
725   char_block *s = *t;
726   int k = *i;
727
728   while (s) {
729     while (k < s->used && isspace(s->buffer[k]))
730       k++;
731     if (k == s->used) {
732       k = 0;
733       s = s->next;
734     }
735     else {
736       *i = k;
737       return TRUE;
738     }
739   }
740   return FALSE;
741 }
742
743 /*
744  *  skip_until_newline - Skip all characters until a newline is seen.
745  *                       The newline is not consumed.
746  */
747
748 void char_buffer::skip_until_newline(char_block **t, int *i)
749 {
750   int j = *i;
751
752   if (*t) {
753     while (j < (*t)->used && (*t)->buffer[j] != '\n')
754       j++;
755     if (j == (*t)->used) {
756       *i = 0;
757       *t = (*t)->next;
758       skip_until_newline(t, i);
759     }
760     else
761       // newline was seen
762       *i = j;
763   }
764 }
765
766 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
767 #define HTML_OUTPUT_FILTER     0
768 #define IMAGE_OUTPUT_FILTER    1
769 #define OUTPUT_STREAM(name)   creat((name), S_IWUSR | S_IRUSR)
770 #define PS_OUTPUT_STREAM      OUTPUT_STREAM(psFileName)
771 #define REGION_OUTPUT_STREAM  OUTPUT_STREAM(regionFileName)
772
773 /*
774  *  emit_troff_output - Write formatted buffer content to the troff
775  *                      post-processor data pipeline.
776  */
777
778 void char_buffer::emit_troff_output(int device_format_selector)
779 {
780   // Handle output for BOTH html and image device formats
781   // if 'device_format_selector' is passed as
782   //
783   //   HTML_FORMAT(HTML_OUTPUT_FILTER)
784   //     Buffer data is written to the output stream
785   //     with template image names translated to actual image names.
786   //
787   //   HTML_FORMAT(IMAGE_OUTPUT_FILTER)
788   //     Buffer data is written to the output stream
789   //     with no translation, for image file creation in the post-processor.
790
791   int idx = 0;
792   char_block *element = head;
793
794   while (element != NULL)
795     write_upto_newline(&element, &idx, device_format_selector);
796
797 #if 0
798   if (close(stdoutfd) < 0)
799     sys_fatal ("close");
800
801   // now we grab fd=1 so that the next pipe cannot use fd=1
802   if (stdoutfd == 1) {
803     if (dup(2) != stdoutfd)
804       sys_fatal ("dup failed to use fd=1");
805   }
806 #endif /* 0 */
807 }
808
809 /*
810  *  The image class remembers the position of all images in the
811  *  postscript file and assigns names for each image.
812  */
813
814 struct imageItem {
815   imageItem *next;
816   int X1;
817   int Y1;
818   int X2;
819   int Y2;
820   char *imageName;
821   int resolution;
822   int maxx;
823   int pageNo;
824
825   imageItem(int x1, int y1, int x2, int y2,
826             int page, int res, int max_width, char *name);
827   ~imageItem();
828 };
829
830 /*
831  *  imageItem - Constructor.
832  */
833
834 imageItem::imageItem(int x1, int y1, int x2, int y2,
835                      int page, int res, int max_width, char *name)
836 {
837   X1 = x1;
838   Y1 = y1;
839   X2 = x2;
840   Y2 = y2;
841   pageNo = page;
842   resolution = res;
843   maxx = max_width;
844   imageName = name;
845   next = NULL;
846 }
847
848 /*
849  *  imageItem - Destructor.
850  */
851
852 imageItem::~imageItem()
853 {
854   if (imageName)
855     free(imageName);
856 }
857
858 /*
859  *  imageList - A class containing a list of imageItems.
860  */
861
862 class imageList {
863 private:
864   imageItem *head;
865   imageItem *tail;
866   int count;
867 public:
868   imageList();
869   ~imageList();
870   void add(int x1, int y1, int x2, int y2,
871            int page, int res, int maxx, char *name);
872   void createImages(void);
873   int createPage(int pageno);
874   void createImage(imageItem *i);
875   int getMaxX(int pageno);
876 };
877
878 /*
879  *  imageList - Constructor.
880  */
881
882 imageList::imageList()
883 : head(0), tail(0), count(0)
884 {
885 }
886
887 /*
888  *  imageList - Destructor.
889  */
890
891 imageList::~imageList()
892 {
893   while (head != NULL) {
894     imageItem *i = head;
895     head = head->next;
896     delete i;
897   }
898 }
899
900 /*
901  *  createPage - Create one image of, page pageno, from the postscript file.
902  */
903
904 int imageList::createPage(int pageno)
905 {
906   char *s;
907
908   if (currentPageNo == pageno)
909     return 0;
910
911   if (currentPageNo >= 1) {
912     /*
913      *  We need to unlink the files which change each time a new page is
914      *  processed.  The final unlink is done by xtmpfile when pre-grohtml
915      *  exits.
916      */
917     unlink(imagePageName);
918     unlink(psPageName);
919   }
920
921   if (show_progress) {
922     fprintf(stderr, "[%d] ", pageno);
923     fflush(stderr);
924   }
925
926 #if defined(DEBUGGING)
927   if (debug)
928     fprintf(stderr, "creating page %d\n", pageno);
929 #endif
930
931   s = make_message("psselect -q -p%d %s %s\n",
932                    pageno, psFileName, psPageName);
933
934   if (s == NULL)
935     sys_fatal("make_message");
936   html_system(s, 1);
937
938   s = make_message("echo showpage | "
939                    "%s%s -q -dBATCH -dSAFER "
940                    "-dDEVICEHEIGHTPOINTS=792 "
941                    "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
942                    "-sDEVICE=%s -r%d %s "
943                    "-sOutputFile=%s %s -\n",
944                    image_gen,
945                    EXE_EXT,
946                    (getMaxX(pageno) * image_res) / postscriptRes,
947                    image_device,
948                    image_res,
949                    antiAlias,
950                    imagePageName,
951                    psPageName);
952   if (s == NULL)
953     sys_fatal("make_message");
954   html_system(s, 1);
955   free(s);
956   currentPageNo = pageno;
957   return 0;
958 }
959
960 /*
961  *  min - Return the minimum of two numbers.
962  */
963
964 int min(int x, int y)
965 {
966   if (x < y)
967     return x;
968   else
969     return y;
970 }
971
972 /*
973  *  max - Return the maximum of two numbers.
974  */
975
976 int max(int x, int y)
977 {
978   if (x > y)
979     return x;
980   else
981     return y;
982 }
983
984 /*
985  *  getMaxX - Return the largest right-hand position for any image
986  *            on, pageno.
987  */
988
989 int imageList::getMaxX(int pageno)
990 {
991   imageItem *h = head;
992   int x = postscriptRes * DEFAULT_LINE_LENGTH;
993
994   while (h != NULL) {
995     if (h->pageNo == pageno)
996       x = max(h->X2, x);
997     h = h->next;
998   }
999   return x;
1000 }
1001
1002 /*
1003  *  createImage - Generate a minimal png file from the set of page images.
1004  */
1005
1006 void imageList::createImage(imageItem *i)
1007 {
1008   if (i->X1 != -1) {
1009     char *s;
1010     int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1011                    - IMAGE_BOARDER_PIXELS,
1012                  0);
1013     int y1 = max(image_res * vertical_offset / 72
1014                    + min(i->Y1, i->Y2) * image_res / postscriptRes
1015                    - IMAGE_BOARDER_PIXELS,
1016                  0);
1017     int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1018              + IMAGE_BOARDER_PIXELS;
1019     int y2 = image_res * vertical_offset / 72
1020              + max(i->Y1, i->Y2) * image_res / postscriptRes
1021              + 1 + IMAGE_BOARDER_PIXELS;
1022     if (createPage(i->pageNo) == 0) {
1023       s = make_message("pnmcut%s %d %d %d %d < %s "
1024                        "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1025                        EXE_EXT,
1026                        x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1027                        imagePageName,
1028                        EXE_EXT,
1029                        TRANSPARENT,
1030                        i->imageName);
1031       if (s == NULL)
1032         sys_fatal("make_message");
1033
1034       html_system(s, 0);
1035       free(s);
1036     }
1037     else {
1038       fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1039       fflush(stderr);
1040     }
1041 #if defined(DEBUGGING)
1042   }
1043   else {
1044     if (debug) {
1045       fprintf(stderr, "ignoring image as x1 coord is -1\n");
1046       fflush(stderr);
1047     }
1048 #endif
1049   }
1050 }
1051
1052 /*
1053  *  add - Add an image description to the imageList.
1054  */
1055
1056 void imageList::add(int x1, int y1, int x2, int y2,
1057                     int page, int res, int maxx, char *name)
1058 {
1059   imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1060
1061   if (head == NULL) {
1062     head = i;
1063     tail = i;
1064   }
1065   else {
1066     tail->next = i;
1067     tail = i;
1068   }
1069 }
1070
1071 /*
1072  *  createImages - For each image descriptor on the imageList,
1073  *                 create the actual image.
1074  */
1075
1076 void imageList::createImages(void)
1077 {
1078   imageItem *h = head;
1079
1080   while (h != NULL) {
1081     createImage(h);
1082     h = h->next;
1083   }
1084 }
1085
1086 static imageList listOfImages;  // List of images defined by the region file.
1087
1088 /*
1089  *  generateImages - Parse the region file and generate images
1090  *                   from the postscript file.  The region file
1091  *                   contains the x1,y1--x2,y2 extents of each
1092  *                   image.
1093  */
1094
1095 static void generateImages(char *region_file_name)
1096 {
1097   pushBackBuffer *f=new pushBackBuffer(region_file_name);
1098
1099   while (f->putPB(f->getPB()) != eof) {
1100     if (f->isString("grohtml-info:page")) {
1101       int page = f->readInt();
1102       int x1 = f->readInt();
1103       int y1 = f->readInt();
1104       int x2 = f->readInt();
1105       int y2 = f->readInt();
1106       int maxx = f->readInt();
1107       char *name = f->readString();
1108       int res = postscriptRes;
1109       listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1110       while (f->putPB(f->getPB()) != '\n'
1111              && f->putPB(f->getPB()) != eof)
1112         (void)f->getPB();
1113       if (f->putPB(f->getPB()) == '\n')
1114         (void)f->getPB();
1115     }
1116     else {
1117       /* Write any error messages out to the user. */
1118       fputc(f->getPB(), stderr);
1119     }
1120   }
1121
1122   listOfImages.createImages();
1123   if (show_progress) {
1124     fprintf(stderr, "done\n");
1125     fflush(stderr);
1126   }
1127   delete f;
1128 }
1129
1130 /*
1131  *  set_redirection - Set up I/O Redirection for handle, was, to refer to
1132  *                    stream on handle, willbe.
1133  */
1134
1135 static void set_redirection(int was, int willbe)
1136 {
1137   // Nothing to do if 'was' and 'willbe' already have same handle.
1138   if (was != willbe) {
1139     // Otherwise attempt the specified redirection.
1140     if (dup2 (willbe, was) < 0) {
1141       // Redirection failed, so issue diagnostic and bail out.
1142       fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1143       if (willbe == STDOUT_FILENO)
1144         fprintf(stderr,
1145                 "likely that stdout should be opened before %d\n", was);
1146       sys_fatal("dup2");
1147     }
1148
1149     // When redirection has been successfully completed assume redundant
1150     // handle 'willbe' is no longer required, so close it.
1151     if (close(willbe) < 0)
1152       // Issue diagnostic if 'close' fails.
1153       sys_fatal("close");
1154   }
1155 }
1156
1157 /*
1158  *  save_and_redirect - Get duplicate handle for stream, was, then
1159  *                      redirect, was, to refer to, willbe.
1160  */
1161
1162 static int save_and_redirect(int was, int willbe)
1163 {
1164   if (was == willbe)
1165     // No redirection specified so don't do anything but silently bailing out.
1166     return (was);
1167
1168   // Proceeding with redirection so first save and verify our duplicate
1169   // handle for 'was'.
1170   int saved = dup(was);
1171   if (saved < 0) {
1172     fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1173     sys_fatal("dup");
1174   }
1175
1176   // Duplicate handle safely established so complete redirection.
1177   set_redirection(was, willbe);
1178
1179   // Finally return the saved duplicate descriptor for the
1180   // original 'was' stream.
1181   return saved;
1182 }
1183
1184 /*
1185  *  alterDeviceTo - If, toImage, is set
1186  *                     the argument list is altered to include
1187  *                     IMAGE_DEVICE and we invoke groff rather than troff.
1188  *                  Else
1189  *                     set -Thtml and groff.
1190  */
1191
1192 static void alterDeviceTo(int argc, char *argv[], int toImage)
1193 {
1194   int i = 0;
1195
1196   if (toImage) {
1197     while (i < argc) {
1198       if ((strcmp(argv[i], "-Thtml") == 0) ||
1199           (strcmp(argv[i], "-Txhtml") == 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         if (dialect == xhtml)
1209           argv[i] = (char *)"-Txhtml";
1210         else
1211           argv[i] = (char *)"-Thtml";
1212       }
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 }