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