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