home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / POSTSCPT / GSVIEW / SRC / PS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-22  |  53.8 KB  |  1,682 lines

  1. /*
  2.  * ps.c -- Postscript scanning and copying routines.
  3.  * Copyright (C) 1992, 1994  Timothy O. Theisen.  All rights reserved.
  4.  *
  5.  * This file is part of Ghostview.
  6.  *
  7.  * Ghostview is distributed with NO WARRANTY OF ANY KIND.  No author or
  8.  * distributor accepts any responsibility for the consequences of using
  9.  * it, or for whether it serves any particular purpose or works at all,
  10.  * unless he or she says so in writing.  Refer to the Ghostview Free
  11.  * Public License (the "License") for full details.
  12.  *
  13.  * Every copy of Ghostview must include a copy of the License, normally
  14.  * in a plain ASCII text file named PUBLIC.  The License grants you the
  15.  * right to copy, modify and redistribute Ghostview, but only under
  16.  * certain conditions described in the License.  Among other things, the
  17.  * License requires that the copyright notice and this notice be preserved
  18.  * on all copies.
  19.  *
  20.  *
  21.  *   Author: Tim Theisen           Systems Programmer
  22.  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
  23.  *     UUCP: uwvax!tim             University of Wisconsin-Madison
  24.  *    Phone: (608)262-0438         1210 West Dayton Street
  25.  *      FAX: (608)262-9777         Madison, WI   53706
  26.  */
  27. /* modified by Russell Lang, rjl@aladdin.com to use with GSview
  28.  * 1995-01-11
  29.  * supports DOS EPS files (binary header)
  30.  * supports the following incorrect DSC usage
  31.  *   %%BeginData lines > 254 char                 (Microsoft Windows)
  32.  *   EPS inclusions surrounded by %MSEPS Preamble/%MSEPS Trailer
  33.  *    instead of %%BeginDocument/%%EndDocument    (Microsoft Windows).
  34.  *   %!PS-Adobe instead of %!PS-Adobe-            (OS/2)
  35.  *   atend instead of (atend)
  36.  * rjl 1995-04-01
  37.  *   Commented out two lines to deal with documents that include
  38.  *   code after %%EndSetup and before %%Page:     (Microsoft Windows)
  39.  */
  40.  
  41. #include <stdio.h>
  42.  
  43. #if defined(__TURBOC__) || defined(OS2)
  44.  
  45. #include <stdlib.h>
  46. #include <string.h>
  47. #define strcasecmp(s,t) stricmp(s,t)
  48. extern void pserror(char *str);
  49.  
  50. #else
  51.  
  52. #if defined(__STDC__)
  53. #include <stdlib.h>
  54. #include <string.h>
  55. #else
  56. #ifndef SEEK_SET
  57. #define SEEK_SET 0
  58. #endif
  59. #ifndef BUFSIZ
  60. #define BUFSIZ 1024
  61. #endif
  62. #include <X11/Xos.h>        /* #includes the appropriate <string.h> */
  63. #endif
  64. #define pserror(str) fprintf(stderr,str)
  65.  
  66. #endif
  67.  
  68. #include <ctype.h>
  69. #include "ps.h"
  70.  
  71. #ifdef BSD4_2
  72. #define memset(a,b,c) bzero(a,c)
  73. #endif
  74.  
  75. /* redefine fgets to use a version that handles Unix, PC, or Mac EOL characters */
  76. #define fgets psfgets
  77. #if NeedFunctionPrototypes
  78. char *psfgets(char *s, int n, FILE *stream);
  79. #else
  80. char *psfgets();
  81. #endif
  82.  
  83. /* length calculates string length at compile time */
  84. /* can only be used with character constants */
  85. #define length(a) (sizeof(a)-1)
  86. #define iscomment(a, b)    (strncmp(a, b, length(b)) == 0)
  87. #define DSCcomment(a) (a[0] == '%' && a[1] == '%')
  88.  
  89.     /* list of standard paper sizes from Adobe's PPD. */
  90.  
  91. struct documentmedia papersizes[] = {
  92.     {"Letter",         612,  792},
  93.     {"LetterSmall",     612,  792},
  94.     {"Tabloid",         792, 1224},
  95.     {"Ledger",        1224,  792},
  96.     {"Legal",         612, 1008},
  97.     {"Statement",     396,  612},
  98.     {"Executive",     540,  720},
  99.     {"A3",         842, 1190},
  100.     {"A4",         595,  842},
  101.     {"A4Small",         595,  842},
  102.     {"A5",         420,  595},
  103.     {"B4",         729, 1032},
  104.     {"B5",         516,  729},
  105.     {"Folio",         612,  936},
  106.     {"Quarto",         610,  780},
  107.     {"10x14",         720, 1008},
  108.     {NULL,           0,    0}
  109. };
  110.  
  111.  
  112. #if NeedFunctionPrototypes
  113. static char *readline(char *line, int size, FILE *fp, unsigned long enddoseps, 
  114.   long *position, unsigned int *line_len, unsigned int *line_count);
  115. #else
  116. static char *readline();
  117. #endif
  118.  
  119. #if NeedFunctionPrototypes
  120. static char *gettextline(char *line);
  121. #else
  122. static char *gettextline();
  123. #endif
  124.  
  125. #if NeedFunctionPrototypes
  126. static char *gettext(char *line, char **next_char);
  127. #else
  128. static char *gettext();
  129. #endif
  130.  
  131. #if NeedFunctionPrototypes
  132. static int  blank(char *line);
  133. #else
  134. static int  blank();
  135. #endif
  136.  
  137. /*
  138.  *    psscan -- scan the PostScript file for document structuring comments.
  139.  *
  140.  *    This scanner is designed to retrieve the information necessary for
  141.  *    the ghostview previewer.  It will scan files that conform to any
  142.  *    version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
  143.  *    It does not really care which version of comments the file contains.
  144.  *    (The comments are largely upward compatible.)  It will scan a number
  145.  *    of non-conforming documents.  (You could have part of the document
  146.  *    conform to V2.0 and the rest conform to V3.0.  It would be similar
  147.  *    to the DC-2 1/2+, it would look funny but it can still fly.)
  148.  *
  149.  *    This routine returns a pointer to the document structure.
  150.  *    The structure contains the information relevant to previewing.
  151.  *      These include EPSF flag (to tell if the file is a encapsulated figure),
  152.  *      Page Media (for the Page Size), Bounding Box (to minimize backing
  153.  *      pixmap size or determine window size for encapsulated PostScript), 
  154.  *      Orientation of Paper (for default transformation matrix), and
  155.  *      Page Order.  The title and CreationDate are also retrieved to
  156.  *      help identify the document.
  157.  *
  158.  *      The following comments are examined:
  159.  *
  160.  *      Header section: 
  161.  *      Must start with %!PS-Adobe.  Version numbers ignored.
  162.  *
  163.  *      %!PS-Adobe* [EPSF-*]
  164.  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
  165.  *      %%CreationDate: <textline>
  166.  *      %%Orientation: Portrait|Landscape|(atend)
  167.  *      %%Pages: <uint> [<int>]|(atend)
  168.  *      %%PageOrder: Ascend|Descend|Special|(atend)
  169.  *      %%Title: <textline>
  170.  *      %%DocumentMedia: <text> <real> <real> <real> <text> <text>
  171.  *      %%DocumentPaperSizes: <text>
  172.  *      %%EndComments
  173.  *
  174.  *      Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
  175.  *            Also either the 2.0 %%DocumentPaperSizes or the 3.0
  176.  *            %%DocumentMedia comments are accepted as well.
  177.  *
  178.  *      The header section ends either explicitly with %%EndComments or
  179.  *      implicitly with any line that does not begin with %X where X is
  180.  *      a not whitespace character.
  181.  *
  182.  *      If the file is encapsulated PostScript the optional Preview section
  183.  *      is next:
  184.  *
  185.  *      %%BeginPreview
  186.  *      %%EndPreview
  187.  *
  188.  *      This section explicitly begins and ends with the above comments.
  189.  *
  190.  *      Next the Defaults section for version 3 page defaults:
  191.  *
  192.  *      %%BeginDefaults
  193.  *      %%PageBoundingBox: <int> <int> <int> <int>
  194.  *      %%PageOrientation: Portrait|Landscape
  195.  *      %%PageMedia: <text>
  196.  *      %%EndDefaults
  197.  *
  198.  *      This section explicitly begins and ends with the above comments.
  199.  *
  200.  *      The prolog section either explicitly starts with %%BeginProlog or
  201.  *      implicitly with any nonblank line.
  202.  *
  203.  *      %%BeginProlog
  204.  *      %%EndProlog
  205.  *
  206.  *      The Prolog should end with %%EndProlog, however the proglog implicitly
  207.  *      ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
  208.  *
  209.  *      The Setup section is where the version 2 page defaults are found.
  210.  *      This section either explicitly begins with %%BeginSetup or implicitly
  211.  *      with any nonblank line after the Prolog.
  212.  *
  213.  *      %%BeginSetup
  214.  *      %%PageBoundingBox: <int> <int> <int> <int>
  215.  *      %%PageOrientation: Portrait|Landscape
  216.  *      %%PaperSize: <text>
  217.  *      %%EndSetup
  218.  *
  219.  *      The Setup should end with %%EndSetup, however the setup implicitly
  220.  *      ends when %%Page, %%Trailer or %%EOF are encountered.
  221.  *
  222.  *      Next each page starts explicitly with %%Page and ends implicitly with
  223.  *      %%Page or %%Trailer or %%EOF.  The following comments are recognized:
  224.  *
  225.  *      %%Page: <text> <uint>
  226.  *      %%PageBoundingBox: <int> <int> <int> <int>|(atend)
  227.  *      %%PageOrientation: Portrait|Landscape
  228.  *      %%PageMedia: <text>
  229.  *      %%PaperSize: <text>
  230.  *
  231.  *      The tralier section start explicitly with %%Trailer and end with %%EOF.
  232.  *      The following comment are examined with the proper (atend) notation
  233.  *      was used in the header:
  234.  *
  235.  *      %%Trailer
  236.  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
  237.  *      %%Orientation: Portrait|Landscape|(atend)
  238.  *      %%Pages: <uint> [<int>]|(atend)
  239.  *      %%PageOrder: Ascend|Descend|Special|(atend)
  240.  *      %%EOF
  241.  *
  242.  *
  243.  *  + A DC-3 received severe damage to one of its wings.  The wing was a total
  244.  *    loss.  There was no replacement readily available, so the mechanic
  245.  *    installed a wing from a DC-2.
  246.  */
  247.  
  248. struct document *
  249. psscan(file)
  250.     FILE *file;
  251. {
  252.     struct document *doc;
  253.     int bb_set = NONE;
  254.     int pages_set = NONE;
  255.     int page_order_set = NONE;
  256.     int orientation_set = NONE;
  257.     int page_bb_set = NONE;
  258.     int page_media_set = NONE;
  259.     int preread;        /* flag which tells the readline isn't needed */
  260.     int i;
  261.     unsigned int maxpages = 0;
  262.     unsigned int nextpage = 1;    /* Next expected page */
  263.     unsigned int thispage;
  264.     int ignore = 0;        /* whether to ignore page ordinals */
  265.     char *label;
  266.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  267.     char text[PSLINELENGTH];    /* Temporary storage for text */
  268.     long position;        /* Position of the current line */
  269.     long beginsection;        /* Position of the beginning of the section */
  270.     unsigned int line_count;    /* line count for error messages */
  271.     unsigned int line_len;     /* Length of the current line */
  272.     unsigned int section_len;    /* Place to accumulate the section length */
  273.     char *next_char;        /* 1st char after text returned by gettext() */
  274.     char *cp;
  275.     struct documentmedia *dmp;
  276.     unsigned long enddoseps;    /* zero of not DOS EPS, otherwise position of end of ps section */
  277.     DOSEPS doseps;
  278.  
  279.  
  280.     line_count = 0;
  281.     rewind(file);
  282.  
  283.     /* rjl: check for DOS EPS files and almost DSC files that start with ^D */
  284.     enddoseps = ps_read_doseps(file, &doseps);
  285.     position = ftell(file);
  286.     if (fgetc(file) != '\004')
  287.     fseek(file, position, SEEK_SET);
  288.  
  289.     if (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) == NULL) {
  290.     pserror("Warning: empty file.\n");
  291.     return(NULL);
  292.     }
  293.  
  294.     /* Header comments */
  295.  
  296.     if (iscomment(line,"%!PS-Adobe")) {  /* rjl: some almost DSC code doesn't include the hyphen or version number */
  297.     doc = (struct document *) malloc(sizeof(struct document));
  298.     if (doc == NULL) {
  299.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  300.         exit(-1);
  301.     }
  302.     memset(doc, 0, sizeof(struct document));
  303.     sscanf(line, "%*s %s", text);
  304.     doc->epsf = iscomment(text, "EPSF-");
  305.     doc->beginheader = position;
  306.     section_len = line_len;
  307.     if (enddoseps) { /* rjl: add doseps header */
  308.         doc->doseps = (DOSEPS *) malloc(sizeof(DOSEPS));
  309.         if (doc == NULL) {
  310.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  311.         exit(-1);
  312.         }
  313.         *(doc->doseps) = doseps;
  314.     }
  315.     } else {
  316.     return(NULL);
  317.     }
  318.  
  319.     preread = 0;
  320.     while (preread || readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  321.     if (!preread) section_len += line_len;
  322.     preread = 0;
  323.     if (line[0] != '%' ||
  324.         iscomment(line+1, "%EndComments") ||
  325.         line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
  326.         !isprint(line[1])) {
  327.         break;
  328.     } else if (line[1] != '%') {
  329.         /* Do nothing */
  330.     } else if (iscomment(line+2, "Begin") || iscomment(line+2, "End") ||
  331.            iscomment(line+2, "Page:") || iscomment(line+2, "Trailer") ||
  332.            iscomment(line+2, "EOF")) {
  333.         break;
  334.     } else if (doc->title == NULL && iscomment(line+2, "Title:")) {
  335.         doc->title = gettextline(line+length("%%Title:"));
  336.     } else if (doc->date == NULL && iscomment(line+2, "CreationDate:")) {
  337.         doc->date = gettextline(line+length("%%CreationDate:"));
  338.     } else if (bb_set == NONE && iscomment(line+2, "BoundingBox:")) {
  339.         sscanf(line+length("%%BoundingBox:"), "%s", text);
  340.         if (strcmp(text, "(atend)") == 0) {
  341.         bb_set = ATEND;
  342.         }
  343.         else if (strcmp(text, "atend") == 0) { /* rjl: some bad DSC omits the () */
  344.         char buf[64];
  345.         sprintf(buf, "Invalid %%%%BoundingBox on line %d\n", line_count);
  346.         pserror(buf);
  347.         bb_set = ATEND;
  348.         } else {
  349.         if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
  350.                &(doc->boundingbox[LLX]),
  351.                &(doc->boundingbox[LLY]),
  352.                &(doc->boundingbox[URX]),
  353.                &(doc->boundingbox[URY])) == 4)
  354.             bb_set = 1;
  355.         else {
  356.             float fllx, flly, furx, fury;
  357.             if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
  358.                    &fllx, &flly, &furx, &fury) == 4) {
  359.             bb_set = 1;
  360.             doc->boundingbox[LLX] = fllx;
  361.             doc->boundingbox[LLY] = flly;
  362.             doc->boundingbox[URX] = furx;
  363.             doc->boundingbox[URY] = fury;
  364.             if (fllx < doc->boundingbox[LLX])
  365.                 doc->boundingbox[LLX]--;
  366.             if (flly < doc->boundingbox[LLY])
  367.                 doc->boundingbox[LLY]--;
  368.             if (furx > doc->boundingbox[URX])
  369.                 doc->boundingbox[URX]++;
  370.             if (fury > doc->boundingbox[URY])
  371.                 doc->boundingbox[URY]++;
  372.             }
  373.         }
  374.         }
  375.     } else if (orientation_set == NONE &&
  376.            iscomment(line+2, "Orientation:")) {
  377.         sscanf(line+length("%%Orientation:"), "%s", text);
  378.         if (strcmp(text, "(atend)") == 0) {
  379.         orientation_set = ATEND;
  380.         }
  381.         else if (strcmp(text, "atend") == 0) { /* rjl: some bad DSC omits the () */
  382.         char buf[64];
  383.         sprintf(buf, "Invalid %%%%Orientation on line %d\n", line_count);
  384.         pserror(buf);
  385.         orientation_set = ATEND;
  386.         } else if (strcmp(text, "Portrait") == 0) {
  387.         doc->orientation = PORTRAIT;
  388.         orientation_set = 1;
  389.         } else if (strcmp(text, "Landscape") == 0) {
  390.         doc->orientation = LANDSCAPE;
  391.         orientation_set = 1;
  392.         }
  393.     } else if (page_order_set == NONE && iscomment(line+2, "PageOrder:")) {
  394.         sscanf(line+length("%%PageOrder:"), "%s", text);
  395.         if (strcmp(text, "(atend)") == 0) {
  396.         page_order_set = ATEND;
  397.         }
  398.         else if (strcmp(text, "atend") == 0) { /* rjl: some bad DSC omits the () */
  399.         char buf[64];
  400.         sprintf(buf, "Invalid %%%%PageOrder on line %d\n", line_count);
  401.         pserror(buf);
  402.         page_order_set = ATEND;
  403.         } else if (strcmp(text, "Ascend") == 0) {
  404.         doc->pageorder = ASCEND;
  405.         page_order_set = 1;
  406.         } else if (strcmp(text, "Descend") == 0) {
  407.         doc->pageorder = DESCEND;
  408.         page_order_set = 1;
  409.         } else if (strcmp(text, "Special") == 0) {
  410.         doc->pageorder = SPECIAL;
  411.         page_order_set = 1;
  412.         }
  413.     } else if (pages_set == NONE && iscomment(line+2, "Pages:")) {
  414.         sscanf(line+length("%%Pages:"), "%s", text);
  415.         if (strcmp(text, "(atend)") == 0) {
  416.         pages_set = ATEND;
  417.         }
  418.         else if (strcmp(text, "atend") == 0) { /* rjl: some bad DSC omits the () */
  419.         char buf[64];
  420.         sprintf(buf, "Invalid %%%%Pages on line %d\n", line_count);
  421.         pserror(buf);
  422.         pages_set = ATEND;
  423.         } else {
  424.         switch (sscanf(line+length("%%Pages:"), "%d %d",
  425.                    &maxpages, &i)) {
  426.             case 2:
  427.             if (page_order_set == NONE) {
  428.                 if (i == -1) {
  429.                 doc->pageorder = DESCEND;
  430.                 page_order_set = 1;
  431.                 } else if (i == 0) {
  432.                 doc->pageorder = SPECIAL;
  433.                 page_order_set = 1;
  434.                 } else if (i == 1) {
  435.                 doc->pageorder = ASCEND;
  436.                 page_order_set = 1;
  437.                 }
  438.             }
  439.             case 1:
  440.             if (maxpages > 0) {
  441.                 doc->pages = (struct page *) calloc(maxpages,
  442.                                sizeof(struct page));
  443.                 if (doc->pages == NULL) {
  444.                 pserror("Fatal Error: Dynamic memory exhausted.\n");
  445.                 exit(-1);
  446.                 }
  447.             }
  448.         }
  449.         }
  450.     } else if (doc->nummedia == NONE &&
  451.            iscomment(line+2, "DocumentMedia:")) {
  452.         float w, h;
  453.         doc->media = (struct documentmedia *)
  454.              malloc(sizeof (struct documentmedia));
  455.         if (doc->media == NULL) {
  456.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  457.         exit(-1);
  458.         }
  459.         doc->media[0].name = gettext(line+length("%%DocumentMedia:"),
  460.                      &next_char);
  461.         if (doc->media[0].name != NULL) {
  462.         if (sscanf(next_char, "%f %f", &w, &h) == 2) {
  463.             doc->media[0].width = w + 0.5;
  464.             doc->media[0].height = h + 0.5;
  465.         }
  466.         if (doc->media[0].width != 0 && doc->media[0].height != 0)
  467.             doc->nummedia = 1;
  468.         else
  469.             free(doc->media[0].name);
  470.         }
  471.         preread=1;
  472.         while (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) &&
  473.            DSCcomment(line) && iscomment(line+2, "+")) {
  474.         section_len += line_len;
  475.         doc->media = (struct documentmedia *)
  476.                  realloc(doc->media,
  477.                      (doc->nummedia+1)*
  478.                      sizeof (struct documentmedia));
  479.         if (doc->media == NULL) {
  480.             pserror("Fatal Error: Dynamic memory exhausted.\n");
  481.             exit(-1);
  482.         }
  483.         doc->media[doc->nummedia].name = gettext(line+length("%%+"),
  484.                              &next_char);
  485.         if (doc->media[doc->nummedia].name != NULL) {
  486.             if (sscanf(next_char, "%f %f", &w, &h) == 2) {
  487.             doc->media[doc->nummedia].width = w + 0.5;
  488.             doc->media[doc->nummedia].height = h + 0.5;
  489.             }
  490.             if (doc->media[doc->nummedia].width != 0 &&
  491.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  492.             else
  493.             free(doc->media[doc->nummedia].name);
  494.         }
  495.         }
  496.         section_len += line_len;
  497.         if (doc->nummedia != 0) doc->default_page_media = doc->media;
  498.     } else if (doc->nummedia == NONE &&
  499.            iscomment(line+2, "DocumentPaperSizes:")) {
  500.  
  501.         doc->media = (struct documentmedia *)
  502.              malloc(sizeof (struct documentmedia));
  503.         if (doc->media == NULL) {
  504.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  505.         exit(-1);
  506.         }
  507.         doc->media[0].name = gettext(line+length("%%DocumentPaperSizes:"),
  508.                      &next_char);
  509.         if (doc->media[0].name != NULL) {
  510.         doc->media[0].width = 0;
  511.         doc->media[0].height = 0;
  512.         for (dmp=papersizes; dmp->name != NULL; dmp++) {
  513.             /* Note: Paper size comment uses down cased paper size
  514.              * name.  Case insensitive compares are only used for
  515.              * PaperSize comments.
  516.              */
  517.             if (strcasecmp(doc->media[0].name, dmp->name) == 0) {
  518.             free(doc->media[0].name);
  519.             doc->media[0].name =
  520.                 (char *)malloc(strlen(dmp->name)+1);
  521.             if (doc->media[0].name == NULL) {
  522.                 pserror("Fatal Error: Dynamic memory exhausted.\n");
  523.                 exit(-1);
  524.             }
  525.             strcpy(doc->media[0].name, dmp->name);
  526.             doc->media[0].width = dmp->width;
  527.             doc->media[0].height = dmp->height;
  528.             break;
  529.             }
  530.         }
  531.         if (doc->media[0].width != 0 && doc->media[0].height != 0)
  532.             doc->nummedia = 1;
  533.         else
  534.             free(doc->media[0].name);
  535.         }
  536.         while ( (cp = gettext(next_char, &next_char)) != NULL ) {
  537.         doc->media = (struct documentmedia *)
  538.                  realloc(doc->media,
  539.                      (doc->nummedia+1)*
  540.                      sizeof (struct documentmedia));
  541.         if (doc->media == NULL) {
  542.             pserror("Fatal Error: Dynamic memory exhausted.\n");
  543.             exit(-1);
  544.         }
  545.         doc->media[doc->nummedia].name = cp;
  546.         doc->media[doc->nummedia].width = 0;
  547.         doc->media[doc->nummedia].height = 0;
  548.         for (dmp=papersizes; dmp->name != NULL; dmp++) {
  549.             /* Note: Paper size comment uses down cased paper size
  550.              * name.  Case insensitive compares are only used for
  551.              * PaperSize comments.
  552.              */
  553.             if (strcasecmp(doc->media[doc->nummedia].name,
  554.                    dmp->name) == 0) {
  555.             free(doc->media[doc->nummedia].name);
  556.             doc->media[doc->nummedia].name =
  557.                 (char *)malloc(strlen(dmp->name)+1);
  558.             if (doc->media[doc->nummedia].name == NULL) {
  559.                 pserror("Fatal Error: Dynamic memory exhausted.\n");
  560.                 exit(-1);
  561.             }
  562.             strcpy(doc->media[doc->nummedia].name, dmp->name);
  563.             doc->media[doc->nummedia].name = dmp->name;
  564.             doc->media[doc->nummedia].width = dmp->width;
  565.             doc->media[doc->nummedia].height = dmp->height;
  566.             break;
  567.             }
  568.         }
  569.         if (doc->media[doc->nummedia].width != 0 &&
  570.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  571.         else
  572.             free(doc->media[doc->nummedia].name);
  573.         }
  574.         preread=1;
  575.         while (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) &&
  576.            DSCcomment(line) && iscomment(line+2, "+")) {
  577.         section_len += line_len;
  578.         next_char = line + length("%%+");
  579.         while ( (cp = gettext(next_char, &next_char)) != NULL ) {
  580.             doc->media = (struct documentmedia *)
  581.                  realloc(doc->media,
  582.                      (doc->nummedia+1)*
  583.                      sizeof (struct documentmedia));
  584.             if (doc->media == NULL) {
  585.             pserror("Fatal Error: Dynamic memory exhausted.\n");
  586.             exit(-1);
  587.             }
  588.             doc->media[doc->nummedia].name = cp;
  589.             doc->media[doc->nummedia].width = 0;
  590.             doc->media[doc->nummedia].height = 0;
  591.             for (dmp=papersizes; dmp->name != NULL; dmp++) {
  592.             /* Note: Paper size comment uses down cased paper size
  593.              * name.  Case insensitive compares are only used for
  594.              * PaperSize comments.
  595.              */
  596.             if (strcasecmp(doc->media[doc->nummedia].name,
  597.                    dmp->name) == 0) {
  598.                 doc->media[doc->nummedia].width = dmp->width;
  599.                 doc->media[doc->nummedia].height = dmp->height;
  600.                 break;
  601.             }
  602.             }
  603.             if (doc->media[doc->nummedia].width != 0 &&
  604.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  605.             else
  606.             free(doc->media[doc->nummedia].name);
  607.         }
  608.         }
  609.         section_len += line_len;
  610.         if (doc->nummedia != 0) doc->default_page_media = doc->media;
  611.     }
  612.     }
  613.  
  614.     if (DSCcomment(line) && iscomment(line+2, "EndComments")) {
  615.     readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  616.     section_len += line_len;
  617.     }
  618.     doc->endheader = position;
  619.     doc->lenheader = section_len - line_len;
  620.  
  621.     /* Optional Preview comments for encapsulated PostScript files */ 
  622.  
  623.     beginsection = position;
  624.     section_len = line_len;
  625.     while (blank(line) &&
  626.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  627.     section_len += line_len;
  628.     }
  629.  
  630.     if (doc->epsf && DSCcomment(line) && iscomment(line+2, "BeginPreview")) {
  631.     doc->beginpreview = beginsection;
  632.     beginsection = 0;
  633.     while (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) &&
  634.            !(DSCcomment(line) && iscomment(line+2, "EndPreview"))) {
  635.         section_len += line_len;
  636.     }
  637.     section_len += line_len;
  638.     readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  639.     section_len += line_len;
  640.     doc->endpreview = position;
  641.     doc->lenpreview = section_len - line_len;
  642.     }
  643.  
  644.     /* Page Defaults for Version 3.0 files */
  645.  
  646.     if (beginsection == 0) {
  647.     beginsection = position;
  648.     section_len = line_len;
  649.     }
  650.     while (blank(line) &&
  651.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  652.     section_len += line_len;
  653.     }
  654.  
  655.     if (DSCcomment(line) && iscomment(line+2, "BeginDefaults")) {
  656.     doc->begindefaults = beginsection;
  657.     beginsection = 0;
  658.     while (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) &&
  659.            !(DSCcomment(line) && iscomment(line+2, "EndDefaults"))) {
  660.         section_len += line_len;
  661.         if (!DSCcomment(line)) {
  662.         /* Do nothing */
  663.         } else if (doc->default_page_orientation == NONE &&
  664.         iscomment(line+2, "PageOrientation:")) {
  665.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  666.         if (strcmp(text, "Portrait") == 0) {
  667.             doc->default_page_orientation = PORTRAIT;
  668.         } else if (strcmp(text, "Landscape") == 0) {
  669.             doc->default_page_orientation = LANDSCAPE;
  670.         }
  671.         } else if (page_media_set == NONE &&
  672.                iscomment(line+2, "PageMedia:")) {
  673.         cp = gettext(line+length("%%PageMedia:"), NULL);
  674.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  675.             if (strcmp(cp, dmp->name) == 0) {
  676.             doc->default_page_media = dmp;
  677.             page_media_set = 1;
  678.             break;
  679.             }
  680.         }
  681.         free(cp);
  682.         } else if (page_bb_set == NONE &&
  683.                iscomment(line+2, "PageBoundingBox:")) {
  684.         if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  685.                &(doc->default_page_boundingbox[LLX]),
  686.                &(doc->default_page_boundingbox[LLY]),
  687.                &(doc->default_page_boundingbox[URX]),
  688.                &(doc->default_page_boundingbox[URY])) == 4)
  689.             page_bb_set = 1;
  690.         else {
  691.             float fllx, flly, furx, fury;
  692.             if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
  693.                    &fllx, &flly, &furx, &fury) == 4) {
  694.             page_bb_set = 1;
  695.             doc->default_page_boundingbox[LLX] = fllx;
  696.             doc->default_page_boundingbox[LLY] = flly;
  697.             doc->default_page_boundingbox[URX] = furx;
  698.             doc->default_page_boundingbox[URY] = fury;
  699.             if (fllx < doc->default_page_boundingbox[LLX])
  700.                 doc->default_page_boundingbox[LLX]--;
  701.             if (flly < doc->default_page_boundingbox[LLY])
  702.                 doc->default_page_boundingbox[LLY]--;
  703.             if (furx > doc->default_page_boundingbox[URX])
  704.                 doc->default_page_boundingbox[URX]++;
  705.             if (fury > doc->default_page_boundingbox[URY])
  706.                 doc->default_page_boundingbox[URY]++;
  707.             }
  708.         }
  709.         }
  710.     }
  711.     section_len += line_len;
  712.     readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  713.     section_len += line_len;
  714.     doc->enddefaults = position;
  715.     doc->lendefaults = section_len - line_len;
  716.     }
  717.  
  718.     /* Document Prolog */
  719.  
  720.     if (beginsection == 0) {
  721.     beginsection = position;
  722.     section_len = line_len;
  723.     }
  724.     while (blank(line) &&
  725.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  726.     section_len += line_len;
  727.     }
  728.  
  729.     if (!(DSCcomment(line) &&
  730.       (iscomment(line+2, "BeginSetup") ||
  731.        iscomment(line+2, "Page:") ||
  732.        iscomment(line+2, "Trailer") ||
  733.        iscomment(line+2, "EOF")))) {
  734.     doc->beginprolog = beginsection;
  735.     beginsection = 0;
  736.     preread = 1;
  737.  
  738.     while ((preread ||
  739.         readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) &&
  740.            !(DSCcomment(line) &&
  741.              (iscomment(line+2, "EndProlog") ||
  742.               iscomment(line+2, "BeginSetup") ||
  743.               iscomment(line+2, "Page:") ||
  744.               iscomment(line+2, "Trailer") ||
  745.               iscomment(line+2, "EOF")))) {
  746.         if (!preread) section_len += line_len;
  747.         preread = 0;
  748.     }
  749.     section_len += line_len;
  750.     if (DSCcomment(line) && iscomment(line+2, "EndProlog")) {
  751.         readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  752.         section_len += line_len;
  753.     }
  754.     doc->endprolog = position;
  755.     doc->lenprolog = section_len - line_len;
  756.     }
  757.  
  758.     /* Document Setup,  Page Defaults found here for Version 2 files */
  759.  
  760.     if (beginsection == 0) {
  761.     beginsection = position;
  762.     section_len = line_len;
  763.     }
  764.     while (blank(line) &&
  765.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  766.     section_len += line_len;
  767.     }
  768.  
  769.     if (!(DSCcomment(line) &&
  770.       (iscomment(line+2, "Page:") ||
  771.        iscomment(line+2, "Trailer") ||
  772.        iscomment(line+2, "EOF")))) {
  773.     doc->beginsetup = beginsection;
  774.     beginsection = 0;
  775.     preread = 1;
  776.     while ((preread ||
  777.         readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) &&
  778.            !(DSCcomment(line) &&
  779.              (iscomment(line+2, "EndSetup") ||
  780.               iscomment(line+2, "Page:") ||
  781.               iscomment(line+2, "Trailer") ||
  782.               iscomment(line+2, "EOF")))) {
  783.         if (!preread) section_len += line_len;
  784.         preread = 0;
  785.         if (!DSCcomment(line)) {
  786.         /* Do nothing */
  787.         } else if (doc->default_page_orientation == NONE &&
  788.         iscomment(line+2, "PageOrientation:")) {
  789.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  790.         if (strcmp(text, "Portrait") == 0) {
  791.             doc->default_page_orientation = PORTRAIT;
  792.         } else if (strcmp(text, "Landscape") == 0) {
  793.             doc->default_page_orientation = LANDSCAPE;
  794.         }
  795.         } else if (page_media_set == NONE &&
  796.                iscomment(line+2, "PaperSize:")) {
  797.         cp = gettext(line+length("%%PaperSize:"), NULL);
  798.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  799.             /* Note: Paper size comment uses down cased paper size
  800.              * name.  Case insensitive compares are only used for
  801.              * PaperSize comments.
  802.              */
  803.             if (strcasecmp(cp, dmp->name) == 0) {
  804.             doc->default_page_media = dmp;
  805.             page_media_set = 1;
  806.             break;
  807.             }
  808.         }
  809.         free(cp);
  810.         } else if (page_bb_set == NONE &&
  811.                iscomment(line+2, "PageBoundingBox:")) {
  812.         if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  813.                &(doc->default_page_boundingbox[LLX]),
  814.                &(doc->default_page_boundingbox[LLY]),
  815.                &(doc->default_page_boundingbox[URX]),
  816.                &(doc->default_page_boundingbox[URY])) == 4)
  817.             page_bb_set = 1;
  818.         else {
  819.             float fllx, flly, furx, fury;
  820.             if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
  821.                    &fllx, &flly, &furx, &fury) == 4) {
  822.             page_bb_set = 1;
  823.             doc->default_page_boundingbox[LLX] = fllx;
  824.             doc->default_page_boundingbox[LLY] = flly;
  825.             doc->default_page_boundingbox[URX] = furx;
  826.             doc->default_page_boundingbox[URY] = fury;
  827.             if (fllx < doc->default_page_boundingbox[LLX])
  828.                 doc->default_page_boundingbox[LLX]--;
  829.             if (flly < doc->default_page_boundingbox[LLY])
  830.                 doc->default_page_boundingbox[LLY]--;
  831.             if (furx > doc->default_page_boundingbox[URX])
  832.                 doc->default_page_boundingbox[URX]++;
  833.             if (fury > doc->default_page_boundingbox[URY])
  834.                 doc->default_page_boundingbox[URY]++;
  835.             }
  836.         }
  837.         }
  838.     }
  839.     section_len += line_len;
  840.     if (DSCcomment(line) && iscomment(line+2, "EndSetup")) {
  841.         readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  842.         section_len += line_len;
  843.     }
  844.     doc->endsetup = position;
  845.     doc->lensetup = section_len - line_len;
  846.     }
  847.  
  848.     /* Individual Pages */
  849.  
  850.     if (beginsection == 0) {
  851.     beginsection = position;
  852.     section_len = line_len;
  853.     }
  854.     while (blank(line) &&
  855.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  856.     section_len += line_len;
  857.     }
  858.  
  859. newpage:
  860.     while (DSCcomment(line) && iscomment(line+2, "Page:")) {
  861.     if (maxpages == 0) {
  862.         maxpages = 1;
  863.         doc->pages = (struct page *) calloc(maxpages, sizeof(struct page));
  864.         if (doc->pages == NULL) {
  865.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  866.         exit(-1);
  867.         }
  868.     }
  869.     label = gettext(line+length("%%Page:"), &next_char);
  870.     if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  871.     if (nextpage == 1) {
  872.         ignore = thispage != 1;
  873.     }
  874.     if (!ignore && thispage != nextpage) {
  875.         free(label);
  876.         doc->numpages--;
  877.         goto continuepage;
  878.     }
  879.     nextpage++;
  880.     if (doc->numpages == maxpages) {
  881.         maxpages++;
  882.         doc->pages = (struct page *)
  883.              realloc(doc->pages, maxpages*sizeof (struct page));
  884.         if (doc->pages == NULL) {
  885.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  886.         exit(-1);
  887.         }
  888.     }
  889.     memset(&(doc->pages[doc->numpages]), 0, sizeof(struct page));
  890.     page_bb_set = NONE;
  891.     doc->pages[doc->numpages].label = label;
  892.     if (beginsection) {
  893.         doc->pages[doc->numpages].begin = beginsection;
  894.         beginsection = 0;
  895.     } else {
  896.         doc->pages[doc->numpages].begin = position;
  897.         section_len = line_len;
  898.     }
  899. continuepage:
  900.     while (readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count) &&
  901.            !(DSCcomment(line) &&
  902.              (iscomment(line+2, "Page:") ||
  903.               iscomment(line+2, "Trailer") ||
  904.               iscomment(line+2, "EOF")))) {
  905.         section_len += line_len;
  906.         if (!DSCcomment(line)) {
  907.         /* Do nothing */
  908.         } else if (doc->pages[doc->numpages].orientation == NONE &&
  909.         iscomment(line+2, "PageOrientation:")) {
  910.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  911.         if (strcmp(text, "Portrait") == 0) {
  912.             doc->pages[doc->numpages].orientation = PORTRAIT;
  913.         } else if (strcmp(text, "Landscape") == 0) {
  914.             doc->pages[doc->numpages].orientation = LANDSCAPE;
  915.         }
  916.         } else if (doc->pages[doc->numpages].media == NULL &&
  917.                iscomment(line+2, "PageMedia:")) {
  918.         cp = gettext(line+length("%%PageMedia:"), NULL);
  919.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  920.             if (strcmp(cp, dmp->name) == 0) {
  921.             doc->pages[doc->numpages].media = dmp;
  922.             break;
  923.             }
  924.         }
  925.         free(cp);
  926.         } else if (doc->pages[doc->numpages].media == NULL &&
  927.                iscomment(line+2, "PaperSize:")) {
  928.         cp = gettext(line+length("%%PaperSize:"), NULL);
  929.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  930.             /* Note: Paper size comment uses down cased paper size
  931.              * name.  Case insensitive compares are only used for
  932.              * PaperSize comments.
  933.              */
  934.             if (strcasecmp(cp, dmp->name) == 0) {
  935.             doc->pages[doc->numpages].media = dmp;
  936.             break;
  937.             }
  938.         }
  939.         free(cp);
  940.         } else if ((page_bb_set == NONE || page_bb_set == ATEND) &&
  941.                iscomment(line+2, "PageBoundingBox:")) {
  942.         sscanf(line+length("%%PageBoundingBox:"), "%s", text);
  943.         if (strcmp(text, "(atend)") == 0) {
  944.             page_bb_set = ATEND;
  945.         }
  946.         else if (strcmp(text, "atend") == 0) { /* rjl: some bad DSC omits the () */
  947.             char buf[64];
  948.             sprintf(buf, "Invalid %%%%PageBoundingBox on line %d\n", line_count);
  949.             pserror(buf);
  950.             page_bb_set = ATEND;
  951.         } else {
  952.             if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  953.                 &(doc->pages[doc->numpages].boundingbox[LLX]),
  954.                 &(doc->pages[doc->numpages].boundingbox[LLY]),
  955.                 &(doc->pages[doc->numpages].boundingbox[URX]),
  956.                 &(doc->pages[doc->numpages].boundingbox[URY])) == 4)
  957.             if (page_bb_set == NONE) page_bb_set = 1;
  958.             else {
  959.             float fllx, flly, furx, fury;
  960.             if (sscanf(line+length("%%PageBoundingBox:"),
  961.                    "%f %f %f %f",
  962.                    &fllx, &flly, &furx, &fury) == 4) {
  963.                 if (page_bb_set == NONE) page_bb_set = 1;
  964.                 doc->pages[doc->numpages].boundingbox[LLX] = fllx;
  965.                 doc->pages[doc->numpages].boundingbox[LLY] = flly;
  966.                 doc->pages[doc->numpages].boundingbox[URX] = furx;
  967.                 doc->pages[doc->numpages].boundingbox[URY] = fury;
  968.                 if (fllx <
  969.                     doc->pages[doc->numpages].boundingbox[LLX])
  970.                 doc->pages[doc->numpages].boundingbox[LLX]--;
  971.                 if (flly <
  972.                     doc->pages[doc->numpages].boundingbox[LLY])
  973.                 doc->pages[doc->numpages].boundingbox[LLY]--;
  974.                 if (furx >
  975.                     doc->pages[doc->numpages].boundingbox[URX])
  976.                 doc->pages[doc->numpages].boundingbox[URX]++;
  977.                 if (fury >
  978.                     doc->pages[doc->numpages].boundingbox[URY])
  979.                 doc->pages[doc->numpages].boundingbox[URY]++;
  980.             }
  981.             }
  982.         }
  983.         }
  984.     }
  985.     section_len += line_len;
  986.     doc->pages[doc->numpages].end = position;
  987.     doc->pages[doc->numpages].len = section_len - line_len;
  988.     doc->numpages++;
  989.     }
  990.  
  991.     /* Document Trailer */
  992.  
  993.     if (beginsection) {
  994.     doc->begintrailer = beginsection;
  995.     beginsection = 0;
  996.     } else {
  997.     doc->begintrailer = position;
  998.     section_len = line_len;
  999.     }
  1000.  
  1001.     preread = 1;
  1002.     while ((preread ||
  1003.         readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) &&
  1004.        !(DSCcomment(line) && iscomment(line+2, "EOF"))) {
  1005.     if (!preread) section_len += line_len;
  1006.     preread = 0;
  1007.     if (!DSCcomment(line)) {
  1008.         /* Do nothing */
  1009.     } else if (iscomment(line+2, "Page:")) {
  1010.         free(gettext(line+length("%%Page:"), &next_char));
  1011.         if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  1012.         if (!ignore && thispage == nextpage) {
  1013.         if (doc->numpages > 0) {
  1014.             doc->pages[doc->numpages-1].end = position;
  1015.             doc->pages[doc->numpages-1].len += section_len - line_len;
  1016.         } else {
  1017.             if (doc->endsetup) {
  1018.             doc->endsetup = position;
  1019. /* following line removed rjl 1995-04-01
  1020.             doc->endsetup += section_len - line_len;
  1021. */
  1022.             } else if (doc->endprolog) {
  1023.             doc->endprolog = position;
  1024. /* following line removed rjl 1995-04-01
  1025.             doc->endprolog += section_len - line_len;
  1026. */
  1027.             }
  1028.         }
  1029.         goto newpage;
  1030.         }
  1031.     } else if (bb_set == ATEND && iscomment(line+2, "BoundingBox:")) {
  1032.         if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
  1033.                &(doc->boundingbox[LLX]),
  1034.                &(doc->boundingbox[LLY]),
  1035.                &(doc->boundingbox[URX]),
  1036.                &(doc->boundingbox[URY])) != 4) {
  1037.         float fllx, flly, furx, fury;
  1038.         if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
  1039.                &fllx, &flly, &furx, &fury) == 4) {
  1040.             doc->boundingbox[LLX] = fllx;
  1041.             doc->boundingbox[LLY] = flly;
  1042.             doc->boundingbox[URX] = furx;
  1043.             doc->boundingbox[URY] = fury;
  1044.             if (fllx < doc->boundingbox[LLX])
  1045.             doc->boundingbox[LLX]--;
  1046.             if (flly < doc->boundingbox[LLY])
  1047.             doc->boundingbox[LLY]--;
  1048.             if (furx > doc->boundingbox[URX])
  1049.             doc->boundingbox[URX]++;
  1050.             if (fury > doc->boundingbox[URY])
  1051.             doc->boundingbox[URY]++;
  1052.         }
  1053.         }
  1054.     } else if (orientation_set == ATEND &&
  1055.            iscomment(line+2, "Orientation:")) {
  1056.         sscanf(line+length("%%Orientation:"), "%s", text);
  1057.         if (strcmp(text, "Portrait") == 0) {
  1058.         doc->orientation = PORTRAIT;
  1059.         } else if (strcmp(text, "Landscape") == 0) {
  1060.         doc->orientation = LANDSCAPE;
  1061.         }
  1062.     } else if (page_order_set == ATEND && iscomment(line+2, "PageOrder:")) {
  1063.         sscanf(line+length("%%PageOrder:"), "%s", text);
  1064.         if (strcmp(text, "Ascend") == 0) {
  1065.         doc->pageorder = ASCEND;
  1066.         } else if (strcmp(text, "Descend") == 0) {
  1067.         doc->pageorder = DESCEND;
  1068.         } else if (strcmp(text, "Special") == 0) {
  1069.         doc->pageorder = SPECIAL;
  1070.         }
  1071.     } else if (pages_set == ATEND && iscomment(line+2, "Pages:")) {
  1072.         if (sscanf(line+length("%%Pages:"), "%*u %d", &i) == 1) {
  1073.         if (page_order_set == NONE) {
  1074.             if (i == -1) doc->pageorder = DESCEND;
  1075.             else if (i == 0) doc->pageorder = SPECIAL;
  1076.             else if (i == 1) doc->pageorder = ASCEND;
  1077.         }
  1078.         }
  1079.     }
  1080.     }
  1081.     section_len += line_len;
  1082.     if (DSCcomment(line) && iscomment(line+2, "EOF")) {
  1083.     readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count);
  1084.     section_len += line_len;
  1085.     }
  1086.     doc->endtrailer = position;
  1087.     doc->lentrailer = section_len - line_len;
  1088.  
  1089. #if 0
  1090.     section_len = line_len;
  1091.     preread = 1;
  1092.     while (preread ||
  1093.        readline(line, sizeof line, file, enddoseps, &position, &line_len, &line_count)) {
  1094.     if (!preread) section_len += line_len;
  1095.     preread = 0;
  1096.     if (DSCcomment(line) && iscomment(line+2, "Page:")) {
  1097.         free(gettext(line+length("%%Page:"), &next_char));
  1098.         if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  1099.         if (!ignore && thispage == nextpage) {
  1100.         if (doc->numpages > 0) {
  1101.             doc->pages[doc->numpages-1].end = position;
  1102.             doc->pages[doc->numpages-1].len += doc->lentrailer +
  1103.                                section_len - line_len;
  1104.         } else {
  1105.             if (doc->endsetup) {
  1106.             doc->endsetup = position;
  1107.             doc->endsetup += doc->lentrailer +
  1108.                      section_len - line_len;
  1109.             } else if (doc->endprolog) {
  1110.             doc->endprolog = position;
  1111.             doc->endprolog += doc->lentrailer +
  1112.                       section_len - line_len;
  1113.             }
  1114.         }
  1115.         goto newpage;
  1116.         }
  1117.     }
  1118.     }
  1119. #endif
  1120.     return doc;
  1121. }
  1122.  
  1123. /*
  1124.  *    psfree -- free dynamic storage associated with document structure.
  1125.  */
  1126.  
  1127. void
  1128. psfree(doc)
  1129.     struct document *doc;
  1130. {
  1131.     int i;
  1132.  
  1133.     if (doc) {
  1134.     for (i=0; i<doc->numpages; i++) {
  1135.         if (doc->pages[i].label) free(doc->pages[i].label);
  1136.     }
  1137.     for (i=0; i<doc->nummedia; i++) {
  1138.         if (doc->media[i].name) free(doc->media[i].name);
  1139.     }
  1140.     if (doc->title) free(doc->title);
  1141.     if (doc->date) free(doc->date);
  1142.     if (doc->pages) free(doc->pages);
  1143.     if (doc->media) free(doc->media);
  1144.     if (doc->doseps) free(doc->doseps); /* rjl: */
  1145.     free(doc);
  1146.     }
  1147. }
  1148.  
  1149. /*
  1150.  * gettextine -- skip over white space and return the rest of the line.
  1151.  *               If the text begins with '(' return the text string
  1152.  *         using gettext().
  1153.  */
  1154.  
  1155. static char *
  1156. gettextline(line)
  1157.     char *line;
  1158. {
  1159.     char *cp;
  1160.  
  1161.     while (*line && (*line == ' ' || *line == '\t')) line++;
  1162.     if (*line == '(') {
  1163.     return gettext(line, NULL);
  1164.     } else {
  1165.     if (strlen(line) == 0) return NULL;
  1166.     cp = (char *) malloc(strlen(line));
  1167.     if (cp == NULL) {
  1168.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  1169.         exit(-1);
  1170.     }
  1171.     strncpy(cp, line, strlen(line)-1);
  1172.     cp[strlen(line)-1] = '\0';
  1173.     return cp;
  1174.     }
  1175. }
  1176.  
  1177. /*
  1178.  *    gettext -- return the next text string on the line.
  1179.  *           return NULL if nothing is present.
  1180.  */
  1181.  
  1182. static char *
  1183. gettext(line, next_char)
  1184.     char *line;
  1185.     char **next_char;
  1186. {
  1187.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1188.     char *cp;
  1189.     int quoted=0;
  1190.  
  1191.     while (*line && (*line == ' ' || *line == '\t')) line++;
  1192.     cp = text;
  1193.     if (*line == '(') {
  1194.     int level = 0;
  1195.     quoted=1;
  1196.     line++;
  1197.     while (*line && !(*line == ')' && level == 0 )) {
  1198.         if (*line == '\\') {
  1199.         if (*(line+1) == 'n') {
  1200.             *cp++ = '\n';
  1201.             line += 2;
  1202.         } else if (*(line+1) == 'r') {
  1203.             *cp++ = '\r';
  1204.             line += 2;
  1205.         } else if (*(line+1) == 't') {
  1206.             *cp++ = '\t';
  1207.             line += 2;
  1208.         } else if (*(line+1) == 'b') {
  1209.             *cp++ = '\b';
  1210.             line += 2;
  1211.         } else if (*(line+1) == 'f') {
  1212.             *cp++ = '\f';
  1213.             line += 2;
  1214.         } else if (*(line+1) == '\\') {
  1215.             *cp++ = '\\';
  1216.             line += 2;
  1217.         } else if (*(line+1) == '(') {
  1218.             *cp++ = '(';
  1219.             line += 2;
  1220.         } else if (*(line+1) == ')') {
  1221.             *cp++ = ')';
  1222.             line += 2;
  1223.         } else if (*(line+1) >= '0' && *(line+1) <= '9') {
  1224.             if (*(line+2) >= '0' && *(line+2) <= '9') {
  1225.             if (*(line+3) >= '0' && *(line+3) <= '9') {
  1226.                 *cp++ = (unsigned char)(((*(line+1) - '0')*8 + *(line+2) - '0')*8 +
  1227.                     *(line+3) - '0');
  1228.                 line += 4;
  1229.             } else {
  1230.                 *cp++ = (unsigned char)((*(line+1) - '0')*8 + *(line+2) - '0');
  1231.                 line += 3;
  1232.             }
  1233.             } else {
  1234.             *cp++ = (unsigned char)(*(line+1) - '0');
  1235.             line += 2;
  1236.             }
  1237.         } else {
  1238.             line++;
  1239.             *cp++ = *line++;
  1240.         }
  1241.         } else if (*line == '(') {
  1242.         level++;
  1243.         *cp++ = *line++;
  1244.         } else if (*line == ')') {
  1245.         level--;
  1246.         *cp++ = *line++;
  1247.         } else {
  1248.         *cp++ = *line++;
  1249.         }
  1250.     }
  1251.     } else {
  1252.     while (*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
  1253.         *cp++ = *line++;
  1254.     }
  1255.     *cp = '\0';
  1256.     if (next_char) *next_char = line;
  1257.     if (!quoted && strlen(text) == 0) return NULL;
  1258.     cp = (char *) malloc(strlen(text)+1);
  1259.     if (cp == NULL) {
  1260.     pserror("Fatal Error: Dynamic memory exhausted.\n");
  1261.     exit(-1);
  1262.     }
  1263.     strcpy(cp, text);
  1264.     return cp;
  1265. }
  1266.  
  1267. /*
  1268.  *    readline -- Read the next line in the postscript file.
  1269.  *                  Automatically skip over data (as indicated by
  1270.  *                  %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1271.  *            comments.)
  1272.  *            Also, skip over included documents (as indicated by
  1273.  *            %%BeginDocument/%%EndDocument comments.)
  1274.  */
  1275.  
  1276. static char *
  1277. readline(line, size, fp, enddoseps, position, line_len, line_count)
  1278.     char *line;
  1279.     int size;
  1280.     FILE *fp;
  1281.     unsigned long enddoseps;
  1282.     long *position;
  1283.     unsigned int *line_len;
  1284.     unsigned int *line_count;
  1285. {
  1286.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1287.     char save[PSLINELENGTH];    /* Temporary storage for text */
  1288.     char *cp;
  1289.     unsigned int num;
  1290.     unsigned int nbytes;
  1291.     int i;
  1292.     char buf[BUFSIZ];
  1293.  
  1294.     if (position) *position = ftell(fp);
  1295.     if (position && enddoseps) {
  1296.     if (*position >= enddoseps)
  1297.         return NULL;    /* don't read any more, we have reached end of dos eps section */
  1298.     if (size > enddoseps - *position + 1)
  1299.         size = enddoseps - *position + 1;
  1300.     }
  1301.     cp = fgets(line, size, fp);
  1302.     *line_count += 1;
  1303.     if (cp == NULL) line[0] = '\0';
  1304.     *line_len = strlen(line);
  1305. #if defined(__TURBOC__) || defined(OS2)
  1306.     /* remove MS-DOS carriage-return */
  1307.     if ((i = *line_len) >= 2) {
  1308.     if ((line[i-2] == '\r') && (line[i-1] == '\n')) {
  1309.         line[i-2] = '\n';
  1310.         line[i-1] = '\0';
  1311.     }
  1312.     }
  1313. #endif
  1314.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1315.         /* rjl: skip over BUGGY EPS includes from Microsoft Word */
  1316.         /* Microsoft's EPSIMP.FLT needs to be fixed */
  1317.     if ( (line[0] == '%') && iscomment(line+1, "MSEPS Preamble") ) {
  1318.         strcpy(save, line);
  1319.         while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1320.            !(line[0] == '%' && iscomment(line+1, "MSEPS Trailer"))) {
  1321.         *line_len += nbytes;
  1322.         }
  1323.         *line_len += nbytes;
  1324.         strcpy(line, save);
  1325.     }
  1326.     else {
  1327.         /* Do nothing */
  1328.     }
  1329.     } else if (iscomment(line+7, "Document:")) {
  1330.     strcpy(save, line+7);
  1331.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1332.            !(DSCcomment(line) && iscomment(line+2, "EndDocument"))) {
  1333.         *line_len += nbytes;
  1334.     }
  1335.     *line_len += nbytes;
  1336.     strcpy(line, save);
  1337.     } else if (iscomment(line+7, "Feature:")) {
  1338.     strcpy(save, line+7);
  1339.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1340.            !(DSCcomment(line) && iscomment(line+2, "EndFeature"))) {
  1341.         *line_len += nbytes;
  1342.     }
  1343.     *line_len += nbytes;
  1344.     strcpy(line, save);
  1345.     } else if (iscomment(line+7, "File:")) {
  1346.     strcpy(save, line+7);
  1347.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1348.            !(DSCcomment(line) && iscomment(line+2, "EndFile"))) {
  1349.         *line_len += nbytes;
  1350.     }
  1351.     *line_len += nbytes;
  1352.     strcpy(line, save);
  1353.     } else if (iscomment(line+7, "Font:")) {
  1354.     strcpy(save, line+7);
  1355.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1356.            !(DSCcomment(line) && iscomment(line+2, "EndFont"))) {
  1357.         *line_len += nbytes;
  1358.     }
  1359.     *line_len += nbytes;
  1360.     strcpy(line, save);
  1361.     } else if (iscomment(line+7, "ProcSet:")) {
  1362.     strcpy(save, line+7);
  1363.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1364.            !(DSCcomment(line) && iscomment(line+2, "EndProcSet"))) {
  1365.         *line_len += nbytes;
  1366.     }
  1367.     *line_len += nbytes;
  1368.     strcpy(line, save);
  1369.     } else if (iscomment(line+7, "Resource:")) {
  1370.     strcpy(save, line+7);
  1371.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1372.            !(DSCcomment(line) && iscomment(line+2, "EndResource"))) {
  1373.         *line_len += nbytes;
  1374.     }
  1375.     *line_len += nbytes;
  1376.     strcpy(line, save);
  1377.     } else if (iscomment(line+7, "Data:")) {
  1378.     text[0] = '\0';
  1379.     strcpy(save, line+7);
  1380.     if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
  1381.         if (strcmp(text, "Lines") == 0) {
  1382.         for (i=0; i < num; i++) {
  1383.           *line_count += 1;
  1384.           do { /* rjl: handle antisocial PostScript with excessively long lines */
  1385.             cp = fgets(line, size, fp);
  1386.             *line_len += cp ? strlen(line) : 0;
  1387.           } while ( (strlen(line) == size-1) && (line[size-2] != '\n') );
  1388.         }
  1389.         } else {
  1390.         while (num > BUFSIZ) {
  1391.             fread(buf, sizeof (char), BUFSIZ, fp);
  1392.             *line_len += BUFSIZ;
  1393.             num -= BUFSIZ;
  1394.         }
  1395.         fread(buf, sizeof (char), num, fp);
  1396.         *line_len += num;
  1397.         }
  1398.     }
  1399.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1400.            !(DSCcomment(line) && iscomment(line+2, "EndData"))) {
  1401.         *line_len += nbytes;
  1402.     }
  1403.     *line_len += nbytes;
  1404.     strcpy(line, save);
  1405.     } else if (iscomment(line+7, "Binary:")) {
  1406.     strcpy(save, line+7);
  1407.     if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1408.         while (num > BUFSIZ) {
  1409.         fread(buf, sizeof (char), BUFSIZ, fp);
  1410.         *line_len += BUFSIZ;
  1411.         num -= BUFSIZ;
  1412.         }
  1413.         fread(buf, sizeof (char), num, fp);
  1414.         *line_len += num;
  1415.     }
  1416.     while (readline(line, size, fp, enddoseps, NULL, &nbytes, line_count) &&
  1417.            !(DSCcomment(line) && iscomment(line+2, "EndBinary"))) {
  1418.         *line_len += nbytes;
  1419.     }
  1420.     *line_len += nbytes;
  1421.     strcpy(line, save);
  1422.     }
  1423.     return cp;
  1424. }
  1425.  
  1426. /*
  1427.  *    pscopy -- copy lines of Postscript from a section of one file
  1428.  *          to another file.
  1429.  *                Automatically switch to binary copying whenever
  1430.  *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1431.  *          comments are encountered.
  1432.  */
  1433.  
  1434. void
  1435. pscopy(from, to, begin, end)
  1436.     FILE *from;
  1437.     FILE *to;
  1438.     long begin;            /* set negative to avoid initial seek */
  1439.     long end;
  1440. {
  1441.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  1442.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1443.     unsigned int num;
  1444.     int i;
  1445.     char buf[BUFSIZ];
  1446.  
  1447.     if (begin >= 0) fseek(from, begin, SEEK_SET);
  1448.     while (ftell(from) < end) {
  1449.  
  1450.     fgets(line, sizeof line, from);
  1451.     fputs(line, to);
  1452.  
  1453.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1454.         /* Do nothing */
  1455.     } else if (iscomment(line+7, "Data:")) {
  1456.         text[0] = '\0';
  1457.         if (sscanf(line+length("%%BeginData:"),
  1458.                "%d %*s %s", &num, text) >= 1) {
  1459.         if (strcmp(text, "Lines") == 0) {
  1460.             for (i=0; i < num; i++) {
  1461.               do { /* rjl: handle antisocial PostScript with excessively long lines */
  1462.             fgets(line, sizeof line, from);
  1463.             fputs(line, to);
  1464.               } while ( (strlen(line) == sizeof(line)-1) && (line[sizeof(line)-2] != '\n') );
  1465.             }
  1466.         } else {
  1467.             while (num > BUFSIZ) {
  1468.             fread(buf, sizeof (char), BUFSIZ, from);
  1469.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1470.             num -= BUFSIZ;
  1471.             }
  1472.             fread(buf, sizeof (char), num, from);
  1473.             fwrite(buf, sizeof (char), num, to);
  1474.         }
  1475.         }
  1476.     } else if (iscomment(line+7, "Binary:")) {
  1477.         if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1478.         while (num > BUFSIZ) {
  1479.             fread(buf, sizeof (char), BUFSIZ, from);
  1480.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1481.             num -= BUFSIZ;
  1482.         }
  1483.         fread(buf, sizeof (char), num, from);
  1484.         fwrite(buf, sizeof (char), num, to);
  1485.         }
  1486.     }
  1487.     }
  1488. }
  1489.  
  1490. /*
  1491.  *    pscopyuntil -- copy lines of Postscript from a section of one file
  1492.  *               to another file until a particular comment is reached.
  1493.  *                     Automatically switch to binary copying whenever
  1494.  *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1495.  *               comments are encountered.
  1496.  */
  1497.  
  1498. char *
  1499. pscopyuntil(from, to, begin, end, comment)
  1500.     FILE *from;
  1501.     FILE *to;
  1502.     long begin;            /* set negative to avoid initial seek */
  1503.     long end;
  1504. #if NeedFunctionPrototypes
  1505.     const
  1506. #endif
  1507.     char *comment;
  1508. {
  1509.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  1510.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1511.     unsigned int num;
  1512.     int comment_length;
  1513.     int i;
  1514.     char buf[BUFSIZ];
  1515.     char *cp;
  1516.  
  1517.     if (comment)    /* rjl: allow NULL comment to degenerate to pscopy() */
  1518.         comment_length = strlen(comment);
  1519.     if (begin >= 0) fseek(from, begin, SEEK_SET);
  1520.     while (ftell(from) < end) {
  1521.  
  1522.     fgets(line, sizeof line, from);
  1523.  
  1524.     /* iscomment cannot be used here,
  1525.      * because comment_length is not known at compile time. */
  1526.     if (comment && (strncmp(line, comment, comment_length) == 0)) {
  1527.         cp = (char *) malloc(strlen(line)+1);
  1528.         if (cp == NULL) {
  1529.         pserror("Fatal Error: Dynamic memory exhausted.\n");
  1530.         exit(-1);
  1531.         }
  1532.         strcpy(cp, line);
  1533.         return cp;
  1534.     }
  1535.     fputs(line, to);
  1536.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1537.         /* Do nothing */
  1538.     } else if (iscomment(line+7, "Data:")) {
  1539.         text[0] = '\0';
  1540.         if (sscanf(line+length("%%BeginData:"),
  1541.                "%d %*s %s", &num, text) >= 1) {
  1542.         if (strcmp(text, "Lines") == 0) {
  1543.             for (i=0; i < num; i++) {
  1544.               do { /* rjl: handle antisocial PostScript with excessively long lines */
  1545.             fgets(line, sizeof line, from);
  1546.             fputs(line, to);
  1547.               } while ( (strlen(line) == sizeof(line)-1) && (line[sizeof(line)-2] != '\n') );
  1548.             }
  1549.         } else {
  1550.             while (num > BUFSIZ) {
  1551.             fread(buf, sizeof (char), BUFSIZ, from);
  1552.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1553.             num -= BUFSIZ;
  1554.             }
  1555.             fread(buf, sizeof (char), num, from);
  1556.             fwrite(buf, sizeof (char), num, to);
  1557.         }
  1558.         }
  1559.     } else if (iscomment(line+7, "Binary:")) {
  1560.         if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1561.         while (num > BUFSIZ) {
  1562.             fread(buf, sizeof (char), BUFSIZ, from);
  1563.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1564.             num -= BUFSIZ;
  1565.         }
  1566.         fread(buf, sizeof (char), num, from);
  1567.         fwrite(buf, sizeof (char), num, to);
  1568.         }
  1569.     }
  1570.     }
  1571.     return NULL;
  1572. }
  1573.  
  1574. /*
  1575.  *    blank -- determine whether the line contains nothing but whitespace.
  1576.  */
  1577.  
  1578. static int
  1579. blank(line)
  1580.     char *line;
  1581. {
  1582.     char *cp = line;
  1583.  
  1584.     while (*cp == ' ' || *cp == '\t') cp++;
  1585.     return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
  1586. }
  1587.  
  1588. /* rjl: routines to handle reading DOS EPS files */
  1589. static unsigned long dsc_arch = 0x00000001;
  1590.  
  1591. /* change byte order if architecture is big-endian */
  1592. PS_DWORD
  1593. reorder_dword(val)
  1594.     PS_DWORD val;
  1595. {
  1596.     if (*((char *)(&dsc_arch)))
  1597.         return val;    /* little endian machine */
  1598.     else
  1599.     return ((val&0xff) << 24) | ((val&0xff00) << 8)
  1600.              | ((val&0xff0000L) >> 8) | ((val>>24)&0xff);
  1601. }
  1602.  
  1603. /* change byte order if architecture is big-endian */
  1604. PS_WORD
  1605. reorder_word(val)
  1606.     PS_WORD val;
  1607. {
  1608.     if (*((char *)(&dsc_arch)))
  1609.         return val;    /* little endian machine */
  1610.     else
  1611.     return (PS_WORD) ((PS_WORD)(val&0xff) << 8) | (PS_WORD)((val&0xff00) >> 8);
  1612. }
  1613.  
  1614. /* DOS EPS header reading */
  1615. unsigned long
  1616. ps_read_doseps(file, doseps)
  1617.     FILE *file;
  1618.     DOSEPS *doseps;
  1619. {
  1620.     fread(doseps->id, 1, 4, file);
  1621.     if (! ((doseps->id[0]==0xc5) && (doseps->id[1]==0xd0) 
  1622.         && (doseps->id[2]==0xd3) && (doseps->id[3]==0xc6)) ) {
  1623.         /* id is "EPSF" with bit 7 set */
  1624.         rewind(file);
  1625.         return 0;     /* OK */
  1626.     }
  1627.     fread(&doseps->ps_begin,    4, 1, file);    /* PS offset */
  1628.     doseps->ps_begin = (unsigned long)reorder_dword(doseps->ps_begin);
  1629.     fread(&doseps->ps_length,   4, 1, file);    /* PS length */
  1630.     doseps->ps_length = (unsigned long)reorder_dword(doseps->ps_length);
  1631.     fread(&doseps->mf_begin,    4, 1, file);    /* Metafile offset */
  1632.     doseps->mf_begin = (unsigned long)reorder_dword(doseps->mf_begin);
  1633.     fread(&doseps->mf_length,   4, 1, file);    /* Metafile length */
  1634.     doseps->mf_length = (unsigned long)reorder_dword(doseps->mf_length);
  1635.     fread(&doseps->tiff_begin,  4, 1, file);    /* TIFF offset */
  1636.     doseps->tiff_begin = (unsigned long)reorder_dword(doseps->tiff_begin);
  1637.     fread(&doseps->tiff_length, 4, 1, file);    /* TIFF length */
  1638.     doseps->tiff_length = (unsigned long)reorder_dword(doseps->tiff_length);
  1639.     fread(&doseps->checksum,    2, 1, file);
  1640.         doseps->checksum = (unsigned short)reorder_word(doseps->checksum);
  1641.     fseek(file, doseps->ps_begin, SEEK_SET);    /* seek to PS section */
  1642.     return doseps->ps_begin + doseps->ps_length;
  1643. }
  1644.  
  1645.  
  1646. #undef fgets
  1647. char *
  1648. psfgets(s, n, stream)
  1649.     char *s;
  1650.     int n;
  1651.     FILE *stream;
  1652. {
  1653. /*
  1654.     return fgets(s, n, stream);
  1655. */
  1656.     int ch = 0;
  1657.     char *p;
  1658.     p = s;
  1659.     while ( (--n > 0)  && ((ch = fgetc(stream)) != EOF) ) {
  1660.     *p++ = ch;
  1661.     if (ch == '\n')
  1662.         break;
  1663.     if (ch == '\r') {
  1664.         /* cope with MS-DOS \r\n or Mac \r */
  1665.         if (--n > 0) {
  1666.             ch = fgetc(stream);
  1667.             if (ch == EOF)
  1668.             break;
  1669.             if (ch == '\n')
  1670.                 *p++ = ch;
  1671.         else
  1672.             ungetc(ch, stream);
  1673.         break;
  1674.         }
  1675.     }
  1676.     }
  1677.     if ((ch == EOF) && (p == s))
  1678.     return NULL;
  1679.     *p = '\0';
  1680.     return (ferror(stream)) ? NULL : s;
  1681. }
  1682.