LCOV - code coverage report
Current view: top level - devices/grops - psrm.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 327 722 45.3 %
Date: 2026-01-16 17:51:41 Functions: 25 44 56.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14