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