groff before CVS: release 1.06
[s-roff.git] / grops / psrm.cc
blobe26acf4b15c562402c3c30c3248536f6b10b9587
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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.
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.
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "driver.h"
22 #include "stringclass.h"
23 #include "cset.h"
25 #include "ps.h"
27 #define PROLOGUE "prologue"
29 static void print_ps_string(const string &s, FILE *outfp);
31 cset white_space("\n\r \t");
32 string an_empty_string;
34 const char *extension_table[] = {
35 "DPS",
36 "CMYK",
37 "Composite",
38 "FileSystem",
41 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
43 const char *resource_table[] = {
44 "font",
45 "procset",
46 "file",
47 "encoding",
48 "form",
49 "pattern",
52 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
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);
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)
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();
81 resource::~resource()
83 a_delete filename;
86 void resource::print_type_and_name(FILE *outfp)
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);
98 resource_manager::resource_manager()
99 : resource_list(0), extensions(0), language_level(0)
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;
110 resource_manager::~resource_manager()
112 while (resource_list) {
113 resource *tem = resource_list;
114 resource_list = resource_list->next;
115 delete tem;
119 resource *resource_manager::lookup_resource(resource_type type,
120 string &name,
121 string &version,
122 unsigned revision)
124 for (resource *r = resource_list; r; r = r->next)
125 if (r->type == type
126 && r->name == name
127 && r->version == version
128 && r->revision == revision)
129 return r;
130 r = new resource(type, name, version, revision);
131 r->next = resource_list;
132 resource_list = r;
133 return r;
136 // Just a specialized version of lookup_resource().
138 resource *resource_manager::lookup_font(const char *name)
140 for (resource *r = resource_list; r; r = r->next)
141 if (r->type == RESOURCE_FONT
142 && strlen(name) == r->name.length()
143 && memcmp(name, r->name.contents(), r->name.length()) == 0)
144 return r;
145 string s(name);
146 r = new resource(RESOURCE_FONT, s);
147 r->next = resource_list;
148 resource_list = r;
149 return r;
152 void resource_manager::need_font(const char *name)
154 lookup_font(name)->flags |= resource::FONT_NEEDED;
157 typedef resource *Presource; // Work around g++ bug.
159 void resource_manager::document_setup(ps_output &out)
161 int nranks = 0;
162 for (resource *r = resource_list; r; r = r->next)
163 if (r->rank >= nranks)
164 nranks = r->rank + 1;
165 if (nranks > 0) {
166 // Sort resource_list in reverse order of rank.
167 Presource *head = new Presource[nranks + 1];
168 Presource **tail = new Presource *[nranks + 1];
169 for (int i = 0; i < nranks + 1; i++) {
170 head[i] = 0;
171 tail[i] = &head[i];
173 for (r = resource_list; r; r = r->next) {
174 i = r->rank < 0 ? 0 : r->rank + 1;
175 *tail[i] = r;
176 tail[i] = &(*tail[i])->next;
178 resource_list = 0;
179 for (i = 0; i < nranks + 1; i++)
180 if (head[i]) {
181 *tail[i] = resource_list;
182 resource_list = head[i];
184 a_delete head;
185 a_delete tail;
186 // check it
187 for (r = resource_list; r; r = r->next)
188 if (r->next)
189 assert(r->rank >= r->next->rank);
190 for (r = resource_list; r; r = r->next)
191 if (r->type == RESOURCE_FONT && r->rank >= 0)
192 supply_resource(r, -1, out.get_file());
196 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
198 int continued = 0;
199 for (resource *r = resource_list; r; r = r->next)
200 if (r->flags & flag) {
201 if (continued)
202 fputs("%%+ ", outfp);
203 else {
204 fputs(flag == resource::NEEDED
205 ? "%%DocumentNeededResources: "
206 : "%%DocumentSuppliedResources: ",
207 outfp);
208 continued = 1;
210 r->print_type_and_name(outfp);
211 putc('\n', outfp);
215 void resource_manager::print_header_comments(ps_output &out)
217 for (resource *r = resource_list; r; r = r->next)
218 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
219 supply_resource(r, 0, 0);
220 print_resources_comment(resource::NEEDED, out.get_file());
221 print_resources_comment(resource::SUPPLIED, out.get_file());
222 print_language_level_comment(out.get_file());
223 print_extensions_comment(out.get_file());
226 void resource_manager::output_prolog(ps_output &out)
228 FILE *outfp = out.get_file();
229 out.end_line();
230 char *path;
231 FILE *fp = font::open_file(PROLOGUE, &path);
232 if (!fp)
233 fatal("can't find `%1'", PROLOGUE);
234 fputs("%%BeginResource: ", outfp);
235 procset_resource->print_type_and_name(outfp);
236 putc('\n', outfp);
237 process_file(-1, fp, path, outfp);
238 fclose(fp);
239 a_delete path;
240 fputs("%%EndResource\n", outfp);
243 void resource_manager::import_file(const char *filename, ps_output &out)
245 out.end_line();
246 string name(filename);
247 resource *r = lookup_resource(RESOURCE_FILE, name);
248 supply_resource(r, -1, out.get_file(), 1);
251 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
252 int is_document)
254 if (r->flags & resource::BUSY) {
255 r->name += '\0';
256 fatal("loop detected in dependency graph for %1 `%2'",
257 resource_table[r->type],
258 r->name.contents());
260 r->flags |= resource::BUSY;
261 if (rank > r->rank)
262 r->rank = rank;
263 char *path;
264 FILE *fp = 0;
265 if (r->filename != 0) {
266 if (r->type == RESOURCE_FONT) {
267 fp = font::open_file(r->filename, &path);
268 if (!fp) {
269 error("can't find `%1'", r->filename);
270 a_delete r->filename;
271 r->filename = 0;
274 else {
275 errno = 0;
276 fp = fopen(r->filename, "r");
277 if (!fp) {
278 error("can't open `%1': %2", r->filename, strerror(errno));
279 a_delete r->filename;
280 r->filename = 0;
282 else
283 path = r->filename;
286 if (fp) {
287 if (outfp) {
288 if (r->type == RESOURCE_FILE && is_document) {
289 fputs("%%BeginDocument: ", outfp);
290 print_ps_string(r->name, outfp);
291 putc('\n', outfp);
293 else {
294 fputs("%%BeginResource: ", outfp);
295 r->print_type_and_name(outfp);
296 putc('\n', outfp);
299 process_file(rank, fp, path, outfp);
300 fclose(fp);
301 if (r->type == RESOURCE_FONT)
302 a_delete path;
303 if (outfp) {
304 if (r->type == RESOURCE_FILE && is_document)
305 fputs("%%EndDocument\n", outfp);
306 else
307 fputs("%%EndResource\n", outfp);
309 r->flags |= resource::SUPPLIED;
311 else {
312 if (outfp) {
313 if (r->type == RESOURCE_FILE && is_document) {
314 fputs("%%IncludeDocument: ", outfp);
315 print_ps_string(r->name, outfp);
316 putc('\n', outfp);
318 else {
319 fputs("%%IncludeResource: ", outfp);
320 r->print_type_and_name(outfp);
321 putc('\n', outfp);
324 r->flags |= resource::NEEDED;
326 r->flags &= ~resource::BUSY;
330 #define PS_LINE_MAX 255
331 #define PS_MAGIC "%!PS-Adobe-"
333 static int ps_get_line(char *buf, FILE *fp)
335 int c = getc(fp);
336 if (c == EOF) {
337 buf[0] = '\0';
338 return 0;
340 current_lineno++;
341 int i = 0;
342 int err = 0;
343 while (c != '\r' && c != '\n' && c != EOF) {
344 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
345 error("illegal input character code %1", int(c));
346 else if (i < PS_LINE_MAX)
347 buf[i++] = c;
348 else if (!err) {
349 err = 1;
350 error("PostScript file non-conforming "
351 "because length of line exceeds 255");
353 c = getc(fp);
355 buf[i++] = '\n';
356 buf[i] = '\0';
357 if (c == '\r') {
358 c = getc(fp);
359 if (c != EOF && c != '\n')
360 ungetc(c, fp);
362 return 1;
365 static int read_text_arg(const char **pp, string &res)
367 res.clear();
368 while (white_space(**pp))
369 *pp += 1;
370 if (**pp == '\0') {
371 error("missing argument");
372 return 0;
374 if (**pp != '(') {
375 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
376 res += **pp;
377 return 1;
379 *pp += 1;
380 res.clear();
381 int level = 0;
382 for (;;) {
383 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
384 error("missing ')'");
385 return 0;
387 if (**pp == ')') {
388 if (level == 0) {
389 *pp += 1;
390 break;
392 res += **pp;
393 level--;
395 else if (**pp == '(') {
396 level++;
397 res += **pp;
399 else if (**pp == '\\') {
400 *pp += 1;
401 switch (**pp) {
402 case 'n':
403 res += '\n';
404 break;
405 case 'r':
406 res += '\n';
407 break;
408 case 't':
409 res += '\t';
410 break;
411 case 'b':
412 res += '\b';
413 break;
414 case 'f':
415 res += '\f';
416 break;
417 case '0':
418 case '1':
419 case '2':
420 case '3':
421 case '4':
422 case '5':
423 case '6':
424 case '7':
426 int val = **pp - '0';
427 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
428 *pp += 1;
429 val = val*8 + (**pp - '0');
430 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
431 *pp += 1;
432 val = val*8 + (**pp - '0');
436 break;
437 default:
438 res += **pp;
439 break;
442 else
443 res += **pp;
444 *pp += 1;
446 return 1;
449 static int read_uint_arg(const char **pp, unsigned *res)
451 while (white_space(**pp))
452 *pp += 1;
453 if (**pp == '\0') {
454 error("missing argument");
455 return 0;
457 const char *start = *pp;
458 // XXX use strtoul
459 long n = strtol(start, (char **)pp, 10);
460 if (n == 0 && *pp == start) {
461 error("not an integer");
462 return 0;
464 if (n < 0) {
465 error("argument must not be negative");
466 return 0;
468 *res = unsigned(n);
469 return 1;
472 resource *resource_manager::read_file_arg(const char **ptr)
474 string arg;
475 if (!read_text_arg(ptr, arg))
476 return 0;
477 return lookup_resource(RESOURCE_FILE, arg);
480 resource *resource_manager::read_font_arg(const char **ptr)
482 string arg;
483 if (!read_text_arg(ptr, arg))
484 return 0;
485 return lookup_resource(RESOURCE_FONT, arg);
488 resource *resource_manager::read_procset_arg(const char **ptr)
490 string arg;
491 if (!read_text_arg(ptr, arg))
492 return 0;
493 string version;
494 if (!read_text_arg(ptr, version))
495 return 0;
496 unsigned revision;
497 if (!read_uint_arg(ptr, &revision))
498 return 0;
499 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
502 resource *resource_manager::read_resource_arg(const char **ptr)
504 while (white_space(**ptr))
505 *ptr += 1;
506 const char *name = *ptr;
507 while (**ptr != '\0' && !white_space(**ptr))
508 *ptr += 1;
509 if (name == *ptr) {
510 error("missing resource type");
511 return 0;
513 for (int ri = 0; ri < NRESOURCES; ri++)
514 if (strlen(resource_table[ri]) == *ptr - name
515 && memcmp(resource_table[ri], name, *ptr - name) == 0)
516 break;
517 if (ri >= NRESOURCES) {
518 error("unknown resource type");
519 return 0;
521 if (ri == RESOURCE_PROCSET)
522 return read_procset_arg(ptr);
523 string arg;
524 if (!read_text_arg(ptr, arg))
525 return 0;
526 return lookup_resource(resource_type(ri), arg);
529 static const char *matches_comment(const char *buf, const char *comment)
531 if (buf[0] != '%' || buf[1] != '%')
532 return 0;
533 for (buf += 2; *comment; comment++, buf++)
534 if (*buf != *comment)
535 return 0;
536 if (comment[-1] == ':')
537 return buf;
538 if (*buf == '\0' || white_space(*buf))
539 return buf;
540 return 0;
543 // Return 1 if the line should be copied out.
545 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
546 FILE *)
548 resource *r = read_resource_arg(&ptr);
549 if (r)
550 r->flags |= resource::SUPPLIED;
551 return 1;
554 int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
555 FILE *outfp)
557 resource *r = read_resource_arg(&ptr);
558 if (r) {
559 if (r->type == RESOURCE_FONT) {
560 if (rank >= 0)
561 supply_resource(r, rank + 1, outfp);
562 else
563 r->flags |= resource::FONT_NEEDED;
565 else
566 supply_resource(r, rank, outfp);
568 return 0;
571 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
572 FILE *)
574 resource *r = read_file_arg(&ptr);
575 if (r)
576 r->flags |= resource::SUPPLIED;
577 return 1;
580 int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
581 FILE *outfp)
583 resource *r = read_file_arg(&ptr);
584 if (r)
585 supply_resource(r, rank, outfp, 1);
586 return 0;
589 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
590 FILE *outfp)
592 resource *r = read_procset_arg(&ptr);
593 if (r) {
594 r->flags |= resource::SUPPLIED;
595 if (outfp) {
596 fputs("%%BeginResource: ", outfp);
597 r->print_type_and_name(outfp);
598 putc('\n', outfp);
601 return 0;
604 int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
605 FILE *outfp)
607 resource *r = read_procset_arg(&ptr);
608 if (r)
609 supply_resource(r, rank, outfp);
610 return 0;
613 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
614 FILE *outfp)
616 resource *r = read_file_arg(&ptr);
617 if (r) {
618 r->flags |= resource::SUPPLIED;
619 if (outfp) {
620 fputs("%%BeginResource: ", outfp);
621 r->print_type_and_name(outfp);
622 putc('\n', outfp);
625 return 0;
628 int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
629 FILE *outfp)
631 resource *r = read_file_arg(&ptr);
632 if (r)
633 supply_resource(r, rank, outfp);
634 return 0;
637 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
638 FILE *outfp)
640 resource *r = read_font_arg(&ptr);
641 if (r) {
642 r->flags |= resource::SUPPLIED;
643 if (outfp) {
644 fputs("%%BeginResource: ", outfp);
645 r->print_type_and_name(outfp);
646 putc('\n', outfp);
649 return 0;
652 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
653 FILE *outfp)
655 resource *r = read_font_arg(&ptr);
656 if (r) {
657 if (rank >= 0)
658 supply_resource(r, rank + 1, outfp);
659 else
660 r->flags |= resource::FONT_NEEDED;
662 return 0;
665 int resource_manager::change_to_end_resource(const char *, int, FILE *,
666 FILE *outfp)
668 if (outfp)
669 fputs("%%EndResource\n", outfp);
670 return 0;
673 int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
675 char buf[PS_LINE_MAX + 2];
676 do {
677 if (!ps_get_line(buf, fp)) {
678 error("end of file in preview section");
679 break;
681 } while (!matches_comment(buf, "EndPreview"));
682 return 0;
685 int read_one_of(const char **ptr, const char **s, int n)
687 while (white_space(**ptr))
688 *ptr += 1;
689 if (**ptr == '\0')
690 return -1;
691 const char *start = *ptr;
692 do {
693 ++ptr;
694 } while (**ptr != '\0' && !white_space(**ptr));
695 for (int i = 0; i < n; i++)
696 if (strlen(s[i]) == *ptr - start
697 && memcmp(s[i], start, *ptr - start) == 0)
698 return i;
699 return -1;
702 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
703 FILE *outfp)
705 while (white_space(*ptr))
706 ptr++;
707 const char *start = ptr;
708 unsigned numberof;
709 if (!read_uint_arg(&ptr, &numberof))
710 return 0;
711 static const char *types[] = { "Binary", "Hex", "ASCII" };
712 const int Binary = 0;
713 int type = 0;
714 static const char *units[] = { "Bytes", "Lines" };
715 const int Bytes = 0;
716 int unit = Bytes;
717 while (white_space(*ptr))
718 ptr++;
719 if (*ptr != '\0') {
720 type = read_one_of(&ptr, types, 3);
721 if (type < 0) {
722 error("bad data type");
723 return 0;
725 while (white_space(*ptr))
726 ptr++;
727 if (*ptr != '\0') {
728 unit = read_one_of(&ptr, units, 2);
729 if (unit < 0) {
730 error("expected `Bytes' or `Lines'");
731 return 0;
735 if (type != Binary)
736 return 1;
737 if (outfp) {
738 fputs("%%BeginData: ", outfp);
739 fputs(start, outfp);
741 if (numberof > 0) {
742 unsigned bytecount = 0;
743 unsigned linecount = 0;
744 do {
745 int c = getc(fp);
746 if (c == EOF) {
747 error("end of file within data section");
748 return 0;
750 if (outfp)
751 putc(c, outfp);
752 bytecount++;
753 if (c == '\r') {
754 int cc = getc(fp);
755 if (cc != '\n') {
756 linecount++;
757 current_lineno++;
759 if (cc != EOF)
760 ungetc(c, fp);
762 else if (c == '\n') {
763 linecount++;
764 current_lineno++;
766 } while ((unit == Bytes ? bytecount : linecount) < numberof);
768 char buf[PS_LINE_MAX + 2];
769 if (!ps_get_line(buf, fp)) {
770 error("missing %%%%EndData line");
771 return 0;
773 if (!matches_comment(buf, "EndData"))
774 error("bad %%%%EndData line");
775 if (outfp)
776 fputs(buf, outfp);
777 return 0;
780 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
781 FILE *outfp)
783 if (!outfp)
784 return 0;
785 unsigned count;
786 if (!read_uint_arg(&ptr, &count))
787 return 0;
788 if (outfp)
789 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
790 while (count != 0) {
791 int c = getc(fp);
792 if (c == EOF) {
793 error("end of file within binary section");
794 return 0;
796 if (outfp)
797 putc(c, outfp);
798 --count;
799 if (c == '\r') {
800 int cc = getc(fp);
801 if (cc != '\n')
802 current_lineno++;
803 if (cc != EOF)
804 ungetc(c, fp);
806 else if (c == '\n')
807 current_lineno++;
809 char buf[PS_LINE_MAX + 2];
810 if (!ps_get_line(buf, fp)) {
811 error("missing %%%%EndBinary line");
812 return 0;
814 if (!matches_comment(buf, "EndBinary")) {
815 error("bad %%%%EndBinary line");
816 if (outfp)
817 fputs(buf, outfp);
819 else if (outfp)
820 fputs("%%EndData\n", outfp);
821 return 0;
824 static unsigned parse_extensions(const char *ptr)
826 unsigned flags = 0;
827 for (;;) {
828 while (white_space(*ptr))
829 ptr++;
830 if (*ptr == '\0')
831 break;
832 const char *name = ptr;
833 do {
834 ++ptr;
835 } while (*ptr != '\0' && !white_space(*ptr));
836 for (int i = 0; i < NEXTENSIONS; i++)
837 if (strlen(extension_table[i]) == ptr - name
838 && memcmp(extension_table[i], name, ptr - name) == 0) {
839 flags |= (1 << i);
840 break;
842 if (i >= NEXTENSIONS) {
843 string s(name, ptr - name);
844 s += '\0';
845 error("unknown extension `%1'", s.contents());
848 return flags;
851 // XXX if it has not been surrounded with {Begin,End}Document need to strip
852 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
854 // XXX Perhaps the decision whether to use BeginDocument or
855 // BeginResource: file should be postponed till we have seen
856 // the first line of the file.
858 void resource_manager::process_file(int rank, FILE *fp, const char *filename,
859 FILE *outfp)
861 // If none of these comments appear in the header section, and we are
862 // just analyzing the file (ie outfp is 0), then we can return immediately.
863 static const char *header_comment_table[] = {
864 "DocumentNeededResources:",
865 "DocumentSuppliedResources:",
866 "DocumentNeededFonts:",
867 "DocumentSuppliedFonts:",
868 "DocumentNeededProcSets:",
869 "DocumentSuppliedProcSets:",
870 "DocumentNeededFiles:",
871 "DocumentSuppliedFiles:",
874 const int NHEADER_COMMENTS = (sizeof(header_comment_table)
875 / sizeof(header_comment_table[0]));
876 struct comment_info {
877 const char *name;
878 int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
881 static comment_info comment_table[] = {
882 "BeginResource:", &resource_manager::do_begin_resource,
883 "IncludeResource:", &resource_manager::do_include_resource,
884 "BeginDocument:", &resource_manager::do_begin_document,
885 "IncludeDocument:", &resource_manager::do_include_document,
886 "BeginProcSet:", &resource_manager::do_begin_procset,
887 "IncludeProcSet:", &resource_manager::do_include_procset,
888 "BeginFont:", &resource_manager::do_begin_font,
889 "IncludeFont:", &resource_manager::do_include_font,
890 "BeginFile:", &resource_manager::do_begin_file,
891 "IncludeFile:", &resource_manager::do_include_file,
892 "EndProcSet", &resource_manager::change_to_end_resource,
893 "EndFont", &resource_manager::change_to_end_resource,
894 "EndFile", &resource_manager::change_to_end_resource,
895 "BeginPreview:", &resource_manager::do_begin_preview,
896 "BeginData:", &resource_manager::do_begin_data,
897 "BeginBinary:", &resource_manager::do_begin_binary,
900 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
901 char buf[PS_LINE_MAX + 2];
902 int saved_lineno = current_lineno;
903 const char *saved_filename = current_filename;
904 current_filename = filename;
905 current_lineno = 0;
906 if (!ps_get_line(buf, fp)) {
907 current_filename = saved_filename;
908 current_lineno = saved_lineno;
909 return;
911 if (strlen(buf) < sizeof(PS_MAGIC) - 1
912 || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
913 if (outfp) {
914 do {
915 if (!(broken_flags & STRIP_PERCENT_BANG)
916 || buf[0] != '%' || buf[1] != '!')
917 fputs(buf, outfp);
918 } while (ps_get_line(buf, fp));
921 else {
922 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
923 fputs(buf, outfp);
924 int in_header = 1;
925 int interesting = 0;
926 int had_extensions_comment = 0;
927 int had_language_level_comment = 0;
928 for (;;) {
929 if (!ps_get_line(buf, fp))
930 break;
931 int copy_this_line = 1;
932 if (buf[0] == '%') {
933 if (buf[1] == '%') {
934 const char *ptr;
935 for (int i = 0; i < NCOMMENTS; i++)
936 if (ptr = matches_comment(buf, comment_table[i].name)) {
937 copy_this_line
938 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
939 break;
941 if (i >= NCOMMENTS && in_header) {
942 if (ptr = matches_comment(buf, "EndComments"))
943 in_header = 0;
944 else if (!had_extensions_comment
945 && (ptr = matches_comment(buf, "Extensions:"))) {
946 extensions |= parse_extensions(ptr);
947 // XXX handle possibility that next line is %%+
948 had_extensions_comment = 1;
950 else if (!had_language_level_comment
951 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
952 unsigned ll;
953 if (read_uint_arg(&ptr, &ll) && ll > language_level)
954 language_level = ll;
955 had_language_level_comment = 1;
957 else {
958 for (int i = 0; i < NHEADER_COMMENTS; i++)
959 if (matches_comment(buf, header_comment_table[i])) {
960 interesting = 1;
961 break;
965 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
966 && (matches_comment(buf, "EndProlog")
967 || matches_comment(buf, "Page:")
968 || matches_comment(buf, "Trailer")))
969 copy_this_line = 0;
971 else if (buf[1] == '!') {
972 if (broken_flags & STRIP_PERCENT_BANG)
973 copy_this_line = 0;
976 else
977 in_header = 0;
978 if (!outfp && !in_header && !interesting)
979 break;
980 if (copy_this_line && outfp)
981 fputs(buf, outfp);
984 current_filename = saved_filename;
985 current_lineno = saved_lineno;
988 void resource_manager::read_download_file()
990 char *path = 0;
991 FILE *fp = font::open_file("download", &path);
992 if (!fp)
993 fatal("can't find `download'");
994 char buf[512];
995 int lineno = 0;
996 while (fgets(buf, sizeof(buf), fp)) {
997 lineno++;
998 char *p = strtok(buf, " \t\r\n");
999 if (p == 0 || *p == '#')
1000 continue;
1001 char *q = strtok(0, " \t\r\n");
1002 if (!q)
1003 fatal_with_file_and_line(path, lineno, "missing filename");
1004 lookup_font(p)->filename = strsave(q);
1006 a_delete path;
1007 fclose(fp);
1010 // XXX Can we share some code with ps_output::put_string()?
1012 static void print_ps_string(const string &s, FILE *outfp)
1014 int len = s.length();
1015 const char *str = s.contents();
1016 int funny = 0;
1017 if (str[0] == '(')
1018 funny = 1;
1019 else {
1020 for (int i = 0; i < len; i++)
1021 if (str[i] <= 040 || str[i] > 0176) {
1022 funny = 1;
1023 break;
1026 if (!funny) {
1027 put_string(s, outfp);
1028 return;
1030 int level = 0;
1031 for (int i = 0; i < len; i++)
1032 if (str[i] == '(')
1033 level++;
1034 else if (str[i] == ')' && --level < 0)
1035 break;
1036 putc('(', outfp);
1037 for (i = 0; i < len; i++)
1038 switch (str[i]) {
1039 case '(':
1040 case ')':
1041 if (level != 0)
1042 putc('\\', outfp);
1043 putc(str[i], outfp);
1044 break;
1045 case '\\':
1046 fputs("\\\\", outfp);
1047 break;
1048 case '\n':
1049 fputs("\\n", outfp);
1050 break;
1051 case '\r':
1052 fputs("\\r", outfp);
1053 break;
1054 case '\t':
1055 fputs("\\t", outfp);
1056 break;
1057 case '\b':
1058 fputs("\\b", outfp);
1059 break;
1060 case '\f':
1061 fputs("\\f", outfp);
1062 break;
1063 default:
1064 if (str[i] < 040 || str[i] > 0176)
1065 fprintf(outfp, "\\%03o", str[i] & 0377);
1066 else
1067 putc(str[i], outfp);
1068 break;
1070 putc(')', outfp);
1073 void resource_manager::print_extensions_comment(FILE *outfp)
1075 if (extensions) {
1076 fputs("%%Extensions:", outfp);
1077 for (int i = 0; i < NEXTENSIONS; i++)
1078 if (extensions & (1 << i)) {
1079 putc(' ', outfp);
1080 fputs(extension_table[i], outfp);
1082 putc('\n', outfp);
1086 void resource_manager::print_language_level_comment(FILE *outfp)
1088 if (language_level)
1089 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);