1 /* $NetBSD: pre-html.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $ */
2
3 // -*- C++ -*-
4 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
5 Written by Gaius Mulley (gaius@glam.ac.uk).
6
7 This file is part of groff.
8
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
12 version.
13
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
17 for more details.
18
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. */
22
23 #define PREHTMLC
24
25 #include "lib.h"
26
27 #include <signal.h>
28 #include <ctype.h>
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include "errarg.h"
33 #include "error.h"
34 #include "stringclass.h"
35 #include "posix.h"
36 #include "defs.h"
37 #include "searchpath.h"
38 #include "paper.h"
39 #include "device.h"
40 #include "font.h"
41
42 #include <errno.h>
43 #include <sys/types.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47
48 #ifdef _POSIX_VERSION
49 # include <sys/wait.h>
50 # define PID_T pid_t
51 #else /* not _POSIX_VERSION */
52 # define PID_T int
53 #endif /* not _POSIX_VERSION */
54
55 #include <stdarg.h>
56
57 #include "nonposix.h"
58
59 /* Establish some definitions to facilitate discrimination between
60 differing runtime environments. */
61
62 #undef MAY_FORK_CHILD_PROCESS
63 #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
64
65 #if defined(__MSDOS__) || defined(_WIN32)
66
67 // Most MS-DOS and Win32 environments will be missing the `fork' capability
68 // (some like Cygwin have it, but it is best avoided).
69
70 # define MAY_FORK_CHILD_PROCESS 0
71
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
75
76 # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
77
78 // These Win32 implementations allow parent and `spawn...'ed child to
79 // multitask asynchronously.
80
81 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
82
83 # else
84
85 // Others may adopt MS-DOS behaviour where parent must sleep,
86 // from `spawn...' until child terminates.
87
88 # define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
89
90 # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
91
92 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
93 /* When we are building a DEBUGGING version we need to tell pre-grohtml
94 where to put intermediate files (the DEBUGGING version will preserve
95 these on exit).
96
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
101
102 -DDEBUG_FILE_DIR=d:/path/to/debug/files
103
104 in the CPPFLAGS to `make'.) */
105
106 # define DEBUG_FILE_DIR c:/temp
107 # endif
108
109 #else /* not __MSDOS__ or _WIN32 */
110
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
115
116 # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
117 /* For a DEBUGGING version, on the UNIX host, we can also usually rely
118 on being able to use `/tmp' for temporary file storage. (Note that,
119 as in the __MSDOS__ or _WIN32 case above, the user may override this
120 by defining
121
122 -DDEBUG_FILE_DIR=/path/to/debug/files
123
124 in the CPPFLAGS.) */
125
126 # define DEBUG_FILE_DIR /tmp
127 # endif
128
129 #endif /* not __MSDOS__ or _WIN32 */
130
131 #ifdef DEBUGGING
132 // For a DEBUGGING version, we need some additional macros,
133 // to direct the captured debug mode output to appropriately named files
134 // in the specified DEBUG_FILE_DIR.
135
136 # define DEBUG_TEXT(text) #text
137 # define DEBUG_NAME(text) DEBUG_TEXT(text)
138 # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
139 #endif
140
141 extern "C" const char *Version_string;
142
143 #include "pre-html.h"
144 #include "pushback.h"
145 #include "html-strings.h"
146
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 '\\'
151
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
157
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-"
164
165 #if 0
166 # define DEBUGGING
167 #endif
168
169 #if !defined(TRUE)
170 # define TRUE (1==1)
171 #endif
172 #if !defined(FALSE)
173 # define FALSE (1==0)
174 #endif
175
176 typedef enum {
177 CENTERED, LEFT, RIGHT, INLINE
178 } IMAGE_ALIGNMENT;
179
180 static int postscriptRes = -1; // postscript resolution,
181 // dots per inch
182 static int stdoutfd = 1; // output file descriptor -
183 // normally 1 but might move
184 // -1 means closed
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
189 // image regions
190 static char *imagePageName = NULL; // name of bitmap image containing
191 // current page
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
197 // by -D
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
212 #endif
213
214 static char *linebuf = NULL; // for scanning devps/DESC
215 static int linebufsize = 0;
216 static const char *image_gen = NULL; // the `gs' program
217
218 const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
219 static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
220
221
222 /*
223 * Images are generated via postscript, gs, and the pnm utilities.
224 */
225 #define IMAGE_DEVICE "-Tps"
226
227
228 static int do_file(const char *filename);
229
230
231 /*
232 * sys_fatal - Write a fatal error message.
233 * Taken from src/roff/groff/pipeline.c.
234 */
235
sys_fatal(const char * s)236 void sys_fatal(const char *s)
237 {
238 fatal("%1: %2", s, strerror(errno));
239 }
240
241 /*
242 * get_line - Copy a line (w/o newline) from a file to the
243 * global line buffer.
244 */
245
get_line(FILE * f)246 int get_line(FILE *f)
247 {
248 if (f == 0)
249 return 0;
250 if (linebuf == 0) {
251 linebuf = new char[128];
252 linebufsize = 128;
253 }
254 int i = 0;
255 // skip leading whitespace
256 for (;;) {
257 int c = getc(f);
258 if (c == EOF)
259 return 0;
260 if (c != ' ' && c != '\t') {
261 ungetc(c, f);
262 break;
263 }
264 }
265 for (;;) {
266 int c = getc(f);
267 if (c == EOF)
268 break;
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;
274 linebufsize *= 2;
275 }
276 linebuf[i++] = c;
277 if (c == '\n') {
278 i--;
279 break;
280 }
281 }
282 linebuf[i] = '\0';
283 return 1;
284 }
285
286 /*
287 * get_resolution - Return the postscript resolution from devps/DESC.
288 */
289
get_resolution(void)290 static unsigned int get_resolution(void)
291 {
292 char *pathp;
293 FILE *f;
294 unsigned int res;
295 f = font_path.open_file("devps/DESC", &pathp);
296 a_delete pathp;
297 if (f == 0)
298 fatal("can't open devps/DESC");
299 while (get_line(f)) {
300 int n = sscanf(linebuf, "res %u", &res);
301 if (n >= 1) {
302 fclose(f);
303 return res;
304 }
305 }
306 fatal("can't find `res' keyword in devps/DESC");
307 return 0;
308 }
309
310 /*
311 * html_system - A wrapper for system().
312 */
313
html_system(const char * s,int redirect_stdout)314 void html_system(const char *s, int redirect_stdout)
315 {
316 // Redirect standard error to the null device. This is more
317 // portable than using "2> /dev/null", since it doesn't require a
318 // Unixy shell.
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)
323 dup2(fdnull, 2);
324 if (redirect_stdout && save_stdout > 1 && fdnull > 1)
325 dup2(fdnull, 1);
326 if (fdnull >= 0)
327 close(fdnull);
328 int status = system(s);
329 dup2(save_stderr, 2);
330 if (redirect_stdout)
331 dup2(save_stdout, 1);
332 if (status == -1)
333 fprintf(stderr, "Calling `%s' failed\n", s);
334 else if (status)
335 fprintf(stderr, "Calling `%s' returned status %d\n", s, status);
336 close(save_stderr);
337 close(save_stdout);
338 }
339
340 /*
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).
344 */
345
make_message(const char * fmt,...)346 char *make_message(const char *fmt, ...)
347 {
348 /* Guess we need no more than 100 bytes. */
349 int n, size = 100;
350 char *p;
351 char *np;
352 va_list ap;
353 if ((p = (char *)malloc(size)) == NULL)
354 return NULL;
355 while (1) {
356 /* Try to print in the allocated space. */
357 va_start(ap, fmt);
358 n = vsnprintf(p, size, fmt, ap);
359 va_end(ap);
360 /* If that worked, return the string. */
361 if (n > -1 && n < size - 1) { /* glibc 2.1 and pre-ANSI C 99 */
362 if (size > n + 1) {
363 np = strsave(p);
364 free(p);
365 return np;
366 }
367 return p;
368 }
369 /* Else try again with more space. */
370 else /* glibc 2.0 */
371 size *= 2; /* twice the old size */
372 if ((np = (char *)realloc(p, size)) == NULL) {
373 free(p); /* realloc failed, free old, p. */
374 return NULL;
375 }
376 p = np; /* use realloc'ed, p */
377 }
378 }
379
380 /*
381 * the class and methods for retaining ascii text
382 */
383
384 struct char_block {
385 enum { SIZE = 256 };
386 char buffer[SIZE];
387 int used;
388 char_block *next;
389
390 char_block();
391 };
392
393 /*
394 * char_block - Constructor. Set the, used, and, next, fields to zero.
395 */
396
char_block()397 char_block::char_block()
398 : used(0), next(0)
399 {
400 for (int i = 0; i < SIZE; i++)
401 buffer[i] = 0;
402 }
403
404 class char_buffer {
405 public:
406 char_buffer();
407 ~char_buffer();
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);
416 private:
417 char_block *head;
418 char_block *tail;
419 int run_output_filter(int device_format_selector, int argc, char *argv[]);
420 };
421
422 /*
423 * char_buffer - Constructor.
424 */
425
char_buffer()426 char_buffer::char_buffer()
427 : head(0), tail(0)
428 {
429 }
430
431 /*
432 * char_buffer - Destructor. Throw away the whole buffer list.
433 */
434
~char_buffer()435 char_buffer::~char_buffer()
436 {
437 while (head != NULL) {
438 char_block *temp = head;
439 head = head->next;
440 delete temp;
441 }
442 }
443
444 /*
445 * read_file - Read in a complete file, fp, placing the contents inside
446 * char_blocks.
447 */
448
read_file(FILE * fp)449 int char_buffer::read_file(FILE *fp)
450 {
451 int n;
452 while (!feof(fp)) {
453 if (tail == NULL) {
454 tail = new char_block;
455 head = tail;
456 }
457 else {
458 if (tail->used == char_block::SIZE) {
459 tail->next = new char_block;
460 tail = tail->next;
461 }
462 }
463 // at this point we have a tail which is ready for the next SIZE
464 // bytes of the file
465 n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
466 if (n <= 0)
467 // error
468 return 0;
469 else
470 tail->used += n * sizeof(char);
471 }
472 return 1;
473 }
474
475 /*
476 * writeNbytes - Write n bytes to stdout.
477 */
478
writeNbytes(const char * s,int l)479 static void writeNbytes(const char *s, int l)
480 {
481 int n = 0;
482 int r;
483
484 while (n < l) {
485 r = write(stdoutfd, s, l - n);
486 if (r < 0)
487 sys_fatal("write");
488 n += r;
489 s += r;
490 }
491 }
492
493 /*
494 * writeString - Write a string to stdout.
495 */
496
writeString(const char * s)497 static void writeString(const char *s)
498 {
499 writeNbytes(s, strlen(s));
500 }
501
502 /*
503 * makeFileName - Create the image filename template
504 * and the macroset image template.
505 */
506
makeFileName(void)507 static void makeFileName(void)
508 {
509 if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) {
510 error("cannot use a `%%' within the image directory name");
511 exit(1);
512 }
513
514 if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) {
515 error("cannot use a `%%' within the image template");
516 exit(1);
517 }
518
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");
526 }
527
528 if (image_template == NULL)
529 macroset_template = make_message("%sgrohtml-%d", image_dir,
530 (int)getpid());
531 else
532 macroset_template = make_message("%s%s", image_dir, image_template);
533
534 if (macroset_template == NULL)
535 sys_fatal("make_message");
536
537 image_template =
538 (char *)malloc(strlen("-%d") + strlen(macroset_template) + 1);
539 if (image_template == NULL)
540 sys_fatal("malloc");
541 strcpy(image_template, macroset_template);
542 strcat(image_template, "-%d");
543 }
544
545 /*
546 * setupAntiAlias - Set up the antialias string, used when we call gs.
547 */
548
setupAntiAlias(void)549 static void setupAntiAlias(void)
550 {
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);
557 else
558 antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ",
559 textAlphaBits, graphicAlphaBits);
560 }
561
562 /*
563 * checkImageDir - Check whether the image directory is available.
564 */
565
checkImageDir(void)566 static void checkImageDir(void)
567 {
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);
571 exit(1);
572 }
573 }
574
575 /*
576 * write_end_image - End the image. Write out the image extents if we
577 * are using -Tps.
578 */
579
write_end_image(int is_html)580 static void write_end_image(int is_html)
581 {
582 /*
583 * if we are producing html then these
584 * emit image name and enable output
585 * else
586 * we are producing images
587 * in which case these generate image
588 * boundaries
589 */
590 writeString("\\O[4]\\O[2]");
591 if (is_html)
592 writeString("\\O[1]");
593 else
594 writeString("\\O[0]");
595 }
596
597 /*
598 * write_start_image - Write troff code which will:
599 *
600 * (i) disable html output for the following image
601 * (ii) reset the max/min x/y registers during postscript
602 * rendering.
603 */
604
write_start_image(IMAGE_ALIGNMENT pos,int is_html)605 static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
606 {
607 writeString("\\O[5");
608 switch (pos) {
609 case INLINE:
610 writeString("i");
611 break;
612 case LEFT:
613 writeString("l");
614 break;
615 case RIGHT:
616 writeString("r");
617 break;
618 case CENTERED:
619 default:
620 writeString("c");
621 break;
622 }
623 writeString(image_template);
624 writeString(".png]");
625 if (is_html)
626 writeString("\\O[0]\\O[3]");
627 else
628 // reset min/max registers
629 writeString("\\O[1]\\O[3]");
630 }
631
632 /*
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
636 * present.
637 */
638
write_upto_newline(char_block ** t,int * i,int is_html)639 void char_buffer::write_upto_newline(char_block **t, int *i, int is_html)
640 {
641 int j = *i;
642
643 if (*t) {
644 while (j < (*t)->used
645 && (*t)->buffer[j] != '\n'
646 && (*t)->buffer[j] != INLINE_LEADER_CHAR)
647 j++;
648 if (j < (*t)->used
649 && (*t)->buffer[j] == '\n')
650 j++;
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);
657 else {
658 if (j < (*t)->used) {
659 *i = j;
660 j++;
661 writeNbytes((*t)->buffer + (*i), j - (*i));
662 }
663 }
664 }
665 if (j == (*t)->used) {
666 *i = 0;
667 *t = (*t)->next;
668 if (*t && (*t)->buffer[j - 1] != '\n')
669 write_upto_newline(t, i, is_html);
670 }
671 else
672 // newline was seen
673 *i = j;
674 }
675 }
676
677 /*
678 * can_see - Return TRUE if we can see string in t->buffer[i] onwards.
679 */
680
can_see(char_block ** t,int * i,const char * str)681 int char_buffer::can_see(char_block **t, int *i, const char *str)
682 {
683 int j = 0;
684 int l = strlen(str);
685 int k = *i;
686 char_block *s = *t;
687
688 while (s) {
689 while (k < s->used && j < l && s->buffer[k] == str[j]) {
690 j++;
691 k++;
692 }
693 if (j == l) {
694 *i = k;
695 *t = s;
696 return TRUE;
697 }
698 else if (k < s->used && s->buffer[k] != str[j])
699 return( FALSE );
700 s = s->next;
701 k = 0;
702 }
703 return FALSE;
704 }
705
706 /*
707 * skip_spaces - Return TRUE if we have not run out of data.
708 * Consume spaces also.
709 */
710
skip_spaces(char_block ** t,int * i)711 int char_buffer::skip_spaces(char_block **t, int *i)
712 {
713 char_block *s = *t;
714 int k = *i;
715
716 while (s) {
717 while (k < s->used && isspace(s->buffer[k]))
718 k++;
719 if (k == s->used) {
720 k = 0;
721 s = s->next;
722 }
723 else {
724 *i = k;
725 return TRUE;
726 }
727 }
728 return FALSE;
729 }
730
731 /*
732 * skip_until_newline - Skip all characters until a newline is seen.
733 * The newline is not consumed.
734 */
735
skip_until_newline(char_block ** t,int * i)736 void char_buffer::skip_until_newline(char_block **t, int *i)
737 {
738 int j = *i;
739
740 if (*t) {
741 while (j < (*t)->used && (*t)->buffer[j] != '\n')
742 j++;
743 if (j == (*t)->used) {
744 *i = 0;
745 *t = (*t)->next;
746 skip_until_newline(t, i);
747 }
748 else
749 // newline was seen
750 *i = j;
751 }
752 }
753
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)
760
761 /*
762 * emit_troff_output - Write formatted buffer content to the troff
763 * post-processor data pipeline.
764 */
765
emit_troff_output(int device_format_selector)766 void char_buffer::emit_troff_output(int device_format_selector)
767 {
768 // Handle output for BOTH html and image device formats
769 // if `device_format_selector' is passed as
770 //
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.
774 //
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.
778
779 int idx = 0;
780 char_block *element = head;
781
782 while (element != NULL)
783 write_upto_newline(&element, &idx, device_format_selector);
784
785 #if 0
786 if (close(stdoutfd) < 0)
787 sys_fatal ("close");
788
789 // now we grab fd=1 so that the next pipe cannot use fd=1
790 if (stdoutfd == 1) {
791 if (dup(2) != stdoutfd)
792 sys_fatal ("dup failed to use fd=1");
793 }
794 #endif /* 0 */
795 }
796
797 /*
798 * The image class remembers the position of all images in the
799 * postscript file and assigns names for each image.
800 */
801
802 struct imageItem {
803 imageItem *next;
804 int X1;
805 int Y1;
806 int X2;
807 int Y2;
808 char *imageName;
809 int resolution;
810 int maxx;
811 int pageNo;
812
813 imageItem(int x1, int y1, int x2, int y2,
814 int page, int res, int max_width, char *name);
815 ~imageItem();
816 };
817
818 /*
819 * imageItem - Constructor.
820 */
821
imageItem(int x1,int y1,int x2,int y2,int page,int res,int max_width,char * name)822 imageItem::imageItem(int x1, int y1, int x2, int y2,
823 int page, int res, int max_width, char *name)
824 {
825 X1 = x1;
826 Y1 = y1;
827 X2 = x2;
828 Y2 = y2;
829 pageNo = page;
830 resolution = res;
831 maxx = max_width;
832 imageName = name;
833 next = NULL;
834 }
835
836 /*
837 * imageItem - Destructor.
838 */
839
~imageItem()840 imageItem::~imageItem()
841 {
842 if (imageName)
843 free(imageName);
844 }
845
846 /*
847 * imageList - A class containing a list of imageItems.
848 */
849
850 class imageList {
851 private:
852 imageItem *head;
853 imageItem *tail;
854 int count;
855 public:
856 imageList();
857 ~imageList();
858 void add(int x1, int y1, int x2, int y2,
859 int page, int res, int maxx, char *name);
860 void createImages(void);
861 int createPage(int pageno);
862 void createImage(imageItem *i);
863 int getMaxX(int pageno);
864 };
865
866 /*
867 * imageList - Constructor.
868 */
869
imageList()870 imageList::imageList()
871 : head(0), tail(0), count(0)
872 {
873 }
874
875 /*
876 * imageList - Destructor.
877 */
878
~imageList()879 imageList::~imageList()
880 {
881 while (head != NULL) {
882 imageItem *i = head;
883 head = head->next;
884 delete i;
885 }
886 }
887
888 /*
889 * createPage - Create one image of, page pageno, from the postscript file.
890 */
891
createPage(int pageno)892 int imageList::createPage(int pageno)
893 {
894 char *s;
895
896 if (currentPageNo == pageno)
897 return 0;
898
899 if (currentPageNo >= 1) {
900 /*
901 * We need to unlink the files which change each time a new page is
902 * processed. The final unlink is done by xtmpfile when pre-grohtml
903 * exits.
904 */
905 unlink(imagePageName);
906 unlink(psPageName);
907 }
908
909 if (show_progress) {
910 fprintf(stderr, "[%d] ", pageno);
911 fflush(stderr);
912 }
913
914 #if defined(DEBUGGING)
915 if (debug)
916 fprintf(stderr, "creating page %d\n", pageno);
917 #endif
918
919 s = make_message("psselect -q -p%d %s %s\n",
920 pageno, psFileName, psPageName);
921
922 if (s == NULL)
923 sys_fatal("make_message");
924 #if defined(DEBUGGING)
925 if (debug) {
926 fwrite(s, sizeof(char), strlen(s), stderr);
927 fflush(stderr);
928 }
929 #endif
930 html_system(s, 1);
931
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",
938 image_gen,
939 EXE_EXT,
940 (getMaxX(pageno) * image_res) / postscriptRes,
941 image_device,
942 image_res,
943 antiAlias,
944 imagePageName,
945 psPageName);
946 if (s == NULL)
947 sys_fatal("make_message");
948 #if defined(DEBUGGING)
949 if (debug) {
950 fwrite(s, sizeof(char), strlen(s), stderr);
951 fflush(stderr);
952 }
953 #endif
954 html_system(s, 1);
955 free(s);
956 currentPageNo = pageno;
957 return 0;
958 }
959
960 /*
961 * min - Return the minimum of two numbers.
962 */
963
min(int x,int y)964 int min(int x, int y)
965 {
966 if (x < y)
967 return x;
968 else
969 return y;
970 }
971
972 /*
973 * max - Return the maximum of two numbers.
974 */
975
max(int x,int y)976 int max(int x, int y)
977 {
978 if (x > y)
979 return x;
980 else
981 return y;
982 }
983
984 /*
985 * getMaxX - Return the largest right-hand position for any image
986 * on, pageno.
987 */
988
getMaxX(int pageno)989 int imageList::getMaxX(int pageno)
990 {
991 imageItem *h = head;
992 int x = postscriptRes * DEFAULT_LINE_LENGTH;
993
994 while (h != NULL) {
995 if (h->pageNo == pageno)
996 x = max(h->X2, x);
997 h = h->next;
998 }
999 return x;
1000 }
1001
1002 /*
1003 * createImage - Generate a minimal png file from the set of page images.
1004 */
1005
createImage(imageItem * i)1006 void imageList::createImage(imageItem *i)
1007 {
1008 if (i->X1 != -1) {
1009 char *s;
1010 int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
1011 - IMAGE_BOARDER_PIXELS,
1012 0);
1013 int y1 = max(image_res * vertical_offset / 72
1014 + min(i->Y1, i->Y2) * image_res / postscriptRes
1015 - IMAGE_BOARDER_PIXELS,
1016 0);
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",
1025 EXE_EXT,
1026 x1, y1, x2 - x1 + 1, y2 - y1 + 1,
1027 imagePageName,
1028 EXE_EXT,
1029 TRANSPARENT,
1030 i->imageName);
1031 if (s == NULL)
1032 sys_fatal("make_message");
1033
1034 #if defined(DEBUGGING)
1035 if (debug) {
1036 fprintf(stderr, s);
1037 fflush(stderr);
1038 }
1039 #endif
1040 html_system(s, 0);
1041 free(s);
1042 }
1043 else {
1044 fprintf(stderr, "failed to generate image of page %d\n", i->pageNo);
1045 fflush(stderr);
1046 }
1047 #if defined(DEBUGGING)
1048 }
1049 else {
1050 if (debug) {
1051 fprintf(stderr, "ignoring image as x1 coord is -1\n");
1052 fflush(stderr);
1053 }
1054 #endif
1055 }
1056 }
1057
1058 /*
1059 * add - Add an image description to the imageList.
1060 */
1061
add(int x1,int y1,int x2,int y2,int page,int res,int maxx,char * name)1062 void imageList::add(int x1, int y1, int x2, int y2,
1063 int page, int res, int maxx, char *name)
1064 {
1065 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
1066
1067 if (head == NULL) {
1068 head = i;
1069 tail = i;
1070 }
1071 else {
1072 tail->next = i;
1073 tail = i;
1074 }
1075 }
1076
1077 /*
1078 * createImages - For each image descriptor on the imageList,
1079 * create the actual image.
1080 */
1081
createImages(void)1082 void imageList::createImages(void)
1083 {
1084 imageItem *h = head;
1085
1086 while (h != NULL) {
1087 createImage(h);
1088 h = h->next;
1089 }
1090 }
1091
1092 static imageList listOfImages; // List of images defined by the region file.
1093
1094 /*
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
1098 * image.
1099 */
1100
generateImages(char * region_file_name)1101 static void generateImages(char *region_file_name)
1102 {
1103 pushBackBuffer *f=new pushBackBuffer(region_file_name);
1104
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)
1118 (void)f->getPB();
1119 if (f->putPB(f->getPB()) == '\n')
1120 (void)f->getPB();
1121 }
1122 else {
1123 /* Write any error messages out to the user. */
1124 fputc(f->getPB(), stderr);
1125 }
1126 }
1127
1128 listOfImages.createImages();
1129 if (show_progress) {
1130 fprintf(stderr, "done\n");
1131 fflush(stderr);
1132 }
1133 delete f;
1134 }
1135
1136 /*
1137 * set_redirection - Set up I/O Redirection for handle, was, to refer to
1138 * stream on handle, willbe.
1139 */
1140
set_redirection(int was,int willbe)1141 static void set_redirection(int was, int willbe)
1142 {
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)
1150 fprintf(stderr,
1151 "likely that stdout should be opened before %d\n", was);
1152 sys_fatal("dup2");
1153 }
1154
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.
1159 sys_fatal("close");
1160 }
1161 }
1162
1163 /*
1164 * save_and_redirect - Get duplicate handle for stream, was, then
1165 * redirect, was, to refer to, willbe.
1166 */
1167
save_and_redirect(int was,int willbe)1168 static int save_and_redirect(int was, int willbe)
1169 {
1170 if (was == willbe)
1171 // No redirection specified so don't do anything but silently bailing out.
1172 return (was);
1173
1174 // Proceeding with redirection so first save and verify our duplicate
1175 // handle for `was'.
1176 int saved = dup(was);
1177 if (saved < 0) {
1178 fprintf(stderr, "unable to get duplicate handle for %d\n", was);
1179 sys_fatal("dup");
1180 }
1181
1182 // Duplicate handle safely established so complete redirection.
1183 set_redirection(was, willbe);
1184
1185 // Finally return the saved duplicate descriptor for the
1186 // original `was' stream.
1187 return saved;
1188 }
1189
1190 /*
1191 * alterDeviceTo - If, toImage, is set
1192 * the argument list is altered to include
1193 * IMAGE_DEVICE and we invoke groff rather than troff.
1194 * Else
1195 * set -Thtml and groff.
1196 */
1197
alterDeviceTo(int argc,char * argv[],int toImage)1198 static void alterDeviceTo(int argc, char *argv[], int toImage)
1199 {
1200 int i = 0;
1201
1202 if (toImage) {
1203 while (i < argc) {
1204 if (strcmp(argv[i], "-Thtml") == 0)
1205 argv[i] = (char *)IMAGE_DEVICE;
1206 i++;
1207 }
1208 argv[troff_arg] = (char *)"groff"; /* rather than troff */
1209 }
1210 else {
1211 while (i < argc) {
1212 if (strcmp(argv[i], IMAGE_DEVICE) == 0)
1213 argv[i] = (char *)"-Thtml";
1214 i++;
1215 }
1216 argv[troff_arg] = (char *)"groff"; /* use groff -Z */
1217 }
1218 }
1219
1220 /*
1221 * addZ - Append -Z onto the command list for groff.
1222 */
1223
addZ(int argc,char * argv[])1224 char **addZ(int argc, char *argv[])
1225 {
1226 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1227 int i = 0;
1228
1229 if (new_argv == NULL)
1230 sys_fatal("malloc");
1231
1232 if (argc > 0) {
1233 new_argv[i] = argv[i];
1234 i++;
1235 }
1236 new_argv[i] = (char *)"-Z";
1237 while (i < argc) {
1238 new_argv[i + 1] = argv[i];
1239 i++;
1240 }
1241 argc++;
1242 new_argv[argc] = NULL;
1243 return new_argv;
1244 }
1245
1246 /*
1247 * addRegDef - Append a defined register or string onto the command
1248 * list for troff.
1249 */
1250
addRegDef(int argc,char * argv[],const char * numReg)1251 char **addRegDef(int argc, char *argv[], const char *numReg)
1252 {
1253 char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
1254 int i = 0;
1255
1256 if (new_argv == NULL)
1257 sys_fatal("malloc");
1258
1259 while (i < argc) {
1260 new_argv[i] = argv[i];
1261 i++;
1262 }
1263 new_argv[argc] = strsave(numReg);
1264 argc++;
1265 new_argv[argc] = NULL;
1266 return new_argv;
1267 }
1268
1269 /*
1270 * dump_args - Display the argument list.
1271 */
1272
dump_args(int argc,char * argv[])1273 void dump_args(int argc, char *argv[])
1274 {
1275 fprintf(stderr, " %d arguments:", argc);
1276 for (int i = 0; i < argc; i++)
1277 fprintf(stderr, " %s", argv[i]);
1278 fprintf(stderr, "\n");
1279 }
1280
run_output_filter(int filter,int,char ** argv)1281 int char_buffer::run_output_filter(int filter, int /* argc */, char **argv)
1282 {
1283 int pipedes[2];
1284 PID_T child_pid;
1285 int status;
1286
1287 if (pipe(pipedes) < 0)
1288 sys_fatal("pipe");
1289
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.
1293
1294 if ((child_pid = fork()) < 0)
1295 sys_fatal("fork");
1296
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!
1301
1302 set_redirection(STDIN_FILENO, pipedes[0]);
1303
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.
1306
1307 if (close(pipedes[1]) < 0)
1308 sys_fatal("close");
1309
1310 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1311
1312 if (filter == IMAGE_OUTPUT_FILTER) {
1313 // with BOTH `stdout' AND `stderr' diverted to files.
1314
1315 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1316 set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
1317 }
1318
1319 // Now we are ready to launch the output filter.
1320
1321 execvp(argv[0], argv);
1322
1323 // If we get to here then the `exec...' request for the output filter
1324 // failed. Diagnose it and bail out.
1325
1326 error("couldn't exec %1: %2", argv[0], strerror(errno), ((char *)0));
1327 fflush(stderr); // just in case error() didn't
1328 exit(1);
1329 }
1330
1331 else {
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.
1335
1336 if (close(pipedes[0]) < 0)
1337 sys_fatal("close");
1338
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.
1341
1342 pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
1343 emit_troff_output(DEVICE_FORMAT(filter));
1344
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.
1347
1348 set_redirection(STDOUT_FILENO, pipedes[1]);
1349
1350 // Finally, we must wait for the child process to complete.
1351
1352 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1353 sys_fatal("wait");
1354 }
1355
1356 #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
1357
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.
1362
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
1365 // in `pipedes[0]'.
1366
1367 pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
1368
1369 // for the Win32 model,
1370 // we need special provision for saving BOTH `stdout' and `stderr'.
1371
1372 int saved_stdout = dup(STDOUT_FILENO);
1373 int saved_stderr = STDERR_FILENO;
1374
1375 // The IMAGE_OUTPUT_FILTER needs special output redirection...
1376
1377 if (filter == IMAGE_OUTPUT_FILTER) {
1378 // with BOTH `stdout' AND `stderr' diverted to files while saving a
1379 // duplicate handle for `stderr'.
1380
1381 set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
1382 saved_stderr = save_and_redirect(STDERR_FILENO, REGION_OUTPUT_STREAM);
1383 }
1384
1385 // We then use an asynchronous spawn request to start the post-processor.
1386
1387 if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
1388 // Should the spawn request fail we issue a diagnostic and bail out.
1389
1390 error("cannot spawn %1: %2", argv[0], strerror(errno), ((char *)0));
1391 exit(1);
1392 }
1393
1394 // Once the post-processor has been started we revert our `stdin'
1395 // to its original saved source, which also closes the readable handle
1396 // for the pipe.
1397
1398 set_redirection(STDIN_FILENO, pipedes[0]);
1399
1400 // if we redirected `stderr', for use by the image post-processor,
1401 // then we also need to reinstate its original assignment.
1402
1403 if (filter == IMAGE_OUTPUT_FILTER)
1404 set_redirection(STDERR_FILENO, saved_stderr);
1405
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.
1408
1409 set_redirection(STDOUT_FILENO, pipedes[1]);
1410 emit_troff_output(DEVICE_FORMAT(filter));
1411
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.
1414
1415 set_redirection(STDOUT_FILENO, saved_stdout);
1416
1417 // And finally, we must wait for the child process to complete.
1418
1419 if (WAIT(&status, child_pid, _WAIT_CHILD) != child_pid)
1420 sys_fatal("wait");
1421
1422 #else /* can't do asynchronous pipes! */
1423
1424 // TODO: code to support an MS-DOS style process model
1425 // should go here
1426
1427 #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
1428
1429 return 0;
1430 }
1431
1432 /*
1433 * do_html - Set the troff number htmlflip and
1434 * write out the buffer to troff -Thtml.
1435 */
1436
do_html(int argc,char * argv[])1437 int char_buffer::do_html(int argc, char *argv[])
1438 {
1439 string s;
1440
1441 alterDeviceTo(argc, argv, 0);
1442 argv += troff_arg; // skip all arguments up to groff
1443 argc -= troff_arg;
1444 argv = addZ(argc, argv);
1445 argc++;
1446
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());
1452 argc++;
1453
1454 #if defined(DEBUGGING)
1455 # define HTML_DEBUG_STREAM OUTPUT_STREAM(htmlFileName)
1456 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1457 if (debug) {
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);
1461 }
1462 #endif
1463
1464 return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
1465 }
1466
1467 /*
1468 * do_image - Write out the buffer to troff -Tps.
1469 */
1470
do_image(int argc,char * argv[])1471 int char_buffer::do_image(int argc, char *argv[])
1472 {
1473 string s;
1474
1475 alterDeviceTo(argc, argv, 1);
1476 argv += troff_arg; // skip all arguments up to troff/groff
1477 argc -= troff_arg;
1478 argv = addRegDef(argc, argv, "-rps4html=1");
1479 argc++;
1480
1481 s = "-dwww-image-template=";
1482 s += macroset_template;
1483 s += '\0';
1484 argv = addRegDef(argc, argv, s.contents());
1485 argc++;
1486
1487 // override local settings and produce a page size letter postscript file
1488 argv = addRegDef(argc, argv, "-P-pletter");
1489 argc++;
1490
1491 #if defined(DEBUGGING)
1492 # define IMAGE_DEBUG_STREAM OUTPUT_STREAM(troffFileName)
1493 // slight security risk so only enabled if compiled with defined(DEBUGGING)
1494 if (debug) {
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);
1498 }
1499 #endif
1500
1501 return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
1502 }
1503
1504 static char_buffer inputFile;
1505
1506 /*
1507 * usage - Emit usage arguments.
1508 */
1509
usage(FILE * stream)1510 static void usage(FILE *stream)
1511 {
1512 fprintf(stream,
1513 "usage: %s troffname [-Iimage_name] [-Dimage_directory]\n"
1514 " [-P-o vertical_image_offset] [-P-i image_resolution]\n"
1515 " [troff flags]\n",
1516 program_name);
1517 fprintf(stream,
1518 " vertical_image_offset (default %d/72 of an inch)\n",
1519 vertical_offset);
1520 fprintf(stream,
1521 " image_resolution (default %d) pixels per inch\n",
1522 image_res);
1523 fprintf(stream,
1524 " image_name is the name of the stem for all images\n"
1525 " (default is grohtml-<pid>)\n");
1526 fprintf(stream,
1527 " place all png files into image_directory\n");
1528 }
1529
1530 /*
1531 * scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
1532 * and -P-I. Return the argument index of the first
1533 * non-option.
1534 */
1535
scanArguments(int argc,char ** argv)1536 static int scanArguments(int argc, char **argv)
1537 {
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");
1544 int c, i;
1545 static const struct option long_options[] = {
1546 { "help", no_argument, 0, CHAR_MAX + 1 },
1547 { "version", no_argument, 0, 'v' },
1548 { NULL, 0, 0, 0 }
1549 };
1550 while ((c = getopt_long(argc, argv, "+a:bdD:F:g:hi:I:j:lno:prs:S:v",
1551 long_options, NULL))
1552 != EOF)
1553 switch(c) {
1554 case 'a':
1555 textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1556 MAX_ALPHA_BITS);
1557 if (textAlphaBits == 3) {
1558 error("cannot use 3 bits of antialiasing information");
1559 exit(1);
1560 }
1561 break;
1562 case 'b':
1563 // handled by post-grohtml (set background color to white)
1564 break;
1565 case 'd':
1566 #if defined(DEBUGGING)
1567 debug = TRUE;
1568 #endif
1569 break;
1570 case 'D':
1571 image_dir = optarg;
1572 break;
1573 case 'F':
1574 font_path.command_line_dir(optarg);
1575 break;
1576 case 'g':
1577 graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
1578 MAX_ALPHA_BITS);
1579 if (graphicAlphaBits == 3) {
1580 error("cannot use 3 bits of antialiasing information");
1581 exit(1);
1582 }
1583 break;
1584 case 'h':
1585 // handled by post-grohtml
1586 break;
1587 case 'i':
1588 image_res = atoi(optarg);
1589 break;
1590 case 'I':
1591 image_template = optarg;
1592 break;
1593 case 'j':
1594 // handled by post-grohtml (set job name for multiple file output)
1595 break;
1596 case 'l':
1597 // handled by post-grohtml (no automatic section links)
1598 break;
1599 case 'n':
1600 // handled by post-grohtml (generate simple heading anchors)
1601 break;
1602 case 'o':
1603 vertical_offset = atoi(optarg);
1604 break;
1605 case 'p':
1606 show_progress = TRUE;
1607 break;
1608 case 'r':
1609 // handled by post-grohtml (no header and footer lines)
1610 break;
1611 case 's':
1612 // handled by post-grohtml (use font size n as the html base font size)
1613 break;
1614 case 'S':
1615 // handled by post-grohtml (set file split level)
1616 break;
1617 case 'v':
1618 printf("GNU pre-grohtml (groff) version %s\n", Version_string);
1619 exit(0);
1620 case CHAR_MAX + 1: // --help
1621 usage(stdout);
1622 exit(0);
1623 break;
1624 case '?':
1625 usage(stderr);
1626 exit(1);
1627 break;
1628 default:
1629 break;
1630 }
1631
1632 i = optind;
1633 while (i < argc) {
1634 if (strcmp(argv[i], troff_name) == 0)
1635 troff_arg = i;
1636 else if (argv[i][0] != '-')
1637 return i;
1638 i++;
1639 }
1640 a_delete troff_name;
1641
1642 return argc;
1643 }
1644
1645 /*
1646 * makeTempFiles - Name the temporary files.
1647 */
1648
makeTempFiles(void)1649 static int makeTempFiles(void)
1650 {
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 */
1659 FILE *f;
1660
1661 /* psPageName contains a single page of postscript */
1662 f = xtmpfile(&psPageName,
1663 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1664 TRUE);
1665 if (f == NULL) {
1666 sys_fatal("xtmpfile");
1667 return -1;
1668 }
1669 fclose(f);
1670
1671 /* imagePageName contains a bitmap image of the single postscript page */
1672 f = xtmpfile(&imagePageName,
1673 PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT,
1674 TRUE);
1675 if (f == NULL) {
1676 sys_fatal("xtmpfile");
1677 return -1;
1678 }
1679 fclose(f);
1680
1681 /* psFileName contains a postscript file of the complete document */
1682 f = xtmpfile(&psFileName,
1683 PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT,
1684 TRUE);
1685 if (f == NULL) {
1686 sys_fatal("xtmpfile");
1687 return -1;
1688 }
1689 fclose(f);
1690
1691 /* regionFileName contains a list of the images and their boxed coordinates */
1692 f = xtmpfile(®ionFileName,
1693 REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT,
1694 TRUE);
1695 if (f == NULL) {
1696 sys_fatal("xtmpfile");
1697 return -1;
1698 }
1699 fclose(f);
1700
1701 #endif /* not DEBUGGING */
1702 return 0;
1703 }
1704
main(int argc,char ** argv)1705 int main(int argc, char **argv)
1706 {
1707 program_name = argv[0];
1708 int i;
1709 int found = 0;
1710 int ok = 1;
1711
1712 #ifdef CAPTURE_MODE
1713 FILE *dump;
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)
1719 fputc(i, dump);
1720 fclose(dump);
1721 }
1722 exit(1);
1723 #endif /* CAPTURE_MODE */
1724 device = "html";
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);
1732 setupAntiAlias();
1733 checkImageDir();
1734 makeFileName();
1735 while (i < argc) {
1736 if (argv[i][0] != '-') {
1737 /* found source file */
1738 ok = do_file(argv[i]);
1739 if (!ok)
1740 return 0;
1741 found = 1;
1742 }
1743 i++;
1744 }
1745
1746 if (!found)
1747 do_file("-");
1748 if (makeTempFiles())
1749 return 1;
1750 ok = inputFile.do_image(argc, argv);
1751 if (ok == 0) {
1752 generateImages(regionFileName);
1753 ok = inputFile.do_html(argc, argv);
1754 }
1755 return ok;
1756 }
1757
do_file(const char * filename)1758 static int do_file(const char *filename)
1759 {
1760 FILE *fp;
1761
1762 current_filename = filename;
1763 if (strcmp(filename, "-") == 0)
1764 fp = stdin;
1765 else {
1766 fp = fopen(filename, "r");
1767 if (fp == 0) {
1768 error("can't open `%1': %2", filename, strerror(errno));
1769 return 0;
1770 }
1771 }
1772
1773 if (inputFile.read_file(fp)) {
1774 // XXX
1775 }
1776
1777 if (fp != stdin)
1778 fclose(fp);
1779 current_filename = NULL;
1780 return 1;
1781 }
1782