Adapt src/pre-grn (src/preproc/grn)
[s-roff.git] / src / preproc / html / pre-html.cpp
blobb817074ac5afc39965b79a190f406edd78617dc1
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2007, 2008
3 * Free Software Foundation, Inc.
4 * Written by Gaius Mulley (gaius@glam.ac.uk).
6 * This file is part of groff.
8 * groff is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2, or (at your option) any later
11 * version.
13 * groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with groff; see the file COPYING. If not, write to the Free Software
20 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
23 #define PREHTMLC
25 #include "lib.h"
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 "file_case.h"
35 #include "stringclass.h"
36 #include "posix.h"
37 #include "defs.h"
38 #include "searchpath.h"
39 #include "paper.h"
40 #include "device.h"
41 #include "font.h"
43 #include <errno.h>
44 #include <sys/types.h>
45 #ifdef HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif
49 #ifdef _POSIX_VERSION
50 # include <sys/wait.h>
51 # define PID_T pid_t
52 #else /* not _POSIX_VERSION */
53 # define PID_T int
54 #endif /* not _POSIX_VERSION */
56 #include <stdarg.h>
58 #include "nonposix.h"
60 #if 0
61 # define DEBUGGING
62 #endif
64 /* Establish some definitions to facilitate discrimination between
65 differing runtime environments. */
67 #undef MAY_FORK_CHILD_PROCESS
68 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
70 #if defined(__MSDOS__) || defined(_WIN32)
72 // Most MS-DOS and Win32 environments will be missing the `fork' capability
73 // (some like Cygwin have it, but it is best avoided).
75 # define MAY_FORK_CHILD_PROCESS 0
77 // On these systems, we use `spawn...', instead of `fork' ... `exec...'.
78 # include <process.h> // for `spawn...'
79 # include <fcntl.h> // for attributes of pipes
81 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
83 // These Win32 implementations allow parent and `spawn...'ed child to
84 // multitask asynchronously.
86 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
88 # else
90 // Others may adopt MS-DOS behaviour where parent must sleep,
91 // from `spawn...' until child terminates.
93 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
95 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
97 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
98 /* When we are building a DEBUGGING version we need to tell pre-grohtml
99 where to put intermediate files (the DEBUGGING version will preserve
100 these on exit).
102 On a UNIX host, we might simply use `/tmp', but MS-DOS and Win32 will
103 probably not have this on all disk drives, so default to using
104 `c:/temp' instead. (Note that user may choose to override this by
105 supplying a definition such as
107 -DDEBUG_FILE_DIR=d:/path/to/debug/files
109 in the CPPFLAGS to `make'.) */
111 # define DEBUG_FILE_DIR c:/temp
112 # endif
114 #else /* not __MSDOS__ or _WIN32 */
116 // For non-Microsoft environments assume UNIX conventions,
117 // so `fork' is required and child processes are asynchronous.
118 # define MAY_FORK_CHILD_PROCESS 1
119 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
121 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
122 /* For a DEBUGGING version, on the UNIX host, we can also usually rely
123 on being able to use `/tmp' for temporary file storage. (Note that,
124 as in the __MSDOS__ or _WIN32 case above, the user may override this
125 by defining
127 -DDEBUG_FILE_DIR=/path/to/debug/files
129 in the CPPFLAGS.) */
131 # define DEBUG_FILE_DIR /tmp
132 # endif
134 #endif /* not __MSDOS__ or _WIN32 */
136 #ifdef DEBUGGING
137 // For a DEBUGGING version, we need some additional macros,
138 // to direct the captured debug mode output to appropriately named files
139 // in the specified DEBUG_FILE_DIR.
141 # define DEBUG_TEXT(text) #text
142 # define DEBUG_NAME(text) DEBUG_TEXT(text)
143 # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
144 #endif
146 extern "C" const char *Version_string;
148 #include "pre-html.h"
149 #include "pushback.h"
150 #include "html-strings.h"
152 #define DEFAULT_LINE_LENGTH 7 // inches wide
153 #define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution
154 #define IMAGE_BOARDER_PIXELS 0
155 #define INLINE_LEADER_CHAR '\\'
157 // Don't use colour names here! Otherwise there is a dependency on
158 // a file called `rgb.txt' which maps names to colours.
159 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
160 #define MIN_ALPHA_BITS 0
161 #define MAX_ALPHA_BITS 4
163 #define PAGE_TEMPLATE_SHORT "pg"
164 #define PAGE_TEMPLATE_LONG "-page-"
165 #define PS_TEMPLATE_SHORT "ps"
166 #define PS_TEMPLATE_LONG "-ps-"
167 #define REGION_TEMPLATE_SHORT "rg"
168 #define REGION_TEMPLATE_LONG "-regions-"
170 #if !defined(TRUE)
171 # define TRUE (1==1)
172 #endif
173 #if !defined(FALSE)
174 # define FALSE (1==0)
175 #endif
177 typedef enum {
178 CENTERED, LEFT, RIGHT, INLINE
179 } IMAGE_ALIGNMENT;
181 typedef enum {xhtml, html4} html_dialect;
183 static int postscriptRes = -1; // postscript resolution,
184 // dots per inch
185 static int stdoutfd = 1; // output file descriptor -
186 // normally 1 but might move
187 // -1 means closed
188 static char *psFileName = NULL; // name of postscript file
189 static char *psPageName = NULL; // name of file containing
190 // postscript current page
191 static char *regionFileName = NULL; // name of file containing all
192 // image regions
193 static char *imagePageName = NULL; // name of bitmap image containing
194 // current page
195 static const char *image_device = "pnmraw";
196 static int image_res = DEFAULT_IMAGE_RES;
197 static int vertical_offset = 0;
198 static char *image_template = NULL; // image template filename
199 static char *macroset_template= NULL; // image template passed to troff
200 // by -D
201 static int troff_arg = 0; // troff arg index
202 static char *image_dir = NULL; // user specified image directory
203 static int textAlphaBits = MAX_ALPHA_BITS;
204 static int graphicAlphaBits = MAX_ALPHA_BITS;
205 static char *antiAlias = NULL; // antialias arguments we pass to gs
206 static int show_progress = FALSE; // should we display page numbers as
207 // they are processed?
208 static int currentPageNo = -1; // current image page number
209 #if defined(DEBUGGING)
210 static int debug = FALSE;
211 static char *troffFileName = NULL; // output of pre-html output which
212 // is sent to troff -Tps
213 static char *htmlFileName = NULL; // output of pre-html output which
214 // is sent to troff -Thtml
215 #endif
216 static int eqn_flag = FALSE; // must we preprocess via eqn?
218 static char *linebuf = NULL; // for scanning devps/DESC
219 static int linebufsize = 0;
220 static const char *image_gen = NULL; // the `gs' program
222 const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
223 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
224 static html_dialect dialect = html4;
228 * Images are generated via postscript, gs, and the pnm utilities.
230 #define IMAGE_DEVICE "-Tps"
233 static int do_file(const char *filename);
237 * sys_fatal - Write a fatal error message.
238 * Taken from src/roff/groff/pipeline.c.
241 void sys_fatal(const char *s)
243 fatal("%1: %2", s, strerror(errno));
247 * get_line - Copy a line (w/o newline) from a file to the
248 * global line buffer.
251 int get_line(file_case *fcp)
253 if (fcp == NULL)
254 return 0;
255 if (linebuf == 0) {
256 linebuf = new char[128];
257 linebufsize = 128;
259 int i = 0;
260 // skip leading whitespace
261 for (;;) {
262 int c = fcp->get_c();
263 if (c == EOF)
264 return 0;
265 if (c != ' ' && c != '\t') {
266 fcp->unget_c(c);
267 break;
270 for (;;) {
271 int c = fcp->get_c();
272 if (c == EOF)
273 break;
274 if (i + 1 >= linebufsize) {
275 char *old_linebuf = linebuf;
276 linebuf = new char[linebufsize * 2];
277 memcpy(linebuf, old_linebuf, linebufsize);
278 a_delete old_linebuf;
279 linebufsize *= 2;
281 linebuf[i++] = c;
282 if (c == '\n') {
283 i--;
284 break;
287 linebuf[i] = '\0';
288 return 1;
292 * get_resolution - Return the postscript resolution from devps/DESC.
295 static unsigned int get_resolution(void)
297 unsigned int res;
298 file_case *fcp;
299 if ((fcp = font_path.open_file("devps/DESC", fcp->fc_const_path)) == NULL)
300 fatal("can't open devps/DESC");
301 while (get_line(fcp)) {
302 int n = sscanf(linebuf, "res %u", &res);
303 if (n >= 1)
304 goto jleave;
306 fatal("can't find `res' keyword in devps/DESC");
307 jleave:
308 delete fcp;
309 return res;
313 * html_system - A wrapper for system().
316 void html_system(const char *s, int redirect_stdout)
318 #if defined(DEBUGGING)
319 if (debug) {
320 fprintf(stderr, "executing: ");
321 fwrite(s, sizeof(char), strlen(s), stderr);
322 fflush(stderr);
324 #endif
326 // Redirect standard error to the null device. This is more
327 // portable than using "2> /dev/null", since it doesn't require a
328 // Unixy shell.
329 int save_stderr = dup(2);
330 int save_stdout = dup(1);
331 int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
332 if (save_stderr > 2 && fdnull > 2)
333 dup2(fdnull, 2);
334 if (redirect_stdout && save_stdout > 1 && fdnull > 1)
335 dup2(fdnull, 1);
336 if (fdnull >= 0)
337 close(fdnull);
338 int status = system(s);
339 dup2(save_stderr, 2);
340 if (redirect_stdout)
341 dup2(save_stdout, 1);
342 if (status == -1)
343 fprintf(stderr, "Calling `%s' failed\n", s);
344 else if (status)
345 fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
346 close(save_stderr);
347 close(save_stdout);
352 * make_message - Create a string via malloc and place the result of the
353 * va args into string. Finally the new string is returned.
354 * Taken from man page of printf(3).
357 char *make_message(const char *fmt, ...)
359 /* Guess we need no more than 100 bytes. */
360 int n, size = 100;
361 char *p;
362 char *np;
363 va_list ap;
364 if ((p = (char *)malloc(size)) == NULL)
365 return NULL;
366 while (1) {
367 /* Try to print in the allocated space. */
368 va_start(ap, fmt);
369 n = vsnprintf(p, size, fmt, ap);
370 va_end(ap);
371 /* If that worked, return the string. */
372 if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
373 if (size > n + 1) {
374 np = strsave(p);
375 free(p);
376 return np;
378 return p;
380 /* Else try again with more space. */
381 else /* glibc 2.0 */
382 size *= 2; /* twice the old size */
383 if ((np = (char *)realloc(p, size)) == NULL) {
384 free(p); /* realloc failed, free old, p. */
385 return NULL;
387 p = np; /* use realloc'ed, p */
392 * the class and methods for retaining ascii text
395 struct char_block {
396 enum { SIZE = 256 };
397 char buffer[SIZE];
398 int used;
399 char_block *next;
401 char_block();
405 * char_block - Constructor. Set the, used, and, next, fields to zero.
408 char_block::char_block()
409 : used(0), next(0)
411 for (int i = 0; i < SIZE; i++)
412 buffer[i] = 0;
415 class char_buffer {
416 public:
417 char_buffer();
418 ~char_buffer();
419 int read_file(file_case *fcp);
420 int do_html(int argc, char *argv[]);
421 int do_image(int argc, char *argv[]);
422 void emit_troff_output(int device_format_selector);
423 void write_upto_newline(char_block **t, int *i, int is_html);
424 int can_see(char_block **t, int *i, const char *string);
425 int skip_spaces(char_block **t, int *i);
426 void skip_until_newline(char_block **t, int *i);
427 private:
428 char_block *head;
429 char_block *tail;
430 int run_output_filter(int device_format_selector, int argc, char *argv[]);
434 * char_buffer - Constructor.
437 char_buffer::char_buffer()
438 : head(0), tail(0)
443 * char_buffer - Destructor. Throw away the whole buffer list.
446 char_buffer::~char_buffer()
448 while (head != NULL) {
449 char_block *temp = head;
450 head = head->next;
451 delete temp;
456 * read_file - Read in a complete file, fp, placing the contents inside
457 * char_blocks.
460 int char_buffer::read_file(file_case *fcp)
462 int n;
463 while (!fcp->is_eof()) {
464 if (tail == NULL) {
465 tail = new char_block;
466 head = tail;
468 else {
469 if (tail->used == char_block::SIZE) {
470 tail->next = new char_block;
471 tail = tail->next;
474 // at this point we have a tail which is ready for the next SIZE
475 // bytes of the file
476 n = fcp->get_buf(tail->buffer, char_block::SIZE - tail->used);
477 if (n != 0)
478 tail->used += n * sizeof(char);
479 else {
480 n = fcp->is_eof();
481 goto jleave;
484 n = 1;
485 jleave:
486 return n;
490 * writeNbytes - Write n bytes to stdout.
493 static void writeNbytes(const char *s, int l)
495 int n = 0;
496 int r;
498 while (n < l) {
499 r = write(stdoutfd, s, l - n);
500 if (r < 0)
501 sys_fatal("write");
502 n += r;
503 s += r;
508 * writeString - Write a string to stdout.
511 static void writeString(const char *s)
513 writeNbytes(s, strlen(s));
517 * makeFileName - Create the image filename template
518 * and the macroset image template.
521 static void makeFileName(void)
523 if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
524 error("cannot use a `%%' within the image directory name");
525 exit(1);
528 if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
529 error("cannot use a `%%' within the image template");
530 exit(1);
533 if (image_dir == NULL)
534 image_dir = (char *)"";
535 else if (strlen(image_dir) > 0
536 && image_dir[strlen(image_dir) - 1] != '/') {
537 image_dir = make_message("%s/", image_dir);
538 if (image_dir == NULL)
539 sys_fatal("make_message");
542 if (image_template == NULL)
543 macroset_template = make_message("%sgrohtml-%d", image_dir,
544 (int)getpid());
545 else
546 macroset_template = make_message("%s%s", image_dir, image_template);
548 if (macroset_template == NULL)
549 sys_fatal("make_message");
551 image_template =
552 (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
553 if (image_template == NULL)
554 sys_fatal("malloc");
555 strcpy(image_template, macroset_template);
556 strcat(image_template, "-%d");
560 * setupAntiAlias - Set up the antialias string, used when we call gs.
563 static void setupAntiAlias(void)
565 if (textAlphaBits == 0 && graphicAlphaBits == 0)
566 antiAlias = make_message(" ");
567 else if (textAlphaBits == 0)
568 antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
569 else if (graphicAlphaBits == 0)
570 antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
571 else
572 antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
573 textAlphaBits, graphicAlphaBits);
577 * checkImageDir - Check whether the image directory is available.
580 static void checkImageDir(void)
582 if (image_dir != NULL && strcmp(image_dir, "") != 0)
583 if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
584 error("cannot create directory `%1'", image_dir);
585 exit(1);
590 * write_end_image - End the image. Write out the image extents if we
591 * are using -Tps.
594 static void write_end_image(int is_html)
597 * if we are producing html then these
598 * emit image name and enable output
599 * else
600 * we are producing images
601 * in which case these generate image
602 * boundaries
604 writeString("\\O[4]\\O[2]");
605 if (is_html)
606 writeString("\\O[1]");
607 else
608 writeString("\\O[0]");
612 * write_start_image - Write troff code which will:
614 * (i) disable html output for the following image
615 * (ii) reset the max/min x/y registers during postscript
616 * rendering.
619 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
621 writeString("\\O[5");
622 switch (pos) {
623 case INLINE:
624 writeString("i");
625 break;
626 case LEFT:
627 writeString("l");
628 break;
629 case RIGHT:
630 writeString("r");
631 break;
632 case CENTERED:
633 default:
634 writeString("c");
635 break;
637 writeString(image_template);
638 writeString(".png]");
639 if (is_html)
640 writeString("\\O[0]\\O[3]");
641 else
642 // reset min/max registers
643 writeString("\\O[1]\\O[3]");
647 * write_upto_newline - Write the contents of the buffer until a newline
648 * is seen. Check for HTML_IMAGE_INLINE_BEGIN and
649 * HTML_IMAGE_INLINE_END; process them if they are
650 * present.
653 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
655 int j = *i;
657 if (*t) {
658 while (j < (*t)->used
659 && (*t)->buffer[j] != '\n'
660 && (*t)->buffer[j] != INLINE_LEADER_CHAR)
661 j++;
662 if (j < (*t)->used
663 && (*t)->buffer[j] == '\n')
664 j++;
665 writeNbytes((*t)->buffer + (*i), j - (*i));
666 if ((*t)->buffer[j] == INLINE_LEADER_CHAR) {
667 if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
668 write_start_image(INLINE, is_html);
669 else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
670 write_end_image(is_html);
671 else {
672 if (j < (*t)->used) {
673 *i = j;
674 j++;
675 writeNbytes((*t)->buffer + (*i), j - (*i));
679 if (j == (*t)->used) {
680 *i = 0;
681 *t = (*t)->next;
682 if (*t && (*t)->buffer[j - 1] != '\n')
683 write_upto_newline(t, i, is_html);
685 else
686 // newline was seen
687 *i = j;
692 * can_see - Return TRUE if we can see string in t->buffer[i] onwards.
695 int char_buffer::can_see(char_block **t, int *i, const char *str)
697 int j = 0;
698 int l = strlen(str);
699 int k = *i;
700 char_block *s = *t;
702 while (s) {
703 while (k < s->used && j < l && s->buffer[k] == str[j]) {
704 j++;
705 k++;
707 if (j == l) {
708 *i = k;
709 *t = s;
710 return TRUE;
712 else if (k < s->used && s->buffer[k] != str[j])
713 return( FALSE );
714 s = s->next;
715 k = 0;
717 return FALSE;
721 * skip_spaces - Return TRUE if we have not run out of data.
722 * Consume spaces also.
725 int char_buffer::skip_spaces(char_block **t, int *i)
727 char_block *s = *t;
728 int k = *i;
730 while (s) {
731 while (k < s->used && isspace(s->buffer[k]))
732 k++;
733 if (k == s->used) {
734 k = 0;
735 s = s->next;
737 else {
738 *i = k;
739 return TRUE;
742 return FALSE;
746 * skip_until_newline - Skip all characters until a newline is seen.
747 * The newline is not consumed.
750 void char_buffer::skip_until_newline(char_block **t, int *i)
752 int j = *i;
754 if (*t) {
755 while (j < (*t)->used && (*t)->buffer[j] != '\n')
756 j++;
757 if (j == (*t)->used) {
758 *i = 0;
759 *t = (*t)->next;
760 skip_until_newline(t, i);
762 else
763 // newline was seen
764 *i = j;
768 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
769 #define HTML_OUTPUT_FILTER 0
770 #define IMAGE_OUTPUT_FILTER 1
771 #define OUTPUT_STREAM(name) creat((name), S_IWUSR | S_IRUSR)
772 #define PS_OUTPUT_STREAM OUTPUT_STREAM(psFileName)
773 #define REGION_OUTPUT_STREAM OUTPUT_STREAM(regionFileName)
776 * emit_troff_output - Write formatted buffer content to the troff
777 * post-processor data pipeline.
780 void char_buffer::emit_troff_output(int device_format_selector)
782 // Handle output for BOTH html and image device formats
783 // if `device_format_selector' is passed as
785 // HTML_FORMAT(HTML_OUTPUT_FILTER)
786 // Buffer data is written to the output stream
787 // with template image names translated to actual image names.
789 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
790 // Buffer data is written to the output stream
791 // with no translation, for image file creation in the post-processor.
793 int idx = 0;
794 char_block *element = head;
796 while (element != NULL)
797 write_upto_newline(&element, &idx, device_format_selector);
799 #if 0
800 if (close(stdoutfd) < 0)
801 sys_fatal ("close");
803 // now we grab fd=1 so that the next pipe cannot use fd=1
804 if (stdoutfd == 1) {
805 if (dup(2) != stdoutfd)
806 sys_fatal ("dup failed to use fd=1");
808 #endif /* 0 */
812 * The image class remembers the position of all images in the
813 * postscript file and assigns names for each image.
816 struct imageItem {
817 imageItem *next;
818 int X1;
819 int Y1;
820 int X2;
821 int Y2;
822 char *imageName;
823 int resolution;
824 int maxx;
825 int pageNo;
827 imageItem(int x1, int y1, int x2, int y2,
828 int page, int res, int max_width, char *name);
829 ~imageItem();
833 * imageItem - Constructor.
836 imageItem::imageItem(int x1, int y1, int x2, int y2,
837 int page, int res, int max_width, char *name)
839 X1 = x1;
840 Y1 = y1;
841 X2 = x2;
842 Y2 = y2;
843 pageNo = page;
844 resolution = res;
845 maxx = max_width;
846 imageName = name;
847 next = NULL;
851 * imageItem - Destructor.
854 imageItem::~imageItem()
856 if (imageName)
857 free(imageName);
861 * imageList - A class containing a list of imageItems.
864 class imageList {
865 private:
866 imageItem *head;
867 imageItem *tail;
868 int count;
869 public:
870 imageList();
871 ~imageList();
872 void add(int x1, int y1, int x2, int y2,
873 int page, int res, int maxx, char *name);
874 void createImages(void);
875 int createPage(int pageno);
876 void createImage(imageItem *i);
877 int getMaxX(int pageno);
881 * imageList - Constructor.
884 imageList::imageList()
885 : head(0), tail(0), count(0)
890 * imageList - Destructor.
893 imageList::~imageList()
895 while (head != NULL) {
896 imageItem *i = head;
897 head = head->next;
898 delete i;
903 * createPage - Create one image of, page pageno, from the postscript file.
906 int imageList::createPage(int pageno)
908 char *s;
910 if (currentPageNo == pageno)
911 return 0;
913 if (currentPageNo >= 1) {
915 * We need to unlink the files which change each time a new page is
916 * processed. The final unlink is done by xtmpfile when pre-grohtml
917 * exits.
919 unlink(imagePageName);
920 unlink(psPageName);
923 if (show_progress) {
924 fprintf(stderr, "[%d] ", pageno);
925 fflush(stderr);
928 #if defined(DEBUGGING)
929 if (debug)
930 fprintf(stderr, "creating page %d\n", pageno);
931 #endif
933 s = make_message("psselect -q -p%d %s %s\n",
934 pageno, psFileName, psPageName);
936 if (s == NULL)
937 sys_fatal("make_message");
938 html_system(s, 1);
940 s = make_message("echo showpage | "
941 "%s%s -q -dBATCH -dSAFER "
942 "-dDEVICEHEIGHTPOINTS=792 "
943 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
944 "-sDEVICE=%s -r%d %s "
945 "-sOutputFile=%s %s -\n",
946 image_gen,
947 EXE_EXT,
948 (getMaxX(pageno) * image_res) / postscriptRes,
949 image_device,
950 image_res,
951 antiAlias,
952 imagePageName,
953 psPageName);
954 if (s == NULL)
955 sys_fatal("make_message");
956 html_system(s, 1);
957 free(s);
958 currentPageNo = pageno;
959 return 0;
963 * min - Return the minimum of two numbers.
966 int min(int x, int y)
968 if (x < y)
969 return x;
970 else
971 return y;
975 * max - Return the maximum of two numbers.
978 int max(int x, int y)
980 if (x > y)
981 return x;
982 else
983 return y;
987 * getMaxX - Return the largest right-hand position for any image
988 * on, pageno.
991 int imageList::getMaxX(int pageno)
993 imageItem *h = head;
994 int x = postscriptRes * DEFAULT_LINE_LENGTH;
996 while (h != NULL) {
997 if (h->pageNo == pageno)
998 x = max(h->X2, x);
999 h = h->next;
1001 return x;
1005 * createImage - Generate a minimal png file from the set of page images.
1008 void imageList::createImage(imageItem *i)
1010 if (i->X1 != -1) {
1011 char *s;
1012 int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1013 - IMAGE_BOARDER_PIXELS,
1015 int y1 = max(image_res * vertical_offset / 72
1016 + min(i->Y1, i->Y2) * image_res / postscriptRes
1017 - IMAGE_BOARDER_PIXELS,
1019 int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1020 + IMAGE_BOARDER_PIXELS;
1021 int y2 = image_res * vertical_offset / 72
1022 + max(i->Y1, i->Y2) * image_res / postscriptRes
1023 + 1 + IMAGE_BOARDER_PIXELS;
1024 if (createPage(i->pageNo) == 0) {
1025 s = make_message("pnmcut%s %d %d %d %d < %s "
1026 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1027 EXE_EXT,
1028 x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1029 imagePageName,
1030 EXE_EXT,
1031 TRANSPARENT,
1032 i->imageName);
1033 if (s == NULL)
1034 sys_fatal("make_message");
1036 html_system(s, 0);
1037 free(s);
1039 else {
1040 fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1041 fflush(stderr);
1043 #if defined(DEBUGGING)
1045 else {
1046 if (debug) {
1047 fprintf(stderr, "ignoring image as x1 coord is -1\n");
1048 fflush(stderr);
1050 #endif
1055 * add - Add an image description to the imageList.
1058 void imageList::add(int x1, int y1, int x2, int y2,
1059 int page, int res, int maxx, char *name)
1061 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1063 if (head == NULL) {
1064 head = i;
1065 tail = i;
1067 else {
1068 tail->next = i;
1069 tail = i;
1074 * createImages - For each image descriptor on the imageList,
1075 * create the actual image.
1078 void imageList::createImages(void)
1080 imageItem *h = head;
1082 while (h != NULL) {
1083 createImage(h);
1084 h = h->next;
1088 static imageList listOfImages; // List of images defined by the region file.
1091 * generateImages - Parse the region file and generate images
1092 * from the postscript file. The region file
1093 * contains the x1,y1--x2,y2 extents of each
1094 * image.
1097 static void generateImages(char *region_file_name)
1099 pushBackBuffer *f=new pushBackBuffer(region_file_name);
1101 while (f->putPB(f->getPB()) != eof) {
1102 if (f->isString("grohtml-info:page")) {
1103 int page = f->readInt();
1104 int x1 = f->readInt();
1105 int y1 = f->readInt();
1106 int x2 = f->readInt();
1107 int y2 = f->readInt();
1108 int maxx = f->readInt();
1109 char *name = f->readString();
1110 int res = postscriptRes;
1111 listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1112 while (f->putPB(f->getPB()) != '\n'
1113 && f->putPB(f->getPB()) != eof)
1114 (void)f->getPB();
1115 if (f->putPB(f->getPB()) == '\n')
1116 (void)f->getPB();
1118 else {
1119 /* Write any error messages out to the user. */
1120 fputc(f->getPB(), stderr);
1124 listOfImages.createImages();
1125 if (show_progress) {
1126 fprintf(stderr, "done\n");
1127 fflush(stderr);
1129 delete f;
1133 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1134 * stream on handle, willbe.
1137 static void set_redirection(int was, int willbe)
1139 // Nothing to do if `was' and `willbe' already have same handle.
1140 if (was != willbe) {
1141 // Otherwise attempt the specified redirection.
1142 if (dup2 (willbe, was) < 0) {
1143 // Redirection failed, so issue diagnostic and bail out.
1144 fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1145 if (willbe == STDOUT_FILENO)
1146 fprintf(stderr,
1147 "likely that stdout should be opened before %d\n", was);
1148 sys_fatal("dup2");
1151 // When redirection has been successfully completed assume redundant
1152 // handle `willbe' is no longer required, so close it.
1153 if (close(willbe) < 0)
1154 // Issue diagnostic if `close' fails.
1155 sys_fatal("close");
1160 * save_and_redirect - Get duplicate handle for stream, was, then
1161 * redirect, was, to refer to, willbe.
1164 static int save_and_redirect(int was, int willbe)
1166 if (was == willbe)
1167 // No redirection specified so don't do anything but silently bailing out.
1168 return (was);
1170 // Proceeding with redirection so first save and verify our duplicate
1171 // handle for `was'.
1172 int saved = dup(was);
1173 if (saved < 0) {
1174 fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1175 sys_fatal("dup");
1178 // Duplicate handle safely established so complete redirection.
1179 set_redirection(was, willbe);
1181 // Finally return the saved duplicate descriptor for the
1182 // original `was' stream.
1183 return saved;
1187 * alterDeviceTo - If, toImage, is set
1188 * the argument list is altered to include
1189 * IMAGE_DEVICE and we invoke groff rather than troff.
1190 * Else
1191 * set -Thtml and groff.
1194 static void alterDeviceTo(int argc, char *argv[], int toImage)
1196 int i = 0;
1198 if (toImage) {
1199 while (i < argc) {
1200 if ((strcmp(argv[i], "-Thtml") == 0) ||
1201 (strcmp(argv[i], "-Txhtml") == 0))
1202 argv[i] = (char *)IMAGE_DEVICE;
1203 i++;
1205 argv[troff_arg] = (char *)"groff"; /* rather than troff */
1207 else {
1208 while (i < argc) {
1209 if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1210 if (dialect == xhtml)
1211 argv[i] = (char *)"-Txhtml";
1212 else
1213 argv[i] = (char *)"-Thtml";
1214 i++;
1216 argv[troff_arg] = (char *)"groff"; /* use groff -Z */
1221 * addArg - Append newarg onto the command list for groff.
1224 char **addArg(int argc, char *argv[], char *newarg)
1226 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1227 int i = 0;
1229 if (new_argv == NULL)
1230 sys_fatal("malloc");
1232 if (argc > 0) {
1233 new_argv[i] = argv[i];
1234 i++;
1236 new_argv[i] = newarg;
1237 while (i < argc) {
1238 new_argv[i + 1] = argv[i];
1239 i++;
1241 argc++;
1242 new_argv[argc] = NULL;
1243 return new_argv;
1247 * addRegDef - Append a defined register or string onto the command
1248 * list for troff.
1251 char **addRegDef(int argc, char *argv[], const char *numReg)
1253 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1254 int i = 0;
1256 if (new_argv == NULL)
1257 sys_fatal("malloc");
1259 while (i < argc) {
1260 new_argv[i] = argv[i];
1261 i++;
1263 new_argv[argc] = strsave(numReg);
1264 argc++;
1265 new_argv[argc] = NULL;
1266 return new_argv;
1270 * dump_args - Display the argument list.
1273 void dump_args(int argc, char *argv[])
1275 fprintf(stderr, " %d arguments:", argc);
1276 for (int i = 0; i < argc; i++)
1277 fprintf(stderr, " %s", argv[i]);
1278 fprintf(stderr, "\n");
1282 * print_args - print arguments as if they were issued on the command line.
1285 #if defined(DEBUGGING)
1287 void print_args(int argc, char *argv[])
1289 if (debug) {
1290 fprintf(stderr, "executing: ");
1291 for (int i = 0; i < argc; i++)
1292 fprintf(stderr, "%s ", argv[i]);
1293 fprintf(stderr, "\n");
1297 #else
1299 void print_args(int, char **)
1303 #endif
1305 int char_buffer::run_output_filter(int filter, int argc, char **argv)
1307 int pipedes[2];
1308 PID_T child_pid;
1309 int status;
1311 print_args(argc, argv);
1312 if (pipe(pipedes) < 0)
1313 sys_fatal("pipe");
1315 #if MAY_FORK_CHILD_PROCESS
1316 // This is the UNIX process model. To invoke our post-processor,
1317 // we must `fork' the current process.
1319 if ((child_pid = fork()) < 0)
1320 sys_fatal("fork");
1322 else if (child_pid == 0) {
1323 // This is the child process fork. We redirect its `stdin' stream
1324 // to read data emerging from our pipe. There is no point in saving,
1325 // since we won't be able to restore later!
1327 set_redirection(STDIN_FILENO, pipedes[0]);
1329 // The parent process will be writing this data, so we should release
1330 // the child's writeable handle on the pipe, since we have no use for it.
1332 if (close(pipedes[1]) < 0)
1333 sys_fatal("close");
1335 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1337 if (filter == IMAGE_OUTPUT_FILTER) {
1338 // with BOTH `stdout' AND `stderr' diverted to files.
1340 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1341 set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1344 // Now we are ready to launch the output filter.
1346 execvp(argv[0], argv);
1348 // If we get to here then the `exec...' request for the output filter
1349 // failed. Diagnose it and bail out.
1351 error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1352 fflush(stderr); // just in case error() didn't
1353 exit(1);
1356 else {
1357 // This is the parent process fork. We will be writing data to the
1358 // filter pipeline, and the child will be reading it. We have no further
1359 // use for our read handle on the pipe, and should close it.
1361 if (close(pipedes[0]) < 0)
1362 sys_fatal("close");
1364 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1365 // and push out the appropiately formatted data to the filter.
1367 pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1368 emit_troff_output(DEVICE_FORMAT(filter));
1370 // After emitting all the data we close our connection to the inlet
1371 // end of the pipe so the child process will detect end of data.
1373 set_redirection(STDOUT_FILENO, pipedes[1]);
1375 // Finally, we must wait for the child process to complete.
1377 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1378 sys_fatal("wait");
1381 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1383 // We do not have `fork', (or we prefer not to use it),
1384 // but asynchronous processes are allowed, passing data through pipes.
1385 // This should be ok for most Win32 systems and is preferred to `fork'
1386 // for starting child processes under Cygwin.
1388 // Before we start the post-processor we bind its inherited `stdin'
1389 // stream to the readable end of our pipe, saving our own `stdin' stream
1390 // in `pipedes[0]'.
1392 pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1394 // for the Win32 model,
1395 // we need special provision for saving BOTH `stdout' and `stderr'.
1397 int saved_stdout = dup(STDOUT_FILENO);
1398 int saved_stderr = STDERR_FILENO;
1400 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1402 if (filter == IMAGE_OUTPUT_FILTER) {
1403 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1404 // duplicate handle for `stderr'.
1406 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1407 saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1410 // We then use an asynchronous spawn request to start the post-processor.
1412 if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1413 // Should the spawn request fail we issue a diagnostic and bail out.
1415 error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1416 exit(1);
1419 // Once the post-processor has been started we revert our `stdin'
1420 // to its original saved source, which also closes the readable handle
1421 // for the pipe.
1423 set_redirection(STDIN_FILENO, pipedes[0]);
1425 // if we redirected `stderr', for use by the image post-processor,
1426 // then we also need to reinstate its original assignment.
1428 if (filter == IMAGE_OUTPUT_FILTER)
1429 set_redirection(STDERR_FILENO, saved_stderr);
1431 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1432 // and push out the appropiately formatted data to the filter.
1434 set_redirection(STDOUT_FILENO, pipedes[1]);
1435 emit_troff_output(DEVICE_FORMAT(filter));
1437 // After emitting all the data we close our connection to the inlet
1438 // end of the pipe so the child process will detect end of data.
1440 set_redirection(STDOUT_FILENO, saved_stdout);
1442 // And finally, we must wait for the child process to complete.
1444 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1445 sys_fatal("wait");
1447 #else /* can't do asynchronous pipes! */
1449 // TODO: code to support an MS-DOS style process model
1450 // should go here
1452 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1454 return 0;
1458 * do_html - Set the troff number htmlflip and
1459 * write out the buffer to troff -Thtml.
1462 int char_buffer::do_html(int argc, char *argv[])
1464 string s;
1466 alterDeviceTo(argc, argv, 0);
1467 argv += troff_arg; // skip all arguments up to groff
1468 argc -= troff_arg;
1469 argv = addArg(argc, argv, (char *)"-Z");
1470 argc++;
1472 s = (char *)"-dwww-image-template=";
1473 s += macroset_template; // do not combine these statements,
1474 // otherwise they will not work
1475 s += '\0'; // the trailing `\0' is ignored
1476 argv = addRegDef(argc, argv, s.contents());
1477 argc++;
1479 if (dialect == xhtml) {
1480 argv = addRegDef(argc, argv, "-rxhtml=1");
1481 argc++;
1482 if (eqn_flag) {
1483 argv = addRegDef(argc, argv, "-e");
1484 argc++;
1488 #if defined(DEBUGGING)
1489 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1490 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1491 if (debug) {
1492 int saved_stdout = save_and_redirect(STDOUT_FILENO, HTML_DEBUG_STREAM);
1493 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
1494 set_redirection(STDOUT_FILENO, saved_stdout);
1496 #endif
1498 return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1502 * do_image - Write out the buffer to troff -Tps.
1505 int char_buffer::do_image(int argc, char *argv[])
1507 string s;
1509 alterDeviceTo(argc, argv, 1);
1510 argv += troff_arg; // skip all arguments up to troff/groff
1511 argc -= troff_arg;
1512 argv = addRegDef(argc, argv, "-rps4html=1");
1513 argc++;
1515 s = "-dwww-image-template=";
1516 s += macroset_template;
1517 s += '\0';
1518 argv = addRegDef(argc, argv, s.contents());
1519 argc++;
1521 // override local settings and produce a page size letter postscript file
1522 argv = addRegDef(argc, argv, "-P-pletter");
1523 argc++;
1525 if (dialect == xhtml) {
1526 if (eqn_flag) {
1527 argv = addRegDef(argc, argv, "-rxhtml=1");
1528 argc++;
1530 argv = addRegDef(argc, argv, "-e");
1531 argc++;
1534 #if defined(DEBUGGING)
1535 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1536 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1537 if (debug) {
1538 int saved_stdout = save_and_redirect(STDOUT_FILENO, IMAGE_DEBUG_STREAM);
1539 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
1540 set_redirection(STDOUT_FILENO, saved_stdout);
1542 #endif
1544 return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1547 static char_buffer inputFile;
1550 * usage - Emit usage arguments.
1553 static void usage(FILE *stream)
1555 fprintf(stream,
1556 "\n"
1557 "This program is not intended to be called stand-alone;\n"
1558 "it is part of the groff pipeline to produce HTML output.\n"
1559 "\n"
1560 "If there is ever the need to call it manually (e.g., for\n"
1561 "debugging purposes), add command line option `-V' while calling\n"
1562 "the `groff' program to see which arguments are passed to it.\n"
1563 "\n");
1567 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1568 * and -P-I. Return the argument index of the first
1569 * non-option.
1572 static int scanArguments(int argc, char **argv)
1574 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
1575 if (!command_prefix)
1576 command_prefix = PROG_PREFIX;
1577 char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1];
1578 strcpy(troff_name, command_prefix);
1579 strcat(troff_name, "troff");
1580 int c, i;
1581 static const struct option long_options[] = {
1582 { "help", no_argument, 0, CHAR_MAX + 1 },
1583 { "version", no_argument, 0, 'v' },
1584 { NULL, 0, 0, 0 }
1586 while ((c = getopt_long(argc, argv, "+a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
1587 long_options, NULL))
1588 != EOF)
1589 switch(c) {
1590 case 'a':
1591 textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1592 MAX_ALPHA_BITS);
1593 if (textAlphaBits == 3) {
1594 error("cannot use 3 bits of antialiasing information");
1595 exit(1);
1597 break;
1598 case 'b':
1599 // handled by post-grohtml (set background color to white)
1600 break;
1601 case 'd':
1602 #if defined(DEBUGGING)
1603 debug = TRUE;
1604 #endif
1605 break;
1606 case 'D':
1607 image_dir = optarg;
1608 break;
1609 case 'e':
1610 eqn_flag = TRUE;
1611 break;
1612 case 'F':
1613 font_path.command_line_dir(optarg);
1614 break;
1615 case 'g':
1616 graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1617 MAX_ALPHA_BITS);
1618 if (graphicAlphaBits == 3) {
1619 error("cannot use 3 bits of antialiasing information");
1620 exit(1);
1622 break;
1623 case 'h':
1624 // handled by post-grohtml
1625 break;
1626 case 'i':
1627 image_res = atoi(optarg);
1628 break;
1629 case 'I':
1630 image_template = optarg;
1631 break;
1632 case 'j':
1633 // handled by post-grohtml (set job name for multiple file output)
1634 break;
1635 case 'l':
1636 // handled by post-grohtml (no automatic section links)
1637 break;
1638 case 'n':
1639 // handled by post-grohtml (generate simple heading anchors)
1640 break;
1641 case 'o':
1642 vertical_offset = atoi(optarg);
1643 break;
1644 case 'p':
1645 show_progress = TRUE;
1646 break;
1647 case 'r':
1648 // handled by post-grohtml (no header and footer lines)
1649 break;
1650 case 's':
1651 // handled by post-grohtml (use font size n as the html base font size)
1652 break;
1653 case 'S':
1654 // handled by post-grohtml (set file split level)
1655 break;
1656 case 'v':
1657 printf("GNU pre-grohtml (groff) version %s\n", Version_string);
1658 exit(0);
1659 case 'V':
1660 // handled by post-grohtml (create validator button)
1661 break;
1662 case 'x':
1663 // html dialect
1664 if (strcmp(optarg, "x") == 0)
1665 dialect = xhtml;
1666 else if (strcmp(optarg, "4") == 0)
1667 dialect = html4;
1668 else
1669 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
1670 break;
1671 case 'y':
1672 // handled by post-grohtml (create groff signature)
1673 break;
1674 case CHAR_MAX + 1: // --help
1675 usage(stdout);
1676 exit(0);
1677 break;
1678 case '?':
1679 usage(stderr);
1680 exit(1);
1681 break;
1682 default:
1683 break;
1686 i = optind;
1687 while (i < argc) {
1688 if (strcmp(argv[i], troff_name) == 0)
1689 troff_arg = i;
1690 else if (argv[i][0] != '-')
1691 return i;
1692 i++;
1694 a_delete troff_name;
1696 return argc;
1700 * makeTempFiles - Name the temporary files.
1703 static int makeTempFiles(void)
1705 #if defined(DEBUGGING)
1706 psFileName = DEBUG_FILE("prehtml-ps");
1707 regionFileName = DEBUG_FILE("prehtml-region");
1708 imagePageName = DEBUG_FILE("prehtml-page");
1709 psPageName = DEBUG_FILE("prehtml-psn");
1710 troffFileName = DEBUG_FILE("prehtml-troff");
1711 htmlFileName = DEBUG_FILE("prehtml-html");
1712 #else /* not DEBUGGING */
1713 FILE *f;
1715 /* psPageName contains a single page of postscript */
1716 f = xtmpfile(&psPageName,
1717 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1718 TRUE);
1719 if (f == NULL) {
1720 sys_fatal("xtmpfile");
1721 return -1;
1723 fclose(f);
1725 /* imagePageName contains a bitmap image of the single postscript page */
1726 f = xtmpfile(&imagePageName,
1727 PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1728 TRUE);
1729 if (f == NULL) {
1730 sys_fatal("xtmpfile");
1731 return -1;
1733 fclose(f);
1735 /* psFileName contains a postscript file of the complete document */
1736 f = xtmpfile(&psFileName,
1737 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1738 TRUE);
1739 if (f == NULL) {
1740 sys_fatal("xtmpfile");
1741 return -1;
1743 fclose(f);
1745 /* regionFileName contains a list of the images and their boxed coordinates */
1746 f = xtmpfile(&regionFileName,
1747 REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1748 TRUE);
1749 if (f == NULL) {
1750 sys_fatal("xtmpfile");
1751 return -1;
1753 fclose(f);
1755 #endif /* not DEBUGGING */
1756 return 0;
1759 int main(int argc, char **argv)
1761 program_name = argv[0];
1762 int i;
1763 int found = 0;
1764 int ok = 1;
1766 #ifdef CAPTURE_MODE
1767 FILE *dump;
1768 fprintf(stderr, "%s: invoked with %d arguments ...\n", argv[0], argc);
1769 for (i = 0; i < argc; i++)
1770 fprintf(stderr, "%2d: %s\n", i, argv[i]);
1771 if ((dump = fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL) {
1772 while((i = fgetc(stdin)) >= 0)
1773 fputc(i, dump);
1774 fclose(dump);
1776 exit(1);
1777 #endif /* CAPTURE_MODE */
1778 device = "html";
1779 if (!font::load_desc())
1780 fatal("cannot find devhtml/DESC exiting");
1781 image_gen = font::image_generator;
1782 if (image_gen == NULL || (strcmp(image_gen, "") == 0))
1783 fatal("devhtml/DESC must set the image_generator field, exiting");
1784 postscriptRes = get_resolution();
1785 i = scanArguments(argc, argv);
1786 setupAntiAlias();
1787 checkImageDir();
1788 makeFileName();
1789 while (i < argc) {
1790 if (argv[i][0] != '-') {
1791 /* found source file */
1792 ok = do_file(argv[i]);
1793 if (!ok)
1794 return 0;
1795 found = 1;
1797 i++;
1800 if (!found)
1801 do_file("-");
1802 if (makeTempFiles())
1803 return 1;
1804 ok = inputFile.do_image(argc, argv);
1805 if (ok == 0) {
1806 generateImages(regionFileName);
1807 ok = inputFile.do_html(argc, argv);
1809 return ok;
1812 static int do_file(const char *filename)
1814 file_case *fcp;
1816 current_filename = filename;
1817 fcp = file_case::muxer(filename);
1818 if (fcp == NULL) {
1819 assert(strcmp(filename, "-"));
1820 error("can't open `%1': %2", filename, strerror(errno));
1821 return 0;
1824 if (inputFile.read_file(fcp)) {
1825 // XXX
1828 delete fcp;
1829 current_filename = NULL;
1830 return 1;