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