Adapt src/dev-html (src/devices/grohtml)
[s-roff.git] / src / devices / grops / psrm.cpp
blobdf7b078d0ae4f5838d6ea33e7c7d2c4461c8d5a2
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "driver.h"
23 #include "file_case.h"
24 #include "stringclass.h"
25 #include "cset.h"
27 #include "ps.h"
29 #ifdef NEED_DECLARATION_PUTENV
30 extern "C" {
31 int putenv(const char *);
33 #endif /* NEED_DECLARATION_PUTENV */
35 #define GROPS_PROLOGUE "prologue"
37 static void print_ps_string(const string &s, FILE *outfp);
39 cset white_space("\n\r \t\f");
40 string an_empty_string;
42 char valid_input_table[256]= {
43 #ifndef IS_EBCDIC_HOST
44 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1,
49 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
51 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
59 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
61 #else
62 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
63 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
67 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
68 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
69 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
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, 1,
76 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
78 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
79 #endif
82 const char *extension_table[] = {
83 "DPS",
84 "CMYK",
85 "Composite",
86 "FileSystem",
89 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
91 const char *resource_table[] = {
92 "font",
93 "procset",
94 "file",
95 "encoding",
96 "form",
97 "pattern",
100 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
102 static int read_uint_arg(const char **pp, unsigned *res)
104 while (white_space(**pp))
105 *pp += 1;
106 if (**pp == '\0') {
107 error("missing argument");
108 return 0;
110 const char *start = *pp;
111 // XXX use strtoul
112 long n = strtol(start, (char **)pp, 10);
113 if (n == 0 && *pp == start) {
114 error("not an integer");
115 return 0;
117 if (n < 0) {
118 error("argument must not be negative");
119 return 0;
121 *res = unsigned(n);
122 return 1;
125 struct resource {
126 resource *next;
127 resource_type type;
128 string name;
129 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
130 unsigned flags;
131 string version;
132 unsigned revision;
133 char *filename;
134 int rank;
135 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
136 ~resource();
137 void print_type_and_name(FILE *outfp);
140 resource::resource(resource_type t, string &n, string &v, unsigned r)
141 : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
143 name.move(n);
144 version.move(v);
145 if (type == RESOURCE_FILE) {
146 if (name.search('\0') >= 0)
147 error("filename contains a character with code 0");
148 filename = name.extract();
152 resource::~resource()
154 a_delete filename;
157 void resource::print_type_and_name(FILE *outfp)
159 fputs(resource_table[type], outfp);
160 putc(' ', outfp);
161 print_ps_string(name, outfp);
162 if (type == RESOURCE_PROCSET) {
163 putc(' ', outfp);
164 print_ps_string(version, outfp);
165 fprintf(outfp, " %u", revision);
169 resource_manager::resource_manager()
170 : extensions(0), language_level(0), resource_list(0)
172 read_download_file();
173 string procset_name("grops");
174 extern const char *version_string;
175 extern const char *revision_string;
176 unsigned revision_uint;
177 if (!read_uint_arg(&revision_string, &revision_uint))
178 revision_uint = 0;
179 string procset_version(version_string);
180 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
181 procset_version, revision_uint);
182 procset_resource->flags |= resource::SUPPLIED;
185 resource_manager::~resource_manager()
187 while (resource_list) {
188 resource *tem = resource_list;
189 resource_list = resource_list->next;
190 delete tem;
194 resource *resource_manager::lookup_resource(resource_type type,
195 string &name,
196 string &version,
197 unsigned revision)
199 resource *r;
200 for (r = resource_list; r; r = r->next)
201 if (r->type == type
202 && r->name == name
203 && r->version == version
204 && r->revision == revision)
205 return r;
206 r = new resource(type, name, version, revision);
207 r->next = resource_list;
208 resource_list = r;
209 return r;
212 // Just a specialized version of lookup_resource().
214 resource *resource_manager::lookup_font(const char *name)
216 resource *r;
217 for (r = resource_list; r; r = r->next)
218 if (r->type == RESOURCE_FONT
219 && strlen(name) == (size_t)r->name.length()
220 && memcmp(name, r->name.contents(), r->name.length()) == 0)
221 return r;
222 string s(name);
223 r = new resource(RESOURCE_FONT, s);
224 r->next = resource_list;
225 resource_list = r;
226 return r;
229 void resource_manager::need_font(const char *name)
231 lookup_font(name)->flags |= resource::FONT_NEEDED;
234 typedef resource *Presource; // Work around g++ bug.
236 void resource_manager::document_setup(ps_output &out)
238 int nranks = 0;
239 resource *r;
240 for (r = resource_list; r; r = r->next)
241 if (r->rank >= nranks)
242 nranks = r->rank + 1;
243 if (nranks > 0) {
244 // Sort resource_list in reverse order of rank.
245 Presource *head = new Presource[nranks + 1];
246 Presource **tail = new Presource *[nranks + 1];
247 int i;
248 for (i = 0; i < nranks + 1; i++) {
249 head[i] = 0;
250 tail[i] = &head[i];
252 for (r = resource_list; r; r = r->next) {
253 i = r->rank < 0 ? 0 : r->rank + 1;
254 *tail[i] = r;
255 tail[i] = &(*tail[i])->next;
257 resource_list = 0;
258 for (i = 0; i < nranks + 1; i++)
259 if (head[i]) {
260 *tail[i] = resource_list;
261 resource_list = head[i];
263 a_delete head;
264 a_delete tail;
265 // check it
266 for (r = resource_list; r; r = r->next)
267 if (r->next)
268 assert(r->rank >= r->next->rank);
269 for (r = resource_list; r; r = r->next)
270 if (r->type == RESOURCE_FONT && r->rank >= 0)
271 supply_resource(r, -1, out.get_file());
275 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
277 int continued = 0;
278 for (resource *r = resource_list; r; r = r->next)
279 if (r->flags & flag) {
280 if (continued)
281 fputs("%%+ ", outfp);
282 else {
283 fputs(flag == resource::NEEDED
284 ? "%%DocumentNeededResources: "
285 : "%%DocumentSuppliedResources: ",
286 outfp);
287 continued = 1;
289 r->print_type_and_name(outfp);
290 putc('\n', outfp);
294 void resource_manager::print_header_comments(ps_output &out)
296 for (resource *r = resource_list; r; r = r->next)
297 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
298 supply_resource(r, 0, 0);
299 print_resources_comment(resource::NEEDED, out.get_file());
300 print_resources_comment(resource::SUPPLIED, out.get_file());
301 print_language_level_comment(out.get_file());
302 print_extensions_comment(out.get_file());
305 void resource_manager::output_prolog(ps_output &out)
307 FILE *outfp = out.get_file();
308 out.end_line();
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");
317 char *prologue = getenv("GROPS_PROLOGUE");
319 file_case *fcp = font::open_file(prologue);
320 if (fcp == NULL)
321 fatal("can't find `%1'", prologue);
323 fputs("%%BeginResource: ", outfp);
324 procset_resource->print_type_and_name(outfp);
325 putc('\n', outfp);
326 process_file(-1, fcp, outfp);
327 fputs("%%EndResource\n", outfp);
329 delete fcp;
332 void resource_manager::import_file(const char *filename, ps_output &out)
334 out.end_line();
335 string name(filename);
336 resource *r = lookup_resource(RESOURCE_FILE, name);
337 supply_resource(r, -1, out.get_file(), 1);
340 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
341 int is_document)
343 if (r->flags & resource::BUSY) {
344 r->name += '\0';
345 fatal("loop detected in dependency graph for %1 `%2'",
346 resource_table[r->type],
347 r->name.contents());
349 r->flags |= resource::BUSY;
350 if (rank > r->rank)
351 r->rank = rank;
353 file_case *fcp = NULL;
354 if (r->filename != NULL) {
355 if (r->type == RESOURCE_FONT) {
356 if ((fcp = font::open_file(r->filename)) == NULL)
357 error("can't find `%1'", r->filename);
358 } else {
359 if ((fcp = include_search_path.open_file_cautious(r->filename)) == NULL)
360 error("can't open `%1': %2", r->filename, strerror(errno));
362 if (fcp == NULL) {
363 a_delete r->filename;
364 r->filename = NULL;
368 if (fcp != NULL) {
369 if (outfp) {
370 if (r->type == RESOURCE_FILE && is_document) {
371 fputs("%%BeginDocument: ", outfp);
372 print_ps_string(r->name, outfp);
373 putc('\n', outfp);
374 } else {
375 fputs("%%BeginResource: ", outfp);
376 r->print_type_and_name(outfp);
377 putc('\n', outfp);
380 process_file(rank, fcp, outfp);
381 delete fcp;
383 if (outfp) {
384 if (r->type == RESOURCE_FILE && is_document)
385 fputs("%%EndDocument\n", outfp);
386 else
387 fputs("%%EndResource\n", outfp);
389 r->flags |= resource::SUPPLIED;
390 } else {
391 if (outfp) {
392 if (r->type == RESOURCE_FILE && is_document) {
393 fputs("%%IncludeDocument: ", outfp);
394 print_ps_string(r->name, outfp);
395 putc('\n', outfp);
396 } else {
397 fputs("%%IncludeResource: ", outfp);
398 r->print_type_and_name(outfp);
399 putc('\n', outfp);
402 r->flags |= resource::NEEDED;
404 r->flags &= ~resource::BUSY;
407 #define PS_MAGIC "%!PS-Adobe-"
409 static int ps_get_line(string &buf, file_case *fcp)
411 buf.clear();
412 int c = fcp->get_c();
413 if (c == EOF)
414 return 0;
415 current_lineno++;
416 while (c != '\r' && c != '\n' && c != EOF) {
417 if (!valid_input_table[c])
418 error("invalid input character code %1", int(c));
419 buf += c;
420 c = fcp->get_c();
422 buf += '\n';
423 buf += '\0';
424 if (c == '\r') {
425 c = fcp->get_c();
426 if (c != EOF && c != '\n')
427 fcp->unget_c(c);
429 return 1;
432 static int read_text_arg(const char **pp, string &res)
434 res.clear();
435 while (white_space(**pp))
436 *pp += 1;
437 if (**pp == '\0') {
438 error("missing argument");
439 return 0;
441 if (**pp != '(') {
442 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
443 res += **pp;
444 return 1;
446 *pp += 1;
447 res.clear();
448 int level = 0;
449 for (;;) {
450 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
451 error("missing ')'");
452 return 0;
454 if (**pp == ')') {
455 if (level == 0) {
456 *pp += 1;
457 break;
459 res += **pp;
460 level--;
462 else if (**pp == '(') {
463 level++;
464 res += **pp;
466 else if (**pp == '\\') {
467 *pp += 1;
468 switch (**pp) {
469 case 'n':
470 res += '\n';
471 break;
472 case 'r':
473 res += '\n';
474 break;
475 case 't':
476 res += '\t';
477 break;
478 case 'b':
479 res += '\b';
480 break;
481 case 'f':
482 res += '\f';
483 break;
484 case '0':
485 case '1':
486 case '2':
487 case '3':
488 case '4':
489 case '5':
490 case '6':
491 case '7':
493 int val = **pp - '0';
494 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
495 *pp += 1;
496 val = val*8 + (**pp - '0');
497 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
498 *pp += 1;
499 val = val*8 + (**pp - '0');
503 break;
504 default:
505 res += **pp;
506 break;
509 else
510 res += **pp;
511 *pp += 1;
513 return 1;
516 resource *resource_manager::read_file_arg(const char **ptr)
518 string arg;
519 if (!read_text_arg(ptr, arg))
520 return 0;
521 return lookup_resource(RESOURCE_FILE, arg);
524 resource *resource_manager::read_font_arg(const char **ptr)
526 string arg;
527 if (!read_text_arg(ptr, arg))
528 return 0;
529 return lookup_resource(RESOURCE_FONT, arg);
532 resource *resource_manager::read_procset_arg(const char **ptr)
534 string arg;
535 if (!read_text_arg(ptr, arg))
536 return 0;
537 string version;
538 if (!read_text_arg(ptr, version))
539 return 0;
540 unsigned revision;
541 if (!read_uint_arg(ptr, &revision))
542 return 0;
543 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
546 resource *resource_manager::read_resource_arg(const char **ptr)
548 while (white_space(**ptr))
549 *ptr += 1;
550 const char *name = *ptr;
551 while (**ptr != '\0' && !white_space(**ptr))
552 *ptr += 1;
553 if (name == *ptr) {
554 error("missing resource type");
555 return 0;
557 int ri;
558 for (ri = 0; ri < NRESOURCES; ri++)
559 if (strlen(resource_table[ri]) == size_t(*ptr - name)
560 && memcmp(resource_table[ri], name, *ptr - name) == 0)
561 break;
562 if (ri >= NRESOURCES) {
563 error("unknown resource type");
564 return 0;
566 if (ri == RESOURCE_PROCSET)
567 return read_procset_arg(ptr);
568 string arg;
569 if (!read_text_arg(ptr, arg))
570 return 0;
571 return lookup_resource(resource_type(ri), arg);
574 static const char *matches_comment(string &buf, const char *comment)
576 if ((size_t)buf.length() < strlen(comment) + 3)
577 return 0;
578 if (buf[0] != '%' || buf[1] != '%')
579 return 0;
580 const char *bufp = buf.contents() + 2;
581 for (; *comment; comment++, bufp++)
582 if (*bufp != *comment)
583 return 0;
584 if (comment[-1] == ':')
585 return bufp;
586 if (*bufp == '\0' || white_space(*bufp))
587 return bufp;
588 return 0;
591 // Return 1 if the line should be copied out.
593 int resource_manager::do_begin_resource(const char *ptr, int, file_case *,
594 FILE *)
596 resource *r = read_resource_arg(&ptr);
597 if (r)
598 r->flags |= resource::SUPPLIED;
599 return 1;
602 int resource_manager::do_include_resource(const char *ptr, int rank,
603 file_case *, FILE *outfp)
605 resource *r = read_resource_arg(&ptr);
606 if (r) {
607 if (r->type == RESOURCE_FONT) {
608 if (rank >= 0)
609 supply_resource(r, rank + 1, outfp);
610 else
611 r->flags |= resource::FONT_NEEDED;
613 else
614 supply_resource(r, rank, outfp);
616 return 0;
619 int resource_manager::do_begin_document(const char *ptr, int, file_case *,
620 FILE *)
622 resource *r = read_file_arg(&ptr);
623 if (r)
624 r->flags |= resource::SUPPLIED;
625 return 1;
628 int resource_manager::do_include_document(const char *ptr, int rank,
629 file_case *, FILE *outfp)
631 resource *r = read_file_arg(&ptr);
632 if (r)
633 supply_resource(r, rank, outfp, 1);
634 return 0;
637 int resource_manager::do_begin_procset(const char *ptr, int, file_case *,
638 FILE *outfp)
640 resource *r = read_procset_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_procset(const char *ptr, int rank,
653 file_case *, FILE *outfp)
655 resource *r = read_procset_arg(&ptr);
656 if (r)
657 supply_resource(r, rank, outfp);
658 return 0;
661 int resource_manager::do_begin_file(const char *ptr, int, file_case *,
662 FILE *outfp)
664 resource *r = read_file_arg(&ptr);
665 if (r) {
666 r->flags |= resource::SUPPLIED;
667 if (outfp) {
668 fputs("%%BeginResource: ", outfp);
669 r->print_type_and_name(outfp);
670 putc('\n', outfp);
673 return 0;
676 int resource_manager::do_include_file(const char *ptr, int rank, file_case *,
677 FILE *outfp)
679 resource *r = read_file_arg(&ptr);
680 if (r)
681 supply_resource(r, rank, outfp);
682 return 0;
685 int resource_manager::do_begin_font(const char *ptr, int, file_case *,
686 FILE *outfp)
688 resource *r = read_font_arg(&ptr);
689 if (r) {
690 r->flags |= resource::SUPPLIED;
691 if (outfp) {
692 fputs("%%BeginResource: ", outfp);
693 r->print_type_and_name(outfp);
694 putc('\n', outfp);
697 return 0;
700 int resource_manager::do_include_font(const char *ptr, int rank, file_case *,
701 FILE *outfp)
703 resource *r = read_font_arg(&ptr);
704 if (r) {
705 if (rank >= 0)
706 supply_resource(r, rank + 1, outfp);
707 else
708 r->flags |= resource::FONT_NEEDED;
710 return 0;
713 int resource_manager::change_to_end_resource(const char *, int, file_case *,
714 FILE *outfp)
716 if (outfp)
717 fputs("%%EndResource\n", outfp);
718 return 0;
721 int resource_manager::do_begin_preview(const char *, int, file_case *fcp,
722 FILE *)
724 string buf;
725 do {
726 if (!ps_get_line(buf, fcp)) {
727 error("end of file in preview section");
728 break;
730 } while (!matches_comment(buf, "EndPreview"));
731 return 0;
734 int read_one_of(const char **ptr, const char **s, int n)
736 while (white_space(**ptr))
737 *ptr += 1;
738 if (**ptr == '\0')
739 return -1;
740 const char *start = *ptr;
741 do {
742 ++(*ptr);
743 } while (**ptr != '\0' && !white_space(**ptr));
744 for (int i = 0; i < n; i++)
745 if (strlen(s[i]) == size_t(*ptr - start)
746 && memcmp(s[i], start, *ptr - start) == 0)
747 return i;
748 return -1;
751 void skip_possible_newline(file_case *fcp, FILE *outfp)
753 int c = fcp->get_c();
754 if (c == '\r') {
755 current_lineno++;
756 if (outfp)
757 putc(c, outfp);
758 int cc = fcp->get_c();
759 if (cc != '\n') {
760 if (cc != EOF)
761 fcp->unget_c(cc);
763 else {
764 if (outfp)
765 putc(cc, outfp);
768 else if (c == '\n') {
769 current_lineno++;
770 if (outfp)
771 putc(c, outfp);
773 else if (c != EOF)
774 fcp->unget_c(c);
777 int resource_manager::do_begin_data(const char *ptr, int, file_case *fcp,
778 FILE *outfp)
780 while (white_space(*ptr))
781 ptr++;
782 const char *start = ptr;
783 unsigned numberof;
784 if (!read_uint_arg(&ptr, &numberof))
785 return 0;
786 static const char *types[] = { "Binary", "Hex", "ASCII" };
787 const int Binary = 0;
788 int type = 0;
789 static const char *units[] = { "Bytes", "Lines" };
790 const int Bytes = 0;
791 int unit = Bytes;
792 while (white_space(*ptr))
793 ptr++;
794 if (*ptr != '\0') {
795 type = read_one_of(&ptr, types, 3);
796 if (type < 0) {
797 error("bad data type");
798 return 0;
800 while (white_space(*ptr))
801 ptr++;
802 if (*ptr != '\0') {
803 unit = read_one_of(&ptr, units, 2);
804 if (unit < 0) {
805 error("expected `Bytes' or `Lines'");
806 return 0;
810 if (type != Binary)
811 return 1;
812 if (outfp) {
813 fputs("%%BeginData: ", outfp);
814 fputs(start, outfp);
816 if (numberof > 0) {
817 unsigned bytecount = 0;
818 unsigned linecount = 0;
819 do {
820 int c = fcp->get_c();
821 if (c == EOF) {
822 error("end of file within data section");
823 return 0;
825 if (outfp)
826 putc(c, outfp);
827 bytecount++;
828 if (c == '\r') {
829 int cc = fcp->get_c();
830 if (cc != '\n') {
831 linecount++;
832 current_lineno++;
834 if (cc != EOF)
835 fcp->unget_c(cc);
837 else if (c == '\n') {
838 linecount++;
839 current_lineno++;
841 } while ((unit == Bytes ? bytecount : linecount) < numberof);
843 skip_possible_newline(fcp, outfp);
844 string buf;
845 if (!ps_get_line(buf, fcp)) {
846 error("missing %%%%EndData line");
847 return 0;
849 if (!matches_comment(buf, "EndData"))
850 error("bad %%%%EndData line");
851 if (outfp)
852 fputs(buf.contents(), outfp);
853 return 0;
856 int resource_manager::do_begin_binary(const char *ptr, int, file_case *fcp,
857 FILE *outfp)
859 if (!outfp)
860 return 0;
861 unsigned count;
862 if (!read_uint_arg(&ptr, &count))
863 return 0;
864 if (outfp)
865 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
866 while (count != 0) {
867 int c = fcp->get_c();
868 if (c == EOF) {
869 error("end of file within binary section");
870 return 0;
872 if (outfp)
873 putc(c, outfp);
874 --count;
875 if (c == '\r') {
876 int cc = fcp->get_c();
877 if (cc != '\n')
878 current_lineno++;
879 if (cc != EOF)
880 fcp->unget_c(cc);
882 else if (c == '\n')
883 current_lineno++;
885 skip_possible_newline(fcp, outfp);
886 string buf;
887 if (!ps_get_line(buf, fcp)) {
888 error("missing %%%%EndBinary line");
889 return 0;
891 if (!matches_comment(buf, "EndBinary")) {
892 error("bad %%%%EndBinary line");
893 if (outfp)
894 fputs(buf.contents(), outfp);
896 else if (outfp)
897 fputs("%%EndData\n", outfp);
898 return 0;
901 static unsigned parse_extensions(const char *ptr)
903 unsigned flags = 0;
904 for (;;) {
905 while (white_space(*ptr))
906 ptr++;
907 if (*ptr == '\0')
908 break;
909 const char *name = ptr;
910 do {
911 ++ptr;
912 } while (*ptr != '\0' && !white_space(*ptr));
913 int i;
914 for (i = 0; i < NEXTENSIONS; i++)
915 if (strlen(extension_table[i]) == size_t(ptr - name)
916 && memcmp(extension_table[i], name, ptr - name) == 0) {
917 flags |= (1 << i);
918 break;
920 if (i >= NEXTENSIONS) {
921 string s(name, ptr - name);
922 s += '\0';
923 error("unknown extension `%1'", s.contents());
926 return flags;
929 // XXX if it has not been surrounded with {Begin,End}Document need to strip
930 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
932 // XXX Perhaps the decision whether to use BeginDocument or
933 // BeginResource: file should be postponed till we have seen
934 // the first line of the file.
936 void resource_manager::process_file(int rank, file_case *fcp, FILE *outfp)
938 // If none of these comments appear in the header section, and we are
939 // just analyzing the file (ie outfp is 0), then we can return immediately.
940 static const char *header_comment_table[] = {
941 "DocumentNeededResources:",
942 "DocumentSuppliedResources:",
943 "DocumentNeededFonts:",
944 "DocumentSuppliedFonts:",
945 "DocumentNeededProcSets:",
946 "DocumentSuppliedProcSets:",
947 "DocumentNeededFiles:",
948 "DocumentSuppliedFiles:",
951 const int NHEADER_COMMENTS = sizeof(header_comment_table)
952 / sizeof(header_comment_table[0]);
953 struct comment_info {
954 const char *name;
955 int (resource_manager::*proc)(const char *, int, file_case *, FILE *);
958 static comment_info comment_table[] = {
959 { "BeginResource:", &resource_manager::do_begin_resource },
960 { "IncludeResource:", &resource_manager::do_include_resource },
961 { "BeginDocument:", &resource_manager::do_begin_document },
962 { "IncludeDocument:", &resource_manager::do_include_document },
963 { "BeginProcSet:", &resource_manager::do_begin_procset },
964 { "IncludeProcSet:", &resource_manager::do_include_procset },
965 { "BeginFont:", &resource_manager::do_begin_font },
966 { "IncludeFont:", &resource_manager::do_include_font },
967 { "BeginFile:", &resource_manager::do_begin_file },
968 { "IncludeFile:", &resource_manager::do_include_file },
969 { "EndProcSet", &resource_manager::change_to_end_resource },
970 { "EndFont", &resource_manager::change_to_end_resource },
971 { "EndFile", &resource_manager::change_to_end_resource },
972 { "BeginPreview:", &resource_manager::do_begin_preview },
973 { "BeginData:", &resource_manager::do_begin_data },
974 { "BeginBinary:", &resource_manager::do_begin_binary },
977 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
978 string buf;
979 int saved_lineno = current_lineno;
980 const char *saved_filename = current_filename;
981 current_filename = fcp->path();
982 current_lineno = 0;
983 if (!ps_get_line(buf, fcp)) {
984 current_filename = saved_filename;
985 current_lineno = saved_lineno;
986 return;
988 if ((size_t)buf.length() < sizeof(PS_MAGIC)
989 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
990 if (outfp) {
991 do {
992 if (!(broken_flags & STRIP_PERCENT_BANG)
993 || buf[0] != '%' || buf[1] != '!')
994 fputs(buf.contents(), outfp);
995 } while (ps_get_line(buf, fcp));
998 else {
999 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
1000 fputs(buf.contents(), outfp);
1001 int in_header = 1;
1002 int interesting = 0;
1003 int had_extensions_comment = 0;
1004 int had_language_level_comment = 0;
1005 for (;;) {
1006 if (!ps_get_line(buf, fcp))
1007 break;
1008 int copy_this_line = 1;
1009 if (buf[0] == '%') {
1010 if (buf[1] == '%') {
1011 const char *ptr;
1012 int i;
1013 for (i = 0; i < NCOMMENTS; i++)
1014 if ((ptr = matches_comment(buf, comment_table[i].name))) {
1015 copy_this_line
1016 = (this->*(comment_table[i].proc))(ptr, rank, fcp, outfp);
1017 break;
1019 if (i >= NCOMMENTS && in_header) {
1020 if ((ptr = matches_comment(buf, "EndComments")))
1021 in_header = 0;
1022 else if (!had_extensions_comment
1023 && (ptr = matches_comment(buf, "Extensions:"))) {
1024 extensions |= parse_extensions(ptr);
1025 // XXX handle possibility that next line is %%+
1026 had_extensions_comment = 1;
1028 else if (!had_language_level_comment
1029 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
1030 unsigned ll;
1031 if (read_uint_arg(&ptr, &ll) && ll > language_level)
1032 language_level = ll;
1033 had_language_level_comment = 1;
1035 else {
1036 for (i = 0; i < NHEADER_COMMENTS; i++)
1037 if (matches_comment(buf, header_comment_table[i])) {
1038 interesting = 1;
1039 break;
1043 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
1044 && (matches_comment(buf, "EndProlog")
1045 || matches_comment(buf, "Page:")
1046 || matches_comment(buf, "Trailer")))
1047 copy_this_line = 0;
1049 else if (buf[1] == '!') {
1050 if (broken_flags & STRIP_PERCENT_BANG)
1051 copy_this_line = 0;
1054 else
1055 in_header = 0;
1056 if (!outfp && !in_header && !interesting)
1057 break;
1058 if (copy_this_line && outfp)
1059 fputs(buf.contents(), outfp);
1062 current_filename = saved_filename;
1063 current_lineno = saved_lineno;
1066 void resource_manager::read_download_file()
1068 file_case *fcp = font::open_file("download");
1069 if (fcp == NULL)
1070 fatal("can't find `download'");
1072 char buf[512];
1073 for (int lineno = 1; fcp->get_line(buf, sizeof(buf)) != NULL; ++lineno) {
1074 char *p = strtok(buf, " \t\r\n");
1075 if (p == NULL || *p == '#')
1076 continue;
1077 char *q = strtok(0, " \t\r\n");
1078 if (q == NULL)
1079 fatal_with_file_and_line(fcp->path(), lineno, "missing filename");
1080 lookup_font(p)->filename = strsave(q);
1083 delete fcp;
1086 // XXX Can we share some code with ps_output::put_string()?
1088 static void print_ps_string(const string &s, FILE *outfp)
1090 int len = s.length();
1091 const char *str = s.contents();
1092 int funny = 0;
1093 if (str[0] == '(')
1094 funny = 1;
1095 else {
1096 for (int i = 0; i < len; i++)
1097 if (str[i] <= 040 || str[i] > 0176) {
1098 funny = 1;
1099 break;
1102 if (!funny) {
1103 put_string(s, outfp);
1104 return;
1106 int level = 0;
1107 int i;
1108 for (i = 0; i < len; i++)
1109 if (str[i] == '(')
1110 level++;
1111 else if (str[i] == ')' && --level < 0)
1112 break;
1113 putc('(', outfp);
1114 for (i = 0; i < len; i++)
1115 switch (str[i]) {
1116 case '(':
1117 case ')':
1118 if (level != 0)
1119 putc('\\', outfp);
1120 putc(str[i], outfp);
1121 break;
1122 case '\\':
1123 fputs("\\\\", outfp);
1124 break;
1125 case '\n':
1126 fputs("\\n", outfp);
1127 break;
1128 case '\r':
1129 fputs("\\r", outfp);
1130 break;
1131 case '\t':
1132 fputs("\\t", outfp);
1133 break;
1134 case '\b':
1135 fputs("\\b", outfp);
1136 break;
1137 case '\f':
1138 fputs("\\f", outfp);
1139 break;
1140 default:
1141 if (str[i] < 040 || str[i] > 0176)
1142 fprintf(outfp, "\\%03o", str[i] & 0377);
1143 else
1144 putc(str[i], outfp);
1145 break;
1147 putc(')', outfp);
1150 void resource_manager::print_extensions_comment(FILE *outfp)
1152 if (extensions) {
1153 fputs("%%Extensions:", outfp);
1154 for (int i = 0; i < NEXTENSIONS; i++)
1155 if (extensions & (1 << i)) {
1156 putc(' ', outfp);
1157 fputs(extension_table[i], outfp);
1159 putc('\n', outfp);
1163 void resource_manager::print_language_level_comment(FILE *outfp)
1165 if (language_level)
1166 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);