2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3 Written by Gaius Mulley (gaius@glam.ac.uk).
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
32 #include "stringclass.h"
35 #include "searchpath.h"
40 #include <sys/types.h>
46 # include <sys/wait.h>
48 #else /* not _POSIX_VERSION */
50 #endif /* not _POSIX_VERSION */
56 /* Establish some definitions to facilitate discrimination between
57 differing runtime environments. */
59 #undef MAY_FORK_CHILD_PROCESS
60 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
62 #if defined(__MSDOS__) || defined(_WIN32)
64 // Most MS-DOS and Win32 environments will be missing the `fork' capability
65 // (some like Cygwin have it, but it is best avoided).
67 # define MAY_FORK_CHILD_PROCESS 0
69 // On these systems, we use `spawn...', instead of `fork' ... `exec...'.
70 # include <process.h> // for `spawn...'
71 # include <fcntl.h> // for attributes of pipes
73 # if defined(__CYGWIN__) || defined(_UWIN) \
74 || defined(__MINGW32__) || defined(_WIN32)
76 // These Win32 implementations allow parent and `spawn...'ed child to
77 // multitask asynchronously.
80 extern "C" spawnvp_wrapper(int, char *, char **);
83 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
87 // Others may adopt MS-DOS behaviour where parent must sleep,
88 // from `spawn...' until child terminates. */
90 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
92 # endif /* not defined __CYGWIN__, _UWIN, or __MINGW32__ */
94 # if defined(DEBUGGING) && !defined(DEBUG_FILE)
95 /* When we are building a DEBUGGING version we need to tell pre-grohtml
96 where to put intermediate files (the DEBUGGING version will preserve
99 On a UNIX host, we might simply use `/tmp', but MS-DOS and Win32 will
100 probably not have this on all disk drives, so default to using
101 `c:/temp' instead. (Note that user may choose to override this by
102 supplying a definition such as
104 -DDEBUG_FILE="d:/path/to/debug/files/"
106 in the CPPFLAGS to `make'. If overriding in this manner, the trailing
107 `/' MUST be included in the definition.) */
108 # define DEBUG_FILE "c:/temp/"
111 #else /* not __MSDOS__ or _WIN32 */
113 // For non-Microsoft environments assume UNIX conventions,
114 // so `fork' is required and child processes are asynchronous.
115 # define MAY_FORK_CHILD_PROCESS 1
116 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
118 # if defined(DEBUGGING) && !defined(DEBUG_FILE)
119 /* For a DEBUGGING version, on the UNIX host, we can also usually rely
120 on being able to use `/tmp' for temporary file storage. (Note that,
121 as in the __MSDOS__ or _WIN32 case above, the user may override this
124 -DDEBUG_FILE="/path/to/debug/files/"
126 in the CPPFLAGS, again noting that the trailing `/' is REQUIRED.) */
127 # define DEBUG_FILE "/tmp/"
130 #endif /* not __MSDOS__ or _WIN32 */
132 extern "C" const char *Version_string
;
134 #include "pre-html.h"
135 #include "pushback.h"
136 #include "html-strings.h"
138 #define DEFAULT_LINE_LENGTH 7 // inches wide
139 #define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution
140 #define IMAGE_BOARDER_PIXELS 0
141 #define INLINE_LEADER_CHAR '\\'
143 // Don't use colour names here! Otherwise there is a dependency on
144 // a file called `rgb.txt' which maps names to colours.
145 #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
146 #define MIN_ALPHA_BITS 0
147 #define MAX_ALPHA_BITS 4
149 #define PAGE_TEMPLATE_SHORT "pg"
150 #define PAGE_TEMPLATE_LONG "-page-"
151 #define PS_TEMPLATE_SHORT "ps"
152 #define PS_TEMPLATE_LONG "-ps-"
153 #define REGION_TEMPLATE_SHORT "rg"
154 #define REGION_TEMPLATE_LONG "-regions-"
164 # define FALSE (1==0)
168 CENTERED
, LEFT
, RIGHT
, INLINE
171 static int postscriptRes
= -1; // postscript resolution,
173 static int stdoutfd
= 1; // output file descriptor -
174 // normally 1 but might move
176 static char *psFileName
= NULL
; // name of postscript file
177 static char *psPageName
= NULL
; // name of file containing
178 // postscript current page
179 static char *regionFileName
= NULL
; // name of file containing all
181 static char *imagePageName
= NULL
; // name of bitmap image containing
183 static const char *image_device
= "pnmraw";
184 static int image_res
= DEFAULT_IMAGE_RES
;
185 static int vertical_offset
= 0;
186 static char *image_template
= NULL
; // image template filename
187 static char *macroset_template
= NULL
; // image template passed to troff
189 static int troff_arg
= 0; // troff arg index
190 static char *image_dir
= NULL
; // user specified image directory
191 static int textAlphaBits
= MAX_ALPHA_BITS
;
192 static int graphicAlphaBits
= MAX_ALPHA_BITS
;
193 static char *antiAlias
= NULL
; // antialias arguments we pass to gs
194 static int show_progress
= FALSE
; // should we display page numbers as
195 // they are processed?
196 static int currentPageNo
= -1; // current image page number
197 #if defined(DEBUGGING)
198 static int debug
= FALSE
;
199 static char *troffFileName
= NULL
; // output of pre-html output which
200 // is sent to troff -Tps
201 static char *htmlFileName
= NULL
; // output of pre-html output which
202 // is sent to troff -Thtml
205 static char *linebuf
= NULL
; // for scanning devps/DESC
206 static int linebufsize
= 0;
208 const char *const FONT_ENV_VAR
= "GROFF_FONT_PATH";
209 static search_path
font_path(FONT_ENV_VAR
, FONTPATH
, 0, 0);
213 * Images are generated via postscript, gs, and the pnm utilities.
215 #define IMAGE_DEVICE "-Tps"
218 static int do_file(const char *filename
);
222 * sys_fatal - Write a fatal error message.
223 * Taken from src/roff/groff/pipeline.c.
226 void sys_fatal(const char *s
)
228 fatal("%1: %2", s
, strerror(errno
));
232 * get_line - Copy a line (w/o newline) from a file to the
233 * global line buffer.
236 int get_line(FILE *f
)
241 linebuf
= new char[128];
245 // skip leading whitespace
250 if (c
!= ' ' && c
!= '\t') {
259 if (i
+ 1 >= linebufsize
) {
260 char *old_linebuf
= linebuf
;
261 linebuf
= new char[linebufsize
* 2];
262 memcpy(linebuf
, old_linebuf
, linebufsize
);
263 a_delete old_linebuf
;
277 * get_resolution - Return the postscript resolution from devps/DESC.
280 static unsigned int get_resolution(void)
285 f
= font_path
.open_file("devps/DESC", &pathp
);
288 fatal("can't open devps/DESC");
289 while (get_line(f
)) {
290 int n
= sscanf(linebuf
, "res %u", &res
);
296 fatal("can't find `res' keyword in devps/DESC");
301 * html_system - A wrapper for system().
304 void html_system(const char *s
, int redirect_stdout
)
306 // Redirect standard error to the null device. This is more
307 // portable than using "2> /dev/null", since it doesn't require a
309 int save_stderr
= dup(2);
310 int save_stdout
= dup(1);
311 int fdnull
= open(NULL_DEV
, O_WRONLY
|O_BINARY
, 0666);
312 if (save_stderr
> 2 && fdnull
> 2)
314 if (redirect_stdout
&& save_stdout
> 1 && fdnull
> 1)
318 int status
= system(s
);
319 dup2(save_stderr
, 2);
321 dup2(save_stdout
, 1);
323 fprintf(stderr
, "Calling `%s' failed\n", s
);
325 fprintf(stderr
, "Calling `%s' returned status %d\n", s
, status
);
331 * make_message - Create a string via malloc and place the result of the
332 * va args into string. Finally the new string is returned.
333 * Taken from man page of printf(3).
336 char *make_message(const char *fmt
, ...)
338 /* Guess we need no more than 100 bytes. */
343 if ((p
= (char *)malloc(size
)) == NULL
)
346 /* Try to print in the allocated space. */
348 n
= vsnprintf(p
, size
, fmt
, ap
);
350 /* If that worked, return the string. */
351 if (n
> -1 && n
< size
) {
359 /* Else try again with more space. */
360 if (n
> -1) /* glibc 2.1 */
361 size
= n
+ 1; /* precisely what is needed */
363 size
*= 2; /* twice the old size */
364 if ((np
= (char *)realloc(p
, size
)) == NULL
) {
365 free(p
); /* realloc failed, free old, p. */
368 p
= np
; /* use realloc'ed, p */
373 * the class and methods for retaining ascii text
386 * char_block - Constructor. Set the, used, and, next, fields to zero.
389 char_block::char_block()
392 for (int i
= 0; i
< SIZE
; i
++)
400 int read_file(FILE *fp
);
401 int do_html(int argc
, char *argv
[]);
402 int do_image(int argc
, char *argv
[]);
403 void emit_troff_output(int device_format_selector
);
404 void write_upto_newline(char_block
**t
, int *i
, int is_html
);
405 int can_see(char_block
**t
, int *i
, const char *string
);
406 int skip_spaces(char_block
**t
, int *i
);
407 void skip_until_newline(char_block
**t
, int *i
);
411 int run_output_filter(int device_format_selector
, int argc
, char *argv
[]);
415 * char_buffer - Constructor.
418 char_buffer::char_buffer()
424 * char_buffer - Destructor. Throw away the whole buffer list.
427 char_buffer::~char_buffer()
429 while (head
!= NULL
) {
430 char_block
*temp
= head
;
437 * read_file - Read in a complete file, fp, placing the contents inside
441 int char_buffer::read_file(FILE *fp
)
446 tail
= new char_block
;
450 if (tail
->used
== char_block::SIZE
) {
451 tail
->next
= new char_block
;
455 // at this point we have a tail which is ready for the next SIZE
457 n
= fread(tail
->buffer
, sizeof(char), char_block::SIZE
-tail
->used
, fp
);
462 tail
->used
+= n
* sizeof(char);
468 * writeNbytes - Write n bytes to stdout.
471 static void writeNbytes(const char *s
, int l
)
477 r
= write(stdoutfd
, s
, l
- n
);
486 * writeString - Write a string to stdout.
489 static void writeString(const char *s
)
491 writeNbytes(s
, strlen(s
));
495 * makeFileName - Create the image filename template
496 * and the macroset image template.
499 static void makeFileName(void)
501 if ((image_dir
!= NULL
) && (strchr(image_dir
, '%') != NULL
)) {
502 error("cannot use a `%%' within the image directory name");
506 if ((image_template
!= NULL
) && (strchr(image_template
, '%') != NULL
)) {
507 error("cannot use a `%%' within the image template");
511 if (image_dir
== NULL
)
513 else if (strlen(image_dir
) > 0
514 && image_dir
[strlen(image_dir
) - 1] != '/') {
515 image_dir
= make_message("%s/", image_dir
);
516 if (image_dir
== NULL
)
517 sys_fatal("make_message");
520 if (image_template
== NULL
)
521 macroset_template
= make_message("%sgrohtml-%d", image_dir
,
524 macroset_template
= make_message("%s%s", image_dir
, image_template
);
526 if (macroset_template
== NULL
)
527 sys_fatal("make_message");
530 (char *)malloc(strlen("-%d") + strlen(macroset_template
) + 1);
531 if (image_template
== NULL
)
533 strcpy(image_template
, macroset_template
);
534 strcat(image_template
, "-%d");
538 * setupAntiAlias - Set up the antialias string, used when we call gs.
541 static void setupAntiAlias(void)
543 if (textAlphaBits
== 0 && graphicAlphaBits
== 0)
544 antiAlias
= make_message(" ");
545 else if (textAlphaBits
== 0)
546 antiAlias
= make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits
);
547 else if (graphicAlphaBits
== 0)
548 antiAlias
= make_message("-dTextAlphaBits=%d ", textAlphaBits
);
550 antiAlias
= make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
551 textAlphaBits
, graphicAlphaBits
);
555 * checkImageDir - Check whether the image directory is available.
558 static void checkImageDir(void)
560 if (image_dir
!= NULL
&& strcmp(image_dir
, "") != 0)
561 if (!(mkdir(image_dir
, 0777) == 0 || errno
== EEXIST
)) {
562 error("cannot create directory `%1'", image_dir
);
568 * write_end_image - End the image. Write out the image extents if we
572 static void write_end_image(int is_html
)
575 * if we are producing html then these
576 * emit image name and enable output
578 * we are producing images
579 * in which case these generate image
582 writeString("\\O[4]\\O[2]");
584 writeString("\\O[1]");
586 writeString("\\O[0]");
590 * write_start_image - Write troff code which will:
592 * (i) disable html output for the following image
593 * (ii) reset the max/min x/y registers during postscript
597 static void write_start_image(IMAGE_ALIGNMENT pos
, int is_html
)
599 writeString("\\O[5");
615 writeString(image_template
);
616 writeString(".png]");
618 writeString("\\O[0]\\O[3]");
620 // reset min/max registers
621 writeString("\\O[1]\\O[3]");
625 * write_upto_newline - Write the contents of the buffer until a newline
626 * is seen. Check for HTML_IMAGE_INLINE_BEGIN and
627 * HTML_IMAGE_INLINE_END; process them if they are
631 void char_buffer::write_upto_newline(char_block
**t
, int *i
, int is_html
)
636 while (j
< (*t
)->used
637 && (*t
)->buffer
[j
] != '\n'
638 && (*t
)->buffer
[j
] != INLINE_LEADER_CHAR
)
641 && (*t
)->buffer
[j
] == '\n')
643 writeNbytes((*t
)->buffer
+ (*i
), j
- (*i
));
644 if ((*t
)->buffer
[j
] == INLINE_LEADER_CHAR
) {
645 if (can_see(t
, &j
, HTML_IMAGE_INLINE_BEGIN
))
646 write_start_image(INLINE
, is_html
);
647 else if (can_see(t
, &j
, HTML_IMAGE_INLINE_END
))
648 write_end_image(is_html
);
650 if (j
< (*t
)->used
) {
653 writeNbytes((*t
)->buffer
+ (*i
), j
- (*i
));
657 if (j
== (*t
)->used
) {
660 if (*t
&& (*t
)->buffer
[j
- 1] != '\n')
661 write_upto_newline(t
, i
, is_html
);
670 * can_see - Return TRUE if we can see string in t->buffer[i] onwards.
673 int char_buffer::can_see(char_block
**t
, int *i
, const char *string
)
676 int l
= strlen(string
);
681 while (k
< s
->used
&& j
< l
&& s
->buffer
[k
] == string
[j
]) {
690 else if (k
< s
->used
&& s
->buffer
[k
] != string
[j
])
699 * skip_spaces - Return TRUE if we have not run out of data.
700 * Consume spaces also.
703 int char_buffer::skip_spaces(char_block
**t
, int *i
)
709 while (k
< s
->used
&& isspace(s
->buffer
[k
]))
724 * skip_until_newline - Skip all characters until a newline is seen.
725 * The newline is not consumed.
728 void char_buffer::skip_until_newline(char_block
**t
, int *i
)
733 while (j
< (*t
)->used
&& (*t
)->buffer
[j
] != '\n')
735 if (j
== (*t
)->used
) {
738 skip_until_newline(t
, i
);
746 #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
747 #define HTML_OUTPUT_FILTER 0
748 #define IMAGE_OUTPUT_FILTER 1
751 * emit_troff_output - Write formatted buffer content to the troff
752 * post-processor data pipeline.
755 void char_buffer::emit_troff_output(int device_format_selector
)
757 // Handle output for BOTH html and image device formats
758 // if `device_format_selector' is passed as
760 // HTML_FORMAT(HTML_OUTPUT_FILTER)
761 // Buffer data is written to the output stream
762 // with template image names translated to actual image names.
764 // HTML_FORMAT(IMAGE_OUTPUT_FILTER)
765 // Buffer data is written to the output stream
766 // with no translation, for image file creation in the post-processor.
769 char_block
*element
= head
;
771 while (element
!= NULL
)
772 write_upto_newline(&element
, &index
, device_format_selector
);
775 if (close(stdoutfd
) < 0)
778 // now we grab fd=1 so that the next pipe cannot use fd=1
780 if (dup(2) != stdoutfd
)
781 sys_fatal ("dup failed to use fd=1");
787 * The image class remembers the position of all images in the
788 * postscript file and assigns names for each image.
802 imageItem(int x1
, int y1
, int x2
, int y2
,
803 int page
, int res
, int max_width
, char *name
);
808 * imageItem - Constructor.
811 imageItem::imageItem(int x1
, int y1
, int x2
, int y2
,
812 int page
, int res
, int max_width
, char *name
)
826 * imageItem - Destructor.
829 imageItem::~imageItem()
836 * imageList - A class containing a list of imageItems.
847 void add(int x1
, int y1
, int x2
, int y2
,
848 int page
, int res
, int maxx
, char *name
);
849 void createImages(void);
850 int createPage(int pageno
);
851 void createImage(imageItem
*i
);
852 int getMaxX(int pageno
);
856 * imageList - Constructor.
859 imageList::imageList()
860 : head(0), tail(0), count(0)
865 * imageList - Destructor.
868 imageList::~imageList()
870 while (head
!= NULL
) {
878 * createPage - Create one image of, page pageno, from the postscript file.
881 int imageList::createPage(int pageno
)
885 if (currentPageNo
== pageno
)
888 if (currentPageNo
>= 1) {
890 * We need to unlink the files which change each time a new page is
891 * processed. The final unlink is done by xtmpfile when pre-grohtml
894 unlink(imagePageName
);
899 fprintf(stderr
, "[%d] ", pageno
);
903 #if defined(DEBUGGING)
905 fprintf(stderr
, "creating page %d\n", pageno
);
908 s
= make_message("psselect -q -p%d %s %s\n",
909 pageno
, psFileName
, psPageName
);
912 sys_fatal("make_message");
913 #if defined(DEBUGGING)
915 fwrite(s
, sizeof(char), strlen(s
), stderr
);
921 s
= make_message("echo showpage | "
922 "gs%s -q -dBATCH -dSAFER "
923 "-dDEVICEHEIGHTPOINTS=792 "
924 "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
925 "-sDEVICE=%s -r%d %s "
926 "-sOutputFile=%s %s -\n",
928 (getMaxX(pageno
) * image_res
) / postscriptRes
,
935 sys_fatal("make_message");
936 #if defined(DEBUGGING)
938 fwrite(s
, sizeof(char), strlen(s
), stderr
);
944 currentPageNo
= pageno
;
949 * min - Return the minimum of two numbers.
952 int min(int x
, int y
)
961 * max - Return the maximum of two numbers.
964 int max(int x
, int y
)
973 * getMaxX - Return the largest right-hand position for any image
977 int imageList::getMaxX(int pageno
)
980 int x
= postscriptRes
* DEFAULT_LINE_LENGTH
;
983 if (h
->pageNo
== pageno
)
991 * createImage - Generate a minimal png file from the set of page images.
994 void imageList::createImage(imageItem
*i
)
998 int x1
= max(min(i
->X1
, i
->X2
) * image_res
/ postscriptRes
999 - IMAGE_BOARDER_PIXELS
,
1001 int y1
= max(image_res
* vertical_offset
/ 72
1002 + min(i
->Y1
, i
->Y2
) * image_res
/ postscriptRes
1003 - IMAGE_BOARDER_PIXELS
,
1005 int x2
= max(i
->X1
, i
->X2
) * image_res
/ postscriptRes
1006 + IMAGE_BOARDER_PIXELS
;
1007 int y2
= image_res
* vertical_offset
/ 72
1008 + max(i
->Y1
, i
->Y2
) * image_res
/ postscriptRes
1009 + 1 + IMAGE_BOARDER_PIXELS
;
1010 if (createPage(i
->pageNo
) == 0) {
1011 s
= make_message("pnmcut%s %d %d %d %d < %s "
1012 "| pnmcrop -quiet | pnmtopng%s %s > %s\n",
1014 x1
, y1
, x2
- x1
+ 1, y2
- y1
+ 1,
1020 sys_fatal("make_message");
1022 #if defined(DEBUGGING)
1032 fprintf(stderr
, "failed to generate image of page %d\n", i
->pageNo
);
1035 #if defined(DEBUGGING)
1039 fprintf(stderr
, "ignoring image as x1 coord is -1\n");
1047 * add - Add an image description to the imageList.
1050 void imageList::add(int x1
, int y1
, int x2
, int y2
,
1051 int page
, int res
, int maxx
, char *name
)
1053 imageItem
*i
= new imageItem(x1
, y1
, x2
, y2
, page
, res
, maxx
, name
);
1066 * createImages - For each image descriptor on the imageList,
1067 * create the actual image.
1070 void imageList::createImages(void)
1072 imageItem
*h
= head
;
1080 static imageList listOfImages
; // List of images defined by the region file.
1083 * generateImages - Parse the region file and generate images
1084 * from the postscript file. The region file
1085 * contains the x1,y1--x2,y2 extents of each
1089 static void generateImages(char *regionFileName
)
1091 pushBackBuffer
*f
=new pushBackBuffer(regionFileName
);
1093 while (f
->putPB(f
->getPB()) != eof
) {
1094 if (f
->isString("grohtml-info:page")) {
1095 int page
= f
->readInt();
1096 int x1
= f
->readInt();
1097 int y1
= f
->readInt();
1098 int x2
= f
->readInt();
1099 int y2
= f
->readInt();
1100 int maxx
= f
->readInt();
1101 char *name
= f
->readString();
1102 int res
= postscriptRes
;
1103 listOfImages
.add(x1
, y1
, x2
, y2
, page
, res
, maxx
, name
);
1104 while (f
->putPB(f
->getPB()) != '\n'
1105 && f
->putPB(f
->getPB()) != eof
)
1107 if (f
->putPB(f
->getPB()) == '\n')
1111 /* Write any error messages out to the user. */
1112 fputc(f
->getPB(), stderr
);
1116 listOfImages
.createImages();
1117 if (show_progress
) {
1118 fprintf(stderr
, "done\n");
1125 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1126 * stream on handle, willbe.
1129 static void set_redirection(int was
, int willbe
)
1131 // Nothing to do if `was' and `willbe' already have same handle.
1132 if (was
!= willbe
) {
1133 // Otherwise attempt the specified redirection.
1134 if (dup2 (willbe
, was
) < 0) {
1135 // Redirection failed, so issue diagnostic and bail out.
1136 fprintf(stderr
, "failed to replace fd=%d with %d\n", was
, willbe
);
1137 if (willbe
== STDOUT_FILENO
)
1139 "likely that stdout should be opened before %d\n", was
);
1143 // When redirection has been successfully completed assume redundant
1144 // handle `willbe' is no longer required, so close it.
1145 if (close(willbe
) < 0)
1146 // Issue diagnostic if `close' fails.
1152 * save_and_redirect - Get duplicate handle for stream, was, then
1153 * redirect, was, to refer to, willbe.
1156 static int save_and_redirect(int was
, int willbe
)
1159 // No redirection specified so don't do anything but silently bailing out.
1162 // Proceeding with redirection so first save and verify our duplicate
1163 // handle for `was'.
1164 int saved
= dup(was
);
1166 fprintf(stderr
, "unable to get duplicate handle for %d\n", was
);
1170 // Duplicate handle safely established so complete redirection.
1171 set_redirection(was
, willbe
);
1173 // Finally return the saved duplicate descriptor for the
1174 // original `was' stream.
1179 * alterDeviceTo - If, toImage, is set
1180 * the argument list is altered to include
1181 * IMAGE_DEVICE and we invoke groff rather than troff.
1183 * set -Thtml and groff.
1186 static void alterDeviceTo(int argc
, char *argv
[], int toImage
)
1192 if (strcmp(argv
[i
], "-Thtml") == 0)
1193 argv
[i
] = IMAGE_DEVICE
;
1196 argv
[troff_arg
] = "groff"; /* rather than troff */
1200 if (strcmp(argv
[i
], IMAGE_DEVICE
) == 0)
1204 argv
[troff_arg
] = "groff"; /* use groff -Z */
1209 * addZ - Append -Z onto the command list for groff.
1212 char **addZ(int argc
, char *argv
[])
1214 char **new_argv
= (char **)malloc((argc
+ 2) * sizeof(char *));
1217 if (new_argv
== NULL
)
1218 sys_fatal("malloc");
1221 new_argv
[i
] = argv
[i
];
1226 new_argv
[i
+ 1] = argv
[i
];
1230 new_argv
[argc
] = NULL
;
1235 * addRegDef - Append a defined register or string onto the command
1239 char **addRegDef(int argc
, char *argv
[], const char *numReg
)
1241 char **new_argv
= (char **)malloc((argc
+ 2) * sizeof(char *));
1244 if (new_argv
== NULL
)
1245 sys_fatal("malloc");
1248 new_argv
[i
] = argv
[i
];
1251 new_argv
[argc
] = strsave(numReg
);
1253 new_argv
[argc
] = NULL
;
1258 * dump_args - Display the argument list.
1261 void dump_args(int argc
, char *argv
[])
1263 fprintf(stderr
, " %d arguments:", argc
);
1264 for (int i
= 0; i
< argc
; i
++)
1265 fprintf(stderr
, " %s", argv
[i
]);
1266 fprintf(stderr
, "\n");
1269 int char_buffer::run_output_filter(int filter
, int argc
, char **argv
)
1275 if (pipe(pipedes
) < 0)
1278 #if MAY_FORK_CHILD_PROCESS
1280 // This is the UNIX process model. To invoke our post-processor,
1281 // we must `fork' the current process.
1283 if ((child_pid
= fork()) < 0)
1286 else if (child_pid
== 0) {
1287 // This is the child process fork. We redirect its `stdin' stream
1288 // to read data emerging from our pipe. There is no point in saving,
1289 // since we won't be able to restore later!
1291 set_redirection(STDIN_FILENO
, pipedes
[0]);
1293 // The parent process will be writing this data so we should release
1294 // the child's writeable handle on the pipe.
1296 if (close(pipedes
[1]) < 0)
1299 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1301 if (filter
== IMAGE_OUTPUT_FILTER
) {
1302 // with BOTH `stdout' AND `stderr' diverted to files.
1304 set_redirection(STDOUT_FILENO
,
1305 creat(psFileName
, S_IWUSR
| S_IRUSR
));
1306 set_redirection(STDERR_FILENO
,
1307 creat(regionFileName
, S_IWUSR
| S_IRUSR
));
1310 // Now we are ready to launch the output filter.
1312 execvp(argv
[0], argv
);
1314 // If we get to here then the `exec...' request for the output filter
1315 // failed. Diagnose it and bail out.
1317 error("couldn't exec %1: %2", argv
[0], strerror(errno
), ((char *)0));
1318 fflush(stderr
); // just in case error() didn't
1323 // This is the parent process fork. We will be writing data to the
1324 // filter pipeline but we can close our handle on the output end of
1325 // the pipe so we don't block the output data stream.
1327 if (close(pipedes
[0]) < 0)
1330 // Now we redirect the `stdout' stream to the inlet end of the pipe.
1331 // Push out the appropiately formatted data to the filter.
1333 pipedes
[1] = save_and_redirect(STDOUT_FILENO
, pipedes
[1]);
1334 emit_troff_output(DEVICE_FORMAT(filter
));
1336 // After emitting all the data we close our connection to the inlet
1337 // end of the pipe so the child process will detect end of data.
1339 set_redirection(STDOUT_FILENO
, pipedes
[1]);
1341 // Finally we must wait for the child process to complete.
1343 if (WAIT(&status
, &child_pid
, _WAIT_CHILD
) != child_pid
)
1347 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1351 // This should be ok for most Win32 systems and is preferred to `fork'
1352 // for starting child processes under Cygwin.
1354 // Before we start the post-processor we bind its inherited `stdin'
1355 // stream to the readable end of our pipe, saving our own `stdin' stream
1358 pipedes
[0] = save_and_redirect(STDIN_FILENO
, pipedes
[0]);
1360 int saved_stdout
= dup(STDOUT_FILENO
);
1361 int saved_stderr
= STDERR_FILENO
;
1363 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1365 if (filter
== IMAGE_OUTPUT_FILTER
) {
1366 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1367 // duplicate handle for `stderr'.
1369 set_redirection(STDOUT_FILENO
, creat(psFileName
, S_IWUSR
| S_IRUSR
));
1371 save_and_redirect(STDERR_FILENO
,
1372 creat(regionFileName
, S_IWUSR
| S_IRUSR
));
1375 // We then use an asynchronous spawn request to start the post-processor.
1377 if ((child_pid
= spawnvp(_P_NOWAIT
, argv
[0], argv
)) < 0) {
1378 // Should the spawn request fail we issue a diagnostic and bail out.
1380 error("cannot spawn %1: %2", argv
[0], strerror(errno
), ((char *)0));
1384 // Once the post-processor has been started we revert our `stdin'
1385 // to its original saved source which also closes the readable handle
1388 set_redirection(STDIN_FILENO
, pipedes
[0]);
1390 set_redirection(STDERR_FILENO
, saved_stderr
);
1391 set_redirection(STDOUT_FILENO
, pipedes
[1]);
1392 emit_troff_output(DEVICE_FORMAT(filter
));
1394 set_redirection(STDOUT_FILENO
, saved_stdout
);
1396 if (WAIT(&status
, child_pid
, _WAIT_CHILD
) != child_pid
)
1399 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1405 * do_html - Set the troff number htmlflip and
1406 * write out the buffer to troff -Thtml.
1409 int char_buffer::do_html(int argc
, char *argv
[])
1413 alterDeviceTo(argc
, argv
, 0);
1414 argv
+= troff_arg
; // skip all arguments up to groff
1416 argv
= addZ(argc
, argv
);
1419 s
= "-dwww-image-template=";
1420 s
+= macroset_template
; // do not combine these statements,
1421 // otherwise they will not work
1422 s
+= '\0'; // the trailing `\0' is ignored
1423 argv
= addRegDef(argc
, argv
, s
.contents());
1426 #if defined(DEBUGGING)
1427 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1429 set_redirection(STDOUT_FILENO
, creat(htmlFileName
, S_IWUSR
| S_IRUSR
));
1430 emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER
));
1434 return run_output_filter(HTML_OUTPUT_FILTER
, argc
, argv
);
1438 * do_image - Write out the buffer to troff -Tps.
1441 int char_buffer::do_image(int argc
, char *argv
[])
1445 alterDeviceTo(argc
, argv
, 1);
1446 argv
+= troff_arg
; // skip all arguments up to troff/groff
1448 argv
= addRegDef(argc
, argv
, "-rps4html=1");
1451 s
= "-dwww-image-template=";
1452 s
+= macroset_template
;
1454 argv
= addRegDef(argc
, argv
, s
.contents());
1457 // override local settings and produce a page size letter postscript file
1458 argv
= addRegDef(argc
, argv
, "-P-pletter");
1461 #if defined(DEBUGGING)
1462 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1464 set_redirection(STDOUT_FILENO
, creat(troffFileName
, S_IWUSR
| S_IRUSR
));
1465 emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER
));
1469 return run_output_filter(IMAGE_OUTPUT_FILTER
, argc
, argv
);
1472 static char_buffer inputFile
;
1475 * usage - Emit usage arguments.
1478 static void usage(FILE *stream
)
1481 "usage: %s troffname [-Iimage_name] [-Dimage_directory]\n"
1482 " [-P-o vertical_image_offset] [-P-i image_resolution]\n"
1483 " [troff flags] [files]\n",
1486 " vertical_image_offset (default %d/72 of an inch)\n",
1489 " image_resolution (default %d) pixels per inch\n",
1492 " image_name is the name of the stem for all images\n"
1493 " (default is grohtml-<pid>)\n");
1495 " place all png files into image_directory\n");
1499 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1500 * and -P-I. Return the argument index of the first
1504 static int scanArguments(int argc
, char **argv
)
1506 const char *command_prefix
= getenv("GROFF_COMMAND_PREFIX");
1507 if (!command_prefix
)
1508 command_prefix
= PROG_PREFIX
;
1509 char *troff_name
= new char[strlen(command_prefix
) + strlen("troff") + 1];
1510 strcpy(troff_name
, command_prefix
);
1511 strcat(troff_name
, "troff");
1513 static const struct option long_options
[] = {
1514 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
1515 { "version", no_argument
, 0, 'v' },
1518 while ((c
= getopt_long(argc
, argv
,
1519 "+a:g:o:i:I:j:D:F:vbdhlrnp", long_options
, NULL
))
1523 printf("GNU pre-grohtml (groff) version %s\n", Version_string
);
1526 textAlphaBits
= min(max(MIN_ALPHA_BITS
, atoi(optarg
)),
1528 if (textAlphaBits
== 3) {
1529 error("cannot use 3 bits of antialiasing information");
1534 graphicAlphaBits
= min(max(MIN_ALPHA_BITS
, atoi(optarg
)),
1536 if (graphicAlphaBits
== 3) {
1537 error("cannot use 3 bits of antialiasing information");
1542 // handled by post-grohtml (set background color to white)
1548 image_template
= optarg
;
1551 image_res
= atoi(optarg
);
1554 font_path
.command_line_dir(optarg
);
1557 // handled by post-grohtml (set job name for multiple file output)
1560 vertical_offset
= atoi(optarg
);
1563 show_progress
= TRUE
;
1566 #if defined(DEBUGGING)
1571 // handled by post-grohtml
1573 case CHAR_MAX
+ 1: // --help
1587 if (strcmp(argv
[i
], troff_name
) == 0)
1589 else if (argv
[i
][0] != '-')
1593 a_delete troff_name
;
1599 * makeTempFiles - Name the temporary files.
1602 static int makeTempFiles(void)
1604 #if defined(DEBUGGING)
1605 psFileName
= DEBUG_FILE
"prehtml-ps";
1606 regionFileName
= DEBUG_FILE
"prehtml-region";
1607 imagePageName
= DEBUG_FILE
"prehtml-page";
1608 psPageName
= DEBUG_FILE
"prehtml-psn";
1609 troffFileName
= DEBUG_FILE
"prehtml-troff";
1610 htmlFileName
= DEBUG_FILE
"prehtml-html";
1611 #else /* not DEBUGGING */
1614 /* psPageName contains a single page of postscript */
1615 f
= xtmpfile(&psPageName
,
1616 PS_TEMPLATE_LONG
, PS_TEMPLATE_SHORT
,
1619 sys_fatal("xtmpfile");
1624 /* imagePageName contains a bitmap image of the single postscript page */
1625 f
= xtmpfile(&imagePageName
,
1626 PAGE_TEMPLATE_LONG
, PAGE_TEMPLATE_SHORT
,
1629 sys_fatal("xtmpfile");
1634 /* psFileName contains a postscript file of the complete document */
1635 f
= xtmpfile(&psFileName
,
1636 PS_TEMPLATE_LONG
, PS_TEMPLATE_SHORT
,
1639 sys_fatal("xtmpfile");
1644 /* regionFileName contains a list of the images and their boxed coordinates */
1645 f
= xtmpfile(®ionFileName
,
1646 REGION_TEMPLATE_LONG
, REGION_TEMPLATE_SHORT
,
1649 sys_fatal("xtmpfile");
1654 #endif /* not DEBUGGING */
1658 int main(int argc
, char **argv
)
1660 program_name
= argv
[0];
1667 fprintf(stderr
, "%s: invoked with %d arguments ...\n", argv
[0], argc
);
1668 for (i
= 0; i
< argc
; i
++)
1669 fprintf(stderr
, "%2d: %s\n", i
, argv
[i
]);
1670 if ((dump
= fopen(DEBUG_FILE
"pre-html-data", "wb")) != NULL
) {
1671 while((i
= fgetc(stdin
)) >= 0)
1676 #endif /* CAPTURE_MODE */
1677 postscriptRes
= get_resolution();
1678 i
= scanArguments(argc
, argv
);
1683 if (argv
[i
][0] != '-') {
1684 /* found source file */
1685 ok
= do_file(argv
[i
]);
1695 if (makeTempFiles())
1697 ok
= inputFile
.do_image(argc
, argv
);
1699 generateImages(regionFileName
);
1700 ok
= inputFile
.do_html(argc
, argv
);
1705 static int do_file(const char *filename
)
1709 current_filename
= filename
;
1710 if (strcmp(filename
, "-") == 0)
1713 fp
= fopen(filename
, "r");
1715 error("can't open `%1': %2", filename
, strerror(errno
));
1720 if (inputFile
.read_file(fp
)) {
1726 current_filename
= NULL
;