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