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