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