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