4 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
5 Written by Gaius Mulley (gaius@glam.ac.uk).
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 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 */
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
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
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
104 in the CPPFLAGS to `make'.) */
106 # define DEBUG_FILE_DIR c:/temp
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
122 -DDEBUG_FILE_DIR=/path/to/debug/files
126 # define DEBUG_FILE_DIR /tmp
129 #endif /* not __MSDOS__ or _WIN32 */
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
141 extern "C" const char *Version_string
;
143 #include "pre-html.h"
144 #include "pushback.h"
145 #include "html-strings.h"
147 #define DEFAULT_LINE_LENGTH 7 // inches wide
148 #define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution
149 #define IMAGE_BOARDER_PIXELS 0
150 #define INLINE_LEADER_CHAR '\\'
152 // Don't use colour names here! Otherwise there is a dependency on
153 // a file called `rgb.txt' which maps names to colours.
154 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
155 #define MIN_ALPHA_BITS 0
156 #define MAX_ALPHA_BITS 4
158 #define PAGE_TEMPLATE_SHORT "pg"
159 #define PAGE_TEMPLATE_LONG "-page-"
160 #define PS_TEMPLATE_SHORT "ps"
161 #define PS_TEMPLATE_LONG "-ps-"
162 #define REGION_TEMPLATE_SHORT "rg"
163 #define REGION_TEMPLATE_LONG "-regions-"
173 # define FALSE (1==0)
177 CENTERED
, LEFT
, RIGHT
, INLINE
180 static int postscriptRes
= -1; // postscript resolution,
182 static int stdoutfd
= 1; // output file descriptor -
183 // normally 1 but might move
185 static char *psFileName
= NULL
; // name of postscript file
186 static char *psPageName
= NULL
; // name of file containing
187 // postscript current page
188 static char *regionFileName
= NULL
; // name of file containing all
190 static char *imagePageName
= NULL
; // name of bitmap image containing
192 static const char *image_device
= "pnmraw";
193 static int image_res
= DEFAULT_IMAGE_RES
;
194 static int vertical_offset
= 0;
195 static char *image_template
= NULL
; // image template filename
196 static char *macroset_template
= NULL
; // image template passed to troff
198 static int troff_arg
= 0; // troff arg index
199 static char *image_dir
= NULL
; // user specified image directory
200 static int textAlphaBits
= MAX_ALPHA_BITS
;
201 static int graphicAlphaBits
= MAX_ALPHA_BITS
;
202 static char *antiAlias
= NULL
; // antialias arguments we pass to gs
203 static int show_progress
= FALSE
; // should we display page numbers as
204 // they are processed?
205 static int currentPageNo
= -1; // current image page number
206 #if defined(DEBUGGING)
207 static int debug
= FALSE
;
208 static char *troffFileName
= NULL
; // output of pre-html output which
209 // is sent to troff -Tps
210 static char *htmlFileName
= NULL
; // output of pre-html output which
211 // is sent to troff -Thtml
214 static char *linebuf
= NULL
; // for scanning devps/DESC
215 static int linebufsize
= 0;
216 static const char *image_gen
= NULL
; // the `gs' program
218 const char *const FONT_ENV_VAR
= "GROFF_FONT_PATH";
219 static search_path
font_path(FONT_ENV_VAR
, FONTPATH
, 0, 0);
223 * Images are generated via postscript, gs, and the pnm utilities.
225 #define IMAGE_DEVICE "-Tps"
228 static int do_file(const char *filename
);
232 * sys_fatal - Write a fatal error message.
233 * Taken from src/roff/groff/pipeline.c.
236 void sys_fatal(const char *s
)
238 fatal("%1: %2", s
, strerror(errno
));
242 * get_line - Copy a line (w/o newline) from a file to the
243 * global line buffer.
246 int get_line(FILE *f
)
251 linebuf
= new char[128];
255 // skip leading whitespace
260 if (c
!= ' ' && c
!= '\t') {
269 if (i
+ 1 >= linebufsize
) {
270 char *old_linebuf
= linebuf
;
271 linebuf
= new char[linebufsize
* 2];
272 memcpy(linebuf
, old_linebuf
, linebufsize
);
273 a_delete old_linebuf
;
287 * get_resolution - Return the postscript resolution from devps/DESC.
290 static unsigned int get_resolution(void)
295 f
= font_path
.open_file("devps/DESC", &pathp
);
298 fatal("can't open devps/DESC");
299 while (get_line(f
)) {
300 int n
= sscanf(linebuf
, "res %u", &res
);
306 fatal("can't find `res' keyword in devps/DESC");
311 * html_system - A wrapper for system().
314 void html_system(const char *s
, int redirect_stdout
)
316 // Redirect standard error to the null device. This is more
317 // portable than using "2> /dev/null", since it doesn't require a
319 int save_stderr
= dup(2);
320 int save_stdout
= dup(1);
321 int fdnull
= open(NULL_DEV
, O_WRONLY
|O_BINARY
, 0666);
322 if (save_stderr
> 2 && fdnull
> 2)
324 if (redirect_stdout
&& save_stdout
> 1 && fdnull
> 1)
328 int status
= system(s
);
329 dup2(save_stderr
, 2);
331 dup2(save_stdout
, 1);
333 fprintf(stderr
, "Calling `%s' failed\n", s
);
335 fprintf(stderr
, "Calling `%s' returned status %d\n", s
, status
);
341 * make_message - Create a string via malloc and place the result of the
342 * va args into string. Finally the new string is returned.
343 * Taken from man page of printf(3).
346 char *make_message(const char *fmt
, ...)
348 /* Guess we need no more than 100 bytes. */
353 if ((p
= (char *)malloc(size
)) == NULL
)
356 /* Try to print in the allocated space. */
358 n
= vsnprintf(p
, size
, fmt
, ap
);
360 /* If that worked, return the string. */
361 if (n
> -1 && n
< size
- 1) { /* glibc 2.1 and pre-ANSI C 99 */
369 /* Else try again with more space. */
371 size
*= 2; /* twice the old size */
372 if ((np
= (char *)realloc(p
, size
)) == NULL
) {
373 free(p
); /* realloc failed, free old, p. */
376 p
= np
; /* use realloc'ed, p */
381 * the class and methods for retaining ascii text
394 * char_block - Constructor. Set the, used, and, next, fields to zero.
397 char_block::char_block()
400 for (int i
= 0; i
< SIZE
; i
++)
408 int read_file(FILE *fp
);
409 int do_html(int argc
, char *argv
[]);
410 int do_image(int argc
, char *argv
[]);
411 void emit_troff_output(int device_format_selector
);
412 void write_upto_newline(char_block
**t
, int *i
, int is_html
);
413 int can_see(char_block
**t
, int *i
, const char *string
);
414 int skip_spaces(char_block
**t
, int *i
);
415 void skip_until_newline(char_block
**t
, int *i
);
419 int run_output_filter(int device_format_selector
, int argc
, char *argv
[]);
423 * char_buffer - Constructor.
426 char_buffer::char_buffer()
432 * char_buffer - Destructor. Throw away the whole buffer list.
435 char_buffer::~char_buffer()
437 while (head
!= NULL
) {
438 char_block
*temp
= head
;
445 * read_file - Read in a complete file, fp, placing the contents inside
449 int char_buffer::read_file(FILE *fp
)
454 tail
= new char_block
;
458 if (tail
->used
== char_block::SIZE
) {
459 tail
->next
= new char_block
;
463 // at this point we have a tail which is ready for the next SIZE
465 n
= fread(tail
->buffer
, sizeof(char), char_block::SIZE
-tail
->used
, fp
);
470 tail
->used
+= n
* sizeof(char);
476 * writeNbytes - Write n bytes to stdout.
479 static void writeNbytes(const char *s
, int l
)
485 r
= write(stdoutfd
, s
, l
- n
);
494 * writeString - Write a string to stdout.
497 static void writeString(const char *s
)
499 writeNbytes(s
, strlen(s
));
503 * makeFileName - Create the image filename template
504 * and the macroset image template.
507 static void makeFileName(void)
509 if ((image_dir
!= NULL
) && (strchr(image_dir
, '%') != NULL
)) {
510 error("cannot use a `%%' within the image directory name");
514 if ((image_template
!= NULL
) && (strchr(image_template
, '%') != NULL
)) {
515 error("cannot use a `%%' within the image template");
519 if (image_dir
== NULL
)
520 image_dir
= (char *)"";
521 else if (strlen(image_dir
) > 0
522 && image_dir
[strlen(image_dir
) - 1] != '/') {
523 image_dir
= make_message("%s/", image_dir
);
524 if (image_dir
== NULL
)
525 sys_fatal("make_message");
528 if (image_template
== NULL
)
529 macroset_template
= make_message("%sgrohtml-%d", image_dir
,
532 macroset_template
= make_message("%s%s", image_dir
, image_template
);
534 if (macroset_template
== NULL
)
535 sys_fatal("make_message");
538 (char *)malloc(strlen("-%d") + strlen(macroset_template
) + 1);
539 if (image_template
== NULL
)
541 strcpy(image_template
, macroset_template
);
542 strcat(image_template
, "-%d");
546 * setupAntiAlias - Set up the antialias string, used when we call gs.
549 static void setupAntiAlias(void)
551 if (textAlphaBits
== 0 && graphicAlphaBits
== 0)
552 antiAlias
= make_message(" ");
553 else if (textAlphaBits
== 0)
554 antiAlias
= make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits
);
555 else if (graphicAlphaBits
== 0)
556 antiAlias
= make_message("-dTextAlphaBits=%d ", textAlphaBits
);
558 antiAlias
= make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
559 textAlphaBits
, graphicAlphaBits
);
563 * checkImageDir - Check whether the image directory is available.
566 static void checkImageDir(void)
568 if (image_dir
!= NULL
&& strcmp(image_dir
, "") != 0)
569 if (!(mkdir(image_dir
, 0777) == 0 || errno
== EEXIST
)) {
570 error("cannot create directory `%1'", image_dir
);
576 * write_end_image - End the image. Write out the image extents if we
580 static void write_end_image(int is_html
)
583 * if we are producing html then these
584 * emit image name and enable output
586 * we are producing images
587 * in which case these generate image
590 writeString("\\O[4]\\O[2]");
592 writeString("\\O[1]");
594 writeString("\\O[0]");
598 * write_start_image - Write troff code which will:
600 * (i) disable html output for the following image
601 * (ii) reset the max/min x/y registers during postscript
605 static void write_start_image(IMAGE_ALIGNMENT pos
, int is_html
)
607 writeString("\\O[5");
623 writeString(image_template
);
624 writeString(".png]");
626 writeString("\\O[0]\\O[3]");
628 // reset min/max registers
629 writeString("\\O[1]\\O[3]");
633 * write_upto_newline - Write the contents of the buffer until a newline
634 * is seen. Check for HTML_IMAGE_INLINE_BEGIN and
635 * HTML_IMAGE_INLINE_END; process them if they are
639 void char_buffer::write_upto_newline(char_block
**t
, int *i
, int is_html
)
644 while (j
< (*t
)->used
645 && (*t
)->buffer
[j
] != '\n'
646 && (*t
)->buffer
[j
] != INLINE_LEADER_CHAR
)
649 && (*t
)->buffer
[j
] == '\n')
651 writeNbytes((*t
)->buffer
+ (*i
), j
- (*i
));
652 if ((*t
)->buffer
[j
] == INLINE_LEADER_CHAR
) {
653 if (can_see(t
, &j
, HTML_IMAGE_INLINE_BEGIN
))
654 write_start_image(INLINE
, is_html
);
655 else if (can_see(t
, &j
, HTML_IMAGE_INLINE_END
))
656 write_end_image(is_html
);
658 if (j
< (*t
)->used
) {
661 writeNbytes((*t
)->buffer
+ (*i
), j
- (*i
));
665 if (j
== (*t
)->used
) {
668 if (*t
&& (*t
)->buffer
[j
- 1] != '\n')
669 write_upto_newline(t
, i
, is_html
);
678 * can_see - Return TRUE if we can see string in t->buffer[i] onwards.
681 int char_buffer::can_see(char_block
**t
, int *i
, const char *str
)
689 while (k
< s
->used
&& j
< l
&& s
->buffer
[k
] == str
[j
]) {
698 else if (k
< s
->used
&& s
->buffer
[k
] != str
[j
])
707 * skip_spaces - Return TRUE if we have not run out of data.
708 * Consume spaces also.
711 int char_buffer::skip_spaces(char_block
**t
, int *i
)
717 while (k
< s
->used
&& isspace(s
->buffer
[k
]))
732 * skip_until_newline - Skip all characters until a newline is seen.
733 * The newline is not consumed.
736 void char_buffer::skip_until_newline(char_block
**t
, int *i
)
741 while (j
< (*t
)->used
&& (*t
)->buffer
[j
] != '\n')
743 if (j
== (*t
)->used
) {
746 skip_until_newline(t
, i
);
754 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
755 #define HTML_OUTPUT_FILTER 0
756 #define IMAGE_OUTPUT_FILTER 1
757 #define OUTPUT_STREAM(name) creat((name), S_IWUSR | S_IRUSR)
758 #define PS_OUTPUT_STREAM OUTPUT_STREAM(psFileName)
759 #define REGION_OUTPUT_STREAM OUTPUT_STREAM(regionFileName)
762 * emit_troff_output - Write formatted buffer content to the troff
763 * post-processor data pipeline.
766 void char_buffer::emit_troff_output(int device_format_selector
)
768 // Handle output for BOTH html and image device formats
769 // if `device_format_selector' is passed as
771 // HTML_FORMAT(HTML_OUTPUT_FILTER)
772 // Buffer data is written to the output stream
773 // with template image names translated to actual image names.
775 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
776 // Buffer data is written to the output stream
777 // with no translation, for image file creation in the post-processor.
780 char_block
*element
= head
;
782 while (element
!= NULL
)
783 write_upto_newline(&element
, &idx
, device_format_selector
);
786 if (close(stdoutfd
) < 0)
789 // now we grab fd=1 so that the next pipe cannot use fd=1
791 if (dup(2) != stdoutfd
)
792 sys_fatal ("dup failed to use fd=1");
798 * The image class remembers the position of all images in the
799 * postscript file and assigns names for each image.
813 imageItem(int x1
, int y1
, int x2
, int y2
,
814 int page
, int res
, int max_width
, char *name
);
819 * imageItem - Constructor.
822 imageItem::imageItem(int x1
, int y1
, int x2
, int y2
,
823 int page
, int res
, int max_width
, char *name
)
837 * imageItem - Destructor.
840 imageItem::~imageItem()
847 * imageList - A class containing a list of imageItems.
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
) {
889 * createPage - Create one image of, page pageno, from the postscript file.
892 int imageList::createPage(int pageno
)
896 if (currentPageNo
== pageno
)
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
905 unlink(imagePageName
);
910 fprintf(stderr
, "[%d] ", pageno
);
914 #if defined(DEBUGGING)
916 fprintf(stderr
, "creating page %d\n", pageno
);
919 s
= make_message("psselect -q -p%d %s %s\n",
920 pageno
, psFileName
, psPageName
);
923 sys_fatal("make_message");
924 #if defined(DEBUGGING)
926 fwrite(s
, sizeof(char), strlen(s
), stderr
);
932 s
= make_message("echo showpage | "
933 "%s%s -q -dBATCH -dSAFER "
934 "-dDEVICEHEIGHTPOINTS=792 "
935 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
936 "-sDEVICE=%s -r%d %s "
937 "-sOutputFile=%s %s -\n",
940 (getMaxX(pageno
) * image_res
) / postscriptRes
,
947 sys_fatal("make_message");
948 #if defined(DEBUGGING)
950 fwrite(s
, sizeof(char), strlen(s
), stderr
);
956 currentPageNo
= pageno
;
961 * min - Return the minimum of two numbers.
964 int min(int x
, int y
)
973 * max - Return the maximum of two numbers.
976 int max(int x
, int y
)
985 * getMaxX - Return the largest right-hand position for any image
989 int imageList::getMaxX(int pageno
)
992 int x
= postscriptRes
* DEFAULT_LINE_LENGTH
;
995 if (h
->pageNo
== pageno
)
1003 * createImage - Generate a minimal png file from the set of page images.
1006 void imageList::createImage(imageItem
*i
)
1010 int x1
= max(min(i
->X1
, i
->X2
) * image_res
/ postscriptRes
1011 - IMAGE_BOARDER_PIXELS
,
1013 int y1
= max(image_res
* vertical_offset
/ 72
1014 + min(i
->Y1
, i
->Y2
) * image_res
/ postscriptRes
1015 - IMAGE_BOARDER_PIXELS
,
1017 int x2
= max(i
->X1
, i
->X2
) * image_res
/ postscriptRes
1018 + IMAGE_BOARDER_PIXELS
;
1019 int y2
= image_res
* vertical_offset
/ 72
1020 + max(i
->Y1
, i
->Y2
) * image_res
/ postscriptRes
1021 + 1 + IMAGE_BOARDER_PIXELS
;
1022 if (createPage(i
->pageNo
) == 0) {
1023 s
= make_message("pnmcut%s %d %d %d %d < %s "
1024 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1026 x1
, y1
, x2
- x1
+ 1, y2
- y1
+ 1,
1032 sys_fatal("make_message");
1034 #if defined(DEBUGGING)
1044 fprintf(stderr
, "failed to generate image of page %d\n", i
->pageNo
);
1047 #if defined(DEBUGGING)
1051 fprintf(stderr
, "ignoring image as x1 coord is -1\n");
1059 * add - Add an image description to the imageList.
1062 void imageList::add(int x1
, int y1
, int x2
, int y2
,
1063 int page
, int res
, int maxx
, char *name
)
1065 imageItem
*i
= new imageItem(x1
, y1
, x2
, y2
, page
, res
, maxx
, name
);
1078 * createImages - For each image descriptor on the imageList,
1079 * create the actual image.
1082 void imageList::createImages(void)
1084 imageItem
*h
= head
;
1092 static imageList listOfImages
; // List of images defined by the region file.
1095 * generateImages - Parse the region file and generate images
1096 * from the postscript file. The region file
1097 * contains the x1,y1--x2,y2 extents of each
1101 static void generateImages(char *region_file_name
)
1103 pushBackBuffer
*f
=new pushBackBuffer(region_file_name
);
1105 while (f
->putPB(f
->getPB()) != eof
) {
1106 if (f
->isString("grohtml-info:page")) {
1107 int page
= f
->readInt();
1108 int x1
= f
->readInt();
1109 int y1
= f
->readInt();
1110 int x2
= f
->readInt();
1111 int y2
= f
->readInt();
1112 int maxx
= f
->readInt();
1113 char *name
= f
->readString();
1114 int res
= postscriptRes
;
1115 listOfImages
.add(x1
, y1
, x2
, y2
, page
, res
, maxx
, name
);
1116 while (f
->putPB(f
->getPB()) != '\n'
1117 && f
->putPB(f
->getPB()) != eof
)
1119 if (f
->putPB(f
->getPB()) == '\n')
1123 /* Write any error messages out to the user. */
1124 fputc(f
->getPB(), stderr
);
1128 listOfImages
.createImages();
1129 if (show_progress
) {
1130 fprintf(stderr
, "done\n");
1137 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1138 * stream on handle, willbe.
1141 static void set_redirection(int was
, int willbe
)
1143 // Nothing to do if `was' and `willbe' already have same handle.
1144 if (was
!= willbe
) {
1145 // Otherwise attempt the specified redirection.
1146 if (dup2 (willbe
, was
) < 0) {
1147 // Redirection failed, so issue diagnostic and bail out.
1148 fprintf(stderr
, "failed to replace fd=%d with %d\n", was
, willbe
);
1149 if (willbe
== STDOUT_FILENO
)
1151 "likely that stdout should be opened before %d\n", was
);
1155 // When redirection has been successfully completed assume redundant
1156 // handle `willbe' is no longer required, so close it.
1157 if (close(willbe
) < 0)
1158 // Issue diagnostic if `close' fails.
1164 * save_and_redirect - Get duplicate handle for stream, was, then
1165 * redirect, was, to refer to, willbe.
1168 static int save_and_redirect(int was
, int willbe
)
1171 // No redirection specified so don't do anything but silently bailing out.
1174 // Proceeding with redirection so first save and verify our duplicate
1175 // handle for `was'.
1176 int saved
= dup(was
);
1178 fprintf(stderr
, "unable to get duplicate handle for %d\n", was
);
1182 // Duplicate handle safely established so complete redirection.
1183 set_redirection(was
, willbe
);
1185 // Finally return the saved duplicate descriptor for the
1186 // original `was' stream.
1191 * alterDeviceTo - If, toImage, is set
1192 * the argument list is altered to include
1193 * IMAGE_DEVICE and we invoke groff rather than troff.
1195 * set -Thtml and groff.
1198 static void alterDeviceTo(int argc
, char *argv
[], int toImage
)
1204 if (strcmp(argv
[i
], "-Thtml") == 0)
1205 argv
[i
] = (char *)IMAGE_DEVICE
;
1208 argv
[troff_arg
] = (char *)"groff"; /* rather than troff */
1212 if (strcmp(argv
[i
], IMAGE_DEVICE
) == 0)
1213 argv
[i
] = (char *)"-Thtml";
1216 argv
[troff_arg
] = (char *)"groff"; /* use groff -Z */
1221 * addZ - Append -Z onto the command list for groff.
1224 char **addZ(int argc
, char *argv
[])
1226 char **new_argv
= (char **)malloc((argc
+ 2) * sizeof(char *));
1229 if (new_argv
== NULL
)
1230 sys_fatal("malloc");
1233 new_argv
[i
] = argv
[i
];
1236 new_argv
[i
] = (char *)"-Z";
1238 new_argv
[i
+ 1] = argv
[i
];
1242 new_argv
[argc
] = NULL
;
1247 * addRegDef - Append a defined register or string onto the command
1251 char **addRegDef(int argc
, char *argv
[], const char *numReg
)
1253 char **new_argv
= (char **)malloc((argc
+ 2) * sizeof(char *));
1256 if (new_argv
== NULL
)
1257 sys_fatal("malloc");
1260 new_argv
[i
] = argv
[i
];
1263 new_argv
[argc
] = strsave(numReg
);
1265 new_argv
[argc
] = NULL
;
1270 * dump_args - Display the argument list.
1273 void dump_args(int argc
, char *argv
[])
1275 fprintf(stderr
, " %d arguments:", argc
);
1276 for (int i
= 0; i
< argc
; i
++)
1277 fprintf(stderr
, " %s", argv
[i
]);
1278 fprintf(stderr
, "\n");
1281 int char_buffer::run_output_filter(int filter
, int /* argc */, char **argv
)
1287 if (pipe(pipedes
) < 0)
1290 #if MAY_FORK_CHILD_PROCESS
1291 // This is the UNIX process model. To invoke our post-processor,
1292 // we must `fork' the current process.
1294 if ((child_pid
= fork()) < 0)
1297 else if (child_pid
== 0) {
1298 // This is the child process fork. We redirect its `stdin' stream
1299 // to read data emerging from our pipe. There is no point in saving,
1300 // since we won't be able to restore later!
1302 set_redirection(STDIN_FILENO
, pipedes
[0]);
1304 // The parent process will be writing this data, so we should release
1305 // the child's writeable handle on the pipe, since we have no use for it.
1307 if (close(pipedes
[1]) < 0)
1310 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1312 if (filter
== IMAGE_OUTPUT_FILTER
) {
1313 // with BOTH `stdout' AND `stderr' diverted to files.
1315 set_redirection(STDOUT_FILENO
, PS_OUTPUT_STREAM
);
1316 set_redirection(STDERR_FILENO
, REGION_OUTPUT_STREAM
);
1319 // Now we are ready to launch the output filter.
1321 execvp(argv
[0], argv
);
1323 // If we get to here then the `exec...' request for the output filter
1324 // failed. Diagnose it and bail out.
1326 error("couldn't exec %1: %2", argv
[0], strerror(errno
), ((char *)0));
1327 fflush(stderr
); // just in case error() didn't
1332 // This is the parent process fork. We will be writing data to the
1333 // filter pipeline, and the child will be reading it. We have no further
1334 // use for our read handle on the pipe, and should close it.
1336 if (close(pipedes
[0]) < 0)
1339 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1340 // and push out the appropiately formatted data to the filter.
1342 pipedes
[1] = save_and_redirect(STDOUT_FILENO
, pipedes
[1]);
1343 emit_troff_output(DEVICE_FORMAT(filter
));
1345 // After emitting all the data we close our connection to the inlet
1346 // end of the pipe so the child process will detect end of data.
1348 set_redirection(STDOUT_FILENO
, pipedes
[1]);
1350 // Finally, we must wait for the child process to complete.
1352 if (WAIT(&status
, child_pid
, _WAIT_CHILD
) != child_pid
)
1356 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1358 // We do not have `fork', (or we prefer not to use it),
1359 // but asynchronous processes are allowed, passing data through pipes.
1360 // This should be ok for most Win32 systems and is preferred to `fork'
1361 // for starting child processes under Cygwin.
1363 // Before we start the post-processor we bind its inherited `stdin'
1364 // stream to the readable end of our pipe, saving our own `stdin' stream
1367 pipedes
[0] = save_and_redirect(STDIN_FILENO
, pipedes
[0]);
1369 // for the Win32 model,
1370 // we need special provision for saving BOTH `stdout' and `stderr'.
1372 int saved_stdout
= dup(STDOUT_FILENO
);
1373 int saved_stderr
= STDERR_FILENO
;
1375 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1377 if (filter
== IMAGE_OUTPUT_FILTER
) {
1378 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1379 // duplicate handle for `stderr'.
1381 set_redirection(STDOUT_FILENO
, PS_OUTPUT_STREAM
);
1382 saved_stderr
= save_and_redirect(STDERR_FILENO
, REGION_OUTPUT_STREAM
);
1385 // We then use an asynchronous spawn request to start the post-processor.
1387 if ((child_pid
= spawnvp(_P_NOWAIT
, argv
[0], argv
)) < 0) {
1388 // Should the spawn request fail we issue a diagnostic and bail out.
1390 error("cannot spawn %1: %2", argv
[0], strerror(errno
), ((char *)0));
1394 // Once the post-processor has been started we revert our `stdin'
1395 // to its original saved source, which also closes the readable handle
1398 set_redirection(STDIN_FILENO
, pipedes
[0]);
1400 // if we redirected `stderr', for use by the image post-processor,
1401 // then we also need to reinstate its original assignment.
1403 if (filter
== IMAGE_OUTPUT_FILTER
)
1404 set_redirection(STDERR_FILENO
, saved_stderr
);
1406 // Now we redirect the `stdout' stream to the inlet end of the pipe,
1407 // and push out the appropiately formatted data to the filter.
1409 set_redirection(STDOUT_FILENO
, pipedes
[1]);
1410 emit_troff_output(DEVICE_FORMAT(filter
));
1412 // After emitting all the data we close our connection to the inlet
1413 // end of the pipe so the child process will detect end of data.
1415 set_redirection(STDOUT_FILENO
, saved_stdout
);
1417 // And finally, we must wait for the child process to complete.
1419 if (WAIT(&status
, child_pid
, _WAIT_CHILD
) != child_pid
)
1422 #else /* can't do asynchronous pipes! */
1424 // TODO: code to support an MS-DOS style process model
1427 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1433 * do_html - Set the troff number htmlflip and
1434 * write out the buffer to troff -Thtml.
1437 int char_buffer::do_html(int argc
, char *argv
[])
1441 alterDeviceTo(argc
, argv
, 0);
1442 argv
+= troff_arg
; // skip all arguments up to groff
1444 argv
= addZ(argc
, argv
);
1447 s
= "-dwww-image-template=";
1448 s
+= macroset_template
; // do not combine these statements,
1449 // otherwise they will not work
1450 s
+= '\0'; // the trailing `\0' is ignored
1451 argv
= addRegDef(argc
, argv
, s
.contents());
1454 #if defined(DEBUGGING)
1455 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1456 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1458 int saved_stdout
= save_and_redirect(STDOUT_FILENO
, HTML_DEBUG_STREAM
);
1459 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER
));
1460 set_redirection(STDOUT_FILENO
, saved_stdout
);
1464 return run_output_filter(HTML_OUTPUT_FILTER
, argc
, argv
);
1468 * do_image - Write out the buffer to troff -Tps.
1471 int char_buffer::do_image(int argc
, char *argv
[])
1475 alterDeviceTo(argc
, argv
, 1);
1476 argv
+= troff_arg
; // skip all arguments up to troff/groff
1478 argv
= addRegDef(argc
, argv
, "-rps4html=1");
1481 s
= "-dwww-image-template=";
1482 s
+= macroset_template
;
1484 argv
= addRegDef(argc
, argv
, s
.contents());
1487 // override local settings and produce a page size letter postscript file
1488 argv
= addRegDef(argc
, argv
, "-P-pletter");
1491 #if defined(DEBUGGING)
1492 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1493 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1495 int saved_stdout
= save_and_redirect(STDOUT_FILENO
, IMAGE_DEBUG_STREAM
);
1496 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER
));
1497 set_redirection(STDOUT_FILENO
, saved_stdout
);
1501 return run_output_filter(IMAGE_OUTPUT_FILTER
, argc
, argv
);
1504 static char_buffer inputFile
;
1507 * usage - Emit usage arguments.
1510 static void usage(FILE *stream
)
1513 "usage: %s troffname [-Iimage_name] [-Dimage_directory]\n"
1514 " [-P-o vertical_image_offset] [-P-i image_resolution]\n"
1518 " vertical_image_offset (default %d/72 of an inch)\n",
1521 " image_resolution (default %d) pixels per inch\n",
1524 " image_name is the name of the stem for all images\n"
1525 " (default is grohtml-<pid>)\n");
1527 " place all png files into image_directory\n");
1531 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1532 * and -P-I. Return the argument index of the first
1536 static int scanArguments(int argc
, char **argv
)
1538 const char *command_prefix
= getenv("GROFF_COMMAND_PREFIX");
1539 if (!command_prefix
)
1540 command_prefix
= PROG_PREFIX
;
1541 char *troff_name
= new char[strlen(command_prefix
) + strlen("troff") + 1];
1542 strcpy(troff_name
, command_prefix
);
1543 strcat(troff_name
, "troff");
1545 static const struct option long_options
[] = {
1546 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
1547 { "version", no_argument
, 0, 'v' },
1550 while ((c
= getopt_long(argc
, argv
, "+a:bdD:F:g:hi:I:j:lno:prs:S:v",
1551 long_options
, NULL
))
1555 textAlphaBits
= min(max(MIN_ALPHA_BITS
, atoi(optarg
)),
1557 if (textAlphaBits
== 3) {
1558 error("cannot use 3 bits of antialiasing information");
1563 // handled by post-grohtml (set background color to white)
1566 #if defined(DEBUGGING)
1574 font_path
.command_line_dir(optarg
);
1577 graphicAlphaBits
= min(max(MIN_ALPHA_BITS
, atoi(optarg
)),
1579 if (graphicAlphaBits
== 3) {
1580 error("cannot use 3 bits of antialiasing information");
1585 // handled by post-grohtml
1588 image_res
= atoi(optarg
);
1591 image_template
= optarg
;
1594 // handled by post-grohtml (set job name for multiple file output)
1597 // handled by post-grohtml (no automatic section links)
1600 // handled by post-grohtml (generate simple heading anchors)
1603 vertical_offset
= atoi(optarg
);
1606 show_progress
= TRUE
;
1609 // handled by post-grohtml (no header and footer lines)
1612 // handled by post-grohtml (use font size n as the html base font size)
1615 // handled by post-grohtml (set file split level)
1618 printf("GNU pre-grohtml (groff) version %s\n", Version_string
);
1620 case CHAR_MAX
+ 1: // --help
1634 if (strcmp(argv
[i
], troff_name
) == 0)
1636 else if (argv
[i
][0] != '-')
1640 a_delete troff_name
;
1646 * makeTempFiles - Name the temporary files.
1649 static int makeTempFiles(void)
1651 #if defined(DEBUGGING)
1652 psFileName
= DEBUG_FILE("prehtml-ps");
1653 regionFileName
= DEBUG_FILE("prehtml-region");
1654 imagePageName
= DEBUG_FILE("prehtml-page");
1655 psPageName
= DEBUG_FILE("prehtml-psn");
1656 troffFileName
= DEBUG_FILE("prehtml-troff");
1657 htmlFileName
= DEBUG_FILE("prehtml-html");
1658 #else /* not DEBUGGING */
1661 /* psPageName contains a single page of postscript */
1662 f
= xtmpfile(&psPageName
,
1663 PS_TEMPLATE_LONG
, PS_TEMPLATE_SHORT
,
1666 sys_fatal("xtmpfile");
1671 /* imagePageName contains a bitmap image of the single postscript page */
1672 f
= xtmpfile(&imagePageName
,
1673 PAGE_TEMPLATE_LONG
, PAGE_TEMPLATE_SHORT
,
1676 sys_fatal("xtmpfile");
1681 /* psFileName contains a postscript file of the complete document */
1682 f
= xtmpfile(&psFileName
,
1683 PS_TEMPLATE_LONG
, PS_TEMPLATE_SHORT
,
1686 sys_fatal("xtmpfile");
1691 /* regionFileName contains a list of the images and their boxed coordinates */
1692 f
= xtmpfile(®ionFileName
,
1693 REGION_TEMPLATE_LONG
, REGION_TEMPLATE_SHORT
,
1696 sys_fatal("xtmpfile");
1701 #endif /* not DEBUGGING */
1705 int main(int argc
, char **argv
)
1707 program_name
= argv
[0];
1714 fprintf(stderr
, "%s: invoked with %d arguments ...\n", argv
[0], argc
);
1715 for (i
= 0; i
< argc
; i
++)
1716 fprintf(stderr
, "%2d: %s\n", i
, argv
[i
]);
1717 if ((dump
= fopen(DEBUG_FILE("pre-html-data"), "wb")) != NULL
) {
1718 while((i
= fgetc(stdin
)) >= 0)
1723 #endif /* CAPTURE_MODE */
1725 if (!font::load_desc())
1726 fatal("cannot find devhtml/DESC exiting");
1727 image_gen
= font::image_generator
;
1728 if (image_gen
== NULL
|| (strcmp(image_gen
, "") == 0))
1729 fatal("devhtml/DESC must set the image_generator field, exiting");
1730 postscriptRes
= get_resolution();
1731 i
= scanArguments(argc
, argv
);
1736 if (argv
[i
][0] != '-') {
1737 /* found source file */
1738 ok
= do_file(argv
[i
]);
1748 if (makeTempFiles())
1750 ok
= inputFile
.do_image(argc
, argv
);
1752 generateImages(regionFileName
);
1753 ok
= inputFile
.do_html(argc
, argv
);
1758 static int do_file(const char *filename
)
1762 current_filename
= filename
;
1763 if (strcmp(filename
, "-") == 0)
1766 fp
= fopen(filename
, "r");
1768 error("can't open `%1': %2", filename
, strerror(errno
));
1773 if (inputFile
.read_file(fp
)) {
1779 current_filename
= NULL
;