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