Sync-to-go: update copyright for 2015
[s-roff.git] / src / pre-html / pre-html.cpp
blob717daa909a9b29884a7c26c1e4003563eac0dc08
1 /*@
2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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 #ifdef _POSIX_VERSION
30 # include <sys/wait.h>
31 # define PID_T pid_t
32 #else
33 # define PID_T int
34 #endif
36 #include <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdlib.h>
43 #include "defs.h"
44 #include "device.h"
45 #include "errarg.h"
46 #include "error.h"
47 #include "file_case.h"
48 #include "font.h"
49 #include "lib.h"
50 #include "nonposix.h"
51 #include "paper.h"
52 #include "posix.h"
53 #include "searchpath.h"
54 #include "stringclass.h"
56 #include "html-strings.h"
57 #include "pushback.h"
58 #include "pre-html.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 FIXME what more special to come?
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 #define DEFAULT_LINE_LENGTH 7 // inches wide
147 #define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution
148 #define IMAGE_BOARDER_PIXELS 0
149 #define INLINE_LEADER_CHAR '\\'
151 // Don't use colour names here! Otherwise there is a dependency on
152 // a file called `rgb.txt' which maps names to colours.
153 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
154 #define MIN_ALPHA_BITS 0
155 #define MAX_ALPHA_BITS 4
157 #define PAGE_TEMPLATE_SHORT "pg"
158 #define PAGE_TEMPLATE_LONG "-page-"
159 #define PS_TEMPLATE_SHORT "ps"
160 #define PS_TEMPLATE_LONG "-ps-"
161 #define REGION_TEMPLATE_SHORT "rg"
162 #define REGION_TEMPLATE_LONG "-regions-"
164 typedef enum {
165 CENTERED, LEFT, RIGHT, INLINE
166 } IMAGE_ALIGNMENT;
168 typedef enum {xhtml, html4} html_dialect;
170 static int postscriptRes = -1; // postscript resolution,
171 // dots per inch
172 static int stdoutfd = 1; // output file descriptor -
173 // normally 1 but might move
174 // -1 means closed
175 static char *psFileName = NULL; // name of postscript file
176 static char *psPageName = NULL; // name of file containing
177 // postscript current page
178 static char *regionFileName = NULL; // name of file containing all
179 // image regions
180 static char *imagePageName = NULL; // name of bitmap image containing
181 // current page
182 static const char *image_device = "pnmraw";
183 static int image_res = DEFAULT_IMAGE_RES;
184 static int vertical_offset = 0;
185 static char *image_template = NULL; // image template filename
186 static char *macroset_template= NULL; // image template passed to troff
187 // by -D
188 static int troff_arg = 0; // troff arg index
189 static char *image_dir = NULL; // user specified image directory
190 static int textAlphaBits = MAX_ALPHA_BITS;
191 static int graphicAlphaBits = MAX_ALPHA_BITS;
192 static char *antiAlias = NULL; // antialias arguments we pass to gs
193 static int show_progress = false; // should we display page numbers as
194 // they are processed?
195 static int currentPageNo = -1; // current image page number
196 #if defined(DEBUGGING)
197 static int debug = false;
198 static char *troffFileName = NULL; // output of pre-html output which
199 // is sent to troff -Tps
200 static char *htmlFileName = NULL; // output of pre-html output which
201 // is sent to troff -Thtml
202 #endif
203 static int eqn_flag = false; // must we preprocess via eqn?
205 static char *linebuf = NULL; // for scanning dev-ps/DESC
206 static int linebufsize = 0;
207 static const char *image_gen = NULL; // the `gs' program
209 const char *const FONT_ENV_VAR = U_ROFF_FONT_PATH;
210 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
211 static html_dialect dialect = html4;
214 * Images are generated via postscript, gs, and the pnm utilities.
216 #define IMAGE_DEVICE "-Tps"
218 static int do_file(const char *filename);
221 * sys_fatal - Write a fatal error message.
224 void sys_fatal(const char *s)
226 fatal("%1: %2", s, strerror(errno));
230 * get_line - Copy a line (w/o newline) from a file to the
231 * global line buffer.
234 int get_line(file_case *fcp)
236 if (fcp == NULL)
237 return 0;
238 if (linebuf == 0) {
239 linebuf = new char[128];
240 linebufsize = 128;
242 int i = 0;
243 // skip leading whitespace
244 for (;;) {
245 int c = fcp->get_c();
246 if (c == EOF)
247 return 0;
248 if (c != ' ' && c != '\t') {
249 fcp->unget_c(c);
250 break;
253 for (;;) {
254 int c = fcp->get_c();
255 if (c == EOF)
256 break;
257 if (i + 1 >= linebufsize) {
258 char *old_linebuf = linebuf;
259 linebuf = new char[linebufsize * 2];
260 memcpy(linebuf, old_linebuf, linebufsize);
261 a_delete old_linebuf;
262 linebufsize *= 2;
264 linebuf[i++] = c;
265 if (c == '\n') {
266 i--;
267 break;
270 linebuf[i] = '\0';
271 return 1;
275 * get_resolution - Return the postscript resolution from dev-ps/DESC. FIXME
278 static unsigned int get_resolution(void)
280 unsigned int res;
281 file_case *fcp;
282 if ((fcp = font_path.open_file("dev-ps/DESC", fcp->fc_const_path)) == NULL)
283 fatal("can't open dev-ps/DESC");
284 while (get_line(fcp)) {
285 int n = sscanf(linebuf, "res %u", &res);
286 if (n >= 1)
287 goto jleave;
289 fatal("can't find `res' keyword in dev-ps/DESC");
290 jleave:
291 delete fcp;
292 return res;
296 * html_system - A wrapper for system().
299 void html_system(const char *s, int redirect_stdout)
301 #if defined(DEBUGGING)
302 if (debug) {
303 fprintf(stderr, "executing: ");
304 fwrite(s, sizeof(char), strlen(s), stderr);
305 fflush(stderr);
307 #endif
309 // Redirect standard error to the null device. This is more
310 // portable than using "2> /dev/null", since it doesn't require a
311 // Unixy shell.
312 int save_stderr = dup(2);
313 int save_stdout = dup(1);
314 int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
315 if (save_stderr > 2 && fdnull > 2)
316 dup2(fdnull, 2);
317 if (redirect_stdout && save_stdout > 1 && fdnull > 1)
318 dup2(fdnull, 1);
319 if (fdnull >= 0)
320 close(fdnull);
321 int status = system(s);
322 dup2(save_stderr, 2);
323 if (redirect_stdout)
324 dup2(save_stdout, 1);
325 if (status == -1)
326 fprintf(stderr, "Calling `%s' failed\n", s);
327 else if (status)
328 fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
329 close(save_stderr);
330 close(save_stdout);
335 * make_message - Create a string via malloc and place the result of the
336 * va args into string. Finally the new string is returned.
337 * Taken from man page of printf(3).
340 char *make_message(const char *fmt, ...)
342 /* Guess we need no more than 100 bytes. */
343 int n, size = 100;
344 char *p;
345 char *np;
346 va_list ap;
347 if ((p = (char *)malloc(size)) == NULL)
348 return NULL;
349 while (1) {
350 /* Try to print in the allocated space. */
351 va_start(ap, fmt);
352 n = vsnprintf(p, size, fmt, ap);
353 va_end(ap);
354 /* If that worked, return the string. */
355 if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
356 if (size > n + 1) {
357 np = strsave(p);
358 free(p);
359 return np;
361 return p;
363 /* Else try again with more space. */
364 else /* glibc 2.0 */
365 size *= 2; /* twice the old size */
366 if ((np = (char *)realloc(p, size)) == NULL) {
367 free(p); /* realloc failed, free old, p. */
368 return NULL;
370 p = np; /* use realloc'ed, p */
375 * the class and methods for retaining ascii text
378 class char_block
380 public:
381 enum { SIZE = 256 };
382 char buffer[SIZE];
383 int used;
384 char_block *next;
386 char_block();
390 * char_block - Constructor. Set the, used, and, next, fields to zero.
393 char_block::char_block()
394 : used(0), next(0)
396 for (int i = 0; i < SIZE; i++)
397 buffer[i] = 0;
400 class char_buffer
402 char_block *head;
403 char_block *tail;
405 int run_output_filter(int device_format_selector, int argc, char *argv[]);
407 public:
408 char_buffer();
409 ~char_buffer();
410 int read_file(file_case *fcp);
411 int do_html(int argc, char *argv[]);
412 int do_image(int argc, char *argv[]);
413 void emit_troff_output(int device_format_selector);
414 void write_upto_newline(char_block **t, int *i, int is_html);
415 int can_see(char_block **t, int *i, const char *string);
416 int skip_spaces(char_block **t, int *i);
417 void skip_until_newline(char_block **t, int *i);
421 * char_buffer - Constructor.
424 char_buffer::char_buffer()
425 : head(0), tail(0)
430 * char_buffer - Destructor. Throw away the whole buffer list.
433 char_buffer::~char_buffer()
435 while (head != NULL) {
436 char_block *temp = head;
437 head = head->next;
438 delete temp;
443 * read_file - Read in a complete file, fp, placing the contents inside
444 * char_blocks.
447 int char_buffer::read_file(file_case *fcp)
449 int n;
450 while (!fcp->is_eof()) {
451 if (tail == NULL) {
452 tail = new char_block;
453 head = tail;
455 else {
456 if (tail->used == char_block::SIZE) {
457 tail->next = new char_block;
458 tail = tail->next;
461 // at this point we have a tail which is ready for the next SIZE
462 // bytes of the file
463 n = fcp->get_buf(tail->buffer, char_block::SIZE - tail->used);
464 if (n != 0)
465 tail->used += n * sizeof(char);
466 else {
467 n = fcp->is_eof();
468 goto jleave;
471 n = 1;
472 jleave:
473 return n;
477 * writeNbytes - Write n bytes to stdout.
480 static void writeNbytes(const char *s, int l)
482 int n = 0;
483 int r;
485 while (n < l) {
486 r = write(stdoutfd, s, l - n);
487 if (r < 0)
488 sys_fatal("write");
489 n += r;
490 s += r;
495 * writeString - Write a string to stdout.
498 static void writeString(const char *s)
500 writeNbytes(s, strlen(s));
504 * makeFileName - Create the image filename template
505 * and the macroset image template.
508 static void makeFileName(void)
510 if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
511 error("cannot use a `%%' within the image directory name");
512 exit(1);
515 if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
516 error("cannot use a `%%' within the image template");
517 exit(1);
520 if (image_dir == NULL)
521 image_dir = (char *)"";
522 else if (strlen(image_dir) > 0
523 && image_dir[strlen(image_dir) - 1] != '/') {
524 image_dir = make_message("%s/", image_dir);
525 if (image_dir == NULL)
526 sys_fatal("make_message");
529 if (image_template == NULL)
530 macroset_template = make_message("%s" L_D_HTML "-%d", image_dir,
531 (int)getpid());
532 else
533 macroset_template = make_message("%s%s", image_dir, image_template);
535 if (macroset_template == NULL)
536 sys_fatal("make_message");
538 image_template =
539 (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
540 if (image_template == NULL)
541 sys_fatal("malloc");
542 strcpy(image_template, macroset_template);
543 strcat(image_template, "-%d");
547 * setupAntiAlias - Set up the antialias string, used when we call gs.
550 static void setupAntiAlias(void)
552 if (textAlphaBits == 0 && graphicAlphaBits == 0)
553 antiAlias = make_message(" ");
554 else if (textAlphaBits == 0)
555 antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
556 else if (graphicAlphaBits == 0)
557 antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
558 else
559 antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
560 textAlphaBits, graphicAlphaBits);
564 * checkImageDir - Check whether the image directory is available.
567 static void checkImageDir(void)
569 if (image_dir != NULL && strcmp(image_dir, "") != 0)
570 if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
571 error("cannot create directory `%1'", image_dir);
572 exit(1);
577 * write_end_image - End the image. Write out the image extents if we
578 * are using -Tps.
581 static void write_end_image(int is_html)
584 * if we are producing html then these
585 * emit image name and enable output
586 * else
587 * we are producing images
588 * in which case these generate image
589 * boundaries
591 writeString("\\O[4]\\O[2]");
592 if (is_html)
593 writeString("\\O[1]");
594 else
595 writeString("\\O[0]");
599 * write_start_image - Write troff code which will:
601 * (i) disable html output for the following image
602 * (ii) reset the max/min x/y registers during postscript
603 * rendering.
606 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
608 writeString("\\O[5");
609 switch (pos) {
610 case INLINE:
611 writeString("i");
612 break;
613 case LEFT:
614 writeString("l");
615 break;
616 case RIGHT:
617 writeString("r");
618 break;
619 case CENTERED:
620 default:
621 writeString("c");
622 break;
624 writeString(image_template);
625 writeString(".png]");
626 if (is_html)
627 writeString("\\O[0]\\O[3]");
628 else
629 // reset min/max registers
630 writeString("\\O[1]\\O[3]");
634 * write_upto_newline - Write the contents of the buffer until a newline
635 * is seen. Check for HTML_IMAGE_INLINE_BEGIN and
636 * HTML_IMAGE_INLINE_END; process them if they are
637 * present.
640 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
642 int j = *i;
644 if (*t) {
645 while (j < (*t)->used
646 && (*t)->buffer[j] != '\n'
647 && (*t)->buffer[j] != INLINE_LEADER_CHAR)
648 j++;
649 if (j < (*t)->used
650 && (*t)->buffer[j] == '\n')
651 j++;
652 writeNbytes((*t)->buffer + (*i), j - (*i));
653 if ((*t)->buffer[j] == INLINE_LEADER_CHAR) {
654 if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
655 write_start_image(INLINE, is_html);
656 else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
657 write_end_image(is_html);
658 else {
659 if (j < (*t)->used) {
660 *i = j;
661 j++;
662 writeNbytes((*t)->buffer + (*i), j - (*i));
666 if (j == (*t)->used) {
667 *i = 0;
668 *t = (*t)->next;
669 if (*t && (*t)->buffer[j - 1] != '\n')
670 write_upto_newline(t, i, is_html);
672 else
673 // newline was seen
674 *i = j;
679 * can_see - Return true if we can see string in t->buffer[i] onwards.
682 int char_buffer::can_see(char_block **t, int *i, const char *str)
684 int j = 0;
685 int l = strlen(str);
686 int k = *i;
687 char_block *s = *t;
689 while (s) {
690 while (k < s->used && j < l && s->buffer[k] == str[j]) {
691 j++;
692 k++;
694 if (j == l) {
695 *i = k;
696 *t = s;
697 return true;
699 else if (k < s->used && s->buffer[k] != str[j])
700 return( false );
701 s = s->next;
702 k = 0;
704 return false;
708 * skip_spaces - Return true if we have not run out of data.
709 * Consume spaces also.
712 int char_buffer::skip_spaces(char_block **t, int *i)
714 char_block *s = *t;
715 int k = *i;
717 while (s) {
718 while (k < s->used && isspace(s->buffer[k]))
719 k++;
720 if (k == s->used) {
721 k = 0;
722 s = s->next;
724 else {
725 *i = k;
726 return true;
729 return false;
733 * skip_until_newline - Skip all characters until a newline is seen.
734 * The newline is not consumed.
737 void char_buffer::skip_until_newline(char_block **t, int *i)
739 int j = *i;
741 if (*t) {
742 while (j < (*t)->used && (*t)->buffer[j] != '\n')
743 j++;
744 if (j == (*t)->used) {
745 *i = 0;
746 *t = (*t)->next;
747 skip_until_newline(t, i);
749 else
750 // newline was seen
751 *i = j;
755 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
756 #define HTML_OUTPUT_FILTER 0
757 #define IMAGE_OUTPUT_FILTER 1
758 #define OUTPUT_STREAM(name) creat((name), S_IWUSR | S_IRUSR)
759 #define PS_OUTPUT_STREAM OUTPUT_STREAM(psFileName)
760 #define REGION_OUTPUT_STREAM OUTPUT_STREAM(regionFileName)
763 * emit_troff_output - Write formatted buffer content to the troff
764 * post-processor data pipeline.
767 void char_buffer::emit_troff_output(int device_format_selector)
769 // Handle output for BOTH html and image device formats
770 // if `device_format_selector' is passed as
772 // HTML_FORMAT(HTML_OUTPUT_FILTER)
773 // Buffer data is written to the output stream
774 // with template image names translated to actual image names.
776 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
777 // Buffer data is written to the output stream
778 // with no translation, for image file creation in the post-processor.
780 int idx = 0;
781 char_block *element = head;
783 while (element != NULL)
784 write_upto_newline(&element, &idx, device_format_selector);
786 #if 0
787 if (close(stdoutfd) < 0)
788 sys_fatal ("close");
790 // now we grab fd=1 so that the next pipe cannot use fd=1
791 if (stdoutfd == 1) {
792 if (dup(2) != stdoutfd)
793 sys_fatal ("dup failed to use fd=1");
795 #endif /* 0 */
799 * The image class remembers the position of all images in the
800 * postscript file and assigns names for each image.
803 class imageItem
805 public:
806 imageItem *next;
807 int X1;
808 int Y1;
809 int X2;
810 int Y2;
811 char *imageName;
812 int resolution;
813 int maxx;
814 int pageNo;
816 imageItem(int x1, int y1, int x2, int y2,
817 int page, int res, int max_width, char *name);
818 ~imageItem();
822 * imageItem - Constructor.
825 imageItem::imageItem(int x1, int y1, int x2, int y2,
826 int page, int res, int max_width, char *name)
828 X1 = x1;
829 Y1 = y1;
830 X2 = x2;
831 Y2 = y2;
832 pageNo = page;
833 resolution = res;
834 maxx = max_width;
835 imageName = name;
836 next = NULL;
840 * imageItem - Destructor.
843 imageItem::~imageItem()
845 if (imageName)
846 free(imageName);
850 * imageList - A class containing a list of imageItems.
853 class imageList
855 imageItem *head;
856 imageItem *tail;
857 int count;
859 public:
860 imageList();
861 ~imageList();
862 void add(int x1, int y1, int x2, int y2,
863 int page, int res, int maxx, char *name);
864 void createImages(void);
865 int createPage(int pageno);
866 void createImage(imageItem *i);
867 int getMaxX(int pageno);
871 * imageList - Constructor.
874 imageList::imageList()
875 : head(0), tail(0), count(0)
880 * imageList - Destructor.
883 imageList::~imageList()
885 while (head != NULL) {
886 imageItem *i = head;
887 head = head->next;
888 delete i;
893 * createPage - Create one image of, page pageno, from the postscript file.
896 int imageList::createPage(int pageno)
898 char *s;
900 if (currentPageNo == pageno)
901 return 0;
903 if (currentPageNo >= 1) {
905 * We need to unlink the files which change each time a new page is
906 * processed. The final unlink is done by xtmpfile when pre-grohtml
907 * exits.
909 unlink(imagePageName);
910 unlink(psPageName);
913 if (show_progress) {
914 fprintf(stderr, "[%d] ", pageno);
915 fflush(stderr);
918 #if defined(DEBUGGING)
919 if (debug)
920 fprintf(stderr, "creating page %d\n", pageno);
921 #endif
923 s = make_message("psselect -q -p%d %s %s\n",
924 pageno, psFileName, psPageName);
926 if (s == NULL)
927 sys_fatal("make_message");
928 html_system(s, 1);
930 s = make_message("echo showpage | "
931 "%s%s -q -dBATCH -dSAFER "
932 "-dDEVICEHEIGHTPOINTS=792 "
933 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
934 "-sDEVICE=%s -r%d %s "
935 "-sOutputFile=%s %s -\n",
936 image_gen,
937 EXE_EXT,
938 (getMaxX(pageno) * image_res) / postscriptRes,
939 image_device,
940 image_res,
941 antiAlias,
942 imagePageName,
943 psPageName);
944 if (s == NULL)
945 sys_fatal("make_message");
946 html_system(s, 1);
947 free(s);
948 currentPageNo = pageno;
949 return 0;
953 * min - Return the minimum of two numbers.
956 int min(int x, int y)
958 if (x < y)
959 return x;
960 else
961 return y;
965 * max - Return the maximum of two numbers.
968 int max(int x, int y)
970 if (x > y)
971 return x;
972 else
973 return y;
977 * getMaxX - Return the largest right-hand position for any image
978 * on, pageno.
981 int imageList::getMaxX(int pageno)
983 imageItem *h = head;
984 int x = postscriptRes * DEFAULT_LINE_LENGTH;
986 while (h != NULL) {
987 if (h->pageNo == pageno)
988 x = max(h->X2, x);
989 h = h->next;
991 return x;
995 * createImage - Generate a minimal png file from the set of page images.
998 void imageList::createImage(imageItem *i)
1000 if (i->X1 != -1) {
1001 char *s;
1002 int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1003 - IMAGE_BOARDER_PIXELS,
1005 int y1 = max(image_res * vertical_offset / 72
1006 + min(i->Y1, i->Y2) * image_res / postscriptRes
1007 - IMAGE_BOARDER_PIXELS,
1009 int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1010 + IMAGE_BOARDER_PIXELS;
1011 int y2 = image_res * vertical_offset / 72
1012 + max(i->Y1, i->Y2) * image_res / postscriptRes
1013 + 1 + IMAGE_BOARDER_PIXELS;
1014 if (createPage(i->pageNo) == 0) {
1015 s = make_message("pnmcut%s %d %d %d %d < %s "
1016 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1017 EXE_EXT,
1018 x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1019 imagePageName,
1020 EXE_EXT,
1021 TRANSPARENT,
1022 i->imageName);
1023 if (s == NULL)
1024 sys_fatal("make_message");
1026 html_system(s, 0);
1027 free(s);
1029 else {
1030 fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1031 fflush(stderr);
1033 #if defined(DEBUGGING)
1035 else {
1036 if (debug) {
1037 fprintf(stderr, "ignoring image as x1 coord is -1\n");
1038 fflush(stderr);
1040 #endif
1045 * add - Add an image description to the imageList.
1048 void imageList::add(int x1, int y1, int x2, int y2,
1049 int page, int res, int maxx, char *name)
1051 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1053 if (head == NULL) {
1054 head = i;
1055 tail = i;
1057 else {
1058 tail->next = i;
1059 tail = i;
1064 * createImages - For each image descriptor on the imageList,
1065 * create the actual image.
1068 void imageList::createImages(void)
1070 imageItem *h = head;
1072 while (h != NULL) {
1073 createImage(h);
1074 h = h->next;
1078 static imageList listOfImages; // List of images defined by the region file.
1081 * generateImages - Parse the region file and generate images
1082 * from the postscript file. The region file
1083 * contains the x1,y1--x2,y2 extents of each
1084 * image.
1087 static void generateImages(char *region_file_name)
1089 pushBackBuffer *f=new pushBackBuffer(region_file_name);
1091 while (f->putPB(f->getPB()) != eof) {
1092 if (f->isString(L_D_HTML "-info:page")) {
1093 int page = f->readInt();
1094 int x1 = f->readInt();
1095 int y1 = f->readInt();
1096 int x2 = f->readInt();
1097 int y2 = f->readInt();
1098 int maxx = f->readInt();
1099 char *name = f->readString();
1100 int res = postscriptRes;
1101 listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1102 while (f->putPB(f->getPB()) != '\n'
1103 && f->putPB(f->getPB()) != eof)
1104 (void)f->getPB();
1105 if (f->putPB(f->getPB()) == '\n')
1106 (void)f->getPB();
1108 else {
1109 /* Write any error messages out to the user. */
1110 fputc(f->getPB(), stderr);
1114 listOfImages.createImages();
1115 if (show_progress) {
1116 fprintf(stderr, "done\n");
1117 fflush(stderr);
1119 delete f;
1123 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1124 * stream on handle, willbe.
1127 static void set_redirection(int was, int willbe)
1129 // Nothing to do if `was' and `willbe' already have same handle.
1130 if (was != willbe) {
1131 // Otherwise attempt the specified redirection.
1132 if (dup2 (willbe, was) < 0) {
1133 // Redirection failed, so issue diagnostic and bail out.
1134 fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1135 if (willbe == STDOUT_FILENO)
1136 fprintf(stderr,
1137 "likely that stdout should be opened before %d\n", was);
1138 sys_fatal("dup2");
1141 // When redirection has been successfully completed assume redundant
1142 // handle `willbe' is no longer required, so close it.
1143 if (close(willbe) < 0)
1144 // Issue diagnostic if `close' fails.
1145 sys_fatal("close");
1150 * save_and_redirect - Get duplicate handle for stream, was, then
1151 * redirect, was, to refer to, willbe.
1154 static int save_and_redirect(int was, int willbe)
1156 if (was == willbe)
1157 // No redirection specified so don't do anything but silently bailing out.
1158 return (was);
1160 // Proceeding with redirection so first save and verify our duplicate
1161 // handle for `was'.
1162 int saved = dup(was);
1163 if (saved < 0) {
1164 fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1165 sys_fatal("dup");
1168 // Duplicate handle safely established so complete redirection.
1169 set_redirection(was, willbe);
1171 // Finally return the saved duplicate descriptor for the
1172 // original `was' stream.
1173 return saved;
1177 * alterDeviceTo - If, toImage, is set
1178 * the argument list is altered to include
1179 * IMAGE_DEVICE and we invoke groff rather than troff.
1180 * Else
1181 * set -Thtml and groff.
1184 static void alterDeviceTo(int argc, char *argv[], int toImage)
1186 int i = 0;
1188 if (toImage) {
1189 while (i < argc) {
1190 if ((strcmp(argv[i], "-Thtml") == 0) ||
1191 (strcmp(argv[i], "-Txhtml") == 0))
1192 argv[i] = (char *)IMAGE_DEVICE;
1193 i++;
1195 argv[troff_arg] = (char*)L_ROFF; // rather than troff
1197 else {
1198 while (i < argc) {
1199 if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1200 if (dialect == xhtml)
1201 argv[i] = (char *)"-Txhtml";
1202 else
1203 argv[i] = (char *)"-Thtml";
1204 i++;
1206 argv[troff_arg] = (char*)L_ROFF; // use groff -Z
1211 * addArg - Append newarg onto the command list for groff.
1214 char **addArg(int argc, char *argv[], char *newarg)
1216 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1217 int i = 0;
1219 if (new_argv == NULL)
1220 sys_fatal("malloc");
1222 if (argc > 0) {
1223 new_argv[i] = argv[i];
1224 i++;
1226 new_argv[i] = newarg;
1227 while (i < argc) {
1228 new_argv[i + 1] = argv[i];
1229 i++;
1231 argc++;
1232 new_argv[argc] = NULL;
1233 return new_argv;
1237 * addRegDef - Append a defined register or string onto the command
1238 * list for troff.
1241 char **addRegDef(int argc, char *argv[], const char *numReg)
1243 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1244 int i = 0;
1246 if (new_argv == NULL)
1247 sys_fatal("malloc");
1249 while (i < argc) {
1250 new_argv[i] = argv[i];
1251 i++;
1253 new_argv[argc] = strsave(numReg);
1254 argc++;
1255 new_argv[argc] = NULL;
1256 return new_argv;
1260 * dump_args - Display the argument list.
1263 void dump_args(int argc, char *argv[])
1265 fprintf(stderr, " %d arguments:", argc);
1266 for (int i = 0; i < argc; i++)
1267 fprintf(stderr, " %s", argv[i]);
1268 fprintf(stderr, "\n");
1272 * print_args - print arguments as if they were issued on the command line.
1274 #ifdef DEBUGGING
1275 void print_args(int argc, char *argv[])
1277 if (debug) {
1278 fprintf(stderr, "executing: ");
1279 for (int i = 0; i < argc; i++)
1280 fprintf(stderr, "%s ", argv[i]);
1281 fprintf(stderr, "\n");
1284 #else
1285 void print_args(int, char **)
1288 #endif
1290 int char_buffer::run_output_filter(int filter, int argc, char **argv)
1292 int pipedes[2];
1293 PID_T child_pid;
1294 int status;
1296 print_args(argc, argv);
1297 if (pipe(pipedes) < 0)
1298 sys_fatal("pipe");
1300 #if MAY_FORK_CHILD_PROCESS
1301 // This is the UNIX process model. To invoke our post-processor,
1302 // we must `fork' the current process.
1304 if ((child_pid = fork()) < 0)
1305 sys_fatal("fork");
1307 else if (child_pid == 0) {
1308 // This is the child process fork. We redirect its `stdin' stream
1309 // to read data emerging from our pipe. There is no point in saving,
1310 // since we won't be able to restore later!
1312 set_redirection(STDIN_FILENO, pipedes[0]);
1314 // The parent process will be writing this data, so we should release
1315 // the child's writeable handle on the pipe, since we have no use for it.
1317 if (close(pipedes[1]) < 0)
1318 sys_fatal("close");
1320 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1322 if (filter == IMAGE_OUTPUT_FILTER) {
1323 // with BOTH `stdout' AND `stderr' diverted to files.
1325 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1326 set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1329 // Now we are ready to launch the output filter.
1331 execvp(argv[0], argv);
1333 // If we get to here then the `exec...' request for the output filter
1334 // failed. Diagnose it and bail out.
1336 error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1337 fflush(stderr); // just in case error() didn't
1338 exit(1);
1341 else {
1342 // This is the parent process fork. We will be writing data to the
1343 // filter pipeline, and the child will be reading it. We have no further
1344 // use for our read handle on the pipe, and should close it.
1346 if (close(pipedes[0]) < 0)
1347 sys_fatal("close");
1349 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1350 // and push out the appropiately formatted data to the filter.
1352 pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1353 emit_troff_output(DEVICE_FORMAT(filter));
1355 // After emitting all the data we close our connection to the inlet
1356 // end of the pipe so the child process will detect end of data.
1358 set_redirection(STDOUT_FILENO, pipedes[1]);
1360 // Finally, we must wait for the child process to complete.
1362 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1363 sys_fatal("wait");
1366 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1368 // We do not have `fork', (or we prefer not to use it),
1369 // but asynchronous processes are allowed, passing data through pipes.
1370 // This should be ok for most Win32 systems and is preferred to `fork'
1371 // for starting child processes under Cygwin.
1373 // Before we start the post-processor we bind its inherited `stdin'
1374 // stream to the readable end of our pipe, saving our own `stdin' stream
1375 // in `pipedes[0]'.
1377 pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1379 // for the Win32 model,
1380 // we need special provision for saving BOTH `stdout' and `stderr'.
1382 int saved_stdout = dup(STDOUT_FILENO);
1383 int saved_stderr = STDERR_FILENO;
1385 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1387 if (filter == IMAGE_OUTPUT_FILTER) {
1388 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1389 // duplicate handle for `stderr'.
1391 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1392 saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1395 // We then use an asynchronous spawn request to start the post-processor.
1397 if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1398 // Should the spawn request fail we issue a diagnostic and bail out.
1400 error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1401 exit(1);
1404 // Once the post-processor has been started we revert our `stdin'
1405 // to its original saved source, which also closes the readable handle
1406 // for the pipe.
1408 set_redirection(STDIN_FILENO, pipedes[0]);
1410 // if we redirected `stderr', for use by the image post-processor,
1411 // then we also need to reinstate its original assignment.
1413 if (filter == IMAGE_OUTPUT_FILTER)
1414 set_redirection(STDERR_FILENO, saved_stderr);
1416 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1417 // and push out the appropiately formatted data to the filter.
1419 set_redirection(STDOUT_FILENO, pipedes[1]);
1420 emit_troff_output(DEVICE_FORMAT(filter));
1422 // After emitting all the data we close our connection to the inlet
1423 // end of the pipe so the child process will detect end of data.
1425 set_redirection(STDOUT_FILENO, saved_stdout);
1427 // And finally, we must wait for the child process to complete.
1429 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1430 sys_fatal("wait");
1432 #else /* can't do asynchronous pipes! */
1434 // TODO: code to support an MS-DOS style process model
1435 // should go here
1437 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1439 return 0;
1443 * do_html - Set the troff number htmlflip and
1444 * write out the buffer to troff -Thtml.
1447 int char_buffer::do_html(int argc, char *argv[])
1449 string s;
1451 alterDeviceTo(argc, argv, 0);
1452 argv += troff_arg; // skip all arguments up to groff
1453 argc -= troff_arg;
1454 argv = addArg(argc, argv, (char *)"-Z");
1455 argc++;
1457 s = (char *)"-dwww-image-template=";
1458 s += macroset_template; // do not combine these statements,
1459 // otherwise they will not work
1460 s += '\0'; // the trailing `\0' is ignored
1461 argv = addRegDef(argc, argv, s.contents());
1462 argc++;
1464 if (dialect == xhtml) {
1465 argv = addRegDef(argc, argv, "-rxhtml=1");
1466 argc++;
1467 if (eqn_flag) {
1468 argv = addRegDef(argc, argv, "-e");
1469 argc++;
1473 #if defined(DEBUGGING)
1474 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1475 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1476 if (debug) {
1477 int saved_stdout = save_and_redirect(STDOUT_FILENO, HTML_DEBUG_STREAM);
1478 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
1479 set_redirection(STDOUT_FILENO, saved_stdout);
1481 #endif
1483 return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1487 * do_image - Write out the buffer to troff -Tps.
1490 int char_buffer::do_image(int argc, char *argv[])
1492 string s;
1494 alterDeviceTo(argc, argv, 1);
1495 argv += troff_arg; // skip all arguments up to troff/groff
1496 argc -= troff_arg;
1497 argv = addRegDef(argc, argv, "-rps4html=1");
1498 argc++;
1500 s = "-dwww-image-template=";
1501 s += macroset_template;
1502 s += '\0';
1503 argv = addRegDef(argc, argv, s.contents());
1504 argc++;
1506 // override local settings and produce a page size letter postscript file
1507 argv = addRegDef(argc, argv, "-P-pletter");
1508 argc++;
1510 if (dialect == xhtml) {
1511 if (eqn_flag) {
1512 argv = addRegDef(argc, argv, "-rxhtml=1");
1513 argc++;
1515 argv = addRegDef(argc, argv, "-e");
1516 argc++;
1519 #if defined(DEBUGGING)
1520 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1521 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1522 if (debug) {
1523 int saved_stdout = save_and_redirect(STDOUT_FILENO, IMAGE_DEBUG_STREAM);
1524 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
1525 set_redirection(STDOUT_FILENO, saved_stdout);
1527 #endif
1529 return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1532 static char_buffer inputFile;
1535 * usage - Emit usage arguments.
1538 static void usage(FILE *stream)
1540 fprintf(stream,
1541 "\n"
1542 "This program is not intended to be called stand-alone;\n"
1543 "it is part of the " L_ROFF " pipeline to produce HTML output.\n"
1544 "\n"
1545 "If there is ever the need to call it manually (e.g., for\n"
1546 "debugging purposes), add command line option `-V' while calling\n"
1547 "the `" L_ROFF "' program to see which arguments are passed to it.\n"
1548 "\n");
1552 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1553 * and -P-I. Return the argument index of the first
1554 * non-option.
1557 static int scanArguments(int argc, char **argv)
1559 const char *command_prefix = getenv(U_ROFF_COMMAND_PREFIX);
1560 if (!command_prefix)
1561 command_prefix = PROG_PREFIX;
1562 char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1];
1563 strcpy(troff_name, command_prefix);
1564 strcat(troff_name, "troff");
1565 int c, i;
1566 static const struct option long_options[] = {
1567 { "help", no_argument, 0, CHAR_MAX + 1 },
1568 { "version", no_argument, 0, 'v' },
1569 { NULL, 0, 0, 0 }
1571 while ((c = getopt_long(argc, argv, "+a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
1572 long_options, NULL))
1573 != EOF)
1574 switch(c) {
1575 case 'a':
1576 textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1577 MAX_ALPHA_BITS);
1578 if (textAlphaBits == 3) {
1579 error("cannot use 3 bits of antialiasing information");
1580 exit(1);
1582 break;
1583 case 'b':
1584 // handled by post-grohtml (set background color to white)
1585 break;
1586 case 'd':
1587 #ifdef DEBUGGING
1588 debug = true;
1589 #endif
1590 break;
1591 case 'D':
1592 image_dir = optarg;
1593 break;
1594 case 'e':
1595 eqn_flag = true;
1596 break;
1597 case 'F':
1598 font_path.command_line_dir(optarg);
1599 break;
1600 case 'g':
1601 graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1602 MAX_ALPHA_BITS);
1603 if (graphicAlphaBits == 3) {
1604 error("cannot use 3 bits of antialiasing information");
1605 exit(1);
1607 break;
1608 case 'h':
1609 // handled by post-grohtml
1610 break;
1611 case 'i':
1612 image_res = atoi(optarg);
1613 break;
1614 case 'I':
1615 image_template = optarg;
1616 break;
1617 case 'j':
1618 // handled by post-grohtml (set job name for multiple file output)
1619 break;
1620 case 'l':
1621 // handled by post-grohtml (no automatic section links)
1622 break;
1623 case 'n':
1624 // handled by post-grohtml (generate simple heading anchors)
1625 break;
1626 case 'o':
1627 vertical_offset = atoi(optarg);
1628 break;
1629 case 'p':
1630 show_progress = true;
1631 break;
1632 case 'r':
1633 // handled by post-grohtml (no header and footer lines)
1634 break;
1635 case 's':
1636 // handled by post-grohtml (use font size n as the html base font size)
1637 break;
1638 case 'S':
1639 // handled by post-grohtml (set file split level)
1640 break;
1641 case 'v':
1642 puts(L_D_PREHTML " (" T_ROFF ") v" VERSION);
1643 exit(0);
1644 case 'V':
1645 // handled by post-grohtml (create validator button)
1646 break;
1647 case 'x':
1648 // html dialect
1649 if (strcmp(optarg, "x") == 0)
1650 dialect = xhtml;
1651 else if (strcmp(optarg, "4") == 0)
1652 dialect = html4;
1653 else
1654 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
1655 break;
1656 case 'y':
1657 // handled by post-grohtml (create groff signature)
1658 break;
1659 case CHAR_MAX + 1: // --help
1660 usage(stdout);
1661 exit(0);
1662 break;
1663 case '?':
1664 usage(stderr);
1665 exit(1);
1666 break;
1667 default:
1668 break;
1671 i = optind;
1672 while (i < argc) {
1673 if (strcmp(argv[i], troff_name) == 0)
1674 troff_arg = i;
1675 else if (argv[i][0] != '-')
1676 return i;
1677 i++;
1679 a_delete troff_name;
1681 return argc;
1685 * makeTempFiles - Name the temporary files.
1688 static int makeTempFiles(void)
1690 #if defined(DEBUGGING)
1691 psFileName = DEBUG_FILE("prehtml-ps");
1692 regionFileName = DEBUG_FILE("prehtml-region");
1693 imagePageName = DEBUG_FILE("prehtml-page");
1694 psPageName = DEBUG_FILE("prehtml-psn");
1695 troffFileName = DEBUG_FILE("prehtml-troff");
1696 htmlFileName = DEBUG_FILE("prehtml-html");
1697 #else /* not DEBUGGING */
1698 FILE *f;
1700 /* psPageName contains a single page of postscript */
1701 f = xtmpfile(&psPageName,
1702 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1703 true);
1704 if (f == NULL) {
1705 sys_fatal("xtmpfile");
1706 return -1;
1708 fclose(f);
1710 /* imagePageName contains a bitmap image of the single postscript page */
1711 f = xtmpfile(&imagePageName,
1712 PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1713 true);
1714 if (f == NULL) {
1715 sys_fatal("xtmpfile");
1716 return -1;
1718 fclose(f);
1720 /* psFileName contains a postscript file of the complete document */
1721 f = xtmpfile(&psFileName,
1722 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1723 true);
1724 if (f == NULL) {
1725 sys_fatal("xtmpfile");
1726 return -1;
1728 fclose(f);
1730 /* regionFileName contains a list of the images and their boxed coordinates */
1731 f = xtmpfile(&regionFileName,
1732 REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1733 true);
1734 if (f == NULL) {
1735 sys_fatal("xtmpfile");
1736 return -1;
1738 fclose(f);
1740 #endif /* not DEBUGGING */
1741 return 0;
1744 int main(int argc, char **argv)
1746 program_name = argv[0];
1747 int i;
1748 int found = 0;
1749 int ok = 1;
1751 #ifdef CAPTURE_MODE
1752 FILE *dump;
1753 fprintf(stderr, "%s: invoked with %d arguments ...\n", argv[0], argc);
1754 for (i = 0; i < argc; i++)
1755 fprintf(stderr, "%2d: %s\n", i, argv[i]);
1756 if ((dump = fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL) {
1757 while((i = fgetc(stdin)) >= 0)
1758 fputc(i, dump);
1759 fclose(dump);
1761 exit(1);
1762 #endif /* CAPTURE_MODE */
1763 device = "html";
1764 if (!font::load_desc())
1765 fatal("cannot find html/DESC exiting");
1766 image_gen = font::image_generator;
1767 if (image_gen == NULL || (strcmp(image_gen, "") == 0))
1768 fatal("html/DESC must set the image_generator field, exiting");
1769 postscriptRes = get_resolution();
1770 i = scanArguments(argc, argv);
1771 setupAntiAlias();
1772 checkImageDir();
1773 makeFileName();
1774 while (i < argc) {
1775 if (argv[i][0] != '-') {
1776 /* found source file */
1777 ok = do_file(argv[i]);
1778 if (!ok)
1779 return 0;
1780 found = 1;
1782 i++;
1785 if (!found)
1786 do_file("-");
1787 if (makeTempFiles())
1788 return 1;
1789 ok = inputFile.do_image(argc, argv);
1790 if (ok == 0) {
1791 generateImages(regionFileName);
1792 ok = inputFile.do_html(argc, argv);
1794 return ok;
1797 static int do_file(const char *filename)
1799 file_case *fcp;
1801 current_filename = filename;
1802 fcp = file_case::muxer(filename);
1803 if (fcp == NULL) {
1804 assert(strcmp(filename, "-"));
1805 error("can't open `%1': %2", filename, strerror(errno));
1806 return 0;
1809 if (inputFile.read_file(fcp)) {
1810 // XXX
1813 delete fcp;
1814 current_filename = NULL;
1815 return 1;
1818 // s-it2-mode