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