summaryrefslogtreecommitdiffstats
path: root/src/devices/grops/psrm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/grops/psrm.cpp')
-rw-r--r--src/devices/grops/psrm.cpp1189
1 files changed, 1189 insertions, 0 deletions
diff --git a/src/devices/grops/psrm.cpp b/src/devices/grops/psrm.cpp
new file mode 100644
index 0000000..3c9a8b7
--- /dev/null
+++ b/src/devices/grops/psrm.cpp
@@ -0,0 +1,1189 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#include "ps.h"
+
+#ifdef NEED_DECLARATION_PUTENV
+extern "C" {
+ int putenv(const char *);
+}
+#endif /* NEED_DECLARATION_PUTENV */
+
+#define GROPS_PROLOGUE "prologue"
+
+static void print_ps_string(const string &s, FILE *outfp);
+
+cset white_space("\n\r \t\f");
+string an_empty_string;
+
+char valid_input_table[256]= {
+#ifndef IS_EBCDIC_HOST
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+#else
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+#endif
+};
+
+const char *extension_table[] = {
+ "DPS",
+ "CMYK",
+ "Composite",
+ "FileSystem",
+};
+
+const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
+
+// this must stay in sync with 'resource_type' in 'ps.h'
+const char *resource_table[] = {
+ "font",
+ "fontset",
+ "procset",
+ "file",
+ "encoding",
+ "form",
+ "pattern",
+};
+
+const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
+
+static int read_uint_arg(const char **pp, unsigned *res)
+{
+ while (white_space(**pp))
+ *pp += 1;
+ if (**pp == '\0') {
+ error("missing argument");
+ return 0;
+ }
+ const char *start = *pp;
+ // XXX use strtoul
+ long n = strtol(start, (char **)pp, 10);
+ if (n == 0 && *pp == start) {
+ error("not an integer");
+ return 0;
+ }
+ if (n < 0) {
+ error("argument must not be negative");
+ return 0;
+ }
+ *res = unsigned(n);
+ return 1;
+}
+
+struct resource {
+ resource *next;
+ resource_type type;
+ string name;
+ enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
+ unsigned flags;
+ string version;
+ unsigned revision;
+ char *filename;
+ int rank;
+ resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
+ ~resource();
+ void print_type_and_name(FILE *outfp);
+};
+
+resource::resource(resource_type t, string &n, string &v, unsigned r)
+: next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
+{
+ name.move(n);
+ version.move(v);
+ if (type == RESOURCE_FILE) {
+ if (name.search('\0') >= 0)
+ error("filename contains a character with code 0");
+ filename = name.extract();
+ }
+}
+
+resource::~resource()
+{
+ free(filename);
+}
+
+void resource::print_type_and_name(FILE *outfp)
+{
+ fputs(resource_table[type], outfp);
+ putc(' ', outfp);
+ print_ps_string(name, outfp);
+ if (type == RESOURCE_PROCSET) {
+ putc(' ', outfp);
+ print_ps_string(version, outfp);
+ fprintf(outfp, " %u", revision);
+ }
+}
+
+resource_manager::resource_manager()
+: extensions(0), language_level(0), resource_list(0)
+{
+ read_download_file();
+ string procset_name("grops");
+ extern const char *version_string;
+ extern const char *revision_string;
+ unsigned revision_uint;
+ if (!read_uint_arg(&revision_string, &revision_uint))
+ revision_uint = 0;
+ string procset_version(version_string);
+ procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
+ procset_version, revision_uint);
+ procset_resource->flags |= resource::SUPPLIED;
+}
+
+resource_manager::~resource_manager()
+{
+ while (resource_list) {
+ resource *tem = resource_list;
+ resource_list = resource_list->next;
+ delete tem;
+ }
+}
+
+resource *resource_manager::lookup_resource(resource_type type,
+ string &name,
+ string &version,
+ unsigned revision)
+{
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->type == type
+ && r->name == name
+ && r->version == version
+ && r->revision == revision)
+ return r;
+ r = new resource(type, name, version, revision);
+ r->next = resource_list;
+ resource_list = r;
+ return r;
+}
+
+// Just a specialized version of lookup_resource().
+
+resource *resource_manager::lookup_font(const char *name)
+{
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT
+ && strlen(name) == (size_t)r->name.length()
+ && memcmp(name, r->name.contents(), r->name.length()) == 0)
+ return r;
+ string s(name);
+ r = new resource(RESOURCE_FONT, s);
+ r->next = resource_list;
+ resource_list = r;
+ return r;
+}
+
+void resource_manager::need_font(const char *name)
+{
+ lookup_font(name)->flags |= resource::FONT_NEEDED;
+}
+
+typedef resource *Presource; // Work around g++ bug.
+
+void resource_manager::document_setup(ps_output &out)
+{
+ int nranks = 0;
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->rank >= nranks)
+ nranks = r->rank + 1;
+ if (nranks > 0) {
+ // Sort resource_list in reverse order of rank.
+ Presource *head = new Presource[nranks + 1];
+ Presource **tail = new Presource *[nranks + 1];
+ int i;
+ for (i = 0; i < nranks + 1; i++) {
+ head[i] = 0;
+ tail[i] = &head[i];
+ }
+ for (r = resource_list; r; r = r->next) {
+ i = r->rank < 0 ? 0 : r->rank + 1;
+ *tail[i] = r;
+ tail[i] = &(*tail[i])->next;
+ }
+ resource_list = 0;
+ for (i = 0; i < nranks + 1; i++)
+ if (head[i]) {
+ *tail[i] = resource_list;
+ resource_list = head[i];
+ }
+ delete[] head;
+ delete[] tail;
+ // check it
+ for (r = resource_list; r; r = r->next)
+ if (r->next)
+ assert(r->rank >= r->next->rank);
+ for (r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT && r->rank >= 0)
+ supply_resource(r, -1, out.get_file());
+ }
+}
+
+void resource_manager::print_resources_comment(unsigned flag,
+ FILE *outfp)
+{
+ int continued = 0;
+ for (resource *r = resource_list; r; r = r->next)
+ if (r->flags & flag) {
+ if (continued)
+ fputs("%%+ ", outfp);
+ else {
+ fputs(flag == resource::NEEDED
+ ? "%%DocumentNeededResources: "
+ : "%%DocumentSuppliedResources: ",
+ outfp);
+ continued = 1;
+ }
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+}
+
+void resource_manager::print_header_comments(ps_output &out)
+{
+ for (resource *r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
+ supply_resource(r, 0, 0);
+ print_resources_comment(resource::NEEDED, out.get_file());
+ print_resources_comment(resource::SUPPLIED, out.get_file());
+ print_language_level_comment(out.get_file());
+ print_extensions_comment(out.get_file());
+}
+
+void resource_manager::output_prolog(ps_output &out)
+{
+ FILE *outfp = out.get_file();
+ out.end_line();
+ char *path;
+ if (!getenv("GROPS_PROLOGUE")) {
+ string e = "GROPS_PROLOGUE";
+ e += '=';
+ e += GROPS_PROLOGUE;
+ e += '\0';
+ if (putenv(strsave(e.contents())))
+ fatal("putenv failed");
+ }
+ char *prologue = getenv("GROPS_PROLOGUE");
+ FILE *fp = font::open_file(prologue, &path);
+ if (!fp)
+ fatal("failed to open PostScript prologue '%1': %2", prologue,
+ strerror(errno));
+ fputs("%%BeginResource: ", outfp);
+ procset_resource->print_type_and_name(outfp);
+ putc('\n', outfp);
+ process_file(-1, fp, path, outfp);
+ fclose(fp);
+ free(path);
+ fputs("%%EndResource\n", outfp);
+}
+
+void resource_manager::import_file(const char *filename, ps_output &out)
+{
+ out.end_line();
+ string name(filename);
+ resource *r = lookup_resource(RESOURCE_FILE, name);
+ supply_resource(r, -1, out.get_file(), 1);
+}
+
+void resource_manager::supply_resource(resource *r, int rank,
+ FILE *outfp, int is_document)
+{
+ if (r->flags & resource::BUSY) {
+ r->name += '\0';
+ fatal("loop detected in dependency graph for %1 '%2'",
+ resource_table[r->type],
+ r->name.contents());
+ }
+ r->flags |= resource::BUSY;
+ if (rank > r->rank)
+ r->rank = rank;
+ char *path = 0 /* nullptr */;
+ FILE *fp = 0 /* nullptr */;
+ if (r->filename != 0 /* nullptr */) {
+ if (r->type == RESOURCE_FONT) {
+ fp = font::open_file(r->filename, &path);
+ if (!fp) {
+ error("failed to open PostScript resource '%1': %2",
+ r->filename, strerror(errno));
+ delete[] r->filename;
+ r->filename = 0 /* nullptr */;
+ }
+ }
+ else {
+ errno = 0;
+ fp = include_search_path.open_file_cautious(r->filename);
+ if (!fp) {
+ error("can't open '%1': %2", r->filename, strerror(errno));
+ delete[] r->filename;
+ r->filename = 0 /* nullptr */;
+ }
+ else
+ path = r->filename;
+ }
+ }
+ if (fp) {
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document) {
+ fputs("%%BeginDocument: ", outfp);
+ print_ps_string(r->name, outfp);
+ putc('\n', outfp);
+ }
+ else {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ process_file(rank, fp, path, outfp);
+ fclose(fp);
+ if (r->type == RESOURCE_FONT)
+ free(path);
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document)
+ fputs("%%EndDocument\n", outfp);
+ else
+ fputs("%%EndResource\n", outfp);
+ }
+ r->flags |= resource::SUPPLIED;
+ }
+ else {
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document) {
+ fputs("%%IncludeDocument: ", outfp);
+ print_ps_string(r->name, outfp);
+ putc('\n', outfp);
+ }
+ else {
+ fputs("%%IncludeResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ r->flags |= resource::NEEDED;
+ }
+ r->flags &= ~resource::BUSY;
+}
+
+#define PS_MAGIC "%!PS-Adobe-"
+
+static int ps_get_line(string &buf, FILE *fp)
+{
+ buf.clear();
+ int c = getc(fp);
+ if (c == EOF)
+ return 0;
+ current_lineno++;
+ while (c != '\r' && c != '\n' && c != EOF) {
+ if (!valid_input_table[c])
+ error("invalid input character code %1", int(c));
+ buf += c;
+ c = getc(fp);
+ }
+ buf += '\n';
+ buf += '\0';
+ if (c == '\r') {
+ c = getc(fp);
+ if (c != EOF && c != '\n')
+ ungetc(c, fp);
+ }
+ return 1;
+}
+
+static int read_text_arg(const char **pp, string &res)
+{
+ res.clear();
+ while (white_space(**pp))
+ *pp += 1;
+ if (**pp == '\0') {
+ error("missing argument");
+ return 0;
+ }
+ if (**pp != '(') {
+ for (; **pp != '\0' && !white_space(**pp); *pp += 1)
+ res += **pp;
+ return 1;
+ }
+ *pp += 1;
+ res.clear();
+ int level = 0;
+ for (;;) {
+ if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
+ error("missing ')'");
+ return 0;
+ }
+ if (**pp == ')') {
+ if (level == 0) {
+ *pp += 1;
+ break;
+ }
+ res += **pp;
+ level--;
+ }
+ else if (**pp == '(') {
+ level++;
+ res += **pp;
+ }
+ else if (**pp == '\\') {
+ *pp += 1;
+ switch (**pp) {
+ case 'n':
+ res += '\n';
+ break;
+ case 'r':
+ res += '\n';
+ break;
+ case 't':
+ res += '\t';
+ break;
+ case 'b':
+ res += '\b';
+ break;
+ case 'f':
+ res += '\f';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int val = **pp - '0';
+ if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
+ *pp += 1;
+ val = val*8 + (**pp - '0');
+ if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
+ *pp += 1;
+ val = val*8 + (**pp - '0');
+ }
+ }
+ }
+ break;
+ default:
+ res += **pp;
+ break;
+ }
+ }
+ else
+ res += **pp;
+ *pp += 1;
+ }
+ return 1;
+}
+
+resource *resource_manager::read_file_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(RESOURCE_FILE, arg);
+}
+
+resource *resource_manager::read_font_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(RESOURCE_FONT, arg);
+}
+
+resource *resource_manager::read_procset_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ string version;
+ if (!read_text_arg(ptr, version))
+ return 0;
+ unsigned revision;
+ if (!read_uint_arg(ptr, &revision))
+ return 0;
+ return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
+}
+
+resource *resource_manager::read_resource_arg(const char **ptr)
+{
+ while (white_space(**ptr))
+ *ptr += 1;
+ const char *name = *ptr;
+ while (**ptr != '\0' && !white_space(**ptr))
+ *ptr += 1;
+ if (name == *ptr) {
+ error("missing resource type");
+ return 0;
+ }
+ int ri;
+ for (ri = 0; ri < NRESOURCES; ri++)
+ if (strlen(resource_table[ri]) == size_t(*ptr - name)
+ && strncasecmp(resource_table[ri], name, *ptr - name) == 0)
+ break;
+ if (ri >= NRESOURCES) {
+ error("unknown resource type");
+ return 0;
+ }
+ if (ri == RESOURCE_PROCSET)
+ return read_procset_arg(ptr);
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(resource_type(ri), arg);
+}
+
+static const char *matches_comment(string &buf, const char *comment)
+{
+ if ((size_t)buf.length() < strlen(comment) + 3)
+ return 0;
+ if (buf[0] != '%' || buf[1] != '%')
+ return 0;
+ const char *bufp = buf.contents() + 2;
+ for (; *comment; comment++, bufp++)
+ if (*bufp != *comment)
+ return 0;
+ if (comment[-1] == ':')
+ return bufp;
+ if (*bufp == '\0' || white_space(*bufp))
+ return bufp;
+ return 0;
+}
+
+// Return 1 if the line should be copied out.
+
+int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
+ FILE *)
+{
+ resource *r = read_resource_arg(&ptr);
+ if (r)
+ r->flags |= resource::SUPPLIED;
+ return 1;
+}
+
+int resource_manager::do_include_resource(const char *ptr, int rank,
+ FILE *, FILE *outfp)
+{
+ resource *r = read_resource_arg(&ptr);
+ if (r) {
+ if (r->type == RESOURCE_FONT) {
+ if (rank >= 0)
+ supply_resource(r, rank + 1, outfp);
+ else
+ r->flags |= resource::FONT_NEEDED;
+ }
+ else
+ supply_resource(r, rank, outfp);
+ }
+ return 0;
+}
+
+int resource_manager::do_begin_document(const char *ptr, int, FILE *,
+ FILE *)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ r->flags |= resource::SUPPLIED;
+ return 1;
+}
+
+int resource_manager::do_include_document(const char *ptr, int rank,
+ FILE *, FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp, 1);
+ return 0;
+}
+
+int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_procset_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_procset(const char *ptr, int rank,
+ FILE *, FILE *outfp)
+{
+ resource *r = read_procset_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_file(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_file(const char *ptr, int rank,
+ FILE *, FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_font(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_font_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_font_arg(&ptr);
+ if (r) {
+ if (rank >= 0)
+ supply_resource(r, rank + 1, outfp);
+ else
+ r->flags |= resource::FONT_NEEDED;
+ }
+ return 0;
+}
+
+int resource_manager::change_to_end_resource(const char *, int, FILE *,
+ FILE *outfp)
+{
+ if (outfp)
+ fputs("%%EndResource\n", outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_preview(const char *, int, FILE *fp,
+ FILE *)
+{
+ string buf;
+ do {
+ if (!ps_get_line(buf, fp)) {
+ error("end of file in preview section");
+ break;
+ }
+ } while (!matches_comment(buf, "EndPreview"));
+ return 0;
+}
+
+int read_one_of(const char **ptr, const char **s, int n)
+{
+ while (white_space(**ptr))
+ *ptr += 1;
+ if (**ptr == '\0')
+ return -1;
+ const char *start = *ptr;
+ do {
+ ++(*ptr);
+ } while (**ptr != '\0' && !white_space(**ptr));
+ for (int i = 0; i < n; i++)
+ if (strlen(s[i]) == size_t(*ptr - start)
+ && memcmp(s[i], start, *ptr - start) == 0)
+ return i;
+ return -1;
+}
+
+void skip_possible_newline(FILE *fp, FILE *outfp)
+{
+ int c = getc(fp);
+ if (c == '\r') {
+ current_lineno++;
+ if (outfp)
+ putc(c, outfp);
+ int cc = getc(fp);
+ if (cc != '\n') {
+ if (cc != EOF)
+ ungetc(cc, fp);
+ }
+ else {
+ if (outfp)
+ putc(cc, outfp);
+ }
+ }
+ else if (c == '\n') {
+ current_lineno++;
+ if (outfp)
+ putc(c, outfp);
+ }
+ else if (c != EOF)
+ ungetc(c, fp);
+}
+
+int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
+ FILE *outfp)
+{
+ while (white_space(*ptr))
+ ptr++;
+ const char *start = ptr;
+ unsigned numberof;
+ if (!read_uint_arg(&ptr, &numberof))
+ return 0;
+ static const char *types[] = { "Binary", "Hex", "ASCII" };
+ const int Binary = 0;
+ int type = 0;
+ static const char *units[] = { "Bytes", "Lines" };
+ const int Bytes = 0;
+ int unit = Bytes;
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr != '\0') {
+ type = read_one_of(&ptr, types, 3);
+ if (type < 0) {
+ error("bad data type");
+ return 0;
+ }
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr != '\0') {
+ unit = read_one_of(&ptr, units, 2);
+ if (unit < 0) {
+ error("expected 'Bytes' or 'Lines'");
+ return 0;
+ }
+ }
+ }
+ if (type != Binary)
+ return 1;
+ if (outfp) {
+ fputs("%%BeginData: ", outfp);
+ fputs(start, outfp);
+ }
+ if (numberof > 0) {
+ unsigned bytecount = 0;
+ unsigned linecount = 0;
+ do {
+ int c = getc(fp);
+ if (c == EOF) {
+ error("end of file within data section");
+ return 0;
+ }
+ if (outfp)
+ putc(c, outfp);
+ bytecount++;
+ if (c == '\r') {
+ int cc = getc(fp);
+ if (cc != '\n') {
+ linecount++;
+ current_lineno++;
+ }
+ if (cc != EOF)
+ ungetc(cc, fp);
+ }
+ else if (c == '\n') {
+ linecount++;
+ current_lineno++;
+ }
+ } while ((unit == Bytes ? bytecount : linecount) < numberof);
+ }
+ skip_possible_newline(fp, outfp);
+ string buf;
+ if (!ps_get_line(buf, fp)) {
+ error("missing %%%%EndData line");
+ return 0;
+ }
+ if (!matches_comment(buf, "EndData"))
+ error("bad %%%%EndData line");
+ if (outfp)
+ fputs(buf.contents(), outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
+ FILE *outfp)
+{
+ if (!outfp)
+ return 0;
+ unsigned count;
+ if (!read_uint_arg(&ptr, &count))
+ return 0;
+ if (outfp)
+ fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
+ while (count != 0) {
+ int c = getc(fp);
+ if (c == EOF) {
+ error("end of file within binary section");
+ return 0;
+ }
+ if (outfp)
+ putc(c, outfp);
+ --count;
+ if (c == '\r') {
+ int cc = getc(fp);
+ if (cc != '\n')
+ current_lineno++;
+ if (cc != EOF)
+ ungetc(cc, fp);
+ }
+ else if (c == '\n')
+ current_lineno++;
+ }
+ skip_possible_newline(fp, outfp);
+ string buf;
+ if (!ps_get_line(buf, fp)) {
+ error("missing %%%%EndBinary line");
+ return 0;
+ }
+ if (!matches_comment(buf, "EndBinary")) {
+ error("bad %%%%EndBinary line");
+ if (outfp)
+ fputs(buf.contents(), outfp);
+ }
+ else if (outfp)
+ fputs("%%EndData\n", outfp);
+ return 0;
+}
+
+static unsigned parse_extensions(const char *ptr)
+{
+ unsigned flags = 0;
+ for (;;) {
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ const char *name = ptr;
+ do {
+ ++ptr;
+ } while (*ptr != '\0' && !white_space(*ptr));
+ int i;
+ for (i = 0; i < NEXTENSIONS; i++)
+ if (strlen(extension_table[i]) == size_t(ptr - name)
+ && memcmp(extension_table[i], name, ptr - name) == 0) {
+ flags |= (1 << i);
+ break;
+ }
+ if (i >= NEXTENSIONS) {
+ string s(name, ptr - name);
+ s += '\0';
+ error("unknown extension '%1'", s.contents());
+ }
+ }
+ return flags;
+}
+
+// XXX if it has not been surrounded with {Begin,End}Document need to
+// strip out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
+
+// XXX Perhaps the decision whether to use BeginDocument or
+// BeginResource: file should be postponed till we have seen
+// the first line of the file.
+
+void resource_manager::process_file(int rank, FILE *fp,
+ const char *filename, FILE *outfp)
+{
+ // If none of these comments appear in the header section, and we are
+ // just analyzing the file (i.e., outfp is 0), then we can return
+ // immediately.
+ static const char *header_comment_table[] = {
+ "DocumentNeededResources:",
+ "DocumentSuppliedResources:",
+ "DocumentNeededFonts:",
+ "DocumentSuppliedFonts:",
+ "DocumentNeededProcSets:",
+ "DocumentSuppliedProcSets:",
+ "DocumentNeededFiles:",
+ "DocumentSuppliedFiles:",
+ };
+
+ const int NHEADER_COMMENTS = sizeof(header_comment_table)
+ / sizeof(header_comment_table[0]);
+ struct comment_info {
+ const char *name;
+ int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
+ };
+
+ static const comment_info comment_table[] = {
+ { "BeginResource:", &resource_manager::do_begin_resource },
+ { "IncludeResource:", &resource_manager::do_include_resource },
+ { "BeginDocument:", &resource_manager::do_begin_document },
+ { "IncludeDocument:", &resource_manager::do_include_document },
+ { "BeginProcSet:", &resource_manager::do_begin_procset },
+ { "IncludeProcSet:", &resource_manager::do_include_procset },
+ { "BeginFont:", &resource_manager::do_begin_font },
+ { "IncludeFont:", &resource_manager::do_include_font },
+ { "BeginFile:", &resource_manager::do_begin_file },
+ { "IncludeFile:", &resource_manager::do_include_file },
+ { "EndProcSet", &resource_manager::change_to_end_resource },
+ { "EndFont", &resource_manager::change_to_end_resource },
+ { "EndFile", &resource_manager::change_to_end_resource },
+ { "BeginPreview:", &resource_manager::do_begin_preview },
+ { "BeginData:", &resource_manager::do_begin_data },
+ { "BeginBinary:", &resource_manager::do_begin_binary },
+ };
+
+ const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
+ string buf;
+ int saved_lineno = current_lineno;
+ const char *saved_filename = current_filename;
+ current_filename = filename;
+ current_lineno = 0;
+ if (!ps_get_line(buf, fp)) {
+ current_filename = saved_filename;
+ current_lineno = saved_lineno;
+ return;
+ }
+ if ((size_t)buf.length() < sizeof(PS_MAGIC)
+ || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
+ if (outfp) {
+ do {
+ if (!(broken_flags & STRIP_PERCENT_BANG)
+ || buf[0] != '%' || buf[1] != '!')
+ fputs(buf.contents(), outfp);
+ } while (ps_get_line(buf, fp));
+ }
+ }
+ else {
+ if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
+ fputs(buf.contents(), outfp);
+ int in_header = 1;
+ int interesting = 0;
+ int had_extensions_comment = 0;
+ int had_language_level_comment = 0;
+ for (;;) {
+ if (!ps_get_line(buf, fp))
+ break;
+ int copy_this_line = 1;
+ if (buf[0] == '%') {
+ if (buf[1] == '%') {
+ const char *ptr;
+ int i;
+ for (i = 0; i < NCOMMENTS; i++)
+ if ((ptr = matches_comment(buf, comment_table[i].name))) {
+ copy_this_line
+ = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
+ break;
+ }
+ if (i >= NCOMMENTS && in_header) {
+ if ((ptr = matches_comment(buf, "EndComments")))
+ in_header = 0;
+ else if (!had_extensions_comment
+ && (ptr = matches_comment(buf, "Extensions:"))) {
+ extensions |= parse_extensions(ptr);
+ // XXX handle possibility that next line is %%+
+ had_extensions_comment = 1;
+ }
+ else if (!had_language_level_comment
+ && (ptr = matches_comment(buf, "LanguageLevel:"))) {
+ unsigned ll;
+ if (read_uint_arg(&ptr, &ll) && ll > language_level)
+ language_level = ll;
+ had_language_level_comment = 1;
+ }
+ else {
+ for (i = 0; i < NHEADER_COMMENTS; i++)
+ if (matches_comment(buf, header_comment_table[i])) {
+ interesting = 1;
+ break;
+ }
+ }
+ }
+ if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
+ && (matches_comment(buf, "EndProlog")
+ || matches_comment(buf, "Page:")
+ || matches_comment(buf, "Trailer")))
+ copy_this_line = 0;
+ }
+ else if (buf[1] == '!') {
+ if (broken_flags & STRIP_PERCENT_BANG)
+ copy_this_line = 0;
+ }
+ }
+ else
+ in_header = 0;
+ if (!outfp && !in_header && !interesting)
+ break;
+ if (copy_this_line && outfp)
+ fputs(buf.contents(), outfp);
+ }
+ }
+ current_filename = saved_filename;
+ current_lineno = saved_lineno;
+}
+
+void resource_manager::read_download_file()
+{
+ char *path = 0 /* nullptr */;
+ FILE *fp = font::open_file("download", &path);
+ if (0 /* nullptr */ == fp)
+ fatal("failed to open 'download' file: %1", strerror(errno));
+ char buf[512];
+ int lineno = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ lineno++;
+ char *p = strtok(buf, " \t\r\n");
+ if (p == 0 /* nullptr */ || *p == '#')
+ continue;
+ char *q = strtok(0 /* nullptr */, " \t\r\n");
+ if (!q)
+ fatal_with_file_and_line(path, lineno, "file name missing for"
+ " font '%1'", p);
+ lookup_font(p)->filename = strsave(q);
+ }
+ free(path);
+ fclose(fp);
+}
+
+// XXX Can we share some code with ps_output::put_string()?
+
+static void print_ps_string(const string &s, FILE *outfp)
+{
+ int len = s.length();
+ const char *str = s.contents();
+ int funny = 0;
+ if (str[0] == '(')
+ funny = 1;
+ else {
+ for (int i = 0; i < len; i++)
+ if (str[i] <= 040 || str[i] > 0176) {
+ funny = 1;
+ break;
+ }
+ }
+ if (!funny) {
+ put_string(s, outfp);
+ return;
+ }
+ int level = 0;
+ int i;
+ for (i = 0; i < len; i++)
+ if (str[i] == '(')
+ level++;
+ else if (str[i] == ')' && --level < 0)
+ break;
+ putc('(', outfp);
+ for (i = 0; i < len; i++)
+ switch (str[i]) {
+ case '(':
+ case ')':
+ if (level != 0)
+ putc('\\', outfp);
+ putc(str[i], outfp);
+ break;
+ case '\\':
+ fputs("\\\\", outfp);
+ break;
+ case '\n':
+ fputs("\\n", outfp);
+ break;
+ case '\r':
+ fputs("\\r", outfp);
+ break;
+ case '\t':
+ fputs("\\t", outfp);
+ break;
+ case '\b':
+ fputs("\\b", outfp);
+ break;
+ case '\f':
+ fputs("\\f", outfp);
+ break;
+ default:
+ if (str[i] < 040 || str[i] > 0176)
+ fprintf(outfp, "\\%03o", str[i] & 0377);
+ else
+ putc(str[i], outfp);
+ break;
+ }
+ putc(')', outfp);
+}
+
+void resource_manager::print_extensions_comment(FILE *outfp)
+{
+ if (extensions) {
+ fputs("%%Extensions:", outfp);
+ for (int i = 0; i < NEXTENSIONS; i++)
+ if (extensions & (1 << i)) {
+ putc(' ', outfp);
+ fputs(extension_table[i], outfp);
+ }
+ putc('\n', outfp);
+ }
+}
+
+void resource_manager::print_language_level_comment(FILE *outfp)
+{
+ if (language_level)
+ fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: