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
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
11Software Foundation; either version 2, or (at your option) any later
12version.
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17for more details.
18
19You should have received a copy of the GNU General Public License along
20with groff; see the file COPYING.  If not, write to the Free Software
21Foundation, 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
141extern "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
176typedef enum {
177  CENTERED, LEFT, RIGHT, INLINE
178} IMAGE_ALIGNMENT;
179
180static int postscriptRes = -1;		// postscript resolution,
181					// dots per inch
182static int stdoutfd = 1;		// output file descriptor -
183					// normally 1 but might move
184					// -1 means closed
185static char *psFileName = NULL;		// name of postscript file
186static char *psPageName = NULL;		// name of file containing
187					// postscript current page
188static char *regionFileName = NULL;	// name of file containing all
189					// image regions
190static char *imagePageName = NULL;	// name of bitmap image containing
191					// current page
192static const char *image_device = "pnmraw";
193static int image_res = DEFAULT_IMAGE_RES;
194static int vertical_offset = 0;
195static char *image_template = NULL;	// image template filename
196static char *macroset_template= NULL;	// image template passed to troff
197					// by -D
198static int troff_arg = 0;		// troff arg index
199static char *image_dir = NULL;		// user specified image directory
200static int textAlphaBits = MAX_ALPHA_BITS;
201static int graphicAlphaBits = MAX_ALPHA_BITS;
202static char *antiAlias = NULL;		// antialias arguments we pass to gs
203static int show_progress = FALSE;	// should we display page numbers as
204					// they are processed?
205static int currentPageNo = -1;		// current image page number
206#if defined(DEBUGGING)
207static int debug = FALSE;
208static char *troffFileName = NULL;	// output of pre-html output which
209					// is sent to troff -Tps
210static char *htmlFileName = NULL;	// output of pre-html output which
211					// is sent to troff -Thtml
212#endif
213
214static char *linebuf = NULL;		// for scanning devps/DESC
215static int linebufsize = 0;
216static const char *image_gen = NULL;    // the `gs' program
217
218const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
219static 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
228static 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
236void 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
246int 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
290static 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
314void 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
346char *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
384struct 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
397char_block::char_block()
398: used(0), next(0)
399{
400  for (int i = 0; i < SIZE; i++)
401    buffer[i] = 0;
402}
403
404class char_buffer {
405public:
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);
416private:
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
426char_buffer::char_buffer()
427: head(0), tail(0)
428{
429}
430
431/*
432 *  char_buffer - Destructor.  Throw away the whole buffer list.
433 */
434
435char_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
449int 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
479static 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
497static 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
507static 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
549static 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
566static 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
580static 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
605static 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
639void 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
681int 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
711int 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
736void 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
766void 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
802struct 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
822imageItem::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
840imageItem::~imageItem()
841{
842  if (imageName)
843    free(imageName);
844}
845
846/*
847 *  imageList - A class containing a list of imageItems.
848 */
849
850class imageList {
851private:
852  imageItem *head;
853  imageItem *tail;
854  int count;
855public:
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
870imageList::imageList()
871: head(0), tail(0), count(0)
872{
873}
874
875/*
876 *  imageList - Destructor.
877 */
878
879imageList::~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
892int 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
964int 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
976int 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
989int 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
1006void 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
1062void 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
1082void imageList::createImages(void)
1083{
1084  imageItem *h = head;
1085
1086  while (h != NULL) {
1087    createImage(h);
1088    h = h->next;
1089  }
1090}
1091
1092static 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
1101static 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
1141static 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
1168static 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
1198static 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
1224char **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
1251char **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
1273void 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
1281int 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
1437int 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
1471int 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
1504static char_buffer inputFile;
1505
1506/*
1507 *  usage - Emit usage arguments.
1508 */
1509
1510static 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
1536static 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
1649static 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
1705int 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
1758static 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