2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2007, 2008, 2009
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 3 of the License, or
11 * (at your option) any later 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
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.
34 #include "stringclass.h"
37 #include "searchpath.h"
43 #include <sys/types.h>
49 # include <sys/wait.h>
51 #else /* not _POSIX_VERSION */
53 #endif /* not _POSIX_VERSION */
63 /* Establish some definitions to facilitate discrimination between
64 differing runtime environments. */
66 #undef MAY_FORK_CHILD_PROCESS
67 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
69 #if defined(__MSDOS__) || defined(_WIN32)
71 // Most MS-DOS and Win32 environments will be missing the `fork' capability
72 // (some like Cygwin have it, but it is best avoided).
74 # define MAY_FORK_CHILD_PROCESS 0
76 // On these systems, we use `spawn...', instead of `fork' ... `exec...'.
77 # include <process.h> // for `spawn...'
78 # include <fcntl.h> // for attributes of pipes
80 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
82 // These Win32 implementations allow parent and `spawn...'ed child to
83 // multitask asynchronously.
85 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
89 // Others may adopt MS-DOS behaviour where parent must sleep,
90 // from `spawn...' until child terminates.
92 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
94 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
96 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
97 /* When we are building a DEBUGGING version we need to tell pre-grohtml
98 where to put intermediate files (the DEBUGGING version will preserve
101 On a UNIX host, we might simply use `/tmp', but MS-DOS and Win32 will
102 probably not have this on all disk drives, so default to using
103 `c:/temp' instead. (Note that user may choose to override this by
104 supplying a definition such as
106 -DDEBUG_FILE_DIR=d:/path/to/debug/files
108 in the CPPFLAGS to `make'.) */
110 # define DEBUG_FILE_DIR c:/temp
113 #else /* not __MSDOS__ or _WIN32 */
115 // For non-Microsoft environments assume UNIX conventions,
116 // so `fork' is required and child processes are asynchronous.
117 # define MAY_FORK_CHILD_PROCESS 1
118 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
120 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
121 /* For a DEBUGGING version, on the UNIX host, we can also usually rely
122 on being able to use `/tmp' for temporary file storage. (Note that,
123 as in the __MSDOS__ or _WIN32 case above, the user may override this
126 -DDEBUG_FILE_DIR=/path/to/debug/files
130 # define DEBUG_FILE_DIR /tmp
133 #endif /* not __MSDOS__ or _WIN32 */
136 // For a DEBUGGING version, we need some additional macros,
137 // to direct the captured debug mode output to appropriately named files
138 // in the specified DEBUG_FILE_DIR.
140 # define DEBUG_TEXT(text) #text
141 # define DEBUG_NAME(text) DEBUG_TEXT(text)
142 # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
145 extern "C" const char *Version_string;
147 #include "pre-html.h"
148 #include "pushback.h"
149 #include "html-strings.h"
151 #define DEFAULT_LINE_LENGTH 7 // inches wide
152 #define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution
153 #define IMAGE_BOARDER_PIXELS 0
154 #define INLINE_LEADER_CHAR '\\'
156 // Don't use colour names here! Otherwise there is a dependency on
157 // a file called `rgb.txt' which maps names to colours.
158 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
159 #define MIN_ALPHA_BITS 0
160 #define MAX_ALPHA_BITS 4
162 #define PAGE_TEMPLATE_SHORT "pg"
163 #define PAGE_TEMPLATE_LONG "-page-"
164 #define PS_TEMPLATE_SHORT "ps"
165 #define PS_TEMPLATE_LONG "-ps-"
166 #define REGION_TEMPLATE_SHORT "rg"
167 #define REGION_TEMPLATE_LONG "-regions-"
173 # define FALSE (1==0)
177 CENTERED, LEFT, RIGHT, INLINE
180 typedef enum {xhtml, html4} html_dialect;
182 static int postscriptRes = -1; // postscript resolution,
184 static int stdoutfd = 1; // output file descriptor -
185 // normally 1 but might move
187 static char *psFileName = NULL; // name of postscript file
188 static char *psPageName = NULL; // name of file containing
189 // postscript current page
190 static char *regionFileName = NULL; // name of file containing all
192 static char *imagePageName = NULL; // name of bitmap image containing
194 static const char *image_device = "pnmraw";
195 static int image_res = DEFAULT_IMAGE_RES;
196 static int vertical_offset = 0;
197 static char *image_template = NULL; // image template filename
198 static char *macroset_template= NULL; // image template passed to troff
200 static int troff_arg = 0; // troff arg index
201 static char *image_dir = NULL; // user specified image directory
202 static int textAlphaBits = MAX_ALPHA_BITS;
203 static int graphicAlphaBits = MAX_ALPHA_BITS;
204 static char *antiAlias = NULL; // antialias arguments we pass to gs
205 static int show_progress = FALSE; // should we display page numbers as
206 // they are processed?
207 static int currentPageNo = -1; // current image page number
208 #if defined(DEBUGGING)
209 static int debug = FALSE;
210 static char *troffFileName = NULL; // output of pre-html output which
211 // is sent to troff -Tps
212 static char *htmlFileName = NULL; // output of pre-html output which
213 // is sent to troff -Thtml
215 static int eqn_flag = FALSE; // must we preprocess via eqn?
217 static char *linebuf = NULL; // for scanning devps/DESC
218 static int linebufsize = 0;
219 static const char *image_gen = NULL; // the `gs' program
221 const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
222 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
223 static html_dialect dialect = html4;
227 * Images are generated via postscript, gs, and the pnm utilities.
229 #define IMAGE_DEVICE "-Tps"
232 static int do_file(const char *filename);
236 * sys_fatal - Write a fatal error message.
237 * Taken from src/roff/groff/pipeline.c.
240 void sys_fatal(const char *s)
242 fatal("%1: %2", s, strerror(errno));
246 * get_line - Copy a line (w/o newline) from a file to the
247 * global line buffer.
250 int get_line(FILE *f)
255 linebuf = new char[128];
259 // skip leading whitespace
264 if (c != ' ' && c != '\t') {
273 if (i + 1 >= linebufsize) {
274 char *old_linebuf = linebuf;
275 linebuf = new char[linebufsize * 2];
276 memcpy(linebuf, old_linebuf, linebufsize);
277 a_delete old_linebuf;
291 * get_resolution - Return the postscript resolution from devps/DESC.
294 static unsigned int get_resolution(void)
299 f = font_path.open_file("devps/DESC", &pathp);
302 fatal("can't open devps/DESC");
303 while (get_line(f)) {
304 int n = sscanf(linebuf, "res %u", &res);
310 fatal("can't find `res' keyword in devps/DESC");
315 * html_system - A wrapper for system().
318 void html_system(const char *s, int redirect_stdout)
320 #if defined(DEBUGGING)
322 fprintf(stderr, "executing: ");
323 fwrite(s, sizeof(char), strlen(s), stderr);
328 // Redirect standard error to the null device. This is more
329 // portable than using "2> /dev/null", since it doesn't require a
331 int save_stderr = dup(2);
332 int save_stdout = dup(1);
333 int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
334 if (save_stderr > 2 && fdnull > 2)
336 if (redirect_stdout && save_stdout > 1 && fdnull > 1)
340 int status = system(s);
341 dup2(save_stderr, 2);
343 dup2(save_stdout, 1);
345 fprintf(stderr, "Calling `%s' failed\n", s);
347 fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
354 * make_message - Create a string via malloc and place the result of the
355 * va args into string. Finally the new string is returned.
356 * Taken from man page of printf(3).
359 char *make_message(const char *fmt, ...)
361 /* Guess we need no more than 100 bytes. */
366 if ((p = (char *)malloc(size)) == NULL)
369 /* Try to print in the allocated space. */
371 n = vsnprintf(p, size, fmt, ap);
373 /* If that worked, return the string. */
374 if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
382 /* Else try again with more space. */
384 size *= 2; /* twice the old size */
385 if ((np = (char *)realloc(p, size)) == NULL) {
386 free(p); /* realloc failed, free old, p. */
389 p = np; /* use realloc'ed, p */
394 * the class and methods for retaining ascii text
407 * char_block - Constructor. Set the, used, and, next, fields to zero.
410 char_block::char_block()
413 for (int i = 0; i < SIZE; i++)
421 int read_file(FILE *fp);
422 int do_html(int argc, char *argv[]);
423 int do_image(int argc, char *argv[]);
424 void emit_troff_output(int device_format_selector);
425 void write_upto_newline(char_block **t, int *i, int is_html);
426 int can_see(char_block **t, int *i, const char *string);
427 int skip_spaces(char_block **t, int *i);
428 void skip_until_newline(char_block **t, int *i);
432 int run_output_filter(int device_format_selector, int argc, char *argv[]);
436 * char_buffer - Constructor.
439 char_buffer::char_buffer()
445 * char_buffer - Destructor. Throw away the whole buffer list.
448 char_buffer::~char_buffer()
450 while (head != NULL) {
451 char_block *temp = head;
458 * read_file - Read in a complete file, fp, placing the contents inside
462 int char_buffer::read_file(FILE *fp)
467 tail = new char_block;
471 if (tail->used == char_block::SIZE) {
472 tail->next = new char_block;
476 // at this point we have a tail which is ready for the next SIZE
478 n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
483 tail->used += n * sizeof(char);
489 * writeNbytes - Write n bytes to stdout.
492 static void writeNbytes(const char *s, int l)
498 r = write(stdoutfd, s, l - n);
507 * writeString - Write a string to stdout.
510 static void writeString(const char *s)
512 writeNbytes(s, strlen(s));
516 * makeFileName - Create the image filename template
517 * and the macroset image template.
520 static void makeFileName(void)
522 if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
523 error("cannot use a `%%' within the image directory name");
527 if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
528 error("cannot use a `%%' within the image template");
532 if (image_dir == NULL)
533 image_dir = (char *)"";
534 else if (strlen(image_dir) > 0
535 && image_dir[strlen(image_dir) - 1] != '/') {
536 image_dir = make_message("%s/", image_dir);
537 if (image_dir == NULL)
538 sys_fatal("make_message");
541 if (image_template == NULL)
542 macroset_template = make_message("%sgrohtml-%d", image_dir,
545 macroset_template = make_message("%s%s", image_dir, image_template);
547 if (macroset_template == NULL)
548 sys_fatal("make_message");
551 (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
552 if (image_template == NULL)
554 strcpy(image_template, macroset_template);
555 strcat(image_template, "-%d");
559 * setupAntiAlias - Set up the antialias string, used when we call gs.
562 static void setupAntiAlias(void)
564 if (textAlphaBits == 0 && graphicAlphaBits == 0)
565 antiAlias = make_message(" ");
566 else if (textAlphaBits == 0)
567 antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits);
568 else if (graphicAlphaBits == 0)
569 antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits);
571 antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
572 textAlphaBits, graphicAlphaBits);
576 * checkImageDir - Check whether the image directory is available.
579 static void checkImageDir(void)
581 if (image_dir != NULL && strcmp(image_dir, "") != 0)
582 if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST)) {
583 error("cannot create directory `%1'", image_dir);
589 * write_end_image - End the image. Write out the image extents if we
593 static void write_end_image(int is_html)
596 * if we are producing html then these
597 * emit image name and enable output
599 * we are producing images
600 * in which case these generate image
603 writeString("\\O[4]\\O[2]");
605 writeString("\\O[1]");
607 writeString("\\O[0]");
611 * write_start_image - Write troff code which will:
613 * (i) disable html output for the following image
614 * (ii) reset the max/min x/y registers during postscript
618 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
620 writeString("\\O[5");
636 writeString(image_template);
637 writeString(".png]");
639 writeString("\\O[0]\\O[3]");
641 // reset min/max registers
642 writeString("\\O[1]\\O[3]");
646 * write_upto_newline - Write the contents of the buffer until a newline
647 * is seen. Check for HTML_IMAGE_INLINE_BEGIN and
648 * HTML_IMAGE_INLINE_END; process them if they are
652 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
657 while (j < (*t)->used
658 && (*t)->buffer[j] != '\n'
659 && (*t)->buffer[j] != INLINE_LEADER_CHAR)
662 && (*t)->buffer[j] == '\n')
664 writeNbytes((*t)->buffer + (*i), j - (*i));
665 if ((*t)->buffer[j] == INLINE_LEADER_CHAR) {
666 if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
667 write_start_image(INLINE, is_html);
668 else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
669 write_end_image(is_html);
671 if (j < (*t)->used) {
674 writeNbytes((*t)->buffer + (*i), j - (*i));
678 if (j == (*t)->used) {
681 if (*t && (*t)->buffer[j - 1] != '\n')
682 write_upto_newline(t, i, is_html);
691 * can_see - Return TRUE if we can see string in t->buffer[i] onwards.
694 int char_buffer::can_see(char_block **t, int *i, const char *str)
702 while (k < s->used && j < l && s->buffer[k] == str[j]) {
711 else if (k < s->used && s->buffer[k] != str[j])
720 * skip_spaces - Return TRUE if we have not run out of data.
721 * Consume spaces also.
724 int char_buffer::skip_spaces(char_block **t, int *i)
730 while (k < s->used && isspace(s->buffer[k]))
745 * skip_until_newline - Skip all characters until a newline is seen.
746 * The newline is not consumed.
749 void char_buffer::skip_until_newline(char_block **t, int *i)
754 while (j < (*t)->used && (*t)->buffer[j] != '\n')
756 if (j == (*t)->used) {
759 skip_until_newline(t, i);
767 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
768 #define HTML_OUTPUT_FILTER 0
769 #define IMAGE_OUTPUT_FILTER 1
770 #define OUTPUT_STREAM(name) creat((name), S_IWUSR | S_IRUSR)
771 #define PS_OUTPUT_STREAM OUTPUT_STREAM(psFileName)
772 #define REGION_OUTPUT_STREAM OUTPUT_STREAM(regionFileName)
775 * emit_troff_output - Write formatted buffer content to the troff
776 * post-processor data pipeline.
779 void char_buffer::emit_troff_output(int device_format_selector)
781 // Handle output for BOTH html and image device formats
782 // if `device_format_selector' is passed as
784 // HTML_FORMAT(HTML_OUTPUT_FILTER)
785 // Buffer data is written to the output stream
786 // with template image names translated to actual image names.
788 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
789 // Buffer data is written to the output stream
790 // with no translation, for image file creation in the post-processor.
793 char_block *element = head;
795 while (element != NULL)
796 write_upto_newline(&element, &idx, device_format_selector);
799 if (close(stdoutfd) < 0)
802 // now we grab fd=1 so that the next pipe cannot use fd=1
804 if (dup(2) != stdoutfd)
805 sys_fatal ("dup failed to use fd=1");
811 * The image class remembers the position of all images in the
812 * postscript file and assigns names for each image.
826 imageItem(int x1, int y1, int x2, int y2,
827 int page, int res, int max_width, char *name);
832 * imageItem - Constructor.
835 imageItem::imageItem(int x1, int y1, int x2, int y2,
836 int page, int res, int max_width, char *name)
850 * imageItem - Destructor.
853 imageItem::~imageItem()
860 * imageList - A class containing a list of imageItems.
871 void add(int x1, int y1, int x2, int y2,
872 int page, int res, int maxx, char *name);
873 void createImages(void);
874 int createPage(int pageno);
875 void createImage(imageItem *i);
876 int getMaxX(int pageno);
880 * imageList - Constructor.
883 imageList::imageList()
884 : head(0), tail(0), count(0)
889 * imageList - Destructor.
892 imageList::~imageList()
894 while (head != NULL) {
902 * createPage - Create one image of, page pageno, from the postscript file.
905 int imageList::createPage(int pageno)
909 if (currentPageNo == pageno)
912 if (currentPageNo >= 1) {
914 * We need to unlink the files which change each time a new page is
915 * processed. The final unlink is done by xtmpfile when pre-grohtml
918 unlink(imagePageName);
923 fprintf(stderr, "[%d] ", pageno);
927 #if defined(DEBUGGING)
929 fprintf(stderr, "creating page %d\n", pageno);
932 s = make_message("psselect -q -p%d %s %s\n",
933 pageno, psFileName, psPageName);
936 sys_fatal("make_message");
939 s = make_message("echo showpage | "
940 "%s%s -q -dBATCH -dSAFER "
941 "-dDEVICEHEIGHTPOINTS=792 "
942 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
943 "-sDEVICE=%s -r%d %s "
944 "-sOutputFile=%s %s -\n",
947 (getMaxX(pageno) * image_res) / postscriptRes,
954 sys_fatal("make_message");
957 currentPageNo = pageno;
962 * min - Return the minimum of two numbers.
965 int min(int x, int y)
974 * max - Return the maximum of two numbers.
977 int max(int x, int y)
986 * getMaxX - Return the largest right-hand position for any image
990 int imageList::getMaxX(int pageno)
993 int x = postscriptRes * DEFAULT_LINE_LENGTH;
996 if (h->pageNo == pageno)
1004 * createImage - Generate a minimal png file from the set of page images.
1007 void imageList::createImage(imageItem *i)
1011 int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1012 - IMAGE_BOARDER_PIXELS,
1014 int y1 = max(image_res * vertical_offset / 72
1015 + min(i->Y1, i->Y2) * image_res / postscriptRes
1016 - IMAGE_BOARDER_PIXELS,
1018 int x2 = max(i->X1, i->X2) * image_res / postscriptRes
1019 + IMAGE_BOARDER_PIXELS;
1020 int y2 = image_res * vertical_offset / 72
1021 + max(i->Y1, i->Y2) * image_res / postscriptRes
1022 + 1 + IMAGE_BOARDER_PIXELS;
1023 if (createPage(i->pageNo) == 0) {
1024 s = make_message("pnmcut%s %d %d %d %d < %s "
1025 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1027 x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1033 sys_fatal("make_message");
1039 fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1042 #if defined(DEBUGGING)
1046 fprintf(stderr, "ignoring image as x1 coord is -1\n");
1054 * add - Add an image description to the imageList.
1057 void imageList::add(int x1, int y1, int x2, int y2,
1058 int page, int res, int maxx, char *name)
1060 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1073 * createImages - For each image descriptor on the imageList,
1074 * create the actual image.
1077 void imageList::createImages(void)
1079 imageItem *h = head;
1087 static imageList listOfImages; // List of images defined by the region file.
1090 * generateImages - Parse the region file and generate images
1091 * from the postscript file. The region file
1092 * contains the x1,y1--x2,y2 extents of each
1096 static void generateImages(char *region_file_name)
1098 pushBackBuffer *f=new pushBackBuffer(region_file_name);
1100 while (f->putPB(f->getPB()) != eof) {
1101 if (f->isString("grohtml-info:page")) {
1102 int page = f->readInt();
1103 int x1 = f->readInt();
1104 int y1 = f->readInt();
1105 int x2 = f->readInt();
1106 int y2 = f->readInt();
1107 int maxx = f->readInt();
1108 char *name = f->readString();
1109 int res = postscriptRes;
1110 listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
1111 while (f->putPB(f->getPB()) != '\n'
1112 && f->putPB(f->getPB()) != eof)
1114 if (f->putPB(f->getPB()) == '\n')
1118 /* Write any error messages out to the user. */
1119 fputc(f->getPB(), stderr);
1123 listOfImages.createImages();
1124 if (show_progress) {
1125 fprintf(stderr, "done\n");
1132 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1133 * stream on handle, willbe.
1136 static void set_redirection(int was, int willbe)
1138 // Nothing to do if `was' and `willbe' already have same handle.
1139 if (was != willbe) {
1140 // Otherwise attempt the specified redirection.
1141 if (dup2 (willbe, was) < 0) {
1142 // Redirection failed, so issue diagnostic and bail out.
1143 fprintf(stderr, "failed to replace fd=%d with %d\n", was, willbe);
1144 if (willbe == STDOUT_FILENO)
1146 "likely that stdout should be opened before %d\n", was);
1150 // When redirection has been successfully completed assume redundant
1151 // handle `willbe' is no longer required, so close it.
1152 if (close(willbe) < 0)
1153 // Issue diagnostic if `close' fails.
1159 * save_and_redirect - Get duplicate handle for stream, was, then
1160 * redirect, was, to refer to, willbe.
1163 static int save_and_redirect(int was, int willbe)
1166 // No redirection specified so don't do anything but silently bailing out.
1169 // Proceeding with redirection so first save and verify our duplicate
1170 // handle for `was'.
1171 int saved = dup(was);
1173 fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1177 // Duplicate handle safely established so complete redirection.
1178 set_redirection(was, willbe);
1180 // Finally return the saved duplicate descriptor for the
1181 // original `was' stream.
1186 * alterDeviceTo - If, toImage, is set
1187 * the argument list is altered to include
1188 * IMAGE_DEVICE and we invoke groff rather than troff.
1190 * set -Thtml and groff.
1193 static void alterDeviceTo(int argc, char *argv[], int toImage)
1199 if ((strcmp(argv[i], "-Thtml") == 0) ||
1200 (strcmp(argv[i], "-Txhtml") == 0))
1201 argv[i] = (char *)IMAGE_DEVICE;
1204 argv[troff_arg] = (char *)"groff"; /* rather than troff */
1208 if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1209 if (dialect == xhtml)
1210 argv[i] = (char *)"-Txhtml";
1212 argv[i] = (char *)"-Thtml";
1215 argv[troff_arg] = (char *)"groff"; /* use groff -Z */
1220 * addArg - Append newarg onto the command list for groff.
1223 char **addArg(int argc, char *argv[], char *newarg)
1225 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1228 if (new_argv == NULL)
1229 sys_fatal("malloc");
1232 new_argv[i] = argv[i];
1235 new_argv[i] = newarg;
1237 new_argv[i + 1] = argv[i];
1241 new_argv[argc] = NULL;
1246 * addRegDef - Append a defined register or string onto the command
1250 char **addRegDef(int argc, char *argv[], const char *numReg)
1252 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1255 if (new_argv == NULL)
1256 sys_fatal("malloc");
1259 new_argv[i] = argv[i];
1262 new_argv[argc] = strsave(numReg);
1264 new_argv[argc] = NULL;
1269 * dump_args - Display the argument list.
1272 void dump_args(int argc, char *argv[])
1274 fprintf(stderr, " %d arguments:", argc);
1275 for (int i = 0; i < argc; i++)
1276 fprintf(stderr, " %s", argv[i]);
1277 fprintf(stderr, "\n");
1281 * print_args - print arguments as if they were issued on the command line.
1284 #if defined(DEBUGGING)
1286 void print_args(int argc, char *argv[])
1289 fprintf(stderr, "executing: ");
1290 for (int i = 0; i < argc; i++)
1291 fprintf(stderr, "%s ", argv[i]);
1292 fprintf(stderr, "\n");
1298 void print_args(int, char **)
1304 int char_buffer::run_output_filter(int filter, int argc, char **argv)
1310 print_args(argc, argv);
1311 if (pipe(pipedes) < 0)
1314 #if MAY_FORK_CHILD_PROCESS
1315 // This is the UNIX process model. To invoke our post-processor,
1316 // we must `fork' the current process.
1318 if ((child_pid = fork()) < 0)
1321 else if (child_pid == 0) {
1322 // This is the child process fork. We redirect its `stdin' stream
1323 // to read data emerging from our pipe. There is no point in saving,
1324 // since we won't be able to restore later!
1326 set_redirection(STDIN_FILENO, pipedes[0]);
1328 // The parent process will be writing this data, so we should release
1329 // the child's writeable handle on the pipe, since we have no use for it.
1331 if (close(pipedes[1]) < 0)
1334 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1336 if (filter == IMAGE_OUTPUT_FILTER) {
1337 // with BOTH `stdout' AND `stderr' diverted to files.
1339 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1340 set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1343 // Now we are ready to launch the output filter.
1345 execvp(argv[0], argv);
1347 // If we get to here then the `exec...' request for the output filter
1348 // failed. Diagnose it and bail out.
1350 error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1351 fflush(stderr); // just in case error() didn't
1356 // This is the parent process fork. We will be writing data to the
1357 // filter pipeline, and the child will be reading it. We have no further
1358 // use for our read handle on the pipe, and should close it.
1360 if (close(pipedes[0]) < 0)
1363 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1364 // and push out the appropiately formatted data to the filter.
1366 pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1367 emit_troff_output(DEVICE_FORMAT(filter));
1369 // After emitting all the data we close our connection to the inlet
1370 // end of the pipe so the child process will detect end of data.
1372 set_redirection(STDOUT_FILENO, pipedes[1]);
1374 // Finally, we must wait for the child process to complete.
1376 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1380 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1382 // We do not have `fork', (or we prefer not to use it),
1383 // but asynchronous processes are allowed, passing data through pipes.
1384 // This should be ok for most Win32 systems and is preferred to `fork'
1385 // for starting child processes under Cygwin.
1387 // Before we start the post-processor we bind its inherited `stdin'
1388 // stream to the readable end of our pipe, saving our own `stdin' stream
1391 pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1393 // for the Win32 model,
1394 // we need special provision for saving BOTH `stdout' and `stderr'.
1396 int saved_stdout = dup(STDOUT_FILENO);
1397 int saved_stderr = STDERR_FILENO;
1399 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1401 if (filter == IMAGE_OUTPUT_FILTER) {
1402 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1403 // duplicate handle for `stderr'.
1405 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1406 saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1409 // We then use an asynchronous spawn request to start the post-processor.
1411 if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1412 // Should the spawn request fail we issue a diagnostic and bail out.
1414 error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1418 // Once the post-processor has been started we revert our `stdin'
1419 // to its original saved source, which also closes the readable handle
1422 set_redirection(STDIN_FILENO, pipedes[0]);
1424 // if we redirected `stderr', for use by the image post-processor,
1425 // then we also need to reinstate its original assignment.
1427 if (filter == IMAGE_OUTPUT_FILTER)
1428 set_redirection(STDERR_FILENO, saved_stderr);
1430 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1431 // and push out the appropiately formatted data to the filter.
1433 set_redirection(STDOUT_FILENO, pipedes[1]);
1434 emit_troff_output(DEVICE_FORMAT(filter));
1436 // After emitting all the data we close our connection to the inlet
1437 // end of the pipe so the child process will detect end of data.
1439 set_redirection(STDOUT_FILENO, saved_stdout);
1441 // And finally, we must wait for the child process to complete.
1443 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1446 #else /* can't do asynchronous pipes! */
1448 // TODO: code to support an MS-DOS style process model
1451 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1457 * do_html - Set the troff number htmlflip and
1458 * write out the buffer to troff -Thtml.
1461 int char_buffer::do_html(int argc, char *argv[])
1465 alterDeviceTo(argc, argv, 0);
1466 argv += troff_arg; // skip all arguments up to groff
1468 argv = addArg(argc, argv, (char *)"-Z");
1471 s = (char *)"-dwww-image-template=";
1472 s += macroset_template; // do not combine these statements,
1473 // otherwise they will not work
1474 s += '\0'; // the trailing `\0' is ignored
1475 argv = addRegDef(argc, argv, s.contents());
1478 if (dialect == xhtml) {
1479 argv = addRegDef(argc, argv, "-rxhtml=1");
1482 argv = addRegDef(argc, argv, "-e");
1487 #if defined(DEBUGGING)
1488 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1489 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1491 int saved_stdout = save_and_redirect(STDOUT_FILENO, HTML_DEBUG_STREAM);
1492 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
1493 set_redirection(STDOUT_FILENO, saved_stdout);
1497 return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1501 * do_image - Write out the buffer to troff -Tps.
1504 int char_buffer::do_image(int argc, char *argv[])
1508 alterDeviceTo(argc, argv, 1);
1509 argv += troff_arg; // skip all arguments up to troff/groff
1511 argv = addRegDef(argc, argv, "-rps4html=1");
1514 s = "-dwww-image-template=";
1515 s += macroset_template;
1517 argv = addRegDef(argc, argv, s.contents());
1520 // override local settings and produce a page size letter postscript file
1521 argv = addRegDef(argc, argv, "-P-pletter");
1524 if (dialect == xhtml) {
1526 argv = addRegDef(argc, argv, "-rxhtml=1");
1529 argv = addRegDef(argc, argv, "-e");
1533 #if defined(DEBUGGING)
1534 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1535 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1537 int saved_stdout = save_and_redirect(STDOUT_FILENO, IMAGE_DEBUG_STREAM);
1538 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
1539 set_redirection(STDOUT_FILENO, saved_stdout);
1543 return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1546 static char_buffer inputFile;
1549 * usage - Emit usage arguments.
1552 static void usage(FILE *stream)
1556 "This program is not intended to be called stand-alone;\n"
1557 "it is part of the groff pipeline to produce HTML output.\n"
1559 "If there is ever the need to call it manually (e.g., for\n"
1560 "debugging purposes), add command line option `-V' while calling\n"
1561 "the `groff' program to see which arguments are passed to it.\n"
1566 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1567 * and -P-I. Return the argument index of the first
1571 static int scanArguments(int argc, char **argv)
1573 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
1574 if (!command_prefix)
1575 command_prefix = PROG_PREFIX;
1576 char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1];
1577 strcpy(troff_name, command_prefix);
1578 strcat(troff_name, "troff");
1580 static const struct option long_options[] = {
1581 { "help", no_argument, 0, CHAR_MAX + 1 },
1582 { "version", no_argument, 0, 'v' },
1585 while ((c = getopt_long(argc, argv, "+a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
1586 long_options, NULL))
1590 textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1592 if (textAlphaBits == 3) {
1593 error("cannot use 3 bits of antialiasing information");
1598 // handled by post-grohtml (set background color to white)
1601 #if defined(DEBUGGING)
1612 font_path.command_line_dir(optarg);
1615 graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1617 if (graphicAlphaBits == 3) {
1618 error("cannot use 3 bits of antialiasing information");
1623 // handled by post-grohtml
1626 image_res = atoi(optarg);
1629 image_template = optarg;
1632 // handled by post-grohtml (set job name for multiple file output)
1635 // handled by post-grohtml (no automatic section links)
1638 // handled by post-grohtml (generate simple heading anchors)
1641 vertical_offset = atoi(optarg);
1644 show_progress = TRUE;
1647 // handled by post-grohtml (no header and footer lines)
1650 // handled by post-grohtml (use font size n as the html base font size)
1653 // handled by post-grohtml (set file split level)
1656 printf("GNU pre-grohtml (groff) version %s\n", Version_string);
1659 // handled by post-grohtml (create validator button)
1663 if (strcmp(optarg, "x") == 0)
1665 else if (strcmp(optarg, "4") == 0)
1668 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
1671 // handled by post-grohtml (create groff signature)
1673 case CHAR_MAX + 1: // --help
1687 if (strcmp(argv[i], troff_name) == 0)
1689 else if (argv[i][0] != '-')
1693 a_delete troff_name;
1699 * makeTempFiles - Name the temporary files.
1702 static int makeTempFiles(void)
1704 #if defined(DEBUGGING)
1705 psFileName = DEBUG_FILE("prehtml-ps");
1706 regionFileName = DEBUG_FILE("prehtml-region");
1707 imagePageName = DEBUG_FILE("prehtml-page");
1708 psPageName = DEBUG_FILE("prehtml-psn");
1709 troffFileName = DEBUG_FILE("prehtml-troff");
1710 htmlFileName = DEBUG_FILE("prehtml-html");
1711 #else /* not DEBUGGING */
1714 /* psPageName contains a single page of postscript */
1715 f = xtmpfile(&psPageName,
1716 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1719 sys_fatal("xtmpfile");
1724 /* imagePageName contains a bitmap image of the single postscript page */
1725 f = xtmpfile(&imagePageName,
1726 PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1729 sys_fatal("xtmpfile");
1734 /* psFileName contains a postscript file of the complete document */
1735 f = xtmpfile(&psFileName,
1736 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1739 sys_fatal("xtmpfile");
1744 /* regionFileName contains a list of the images and their boxed coordinates */
1745 f = xtmpfile(®ionFileName,
1746 REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1749 sys_fatal("xtmpfile");
1754 #endif /* not DEBUGGING */
1758 int main(int argc, char **argv)
1760 program_name = argv[0];
1767 fprintf(stderr, "%s: invoked with %d arguments ...\n", argv[0], argc);
1768 for (i = 0; i < argc; i++)
1769 fprintf(stderr, "%2d: %s\n", i, argv[i]);
1770 if ((dump = fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL) {
1771 while((i = fgetc(stdin)) >= 0)
1776 #endif /* CAPTURE_MODE */
1778 if (!font::load_desc())
1779 fatal("cannot find devhtml/DESC exiting");
1780 image_gen = font::image_generator;
1781 if (image_gen == NULL || (strcmp(image_gen, "") == 0))
1782 fatal("devhtml/DESC must set the image_generator field, exiting");
1783 postscriptRes = get_resolution();
1784 i = scanArguments(argc, argv);
1789 if (argv[i][0] != '-') {
1790 /* found source file */
1791 ok = do_file(argv[i]);
1801 if (makeTempFiles())
1803 ok = inputFile.do_image(argc, argv);
1805 generateImages(regionFileName);
1806 ok = inputFile.do_html(argc, argv);
1811 static int do_file(const char *filename)
1815 current_filename = filename;
1816 if (strcmp(filename, "-") == 0)
1819 fp = fopen(filename, "r");
1821 error("can't open `%1': %2", filename, strerror(errno));
1826 if (inputFile.read_file(fp)) {
1832 current_filename = NULL;