// -*- C++ -*- /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 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 2, 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 groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #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"); string an_empty_string; const char *extension_table[] = { "DPS", "CMYK", "Composite", "FileSystem", }; const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]); const char *resource_table[] = { "font", "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() { a_delete 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]; } a_delete head; a_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("can't find `%1'", prologue); fputs("%%BeginResource: ", outfp); procset_resource->print_type_and_name(outfp); putc('\n', outfp); process_file(-1, fp, path, outfp); fclose(fp); a_delete 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; FILE *fp = 0; if (r->filename != 0) { if (r->type == RESOURCE_FONT) { fp = font::open_file(r->filename, &path); if (!fp) { error("can't find `%1'", r->filename); a_delete r->filename; r->filename = 0; } } else { errno = 0; fp = fopen(r->filename, "r"); if (!fp) { error("can't open `%1': %2", r->filename, strerror(errno)); a_delete r->filename; r->filename = 0; } 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) a_delete 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_LINE_MAX 255 #define PS_MAGIC "%!PS-Adobe-" static int ps_get_line(char *buf, FILE *fp) { int c = getc(fp); if (c == EOF) { buf[0] = '\0'; return 0; } current_lineno++; int i = 0; int err = 0; while (c != '\r' && c != '\n' && c != EOF) { if ((c < 0x1b && !white_space(c)) || c == 0x7f) error("invalid input character code %1", int(c)); else if (i < PS_LINE_MAX) buf[i++] = c; else if (!err) { err = 1; error("PostScript file non-conforming " "because length of line exceeds 255"); } c = getc(fp); } buf[i++] = '\n'; buf[i] = '\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) && memcmp(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(const char *buf, const char *comment) { if (buf[0] != '%' || buf[1] != '%') return 0; for (buf += 2; *comment; comment++, buf++) if (*buf != *comment) return 0; if (comment[-1] == ':') return buf; if (*buf == '\0' || white_space(*buf)) return buf; 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 *) { char buf[PS_LINE_MAX + 2]; 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; } 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(c, fp); } else if (c == '\n') { linecount++; current_lineno++; } } while ((unit == Bytes ? bytecount : linecount) < numberof); } char buf[PS_LINE_MAX + 2]; 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, 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++; } char buf[PS_LINE_MAX + 2]; 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, 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 (ie 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 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]); char buf[PS_LINE_MAX + 2]; 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 (strlen(buf) < sizeof(PS_MAGIC) - 1 || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) { if (outfp) { do { if (!(broken_flags & STRIP_PERCENT_BANG) || buf[0] != '%' || buf[1] != '!') fputs(buf, outfp); } while (ps_get_line(buf, fp)); } } else { if (!(broken_flags & STRIP_PERCENT_BANG) && outfp) fputs(buf, 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, outfp); } } current_filename = saved_filename; current_lineno = saved_lineno; } void resource_manager::read_download_file() { char *path = 0; FILE *fp = font::open_file("download", &path); if (!fp) fatal("can't find `download'"); char buf[512]; int lineno = 0; while (fgets(buf, sizeof(buf), fp)) { lineno++; char *p = strtok(buf, " \t\r\n"); if (p == 0 || *p == '#') continue; char *q = strtok(0, " \t\r\n"); if (!q) fatal_with_file_and_line(path, lineno, "missing filename"); lookup_font(p)->filename = strsave(q); } a_delete 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); }