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