home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / groff-1.09-src.lha / src / amiga / groff-1.09 / grops / psrm.cc < prev    next >
C/C++ Source or Header  |  1994-02-13  |  26KB  |  1,092 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "driver.h"
  22. #include "stringclass.h"
  23. #include "cset.h"
  24.  
  25. #include "ps.h"
  26.  
  27. #define PROLOGUE "prologue"
  28.  
  29. static void print_ps_string(const string &s, FILE *outfp);
  30.  
  31. cset white_space("\n\r \t");
  32. string an_empty_string;
  33.  
  34. const char *extension_table[] = {
  35.   "DPS",
  36.   "CMYK",
  37.   "Composite",
  38.   "FileSystem",
  39. };
  40.  
  41. const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
  42.  
  43. const char *resource_table[] = {
  44.   "font",
  45.   "procset",
  46.   "file",
  47.   "encoding",
  48.   "form",
  49.   "pattern",
  50. };
  51.  
  52. const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
  53.  
  54. struct resource {
  55.   resource *next;
  56.   resource_type type;
  57.   string name;
  58.   enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
  59.   unsigned flags;
  60.   string version;
  61.   unsigned revision;
  62.   char *filename;
  63.   int rank;
  64.   resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
  65.   ~resource();
  66.   void print_type_and_name(FILE *outfp);
  67. };
  68.  
  69. resource::resource(resource_type t, string &n, string &v, unsigned r)
  70. : type(t), revision(r), flags (0), filename(0), rank(-1), next(0)
  71. {
  72.   name.move(n);
  73.   version.move(v);
  74.   if (type == RESOURCE_FILE) {
  75.     if (name.search('\0') >= 0)
  76.       error("filename contains a character with code 0");
  77.     filename = name.extract();
  78.   }
  79. }
  80.  
  81. resource::~resource()
  82. {
  83.   a_delete filename;
  84. }
  85.  
  86. void resource::print_type_and_name(FILE *outfp)
  87. {
  88.   fputs(resource_table[type], outfp);
  89.   putc(' ', outfp);
  90.   print_ps_string(name, outfp);
  91.   if (type == RESOURCE_PROCSET) {
  92.     putc(' ', outfp);
  93.     print_ps_string(version, outfp);
  94.     fprintf(outfp, " %u", revision);
  95.   }
  96. }
  97.  
  98. resource_manager::resource_manager()
  99. : resource_list(0), extensions(0), language_level(0)
  100. {
  101.   read_download_file();
  102.   string procset_name("grops");
  103.   extern const char *version_string;
  104.   string procset_version(version_string);
  105.   procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
  106.                      procset_version, 0);
  107.   procset_resource->flags |= resource::SUPPLIED;
  108. }
  109.  
  110. resource_manager::~resource_manager()
  111. {
  112.   while (resource_list) {
  113.     resource *tem = resource_list;
  114.     resource_list = resource_list->next;
  115.     delete tem;
  116.   }
  117. }
  118.  
  119. resource *resource_manager::lookup_resource(resource_type type,
  120.                         string &name,
  121.                         string &version,
  122.                         unsigned revision)
  123. {
  124.   for (resource *r = resource_list; r; r = r->next)
  125.     if (r->type == type
  126.     && r->name == name
  127.     && r->version == version
  128.     && r->revision == revision)
  129.       return r;
  130.   r = new resource(type, name, version, revision);
  131.   r->next = resource_list;
  132.   resource_list = r;
  133.   return r;
  134. }
  135.  
  136. // Just a specialized version of lookup_resource().
  137.  
  138. resource *resource_manager::lookup_font(const char *name)
  139. {
  140.   for (resource *r = resource_list; r; r = r->next)
  141.     if (r->type == RESOURCE_FONT
  142.     && strlen(name) == r->name.length()
  143.     && memcmp(name, r->name.contents(), r->name.length()) == 0)
  144.       return r;
  145.   string s(name);
  146.   r = new resource(RESOURCE_FONT, s);
  147.   r->next = resource_list;
  148.   resource_list = r;
  149.   return r;
  150. }
  151.  
  152. void resource_manager::need_font(const char *name)
  153. {
  154.   lookup_font(name)->flags |= resource::FONT_NEEDED;
  155. }
  156.  
  157. typedef resource *Presource;    // Work around g++ bug.
  158.  
  159. void resource_manager::document_setup(ps_output &out)
  160. {
  161.   int nranks = 0;
  162.   for (resource *r = resource_list; r; r = r->next)
  163.     if (r->rank >= nranks)
  164.       nranks = r->rank + 1;
  165.   if (nranks > 0) {
  166.     // Sort resource_list in reverse order of rank.
  167.     Presource *head = new Presource[nranks + 1];
  168.     Presource **tail = new Presource *[nranks + 1];
  169.     for (int i = 0; i < nranks + 1; i++) {
  170.       head[i] = 0;
  171.       tail[i] = &head[i];
  172.     }
  173.     for (r = resource_list; r; r = r->next) {
  174.       i = r->rank < 0 ? 0 : r->rank + 1;
  175.       *tail[i] = r;
  176.       tail[i] = &(*tail[i])->next;
  177.     }
  178.     resource_list = 0;
  179.     for (i = 0; i < nranks + 1; i++)
  180.       if (head[i]) {
  181.     *tail[i] = resource_list;
  182.     resource_list = head[i];
  183.       }
  184.     a_delete head;
  185.     a_delete tail;
  186.     // check it
  187.     for (r = resource_list; r; r = r->next)
  188.       if (r->next)
  189.     assert(r->rank >= r->next->rank);
  190.     for (r = resource_list; r; r = r->next)
  191.       if (r->type == RESOURCE_FONT && r->rank >= 0)
  192.     supply_resource(r, -1, out.get_file());
  193.   }
  194. }
  195.  
  196. void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
  197. {
  198.   int continued = 0;
  199.   for (resource *r = resource_list; r; r = r->next)
  200.     if (r->flags & flag) {
  201.       if (continued)
  202.     fputs("%%+ ", outfp);
  203.       else {
  204.     fputs(flag == resource::NEEDED
  205.           ? "%%DocumentNeededResources: "
  206.           : "%%DocumentSuppliedResources: ",
  207.           outfp);
  208.     continued = 1;
  209.       }
  210.       r->print_type_and_name(outfp);
  211.       putc('\n', outfp);
  212.     }
  213. }
  214.  
  215. void resource_manager::print_header_comments(ps_output &out)
  216. {
  217.   for (resource *r = resource_list; r; r = r->next)
  218.     if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
  219.       supply_resource(r, 0, 0);
  220.   print_resources_comment(resource::NEEDED, out.get_file());
  221.   print_resources_comment(resource::SUPPLIED, out.get_file());
  222.   print_language_level_comment(out.get_file());
  223.   print_extensions_comment(out.get_file());
  224. }
  225.  
  226. void resource_manager::output_prolog(ps_output &out)
  227. {
  228.   FILE *outfp = out.get_file();
  229.   out.end_line();
  230.   char *path;
  231.   FILE *fp = font::open_file(PROLOGUE, &path);
  232.   if (!fp)
  233.     fatal("can't find `%1'", PROLOGUE);
  234.   fputs("%%BeginResource: ", outfp);
  235.   procset_resource->print_type_and_name(outfp);
  236.   putc('\n', outfp);
  237.   process_file(-1, fp, path, outfp);
  238.   fclose(fp);
  239.   a_delete path;
  240.   fputs("%%EndResource\n", outfp);
  241. }
  242.  
  243. void resource_manager::import_file(const char *filename, ps_output &out)
  244. {
  245.   out.end_line();
  246.   string name(filename);
  247.   resource *r = lookup_resource(RESOURCE_FILE, name);
  248.   supply_resource(r, -1, out.get_file(), 1);
  249. }
  250.  
  251. void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
  252.                        int is_document)
  253. {
  254.   if (r->flags & resource::BUSY) {
  255.     r->name += '\0';
  256.     fatal("loop detected in dependency graph for %1 `%2'",
  257.       resource_table[r->type],
  258.       r->name.contents());
  259.   }
  260.   r->flags |= resource::BUSY;
  261.   if (rank > r->rank)
  262.     r->rank = rank;
  263.   char *path;
  264.   FILE *fp = 0;
  265.   if (r->filename != 0) {
  266.     if (r->type == RESOURCE_FONT) {
  267.       fp = font::open_file(r->filename, &path);
  268.       if (!fp) {
  269.     error("can't find `%1'", r->filename);
  270.     a_delete r->filename;
  271.     r->filename = 0;
  272.       }
  273.     }
  274.     else {
  275.       errno = 0;
  276.       fp = fopen(r->filename, "r");
  277.       if (!fp) {
  278.     error("can't open `%1': %2", r->filename, strerror(errno));
  279.     a_delete r->filename;
  280.     r->filename = 0;
  281.       }
  282.       else
  283.     path = r->filename;
  284.     }
  285.   }
  286.   if (fp) {
  287.     if (outfp) {
  288.       if (r->type == RESOURCE_FILE && is_document) {
  289.     fputs("%%BeginDocument: ", outfp);
  290.     print_ps_string(r->name, outfp);
  291.     putc('\n', outfp);
  292.       }
  293.       else {
  294.     fputs("%%BeginResource: ", outfp);
  295.     r->print_type_and_name(outfp);
  296.     putc('\n', outfp);
  297.       }
  298.     }
  299.     process_file(rank, fp, path, outfp);
  300.     fclose(fp);
  301.     if (r->type == RESOURCE_FONT)
  302.       a_delete path;
  303.     if (outfp) {
  304.       if (r->type == RESOURCE_FILE && is_document)
  305.     fputs("%%EndDocument\n", outfp);
  306.       else
  307.     fputs("%%EndResource\n", outfp);
  308.     }
  309.     r->flags |= resource::SUPPLIED;
  310.   }
  311.   else {
  312.     if (outfp) {
  313.       if (r->type == RESOURCE_FILE && is_document) {
  314.     fputs("%%IncludeDocument: ", outfp);
  315.     print_ps_string(r->name, outfp);
  316.     putc('\n', outfp);
  317.       }
  318.       else {
  319.     fputs("%%IncludeResource: ", outfp);
  320.     r->print_type_and_name(outfp);
  321.     putc('\n', outfp);
  322.       }
  323.     }
  324.     r->flags |= resource::NEEDED;
  325.   }
  326.   r->flags &= ~resource::BUSY;
  327. }
  328.  
  329.  
  330. #define PS_LINE_MAX 255
  331. #define PS_MAGIC "%!PS-Adobe-"
  332.  
  333. static int ps_get_line(char *buf, FILE *fp)
  334. {
  335.   int c = getc(fp);
  336.   if (c == EOF) {
  337.     buf[0] = '\0';
  338.     return 0;
  339.   }
  340.   current_lineno++;
  341.   int i = 0;
  342.   int err = 0;
  343.   while (c != '\r' && c != '\n' && c != EOF) {
  344.     if ((c < 0x1b && !white_space(c)) || c == 0x7f)
  345.       error("illegal input character code %1", int(c));
  346.     else if (i < PS_LINE_MAX)
  347.       buf[i++] = c;
  348.     else if (!err) {
  349.       err = 1;
  350.       error("PostScript file non-conforming "
  351.         "because length of line exceeds 255");
  352.     }
  353.     c = getc(fp);
  354.   }
  355.   buf[i++] = '\n';
  356.   buf[i] = '\0';
  357.   if (c == '\r') {
  358.     c = getc(fp);
  359.     if (c != EOF && c != '\n')
  360.       ungetc(c, fp);
  361.   }
  362.   return 1;
  363. }
  364.  
  365. static int read_text_arg(const char **pp, string &res)
  366. {
  367.   res.clear();
  368.   while (white_space(**pp))
  369.     *pp += 1;
  370.   if (**pp == '\0') {
  371.     error("missing argument");
  372.     return 0;
  373.   }
  374.   if (**pp != '(') {
  375.     for (; **pp != '\0' && !white_space(**pp); *pp += 1)
  376.       res += **pp;
  377.     return 1;
  378.   }
  379.   *pp += 1;
  380.   res.clear();
  381.   int level = 0;
  382.   for (;;) {
  383.     if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
  384.       error("missing ')'");
  385.       return 0;
  386.     }
  387.     if (**pp == ')') {
  388.       if (level == 0) {
  389.     *pp += 1;
  390.     break;
  391.       }
  392.       res += **pp;
  393.       level--;
  394.     }
  395.     else if (**pp == '(') {
  396.       level++;
  397.       res += **pp;
  398.     }
  399.     else if (**pp == '\\') {
  400.       *pp += 1;
  401.       switch (**pp) {
  402.       case 'n':
  403.     res += '\n';
  404.     break;
  405.       case 'r':
  406.     res += '\n';
  407.     break;
  408.       case 't':
  409.     res += '\t';
  410.     break;
  411.       case 'b':
  412.     res += '\b';
  413.     break;
  414.       case 'f':
  415.     res += '\f';
  416.     break;
  417.       case '0':
  418.       case '1':
  419.       case '2':
  420.       case '3':
  421.       case '4':
  422.       case '5':
  423.       case '6':
  424.       case '7':
  425.     {
  426.       int val = **pp - '0';
  427.       if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
  428.         *pp += 1;
  429.         val = val*8 + (**pp - '0');
  430.         if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
  431.           *pp += 1;
  432.           val = val*8 + (**pp - '0');
  433.         }
  434.       }
  435.     }
  436.     break;
  437.       default:
  438.     res += **pp;
  439.     break;
  440.       }
  441.     }
  442.     else
  443.       res += **pp;
  444.     *pp += 1;
  445.   }
  446.   return 1;
  447. }
  448.  
  449. static int read_uint_arg(const char **pp, unsigned *res)
  450. {
  451.   while (white_space(**pp))
  452.     *pp += 1;
  453.   if (**pp == '\0') {
  454.     error("missing argument");
  455.     return 0;
  456.   }
  457.   const char *start = *pp;
  458.   // XXX use strtoul
  459.   long n = strtol(start, (char **)pp, 10);
  460.   if (n == 0 && *pp == start) {
  461.     error("not an integer");
  462.     return 0;
  463.   }
  464.   if (n < 0) {
  465.     error("argument must not be negative");
  466.     return 0;
  467.   }
  468.   *res = unsigned(n);
  469.   return 1;
  470. }
  471.  
  472. resource *resource_manager::read_file_arg(const char **ptr)
  473. {
  474.   string arg;
  475.   if (!read_text_arg(ptr, arg))
  476.     return 0;
  477.   return lookup_resource(RESOURCE_FILE, arg);
  478. }
  479.  
  480. resource *resource_manager::read_font_arg(const char **ptr)
  481. {
  482.   string arg;
  483.   if (!read_text_arg(ptr, arg))
  484.     return 0;
  485.   return lookup_resource(RESOURCE_FONT, arg);
  486. }
  487.  
  488. resource *resource_manager::read_procset_arg(const char **ptr)
  489. {
  490.   string arg;
  491.   if (!read_text_arg(ptr, arg))
  492.     return 0;
  493.   string version;
  494.   if (!read_text_arg(ptr, version))
  495.       return 0;
  496.   unsigned revision;
  497.   if (!read_uint_arg(ptr, &revision))
  498.       return 0;
  499.   return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
  500. }
  501.  
  502. resource *resource_manager::read_resource_arg(const char **ptr)
  503. {
  504.   while (white_space(**ptr))
  505.     *ptr += 1;
  506.   const char *name = *ptr;
  507.   while (**ptr != '\0' && !white_space(**ptr))
  508.     *ptr += 1;
  509.   if (name == *ptr) {
  510.     error("missing resource type");
  511.     return 0;
  512.   }
  513.   for (int ri = 0; ri < NRESOURCES; ri++)
  514.     if (strlen(resource_table[ri]) == *ptr - name
  515.     && memcmp(resource_table[ri], name, *ptr - name) == 0)
  516.       break;
  517.   if (ri >= NRESOURCES) {
  518.     error("unknown resource type");
  519.     return 0;
  520.   }
  521.   if (ri == RESOURCE_PROCSET)
  522.     return read_procset_arg(ptr);
  523.   string arg;
  524.   if (!read_text_arg(ptr, arg))
  525.     return 0;
  526.   return lookup_resource(resource_type(ri), arg);
  527. }
  528.  
  529. static const char *matches_comment(const char *buf, const char *comment)
  530. {
  531.   if (buf[0] != '%' || buf[1] != '%')
  532.     return 0;
  533.   for (buf += 2; *comment; comment++, buf++)
  534.     if (*buf != *comment)
  535.       return 0;
  536.   if (comment[-1] == ':')
  537.     return buf;
  538.   if (*buf == '\0' || white_space(*buf))
  539.     return buf;
  540.   return 0;
  541. }
  542.  
  543. // Return 1 if the line should be copied out.
  544.  
  545. int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
  546.                     FILE *)
  547. {
  548.   resource *r = read_resource_arg(&ptr);
  549.   if (r)
  550.     r->flags |= resource::SUPPLIED;
  551.   return 1;
  552. }
  553.  
  554. int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
  555.                       FILE *outfp)
  556. {
  557.   resource *r = read_resource_arg(&ptr);
  558.   if (r) {
  559.     if (r->type == RESOURCE_FONT) {
  560.       if (rank >= 0)
  561.     supply_resource(r, rank + 1, outfp);
  562.       else
  563.     r->flags |= resource::FONT_NEEDED;
  564.     }
  565.     else
  566.       supply_resource(r, rank, outfp);
  567.   }
  568.   return 0;
  569. }
  570.  
  571. int resource_manager::do_begin_document(const char *ptr, int, FILE *,
  572.                     FILE *)
  573. {
  574.   resource *r = read_file_arg(&ptr);
  575.   if (r)
  576.     r->flags |= resource::SUPPLIED;
  577.   return 1;
  578. }
  579.  
  580. int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
  581.                       FILE *outfp)
  582. {
  583.   resource *r = read_file_arg(&ptr);
  584.   if (r)
  585.     supply_resource(r, rank, outfp, 1);
  586.   return 0;
  587. }
  588.  
  589. int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
  590.                        FILE *outfp)
  591. {
  592.   resource *r = read_procset_arg(&ptr);
  593.   if (r) {
  594.     r->flags |= resource::SUPPLIED;
  595.     if (outfp) {
  596.       fputs("%%BeginResource: ", outfp);
  597.       r->print_type_and_name(outfp);
  598.       putc('\n', outfp);
  599.     }
  600.   }
  601.   return 0;
  602. }
  603.  
  604. int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
  605.                       FILE *outfp)
  606. {
  607.   resource *r = read_procset_arg(&ptr);
  608.   if (r)
  609.     supply_resource(r, rank, outfp);
  610.   return 0;
  611. }
  612.  
  613. int resource_manager::do_begin_file(const char *ptr, int, FILE *,
  614.                     FILE *outfp)
  615. {
  616.   resource *r = read_file_arg(&ptr);
  617.   if (r) {
  618.     r->flags |= resource::SUPPLIED;
  619.     if (outfp) {
  620.       fputs("%%BeginResource: ", outfp);
  621.       r->print_type_and_name(outfp);
  622.       putc('\n', outfp);
  623.     }
  624.   }
  625.   return 0;
  626. }
  627.  
  628. int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
  629.                       FILE *outfp)
  630. {
  631.   resource *r = read_file_arg(&ptr);
  632.   if (r)
  633.     supply_resource(r, rank, outfp);
  634.   return 0;
  635. }
  636.  
  637. int resource_manager::do_begin_font(const char *ptr, int, FILE *,
  638.                     FILE *outfp)
  639. {
  640.   resource *r = read_font_arg(&ptr);
  641.   if (r) {
  642.     r->flags |= resource::SUPPLIED;
  643.     if (outfp) {
  644.       fputs("%%BeginResource: ", outfp);
  645.       r->print_type_and_name(outfp);
  646.       putc('\n', outfp);
  647.     }
  648.   }
  649.   return 0;
  650. }
  651.  
  652. int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
  653.                       FILE *outfp)
  654. {
  655.   resource *r = read_font_arg(&ptr);
  656.   if (r) {
  657.     if (rank >= 0)
  658.       supply_resource(r, rank + 1, outfp);
  659.     else
  660.       r->flags |= resource::FONT_NEEDED;
  661.   }
  662.   return 0;
  663. }
  664.  
  665. int resource_manager::change_to_end_resource(const char *, int, FILE *,
  666.                          FILE *outfp)
  667. {
  668.   if (outfp)
  669.     fputs("%%EndResource\n", outfp);
  670.   return 0;
  671. }
  672.  
  673. int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
  674. {
  675.   char buf[PS_LINE_MAX + 2];
  676.   do {
  677.     if (!ps_get_line(buf, fp)) {
  678.       error("end of file in preview section");
  679.       break;
  680.     }
  681.   } while (!matches_comment(buf, "EndPreview"));
  682.   return 0;
  683. }
  684.  
  685. int read_one_of(const char **ptr, const char **s, int n)
  686. {
  687.   while (white_space(**ptr))
  688.     *ptr += 1;
  689.   if (**ptr == '\0')
  690.     return -1;
  691.   const char *start = *ptr;
  692.   do {
  693.     ++ptr;
  694.   } while (**ptr != '\0' && !white_space(**ptr));
  695.   for (int i = 0; i < n; i++)
  696.     if (strlen(s[i]) == *ptr - start
  697.     && memcmp(s[i], start, *ptr - start) == 0)
  698.       return i;
  699.   return -1;
  700. }
  701.  
  702. int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
  703.                     FILE *outfp)
  704. {
  705.   while (white_space(*ptr))
  706.     ptr++;
  707.   const char *start = ptr;
  708.   unsigned numberof;
  709.   if (!read_uint_arg(&ptr, &numberof))
  710.     return 0;
  711.   static const char *types[] = { "Binary", "Hex", "ASCII" };
  712.   const int Binary = 0;
  713.   int type = 0;
  714.   static const char *units[] = { "Bytes", "Lines" };
  715.   const int Bytes = 0;
  716.   int unit = Bytes;
  717.   while (white_space(*ptr))
  718.     ptr++;
  719.   if (*ptr != '\0') {
  720.     type = read_one_of(&ptr, types, 3);
  721.     if (type < 0) {
  722.       error("bad data type");
  723.       return 0;
  724.     }
  725.     while (white_space(*ptr))
  726.       ptr++;
  727.     if (*ptr != '\0') {
  728.       unit = read_one_of(&ptr, units, 2);
  729.       if (unit < 0) {
  730.     error("expected `Bytes' or `Lines'");
  731.     return 0;
  732.       }
  733.     }
  734.   }
  735.   if (type != Binary)
  736.     return 1;
  737.   if (outfp) {
  738.     fputs("%%BeginData: ", outfp);
  739.     fputs(start, outfp);
  740.   }
  741.   if (numberof > 0) {
  742.     unsigned bytecount = 0;
  743.     unsigned linecount = 0;
  744.     do {
  745.       int c = getc(fp);
  746.       if (c == EOF) {
  747.     error("end of file within data section");
  748.     return 0;
  749.       }
  750.       if (outfp)
  751.     putc(c, outfp);
  752.       bytecount++;
  753.       if (c == '\r') {
  754.     int cc = getc(fp);
  755.     if (cc != '\n') {
  756.       linecount++;
  757.       current_lineno++;
  758.     }
  759.     if (cc != EOF)
  760.       ungetc(c, fp);
  761.       }
  762.       else if (c == '\n') {
  763.     linecount++;
  764.     current_lineno++;
  765.       }
  766.     } while ((unit == Bytes ? bytecount : linecount) < numberof);
  767.   }
  768.   char buf[PS_LINE_MAX + 2];
  769.   if (!ps_get_line(buf, fp)) {
  770.     error("missing %%%%EndData line");
  771.     return 0;
  772.   }
  773.   if (!matches_comment(buf, "EndData"))
  774.     error("bad %%%%EndData line");
  775.   if (outfp)
  776.     fputs(buf, outfp);
  777.   return 0;
  778. }
  779.  
  780. int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
  781.                       FILE *outfp)
  782. {
  783.   if (!outfp)
  784.     return 0;
  785.   unsigned count;
  786.   if (!read_uint_arg(&ptr, &count))
  787.     return 0;
  788.   if (outfp)
  789.     fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
  790.   while (count != 0) {
  791.     int c = getc(fp);
  792.     if (c == EOF) {
  793.       error("end of file within binary section");
  794.       return 0;
  795.     }
  796.     if (outfp)
  797.       putc(c, outfp);
  798.     --count;
  799.     if (c == '\r') {
  800.       int cc = getc(fp);
  801.       if (cc != '\n')
  802.     current_lineno++;
  803.       if (cc != EOF)
  804.     ungetc(c, fp);
  805.     }
  806.     else if (c == '\n')
  807.       current_lineno++;
  808.   }
  809.   char buf[PS_LINE_MAX + 2];
  810.   if (!ps_get_line(buf, fp)) {
  811.     error("missing %%%%EndBinary line");
  812.     return 0;
  813.   }
  814.   if (!matches_comment(buf, "EndBinary")) {
  815.     error("bad %%%%EndBinary line");
  816.     if (outfp)
  817.       fputs(buf, outfp);
  818.   }
  819.   else if (outfp)
  820.     fputs("%%EndData\n", outfp);
  821.   return 0;
  822. }
  823.  
  824. static unsigned parse_extensions(const char *ptr)
  825. {
  826.   unsigned flags = 0;
  827.   for (;;) {
  828.     while (white_space(*ptr))
  829.       ptr++;
  830.     if (*ptr == '\0')
  831.       break;
  832.     const char *name = ptr;
  833.     do {
  834.       ++ptr;
  835.     } while (*ptr != '\0' && !white_space(*ptr));
  836.     for (int i = 0; i < NEXTENSIONS; i++)
  837.       if (strlen(extension_table[i]) == ptr - name
  838.       && memcmp(extension_table[i], name, ptr - name) == 0) {
  839.     flags |= (1 << i);
  840.     break;
  841.       }
  842.     if (i >= NEXTENSIONS) {
  843.       string s(name, ptr - name);
  844.       s += '\0';
  845.       error("unknown extension `%1'", s.contents());
  846.     }
  847.   }
  848.   return flags;
  849. }
  850.  
  851. // XXX if it has not been surrounded with {Begin,End}Document need to strip
  852. // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
  853.  
  854. // XXX Perhaps the decision whether to use BeginDocument or
  855. // BeginResource: file should be postponed till we have seen
  856. // the first line of the file.
  857.  
  858. void resource_manager::process_file(int rank, FILE *fp, const char *filename,
  859.                     FILE *outfp)
  860. {
  861.   // If none of these comments appear in the header section, and we are
  862.   // just analyzing the file (ie outfp is 0), then we can return immediately.
  863.   static const char *header_comment_table[] = {
  864.     "DocumentNeededResources:",
  865.     "DocumentSuppliedResources:",
  866.     "DocumentNeededFonts:",
  867.     "DocumentSuppliedFonts:",
  868.     "DocumentNeededProcSets:",
  869.     "DocumentSuppliedProcSets:",
  870.     "DocumentNeededFiles:",
  871.     "DocumentSuppliedFiles:",
  872.   };
  873.   
  874.   const int NHEADER_COMMENTS = (sizeof(header_comment_table)
  875.                 / sizeof(header_comment_table[0]));
  876.   struct comment_info {
  877.     const char *name;
  878.     int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
  879.   };
  880.  
  881.   static comment_info comment_table[] = {
  882.     { "BeginResource:", &resource_manager::do_begin_resource },
  883.     { "IncludeResource:", &resource_manager::do_include_resource },
  884.     { "BeginDocument:", &resource_manager::do_begin_document },
  885.     { "IncludeDocument:", &resource_manager::do_include_document },
  886.     { "BeginProcSet:", &resource_manager::do_begin_procset },
  887.     { "IncludeProcSet:", &resource_manager::do_include_procset },
  888.     { "BeginFont:", &resource_manager::do_begin_font },
  889.     { "IncludeFont:", &resource_manager::do_include_font },
  890.     { "BeginFile:", &resource_manager::do_begin_file },
  891.     { "IncludeFile:", &resource_manager::do_include_file },
  892.     { "EndProcSet", &resource_manager::change_to_end_resource },
  893.     { "EndFont", &resource_manager::change_to_end_resource },
  894.     { "EndFile", &resource_manager::change_to_end_resource },
  895.     { "BeginPreview:", &resource_manager::do_begin_preview },
  896.     { "BeginData:", &resource_manager::do_begin_data },
  897.     { "BeginBinary:", &resource_manager::do_begin_binary },
  898.   };
  899.   
  900.   const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
  901.   char buf[PS_LINE_MAX + 2];
  902.   int saved_lineno = current_lineno;
  903.   const char *saved_filename = current_filename;
  904.   current_filename = filename;
  905.   current_lineno = 0;
  906.   if (!ps_get_line(buf, fp)) {
  907.     current_filename = saved_filename;
  908.     current_lineno = saved_lineno;
  909.     return;
  910.   }
  911.   if (strlen(buf) < sizeof(PS_MAGIC) - 1
  912.       || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
  913.     if (outfp) {
  914.       do {
  915.     if (!(broken_flags & STRIP_PERCENT_BANG)
  916.         || buf[0] != '%' || buf[1] != '!')
  917.       fputs(buf, outfp);
  918.       } while (ps_get_line(buf, fp));
  919.     }
  920.   }
  921.   else {
  922.     if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
  923.       fputs(buf, outfp);
  924.     int in_header = 1;
  925.     int interesting = 0;
  926.     int had_extensions_comment = 0;
  927.     int had_language_level_comment = 0;
  928.     for (;;) {
  929.       if (!ps_get_line(buf, fp))
  930.     break;
  931.       int copy_this_line = 1;
  932.       if (buf[0] == '%') {
  933.     if (buf[1] == '%') {
  934.       const char *ptr;
  935.       for (int i = 0; i < NCOMMENTS; i++)
  936.         if (ptr = matches_comment(buf, comment_table[i].name)) {
  937.           copy_this_line
  938.         = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
  939.           break;
  940.         }
  941.       if (i >= NCOMMENTS && in_header) {
  942.         if (ptr = matches_comment(buf, "EndComments"))
  943.           in_header = 0;
  944.         else if (!had_extensions_comment
  945.              && (ptr = matches_comment(buf, "Extensions:"))) {
  946.           extensions |= parse_extensions(ptr);
  947.           // XXX handle possibility that next line is %%+
  948.           had_extensions_comment = 1;
  949.         }
  950.         else if (!had_language_level_comment
  951.              && (ptr = matches_comment(buf, "LanguageLevel:"))) {
  952.           unsigned ll;
  953.           if (read_uint_arg(&ptr, &ll) && ll > language_level)
  954.         language_level = ll;
  955.           had_language_level_comment = 1;
  956.         }
  957.         else {
  958.           for (int i = 0; i < NHEADER_COMMENTS; i++)
  959.         if (matches_comment(buf, header_comment_table[i])) {
  960.           interesting = 1;
  961.           break;
  962.         }
  963.         }
  964.       }
  965.       if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
  966.           && (matches_comment(buf, "EndProlog")
  967.           || matches_comment(buf, "Page:")
  968.           || matches_comment(buf, "Trailer")))
  969.         copy_this_line = 0;
  970.     }
  971.     else if (buf[1] == '!') {
  972.       if (broken_flags & STRIP_PERCENT_BANG)
  973.         copy_this_line = 0;
  974.     }
  975.       }
  976.       else
  977.     in_header = 0;
  978.       if (!outfp && !in_header && !interesting)
  979.     break;
  980.       if (copy_this_line && outfp)
  981.     fputs(buf, outfp);
  982.     }
  983.   }
  984.   current_filename = saved_filename;
  985.   current_lineno = saved_lineno;
  986. }
  987.  
  988. void resource_manager::read_download_file()
  989. {
  990.   char *path = 0;
  991.   FILE *fp = font::open_file("download", &path);
  992.   if (!fp)
  993.     fatal("can't find `download'");
  994.   char buf[512];
  995.   int lineno = 0;
  996.   while (fgets(buf, sizeof(buf), fp)) {
  997.     lineno++;
  998.     char *p = strtok(buf, " \t\r\n");
  999.     if (p == 0 || *p == '#')
  1000.       continue;
  1001.     char *q = strtok(0, " \t\r\n");
  1002.     if (!q)
  1003.       fatal_with_file_and_line(path, lineno, "missing filename");
  1004.     lookup_font(p)->filename = strsave(q);
  1005.   }
  1006.   a_delete path;
  1007.   fclose(fp);
  1008. }
  1009.  
  1010. // XXX Can we share some code with ps_output::put_string()?
  1011.  
  1012. static void print_ps_string(const string &s, FILE *outfp)
  1013. {
  1014.   int len = s.length();
  1015.   const char *str = s.contents();
  1016.   int funny = 0;
  1017.   if (str[0] == '(')
  1018.     funny = 1;
  1019.   else {
  1020.     for (int i = 0; i < len; i++)
  1021.       if (str[i] <= 040 || str[i] > 0176) {
  1022.     funny = 1;
  1023.     break;
  1024.       }
  1025.   }
  1026.   if (!funny) {
  1027.     put_string(s, outfp);
  1028.     return;
  1029.   }
  1030.   int level = 0;
  1031.   for (int i = 0; i < len; i++)
  1032.     if (str[i] == '(')
  1033.       level++;
  1034.     else if (str[i] == ')' && --level < 0)
  1035.       break;
  1036.   putc('(', outfp);
  1037.   for (i = 0; i < len; i++)
  1038.     switch (str[i]) {
  1039.     case '(':
  1040.     case ')':
  1041.       if (level != 0)
  1042.     putc('\\', outfp);
  1043.       putc(str[i], outfp);
  1044.       break;
  1045.     case '\\':
  1046.       fputs("\\\\", outfp);
  1047.       break;
  1048.     case '\n':
  1049.       fputs("\\n", outfp);
  1050.       break;
  1051.     case '\r':
  1052.       fputs("\\r", outfp);
  1053.       break;
  1054.     case '\t':
  1055.       fputs("\\t", outfp);
  1056.       break;
  1057.     case '\b':
  1058.       fputs("\\b", outfp);
  1059.       break;
  1060.     case '\f':
  1061.       fputs("\\f", outfp);
  1062.       break;
  1063.     default:
  1064.       if (str[i] < 040 || str[i] > 0176)
  1065.     fprintf(outfp, "\\%03o", str[i] & 0377);
  1066.       else
  1067.     putc(str[i], outfp);
  1068.       break;
  1069.     }
  1070.   putc(')', outfp);
  1071. }
  1072.  
  1073. void resource_manager::print_extensions_comment(FILE *outfp)
  1074. {
  1075.   if (extensions) {
  1076.     fputs("%%Extensions:", outfp);
  1077.     for (int i = 0; i < NEXTENSIONS; i++)
  1078.       if (extensions & (1 << i)) {
  1079.     putc(' ', outfp);
  1080.     fputs(extension_table[i], outfp);
  1081.       }
  1082.     putc('\n', outfp);
  1083.   }
  1084. }
  1085.  
  1086. void resource_manager::print_language_level_comment(FILE *outfp)
  1087. {
  1088.   if (language_level)
  1089.     fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
  1090. }
  1091.  
  1092.