Implement false groff:[09040dbf] used to fix #47161
[s-roff.git] / src / pre-html / pre-html.cpp
blobdd04d6a85176a6fb0eb242c0ce0a1bb678d7dd42
1 /*@
2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 2000 - 2004, 2007, 2008
5 * Free Software Foundation, Inc.
6 * Written by Gaius Mulley (gaius@glam.ac.uk).
8 * This 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 * This 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.
22 #define PREHTMLC
24 #include "config.h"
25 #include "html-config.h"
27 #include <sys/types.h>
29 #include <sys/wait.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
38 #include "defs.h"
39 #include "device.h"
40 #include "errarg.h"
41 #include "error.h"
42 #include "file_case.h"
43 #include "font.h"
44 #include "lib.h"
45 #include "nonposix.h"
46 #include "paper.h"
47 #include "posix.h"
48 #include "searchpath.h"
49 #include "stringclass.h"
51 #include "html-strings.h"
52 #include "pushback.h"
53 #include "pre-html.h"
55 #if 0
56 # define DEBUGGING
57 #endif
59 /* Establish some definitions to facilitate discrimination between
60 differing runtime environments. */
62 #undef MAY_FORK_CHILD_PROCESS
63 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
65 #if defined(__MSDOS__) || defined(_WIN32)
67 // Most MS-DOS and Win32 environments will be missing the `fork' capability
68 // (some like Cygwin have it, but it is best avoided).
70 # define MAY_FORK_CHILD_PROCESS 0
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
76 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
78 // These Win32 implementations allow parent and `spawn...'ed child to
79 // multitask asynchronously.
81 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
83 # else
85 // Others may adopt MS-DOS behaviour where parent must sleep,
86 // from `spawn...' until child terminates.
88 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
90 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
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).
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
102 -DDEBUG_FILE_DIR=d:/path/to/debug/files FIXME what more special to come?
104 in the CPPFLAGS to `make'.) */
106 # define DEBUG_FILE_DIR c:/temp
107 # endif
109 #else /* not __MSDOS__ or _WIN32 */
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
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
122 -DDEBUG_FILE_DIR=/path/to/debug/files
124 in the CPPFLAGS.) */
126 # define DEBUG_FILE_DIR /tmp
127 # endif
129 #endif /* not __MSDOS__ or _WIN32 */
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.
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
141 // Don't use colour names here! Otherwise there is a dependency on
142 // a file called `rgb.txt' which maps names to colours.
143 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
144 #define MIN_ALPHA_BITS 0
145 #define MAX_ALPHA_BITS 4
147 #define PAGE_TEMPLATE_SHORT "pg"
148 #define PAGE_TEMPLATE_LONG "-page-"
149 #define PS_TEMPLATE_SHORT "ps"
150 #define PS_TEMPLATE_LONG "-ps-"
151 #define REGION_TEMPLATE_SHORT "rg"
152 #define REGION_TEMPLATE_LONG "-regions-"
154 typedef enum {
155 CENTERED, LEFT, RIGHT, INLINE
156 } IMAGE_ALIGNMENT;
158 typedef enum {xhtml, html4} html_dialect;
160 static int postscriptRes = -1; // postscript resolution,
161 // dots per inch
162 static int stdoutfd = 1; // output file descriptor -
163 // normally 1 but might move
164 // -1 means closed
165 static char *psFileName = NULL; // name of postscript file
166 static char *psPageName = NULL; // name of file containing
167 // postscript current page
168 static char *regionFileName = NULL; // name of file containing all
169 // image regions
170 static char *imagePageName = NULL; // name of bitmap image containing
171 // current page
172 static const char *image_device = "pnmraw";
173 static int image_res = DEFAULT_IMAGE_RES;
174 static int vertical_offset = 0;
175 static char *image_template = NULL; // image template filename
176 static char *macroset_template= NULL; // image template passed to troff
177 // by -D
178 static int troff_arg = 0; // troff arg index
179 static char *image_dir = NULL; // user specified image directory
180 static int textAlphaBits = MAX_ALPHA_BITS;
181 static int graphicAlphaBits = MAX_ALPHA_BITS;
182 static char *antiAlias = NULL; // antialias arguments we pass to gs
183 static int show_progress = false; // should we display page numbers as
184 // they are processed?
185 static int currentPageNo = -1; // current image page number
186 #if defined(DEBUGGING)
187 static int debug = false;
188 static char *troffFileName = NULL; // output of pre-html output which
189 // is sent to troff -Tps
190 static char *htmlFileName = NULL; // output of pre-html output which
191 // is sent to troff -Thtml
192 #endif
193 static int eqn_flag = false; // must we preprocess via eqn?
195 static char *linebuf = NULL; // for scanning dev-ps/DESC
196 static int linebufsize = 0;
197 static const char *image_gen = NULL; // the `gs' program
199 const char *const FONT_ENV_VAR = U_ROFF_FONT_PATH;
200 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
201 static html_dialect dialect = html4;
203 static int do_file(const char *filename);
206 * sys_fatal - Write a fatal error message.
209 void sys_fatal(const char *s)
211 fatal("%1: %2", s, strerror(errno));
215 * get_line - Copy a line (w/o newline) from a file to the
216 * global line buffer.
219 int get_line(file_case *fcp)
221 if (fcp == NULL)
222 return 0;
223 if (linebuf == 0) {
224 linebuf = new char[128];
225 linebufsize = 128;
227 int i = 0;
228 // skip leading whitespace
229 for (;;) {
230 int c = fcp->get_c();
231 if (c == EOF)
232 return 0;
233 if (c != ' ' && c != '\t') {
234 fcp->unget_c(c);
235 break;
238 for (;;) {
239 int c = fcp->get_c();
240 if (c == EOF)
241 break;
242 if (i + 1 >= linebufsize) {
243 char *old_linebuf = linebuf;
244 linebuf = new char[linebufsize * 2];
245 memcpy(linebuf, old_linebuf, linebufsize);
246 a_delete old_linebuf;
247 linebufsize *= 2;
249 linebuf[i++] = c;
250 if (c == '\n') {
251 i--;
252 break;
255 linebuf[i] = '\0';
256 return 1;
260 * get_resolution - Return the postscript resolution from dev-ps/DESC. FIXME
263 static unsigned int get_resolution(void)
265 unsigned int res;
266 file_case *fcp;
267 if ((fcp = font_path.open_file("dev-ps/DESC", fcp->fc_const_path)) == NULL)
268 fatal("can't open dev-ps/DESC");
269 while (get_line(fcp)) {
270 int n = sscanf(linebuf, "res %u", &res);
271 if (n >= 1)
272 goto jleave;
274 fatal("can't find `res' keyword in dev-ps/DESC");
275 jleave:
276 delete fcp;
277 return res;
281 * html_system - A wrapper for system().
284 void html_system(const char *s, int redirect_stdout)
286 #if defined(DEBUGGING)
287 if (debug) {
288 fprintf(stderr, "executing: ");
289 fwrite(s, sizeof(char), strlen(s), stderr);
290 fflush(stderr);
292 #endif
294 // Redirect standard error to the null device. This is more
295 // portable than using "2> /dev/null", since it doesn't require a
296 // Unixy shell.
297 int save_stderr = dup(2);
298 int save_stdout = dup(1);
299 int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
300 if (save_stderr > 2 && fdnull > 2)
301 dup2(fdnull, 2);
302 if (redirect_stdout && save_stdout > 1 && fdnull > 1)
303 dup2(fdnull, 1);
304 if (fdnull >= 0)
305 close(fdnull);
306 int status = system(s);
307 dup2(save_stderr, 2);
308 if (redirect_stdout)
309 dup2(save_stdout, 1);
310 if (status == -1)
311 fprintf(stderr, "Calling `%s' failed\n", s);
312 else if (status)
313 fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
314 close(save_stderr);
315 close(save_stdout);
320 * make_message - Create a string via malloc and place the result of the
321 * va args into string. Finally the new string is returned.
322 * Taken from man page of printf(3).
325 char *make_message(const char *fmt, ...)
327 /* Guess we need no more than 100 bytes. */
328 int n, size = 100;
329 char *p;
330 char *np;
331 va_list ap;
332 if ((p = (char *)malloc(size)) == NULL)
333 return NULL;
334 while (1) {
335 /* Try to print in the allocated space. */
336 va_start(ap, fmt);
337 n = vsnprintf(p, size, fmt, ap);
338 va_end(ap);
339 /* If that worked, return the string. */
340 if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
341 if (size > n + 1) {
342 np = strsave(p);
343 free(p);
344 return np;
346 return p;
348 /* Else try again with more space. */
349 else /* glibc 2.0 */
350 size *= 2; /* twice the old size */
351 if ((np = (char *)realloc(p, size)) == NULL) {
352 free(p); /* realloc failed, free old, p. */
353 return NULL;
355 p = np; /* use realloc'ed, p */
360 * the class and methods for retaining ascii text
363 class char_block
365 public:
366 enum { SIZE = 256 };
367 char buffer[SIZE];
368 int used; // TODO size_t
369 char_block *next;
371 char_block();
375 * char_block - Constructor. Set the, used, and, next, fields to zero.
378 char_block::char_block()
379 : used(0), next(0)
381 for (int i = 0; i < SIZE; i++)
382 buffer[i] = 0;
385 class char_buffer
387 char_block *head;
388 char_block *tail;
390 int run_output_filter(int device_format_selector, int argc, char *argv[]);
392 public:
393 char_buffer();
394 ~char_buffer();
395 int read_file(file_case *fcp);
396 int do_html(int argc, char *argv[]);
397 int do_image(int argc, char *argv[]);
398 void emit_troff_output(int device_format_selector);
401 // Write the contents of the buffer until a newline is seen.
402 // Check for HTML_IMAGE_INLINE_BEGIN and HTML_IMAGE_INLINE_END; process them
403 // if they are present.
404 static void write_upto_newline(char_block **t, int *i, int is_html);
406 static int can_see(char_block **t, int *i, const char *string);
407 static int skip_spaces(char_block **t, int *i);
408 static void skip_until_newline(char_block **t, int *i);
412 * char_buffer - Constructor.
415 char_buffer::char_buffer()
416 : head(0), tail(0)
421 * char_buffer - Destructor. Throw away the whole buffer list.
424 char_buffer::~char_buffer()
426 while (head != NULL) {
427 char_block *temp = head;
428 head = head->next;
429 delete temp;
434 * read_file - Read in a complete file, fp, placing the contents inside
435 * char_blocks.
438 int char_buffer::read_file(file_case *fcp)
440 int n;
441 while (!fcp->is_eof()) {
442 if (tail == NULL) {
443 tail = new char_block;
444 head = tail;
446 else {
447 if (tail->used == char_block::SIZE) {
448 tail->next = new char_block;
449 tail = tail->next;
452 // at this point we have a tail which is ready for the next SIZE
453 // bytes of the file
454 n = fcp->get_buf(tail->buffer, char_block::SIZE - tail->used);
455 if (n != 0)
456 tail->used += n * sizeof(char);
457 else {
458 n = fcp->is_eof();
459 goto jleave;
462 n = 1;
463 jleave:
464 return n;
468 * writeNbytes - Write n bytes to stdout.
471 static void writeNbytes(const char *s, int l)
473 int n = 0;
474 int r;
476 while (n < l) {
477 r = write(stdoutfd, s, l - n);
478 if (r < 0)
479 sys_fatal("write");
480 n += r;
481 s += r;
486 * writeString - Write a string to stdout.
489 static void writeString(const char *s)
491 writeNbytes(s, strlen(s));
495 * makeFileName - Create the image filename template
496 * and the macroset image template.
499 static void makeFileName(void)
501 if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
502 error("cannot use a `%%' within the image directory name");
503 exit(1);
506 if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
507 error("cannot use a `%%' within the image template");
508 exit(1);
511 if (image_dir == NULL)
512 image_dir = (char *)"";
513 else if (strlen(image_dir) > 0
514 && image_dir[strlen(image_dir) - 1] != '/') {
515 image_dir = make_message("%s/", image_dir);
516 if (image_dir == NULL)
517 sys_fatal("make_message");
520 if (image_template == NULL)
521 macroset_template = make_message("%s" L_D_HTML "-%d", image_dir,
522 (int)getpid());
523 else
524 macroset_template = make_message("%s%s", image_dir, image_template);
526 if (macroset_template == NULL)
527 sys_fatal("make_message");
529 image_template =
530 (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
531 if (image_template == NULL)
532 sys_fatal("malloc");
533 strcpy(image_template, macroset_template);
534 strcat(image_template, "-%d");
538 * setupAntiAlias - Set up the antialias string, used when we call gs.
541 static void setupAntiAlias(void)
543 if (textAlphaBits == 0 && graphicAlphaBits == 0)
544 antiAlias = make_message(" ");
545 else if (textAlphaBits == 0)
546 antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
547 else if (graphicAlphaBits == 0)
548 antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
549 else
550 antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
551 textAlphaBits, graphicAlphaBits);
555 * checkImageDir - Check whether the image directory is available.
558 static void checkImageDir(void)
560 if (image_dir != NULL && strcmp(image_dir, "") != 0)
561 if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
562 error("cannot create directory `%1'", image_dir);
563 exit(1);
568 * write_end_image - End the image. Write out the image extents if we
569 * are using -Tps.
572 static void write_end_image(int is_html)
575 * if we are producing html then these
576 * emit image name and enable output
577 * else
578 * we are producing images
579 * in which case these generate image
580 * boundaries
582 writeString("\\O[4]\\O[2]");
583 if (is_html)
584 writeString("\\O[1]");
585 else
586 writeString("\\O[0]");
590 * write_start_image - Write troff code which will:
592 * (i) disable html output for the following image
593 * (ii) reset the max/min x/y registers during postscript
594 * rendering.
597 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
599 writeString("\\O[5");
600 switch (pos) {
601 case INLINE:
602 writeString("i");
603 break;
604 case LEFT:
605 writeString("l");
606 break;
607 case RIGHT:
608 writeString("r");
609 break;
610 case CENTERED:
611 default:
612 writeString("c");
613 break;
615 writeString(image_template);
616 writeString(".png]");
617 if (is_html)
618 writeString("\\O[0]\\O[3]");
619 else
620 // reset min/max registers
621 writeString("\\O[1]\\O[3]");
624 rf_static void
625 char_buffer::write_upto_newline(char_block **t, int *i, int is_html){
626 enum {a_NONE, a_NL, a_LEADER} ev;
627 char *b;
628 int j, u;
630 if(*t == NULL)
631 goto jleave;
633 j = *i;
634 u = (*t)->used;
635 b = (*t)->buffer;
636 ev = a_NONE;
638 for(; j < u; ++j){
639 if(b[j] == '\n'){
640 ev = (++j < u && b[j] == HTML_INLINE_LEADER_CHAR) ? a_LEADER : a_NL;
641 break;
642 }else if(b[j] == HTML_INLINE_LEADER_CHAR){
643 ev = a_LEADER;
644 break;
648 writeNbytes(&b[*i], j - *i);
649 *i = j;
651 if(ev == a_LEADER){
652 if(can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
653 write_start_image(INLINE, is_html);
654 else if(can_see(t, &j, HTML_IMAGE_INLINE_END))
655 write_end_image(is_html);
656 else if(j < u){
657 ++j;
658 writeNbytes(&b[*i], 1);
662 // Rotate block if all the buffer was consumed
663 if(j == u){
664 *i = 0;
665 if((*t = (*t)->next) != NULL)
666 write_upto_newline(t, i, is_html);
668 jleave:;
672 * can_see - Return true if we can see string in t->buffer[i] onwards.
675 rf_static
676 int char_buffer::can_see(char_block **t, int *i, const char *str)
678 int j = 0;
679 int l = strlen(str);
680 int k = *i;
681 char_block *s = *t;
683 while (s) {
684 while (k < s->used && j < l && s->buffer[k] == str[j]) {
685 j++;
686 k++;
688 if (j == l) {
689 *i = k;
690 *t = s;
691 return true;
693 else if (k < s->used && s->buffer[k] != str[j])
694 return( false );
695 s = s->next;
696 k = 0;
698 return false;
702 * skip_spaces - Return true if we have not run out of data.
703 * Consume spaces also.
706 rf_static
707 int char_buffer::skip_spaces(char_block **t, int *i)
709 char_block *s = *t;
710 int k = *i;
712 while (s) {
713 while (k < s->used && isspace(s->buffer[k]))
714 k++;
715 if (k == s->used) {
716 k = 0;
717 s = s->next;
719 else {
720 *i = k;
721 return true;
724 return false;
728 * skip_until_newline - Skip all characters until a newline is seen.
729 * The newline is not consumed.
732 rf_static
733 void char_buffer::skip_until_newline(char_block **t, int *i)
735 int j = *i;
737 if (*t) {
738 while (j < (*t)->used && (*t)->buffer[j] != '\n')
739 j++;
740 if (j == (*t)->used) {
741 *i = 0;
742 *t = (*t)->next;
743 skip_until_newline(t, i);
745 else
746 // newline was seen
747 *i = j;
751 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
752 #define HTML_OUTPUT_FILTER 0
753 #define IMAGE_OUTPUT_FILTER 1
754 #define OUTPUT_STREAM(name) creat((name), S_IWUSR | S_IRUSR)
755 #define PS_OUTPUT_STREAM OUTPUT_STREAM(psFileName)
756 #define REGION_OUTPUT_STREAM OUTPUT_STREAM(regionFileName)
759 * emit_troff_output - Write formatted buffer content to the troff
760 * post-processor data pipeline.
763 void char_buffer::emit_troff_output(int device_format_selector)
765 // Handle output for BOTH html and image device formats
766 // if `device_format_selector' is passed as
768 // HTML_FORMAT(HTML_OUTPUT_FILTER)
769 // Buffer data is written to the output stream
770 // with template image names translated to actual image names.
772 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
773 // Buffer data is written to the output stream
774 // with no translation, for image file creation in the post-processor.
776 int idx = 0;
777 char_block *element = head;
779 while (element != NULL)
780 write_upto_newline(&element, &idx, device_format_selector);
782 #if 0
783 if (close(stdoutfd) < 0)
784 sys_fatal ("close");
786 // now we grab fd=1 so that the next pipe cannot use fd=1
787 if (stdoutfd == 1) {
788 if (dup(2) != stdoutfd)
789 sys_fatal ("dup failed to use fd=1");
791 #endif /* 0 */
795 * The image class remembers the position of all images in the
796 * postscript file and assigns names for each image.
799 class imageItem
801 public:
802 imageItem *next;
803 int X1;
804 int Y1;
805 int X2;
806 int Y2;
807 char *imageName;
808 int resolution;
809 int maxx;
810 int pageNo;
812 imageItem(int x1, int y1, int x2, int y2,
813 int page, int res, int max_width, char *name);
814 ~imageItem();
818 * imageItem - Constructor.
821 imageItem::imageItem(int x1, int y1, int x2, int y2,
822 int page, int res, int max_width, char *name)
824 X1 = x1;
825 Y1 = y1;
826 X2 = x2;
827 Y2 = y2;
828 pageNo = page;
829 resolution = res;
830 maxx = max_width;
831 imageName = name;
832 next = NULL;
836 * imageItem - Destructor.
839 imageItem::~imageItem()
841 if (imageName)
842 free(imageName);
846 * imageList - A class containing a list of imageItems.
849 class imageList
851 imageItem *head;
852 imageItem *tail;
853 int count;
855 public:
856 imageList();
857 ~imageList();
858 void add(int x1, int y1, int x2, int y2,
859 int page, int res, int maxx, char *name);
860 void createImages(void);
861 int createPage(int pageno);
862 void createImage(imageItem *i);
863 int getMaxX(int pageno);
867 * imageList - Constructor.
870 imageList::imageList()
871 : head(0), tail(0), count(0)
876 * imageList - Destructor.
879 imageList::~imageList()
881 while (head != NULL) {
882 imageItem *i = head;
883 head = head->next;
884 delete i;
889 * createPage - Create one image of, page pageno, from the postscript file.
892 int imageList::createPage(int pageno)
894 char *s;
896 if (currentPageNo == pageno)
897 return 0;
899 if (currentPageNo >= 1) {
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.
905 unlink(imagePageName);
906 unlink(psPageName);
909 if (show_progress) {
910 fprintf(stderr, "[%d] ", pageno);
911 fflush(stderr);
914 #if defined(DEBUGGING)
915 if (debug)
916 fprintf(stderr, "creating page %d\n", pageno);
917 #endif
919 s = make_message("psselect -q -p%d %s %s\n",
920 pageno, psFileName, psPageName);
922 if (s == NULL)
923 sys_fatal("make_message");
924 html_system(s, 1);
926 s = make_message("echo showpage | "
927 "%s%s -q -dBATCH -dSAFER "
928 "-dDEVICEHEIGHTPOINTS=792 "
929 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
930 "-sDEVICE=%s -r%d %s "
931 "-sOutputFile=%s %s -\n",
932 image_gen,
933 EXE_EXT,
934 (getMaxX(pageno) * image_res) / postscriptRes,
935 image_device,
936 image_res,
937 antiAlias,
938 imagePageName,
939 psPageName);
940 if (s == NULL)
941 sys_fatal("make_message");
942 html_system(s, 1);
943 free(s);
944 currentPageNo = pageno;
945 return 0;
949 * min - Return the minimum of two numbers.
952 int min(int x, int y)
954 if (x < y)
955 return x;
956 else
957 return y;
961 * max - Return the maximum of two numbers.
964 int max(int x, int y)
966 if (x > y)
967 return x;
968 else
969 return y;
973 * getMaxX - Return the largest right-hand position for any image
974 * on, pageno.
977 int imageList::getMaxX(int pageno)
979 imageItem *h = head;
980 int x = postscriptRes * DEFAULT_LINE_LENGTH;
982 while (h != NULL) {
983 if (h->pageNo == pageno)
984 x = max(h->X2, x);
985 h = h->next;
987 return x;
991 * createImage - Generate a minimal png file from the set of page images.
994 void imageList::createImage(imageItem *i)
996 if (i->X1 != -1) {
997 char *s;
998 int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
999 - IMAGE_BOARDER_PIXELS,
1001 int y1 = max(image_res * vertical_offset / 72
1002 + min(i->Y1, i->Y2) * image_res / postscriptRes
1003 - IMAGE_BOARDER_PIXELS,
1005 int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1006 + IMAGE_BOARDER_PIXELS;
1007 int y2 = image_res * vertical_offset / 72
1008 + max(i->Y1, i->Y2) * image_res / postscriptRes
1009 + 1 + IMAGE_BOARDER_PIXELS;
1010 if (createPage(i->pageNo) == 0) {
1011 s = make_message("pnmcut%s %d %d %d %d < %s "
1012 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1013 EXE_EXT,
1014 x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1015 imagePageName,
1016 EXE_EXT,
1017 TRANSPARENT,
1018 i->imageName);
1019 if (s == NULL)
1020 sys_fatal("make_message");
1022 html_system(s, 0);
1023 free(s);
1025 else {
1026 fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1027 fflush(stderr);
1029 #if defined(DEBUGGING)
1031 else {
1032 if (debug) {
1033 fprintf(stderr, "ignoring image as x1 coord is -1\n");
1034 fflush(stderr);
1036 #endif
1041 * add - Add an image description to the imageList.
1044 void imageList::add(int x1, int y1, int x2, int y2,
1045 int page, int res, int maxx, char *name)
1047 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1049 if (head == NULL) {
1050 head = i;
1051 tail = i;
1053 else {
1054 tail->next = i;
1055 tail = i;
1060 * createImages - For each image descriptor on the imageList,
1061 * create the actual image.
1064 void imageList::createImages(void)
1066 imageItem *h = head;
1068 while (h != NULL) {
1069 createImage(h);
1070 h = h->next;
1074 static imageList listOfImages; // List of images defined by the region file.
1077 * generateImages - Parse the region file and generate images
1078 * from the postscript file. The region file
1079 * contains the x1,y1--x2,y2 extents of each
1080 * image.
1083 static void generateImages(char *region_file_name)
1085 pushBackBuffer *f=new pushBackBuffer(region_file_name);
1087 while (f->putPB(f->getPB()) != eof) {
1088 if (f->isString(L_D_HTML "-info:page")) {
1089 int page = f->readInt();
1090 int x1 = f->readInt();
1091 int y1 = f->readInt();
1092 int x2 = f->readInt();
1093 int y2 = f->readInt();
1094 int maxx = f->readInt();
1095 char *name = f->readString();
1096 int res = postscriptRes;
1097 listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1098 while (f->putPB(f->getPB()) != '\n'
1099 && f->putPB(f->getPB()) != eof)
1100 (void)f->getPB();
1101 if (f->putPB(f->getPB()) == '\n')
1102 (void)f->getPB();
1104 else {
1105 /* Write any error messages out to the user. */
1106 fputc(f->getPB(), stderr);
1110 listOfImages.createImages();
1111 if (show_progress) {
1112 fprintf(stderr, "done\n");
1113 fflush(stderr);
1115 delete f;
1119 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1120 * stream on handle, willbe.
1123 static void set_redirection(int was, int willbe)
1125 // Nothing to do if `was' and `willbe' already have same handle.
1126 if (was != willbe) {
1127 // Otherwise attempt the specified redirection.
1128 if (dup2 (willbe, was) < 0) {
1129 // Redirection failed, so issue diagnostic and bail out.
1130 fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1131 if (willbe == STDOUT_FILENO)
1132 fprintf(stderr,
1133 "likely that stdout should be opened before %d\n", was);
1134 sys_fatal("dup2");
1137 // When redirection has been successfully completed assume redundant
1138 // handle `willbe' is no longer required, so close it.
1139 if (close(willbe) < 0)
1140 // Issue diagnostic if `close' fails.
1141 sys_fatal("close");
1146 * save_and_redirect - Get duplicate handle for stream, was, then
1147 * redirect, was, to refer to, willbe.
1150 static int save_and_redirect(int was, int willbe)
1152 if (was == willbe)
1153 // No redirection specified so don't do anything but silently bailing out.
1154 return (was);
1156 // Proceeding with redirection so first save and verify our duplicate
1157 // handle for `was'.
1158 int saved = dup(was);
1159 if (saved < 0) {
1160 fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1161 sys_fatal("dup");
1164 // Duplicate handle safely established so complete redirection.
1165 set_redirection(was, willbe);
1167 // Finally return the saved duplicate descriptor for the
1168 // original `was' stream.
1169 return saved;
1173 * alterDeviceTo - If, toImage, is set
1174 * the argument list is altered to include
1175 * IMAGE_DEVICE and we invoke groff rather than troff.
1176 * Else
1177 * set -Thtml and groff.
1180 static void alterDeviceTo(int argc, char *argv[], int toImage)
1182 int i = 0;
1184 if (toImage) {
1185 while (i < argc) {
1186 if ((strcmp(argv[i], "-Thtml") == 0) ||
1187 (strcmp(argv[i], "-Txhtml") == 0))
1188 argv[i] = (char *)IMAGE_DEVICE;
1189 i++;
1191 } else {
1192 while (i < argc) {
1193 if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1194 argv[i] = UNCONST((dialect == xhtml) ? "-Txhtml" : "-Thtml");
1195 i++;
1198 argv[troff_arg] = UNCONST(L_ROFF); // use groff -Z
1202 * addArg - Append newarg onto the command list for groff.
1205 char **addArg(int argc, char *argv[], char *newarg)
1207 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1208 int i = 0;
1210 if (new_argv == NULL)
1211 sys_fatal("malloc");
1213 if (argc > 0) {
1214 new_argv[i] = argv[i];
1215 i++;
1217 new_argv[i] = newarg;
1218 while (i < argc) {
1219 new_argv[i + 1] = argv[i];
1220 i++;
1222 argc++;
1223 new_argv[argc] = NULL;
1224 return new_argv;
1228 * addRegDef - Append a defined register or string onto the command
1229 * list for troff.
1232 char **addRegDef(int argc, char *argv[], const char *numReg)
1234 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1235 int i = 0;
1237 if (new_argv == NULL)
1238 sys_fatal("malloc");
1240 while (i < argc) {
1241 new_argv[i] = argv[i];
1242 i++;
1244 new_argv[argc] = strsave(numReg);
1245 argc++;
1246 new_argv[argc] = NULL;
1247 return new_argv;
1251 * dump_args - Display the argument list.
1254 void dump_args(int argc, char *argv[])
1256 fprintf(stderr, " %d arguments:", argc);
1257 for (int i = 0; i < argc; i++)
1258 fprintf(stderr, " %s", argv[i]);
1259 fprintf(stderr, "\n");
1263 * print_args - print arguments as if they were issued on the command line.
1265 #ifdef DEBUGGING
1266 void print_args(int argc, char *argv[])
1268 if (debug) {
1269 fprintf(stderr, "executing: ");
1270 for (int i = 0; i < argc; i++)
1271 fprintf(stderr, "%s ", argv[i]);
1272 fprintf(stderr, "\n");
1275 #else
1276 void print_args(int, char **)
1279 #endif
1281 int char_buffer::run_output_filter(int filter, int argc, char **argv)
1283 int pipedes[2];
1284 pid_t child_pid;
1285 int status;
1287 print_args(argc, argv);
1288 if (pipe(pipedes) < 0)
1289 sys_fatal("pipe");
1291 #if MAY_FORK_CHILD_PROCESS
1292 // This is the UNIX process model. To invoke our post-processor,
1293 // we must `fork' the current process.
1295 if ((child_pid = fork()) < 0)
1296 sys_fatal("fork");
1298 else if (child_pid == 0) {
1299 // This is the child process fork. We redirect its `stdin' stream
1300 // to read data emerging from our pipe. There is no point in saving,
1301 // since we won't be able to restore later!
1303 set_redirection(STDIN_FILENO, pipedes[0]);
1305 // The parent process will be writing this data, so we should release
1306 // the child's writeable handle on the pipe, since we have no use for it.
1308 if (close(pipedes[1]) < 0)
1309 sys_fatal("close");
1311 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1313 if (filter == IMAGE_OUTPUT_FILTER) {
1314 // with BOTH `stdout' AND `stderr' diverted to files.
1316 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1317 set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1320 // Now we are ready to launch the output filter.
1322 execvp(argv[0], argv);
1324 // If we get to here then the `exec...' request for the output filter
1325 // failed. Diagnose it and bail out.
1327 error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1328 fflush(stderr); // just in case error() didn't
1329 exit(1);
1332 else {
1333 // This is the parent process fork. We will be writing data to the
1334 // filter pipeline, and the child will be reading it. We have no further
1335 // use for our read handle on the pipe, and should close it.
1337 if (close(pipedes[0]) < 0)
1338 sys_fatal("close");
1340 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1341 // and push out the appropiately formatted data to the filter.
1343 pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1344 emit_troff_output(DEVICE_FORMAT(filter));
1346 // After emitting all the data we close our connection to the inlet
1347 // end of the pipe so the child process will detect end of data.
1349 set_redirection(STDOUT_FILENO, pipedes[1]);
1351 // Finally, we must wait for the child process to complete.
1353 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1354 sys_fatal("wait");
1357 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1359 // We do not have `fork', (or we prefer not to use it),
1360 // but asynchronous processes are allowed, passing data through pipes.
1361 // This should be ok for most Win32 systems and is preferred to `fork'
1362 // for starting child processes under Cygwin.
1364 // Before we start the post-processor we bind its inherited `stdin'
1365 // stream to the readable end of our pipe, saving our own `stdin' stream
1366 // in `pipedes[0]'.
1368 pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1370 // for the Win32 model,
1371 // we need special provision for saving BOTH `stdout' and `stderr'.
1373 int saved_stdout = dup(STDOUT_FILENO);
1374 int saved_stderr = STDERR_FILENO;
1376 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1378 if (filter == IMAGE_OUTPUT_FILTER) {
1379 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1380 // duplicate handle for `stderr'.
1382 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1383 saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1386 // We then use an asynchronous spawn request to start the post-processor.
1388 if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1389 // Should the spawn request fail we issue a diagnostic and bail out.
1391 error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1392 exit(1);
1395 // Once the post-processor has been started we revert our `stdin'
1396 // to its original saved source, which also closes the readable handle
1397 // for the pipe.
1399 set_redirection(STDIN_FILENO, pipedes[0]);
1401 // if we redirected `stderr', for use by the image post-processor,
1402 // then we also need to reinstantiate its original assignment.
1404 if (filter == IMAGE_OUTPUT_FILTER)
1405 set_redirection(STDERR_FILENO, saved_stderr);
1407 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1408 // and push out the appropriately formatted data to the filter.
1410 set_redirection(STDOUT_FILENO, pipedes[1]);
1411 emit_troff_output(DEVICE_FORMAT(filter));
1413 // After emitting all the data we close our connection to the inlet
1414 // end of the pipe so the child process will detect end of data.
1416 set_redirection(STDOUT_FILENO, saved_stdout);
1418 // And finally, we must wait for the child process to complete.
1420 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1421 sys_fatal("wait");
1423 #else /* can't do asynchronous pipes! */
1425 // TODO: code to support an MS-DOS style process model
1426 // should go here
1428 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1430 return 0;
1434 * do_html - Set the troff number htmlflip and
1435 * write out the buffer to troff -Thtml.
1438 int char_buffer::do_html(int argc, char *argv[])
1440 string s;
1442 alterDeviceTo(argc, argv, 0);
1443 argv += troff_arg; // skip all arguments up to groff
1444 argc -= troff_arg;
1445 argv = addArg(argc, argv, (char *)"-Z");
1446 argc++;
1448 s = (char *)"-dwww-image-template=";
1449 s += macroset_template; // do not combine these statements,
1450 // otherwise they will not work
1451 s += '\0'; // the trailing `\0' is ignored
1452 argv = addRegDef(argc, argv, s.contents());
1453 argc++;
1455 if (dialect == xhtml) {
1456 argv = addRegDef(argc, argv, "-rxhtml=1");
1457 argc++;
1458 if (eqn_flag) {
1459 argv = addRegDef(argc, argv, "-e");
1460 argc++;
1464 #if defined(DEBUGGING)
1465 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1466 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1467 if (debug) {
1468 int saved_stdout = save_and_redirect(STDOUT_FILENO, HTML_DEBUG_STREAM);
1469 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
1470 set_redirection(STDOUT_FILENO, saved_stdout);
1472 #endif
1474 return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1478 * do_image - Write out the buffer to troff -Tps.
1481 int char_buffer::do_image(int argc, char *argv[])
1483 string s;
1485 alterDeviceTo(argc, argv, 1);
1486 argv += troff_arg; // skip all arguments up to troff/groff
1487 argc -= troff_arg;
1488 argv = addRegDef(argc, argv, "-rps4html=1");
1489 argc++;
1491 s = "-dwww-image-template=";
1492 s += macroset_template;
1493 s += '\0';
1494 argv = addRegDef(argc, argv, s.contents());
1495 argc++;
1497 // override local settings and produce a page size letter postscript file
1498 argv = addRegDef(argc, argv, "-P-pletter");
1499 argc++;
1501 if (dialect == xhtml) {
1502 if (eqn_flag) {
1503 argv = addRegDef(argc, argv, "-rxhtml=1");
1504 argc++;
1506 argv = addRegDef(argc, argv, "-e");
1507 argc++;
1510 #if defined(DEBUGGING)
1511 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1512 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1513 if (debug) {
1514 int saved_stdout = save_and_redirect(STDOUT_FILENO, IMAGE_DEBUG_STREAM);
1515 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
1516 set_redirection(STDOUT_FILENO, saved_stdout);
1518 #endif
1520 return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1523 static char_buffer inputFile;
1526 * usage - Emit usage arguments.
1529 static void usage(FILE *stream)
1531 fprintf(stream,
1532 "\n"
1533 "This program is not intended to be called stand-alone;\n"
1534 "it is part of the " L_ROFF " pipeline to produce HTML output.\n"
1535 "\n"
1536 "If there is ever the need to call it manually (e.g., for\n"
1537 "debugging purposes), add command line option `-V' while calling\n"
1538 "the `" L_ROFF "' program to see which arguments are passed to it.\n"
1539 "\n");
1543 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1544 * and -P-I. Return the argument index of the first
1545 * non-option.
1548 static int scanArguments(int argc, char **argv)
1550 const char *command_prefix = getenv(U_ROFF_COMMAND_PREFIX);
1551 if (!command_prefix)
1552 command_prefix = PROG_PREFIX;
1553 char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1];
1554 strcpy(troff_name, command_prefix);
1555 strcat(troff_name, "troff");
1556 int c, i;
1557 static const struct option long_options[] = {
1558 { "help", no_argument, 0, CHAR_MAX + 1 },
1559 { "version", no_argument, 0, 'v' },
1560 { NULL, 0, 0, 0 }
1562 while ((c = getopt_long(argc, argv, "+a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
1563 long_options, NULL))
1564 != EOF)
1565 switch(c) {
1566 case 'a':
1567 textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1568 MAX_ALPHA_BITS);
1569 if (textAlphaBits == 3) {
1570 error("cannot use 3 bits of antialiasing information");
1571 exit(1);
1573 break;
1574 case 'b':
1575 // handled by post-grohtml (set background color to white)
1576 break;
1577 case 'd':
1578 #ifdef DEBUGGING
1579 debug = true;
1580 #endif
1581 break;
1582 case 'D':
1583 image_dir = optarg;
1584 break;
1585 case 'e':
1586 eqn_flag = true;
1587 break;
1588 case 'F':
1589 font_path.command_line_dir(optarg);
1590 break;
1591 case 'g':
1592 graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1593 MAX_ALPHA_BITS);
1594 if (graphicAlphaBits == 3) {
1595 error("cannot use 3 bits of antialiasing information");
1596 exit(1);
1598 break;
1599 case 'h':
1600 // handled by post-grohtml
1601 break;
1602 case 'i':
1603 image_res = atoi(optarg);
1604 break;
1605 case 'I':
1606 image_template = optarg;
1607 break;
1608 case 'j':
1609 // handled by post-grohtml (set job name for multiple file output)
1610 break;
1611 case 'l':
1612 // handled by post-grohtml (no automatic section links)
1613 break;
1614 case 'n':
1615 // handled by post-grohtml (generate simple heading anchors)
1616 break;
1617 case 'o':
1618 vertical_offset = atoi(optarg);
1619 break;
1620 case 'p':
1621 show_progress = true;
1622 break;
1623 case 'r':
1624 // handled by post-grohtml (no header and footer lines)
1625 break;
1626 case 's':
1627 // handled by post-grohtml (use font size n as the html base font size)
1628 break;
1629 case 'S':
1630 // handled by post-grohtml (set file split level)
1631 break;
1632 case 'v':
1633 puts(L_D_PREHTML " (" T_ROFF ") v" VERSION);
1634 exit(0);
1635 case 'V':
1636 // handled by post-grohtml (create validator button)
1637 break;
1638 case 'x':
1639 // html dialect
1640 if (strcmp(optarg, "x") == 0)
1641 dialect = xhtml;
1642 else if (strcmp(optarg, "4") == 0)
1643 dialect = html4;
1644 else
1645 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
1646 break;
1647 case 'y':
1648 // handled by post-grohtml (create groff signature)
1649 break;
1650 case CHAR_MAX + 1: // --help
1651 usage(stdout);
1652 exit(0);
1653 break;
1654 case '?':
1655 usage(stderr);
1656 exit(1);
1657 break;
1658 default:
1659 break;
1662 i = optind;
1663 while (i < argc) {
1664 if (strcmp(argv[i], troff_name) == 0)
1665 troff_arg = i;
1666 else if (argv[i][0] != '-')
1667 return i;
1668 i++;
1670 a_delete troff_name;
1672 return argc;
1676 * makeTempFiles - Name the temporary files.
1679 static int makeTempFiles(void)
1681 #if defined(DEBUGGING)
1682 psFileName = DEBUG_FILE("prehtml-ps");
1683 regionFileName = DEBUG_FILE("prehtml-region");
1684 imagePageName = DEBUG_FILE("prehtml-page");
1685 psPageName = DEBUG_FILE("prehtml-psn");
1686 troffFileName = DEBUG_FILE("prehtml-troff");
1687 htmlFileName = DEBUG_FILE("prehtml-html");
1688 #else /* not DEBUGGING */
1689 FILE *f;
1691 /* psPageName contains a single page of postscript */
1692 f = xtmpfile(&psPageName,
1693 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1694 true);
1695 if (f == NULL) {
1696 sys_fatal("xtmpfile");
1697 return -1;
1699 fclose(f);
1701 /* imagePageName contains a bitmap image of the single postscript page */
1702 f = xtmpfile(&imagePageName,
1703 PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1704 true);
1705 if (f == NULL) {
1706 sys_fatal("xtmpfile");
1707 return -1;
1709 fclose(f);
1711 /* psFileName contains a postscript file of the complete document */
1712 f = xtmpfile(&psFileName,
1713 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1714 true);
1715 if (f == NULL) {
1716 sys_fatal("xtmpfile");
1717 return -1;
1719 fclose(f);
1721 /* regionFileName contains a list of the images and their boxed coordinates */
1722 f = xtmpfile(&regionFileName,
1723 REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1724 true);
1725 if (f == NULL) {
1726 sys_fatal("xtmpfile");
1727 return -1;
1729 fclose(f);
1731 #endif /* not DEBUGGING */
1732 return 0;
1735 int main(int argc, char **argv)
1737 program_name = argv[0];
1738 int i;
1739 int found = 0;
1740 int ok = 1;
1742 #ifdef CAPTURE_MODE
1743 FILE *dump;
1744 fprintf(stderr, "%s: invoked with %d arguments ...\n", argv[0], argc);
1745 for (i = 0; i < argc; i++)
1746 fprintf(stderr, "%2d: %s\n", i, argv[i]);
1747 if ((dump = fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL) {
1748 while((i = fgetc(stdin)) >= 0)
1749 fputc(i, dump);
1750 fclose(dump);
1752 exit(1);
1753 #endif /* CAPTURE_MODE */
1754 device = "html";
1755 if (!font::load_desc())
1756 fatal("cannot find dev-html/DESC, exiting");
1757 image_gen = font::image_generator;
1758 if (image_gen == NULL || (strcmp(image_gen, "") == 0))
1759 fatal("dev-html/DESC must set the image_generator field, exiting");
1760 postscriptRes = get_resolution();
1761 i = scanArguments(argc, argv);
1762 setupAntiAlias();
1763 checkImageDir();
1764 makeFileName();
1765 while (i < argc) {
1766 if (argv[i][0] != '-') {
1767 /* found source file */
1768 ok = do_file(argv[i]);
1769 if (!ok)
1770 return 0;
1771 found = 1;
1773 i++;
1776 if (!found)
1777 do_file("-");
1778 if (makeTempFiles())
1779 return 1;
1780 ok = inputFile.do_image(argc, argv);
1781 if (ok == 0) {
1782 generateImages(regionFileName);
1783 ok = inputFile.do_html(argc, argv);
1785 return ok;
1788 static int do_file(const char *filename)
1790 file_case *fcp;
1792 current_filename = filename;
1793 fcp = file_case::muxer(filename);
1794 if (fcp == NULL) {
1795 assert(strcmp(filename, "-"));
1796 error("can't open `%1': %2", filename, strerror(errno));
1797 return 0;
1800 if (inputFile.read_file(fcp)) {
1801 // XXX
1804 delete fcp;
1805 current_filename = NULL;
1806 return 1;
1809 // s-it2-mode