groff before CVS: release 1.10
[s-roff.git] / grops / psrm.cc
blob3030aef8824949b983767e9eb4d1013a3539b607
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 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;
137 // Just a specialized version of lookup_resource().
139 resource *resource_manager::lookup_font(const char *name)
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;
154 void resource_manager::need_font(const char *name)
156 lookup_font(name)->flags |= resource::FONT_NEEDED;
159 typedef resource *Presource; // Work around g++ bug.
161 void resource_manager::document_setup(ps_output &out)
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];
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;
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];
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());
200 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
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;
214 r->print_type_and_name(outfp);
215 putc('\n', outfp);
219 void resource_manager::print_header_comments(ps_output &out)
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());
230 void resource_manager::output_prolog(ps_output &out)
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);
247 void resource_manager::import_file(const char *filename, ps_output &out)
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);
255 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
256 int is_document)
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());
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;
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;
286 else
287 path = r->filename;
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);
297 else {
298 fputs("%%BeginResource: ", outfp);
299 r->print_type_and_name(outfp);
300 putc('\n', outfp);
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);
313 r->flags |= resource::SUPPLIED;
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);
322 else {
323 fputs("%%IncludeResource: ", outfp);
324 r->print_type_and_name(outfp);
325 putc('\n', outfp);
328 r->flags |= resource::NEEDED;
330 r->flags &= ~resource::BUSY;
334 #define PS_LINE_MAX 255
335 #define PS_MAGIC "%!PS-Adobe-"
337 static int ps_get_line(char *buf, FILE *fp)
339 int c = getc(fp);
340 if (c == EOF) {
341 buf[0] = '\0';
342 return 0;
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");
357 c = getc(fp);
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);
366 return 1;
369 static int read_text_arg(const char **pp, string &res)
371 res.clear();
372 while (white_space(**pp))
373 *pp += 1;
374 if (**pp == '\0') {
375 error("missing argument");
376 return 0;
378 if (**pp != '(') {
379 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
380 res += **pp;
381 return 1;
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;
391 if (**pp == ')') {
392 if (level == 0) {
393 *pp += 1;
394 break;
396 res += **pp;
397 level--;
399 else if (**pp == '(') {
400 level++;
401 res += **pp;
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':
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');
440 break;
441 default:
442 res += **pp;
443 break;
446 else
447 res += **pp;
448 *pp += 1;
450 return 1;
453 static int read_uint_arg(const char **pp, unsigned *res)
455 while (white_space(**pp))
456 *pp += 1;
457 if (**pp == '\0') {
458 error("missing argument");
459 return 0;
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;
468 if (n < 0) {
469 error("argument must not be negative");
470 return 0;
472 *res = unsigned(n);
473 return 1;
476 resource *resource_manager::read_file_arg(const char **ptr)
478 string arg;
479 if (!read_text_arg(ptr, arg))
480 return 0;
481 return lookup_resource(RESOURCE_FILE, arg);
484 resource *resource_manager::read_font_arg(const char **ptr)
486 string arg;
487 if (!read_text_arg(ptr, arg))
488 return 0;
489 return lookup_resource(RESOURCE_FONT, arg);
492 resource *resource_manager::read_procset_arg(const char **ptr)
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);
506 resource *resource_manager::read_resource_arg(const char **ptr)
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;
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;
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);
534 static const char *matches_comment(const char *buf, const char *comment)
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;
548 // Return 1 if the line should be copied out.
550 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
551 FILE *)
553 resource *r = read_resource_arg(&ptr);
554 if (r)
555 r->flags |= resource::SUPPLIED;
556 return 1;
559 int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
560 FILE *outfp)
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;
570 else
571 supply_resource(r, rank, outfp);
573 return 0;
576 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
577 FILE *)
579 resource *r = read_file_arg(&ptr);
580 if (r)
581 r->flags |= resource::SUPPLIED;
582 return 1;
585 int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
586 FILE *outfp)
588 resource *r = read_file_arg(&ptr);
589 if (r)
590 supply_resource(r, rank, outfp, 1);
591 return 0;
594 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
595 FILE *outfp)
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);
606 return 0;
609 int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
610 FILE *outfp)
612 resource *r = read_procset_arg(&ptr);
613 if (r)
614 supply_resource(r, rank, outfp);
615 return 0;
618 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
619 FILE *outfp)
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);
630 return 0;
633 int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
634 FILE *outfp)
636 resource *r = read_file_arg(&ptr);
637 if (r)
638 supply_resource(r, rank, outfp);
639 return 0;
642 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
643 FILE *outfp)
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);
654 return 0;
657 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
658 FILE *outfp)
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;
667 return 0;
670 int resource_manager::change_to_end_resource(const char *, int, FILE *,
671 FILE *outfp)
673 if (outfp)
674 fputs("%%EndResource\n", outfp);
675 return 0;
678 int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
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;
686 } while (!matches_comment(buf, "EndPreview"));
687 return 0;
690 int read_one_of(const char **ptr, const char **s, int n)
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;
707 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
708 FILE *outfp)
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;
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;
740 if (type != Binary)
741 return 1;
742 if (outfp) {
743 fputs("%%BeginData: ", outfp);
744 fputs(start, outfp);
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;
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++;
764 if (cc != EOF)
765 ungetc(c, fp);
767 else if (c == '\n') {
768 linecount++;
769 current_lineno++;
771 } while ((unit == Bytes ? bytecount : linecount) < numberof);
773 char buf[PS_LINE_MAX + 2];
774 if (!ps_get_line(buf, fp)) {
775 error("missing %%%%EndData line");
776 return 0;
778 if (!matches_comment(buf, "EndData"))
779 error("bad %%%%EndData line");
780 if (outfp)
781 fputs(buf, outfp);
782 return 0;
785 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
786 FILE *outfp)
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;
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);
811 else if (c == '\n')
812 current_lineno++;
814 char buf[PS_LINE_MAX + 2];
815 if (!ps_get_line(buf, fp)) {
816 error("missing %%%%EndBinary line");
817 return 0;
819 if (!matches_comment(buf, "EndBinary")) {
820 error("bad %%%%EndBinary line");
821 if (outfp)
822 fputs(buf, outfp);
824 else if (outfp)
825 fputs("%%EndData\n", outfp);
826 return 0;
829 static unsigned parse_extensions(const char *ptr)
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;
848 if (i >= NEXTENSIONS) {
849 string s(name, ptr - name);
850 s += '\0';
851 error("unknown extension `%1'", s.contents());
854 return flags;
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.
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.
864 void resource_manager::process_file(int rank, FILE *fp, const char *filename,
865 FILE *outfp)
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:",
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 *);
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 },
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;
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));
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;
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;
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;
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;
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;
978 else if (buf[1] == '!') {
979 if (broken_flags & STRIP_PERCENT_BANG)
980 copy_this_line = 0;
983 else
984 in_header = 0;
985 if (!outfp && !in_header && !interesting)
986 break;
987 if (copy_this_line && outfp)
988 fputs(buf, outfp);
991 current_filename = saved_filename;
992 current_lineno = saved_lineno;
995 void resource_manager::read_download_file()
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);
1013 a_delete path;
1014 fclose(fp);
1017 // XXX Can we share some code with ps_output::put_string()?
1019 static void print_ps_string(const string &s, FILE *outfp)
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;
1033 if (!funny) {
1034 put_string(s, outfp);
1035 return;
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;
1078 putc(')', outfp);
1081 void resource_manager::print_extensions_comment(FILE *outfp)
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);
1090 putc('\n', outfp);
1094 void resource_manager::print_language_level_comment(FILE *outfp)
1096 if (language_level)
1097 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);