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