* Makefile.comm (install_dev, uninstall_dev): Protect `for' loops
[s-roff.git] / src / devices / grops / psrm.cpp
blob03e38bb4a9b25f426af3c654a05c71687157965b
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include "driver.h"
23 #include "stringclass.h"
24 #include "cset.h"
26 #include "ps.h"
28 #ifdef NEED_DECLARATION_PUTENV
29 extern "C" {
30 int putenv(const char *);
32 #endif /* NEED_DECLARATION_PUTENV */
34 #define GROPS_PROLOGUE "prologue"
36 static void print_ps_string(const string &s, FILE *outfp);
38 cset white_space("\n\r \t\f");
39 string an_empty_string;
41 char valid_input_table[256]= {
42 #ifndef IS_EBCDIC_HOST
43 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
45 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
48 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 0,
52 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
53 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
54 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
55 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
56 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
57 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
58 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 #else
61 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
62 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
66 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
67 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,
70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
72 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
73 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 0,
78 #endif
81 const char *extension_table[] = {
82 "DPS",
83 "CMYK",
84 "Composite",
85 "FileSystem",
88 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
90 const char *resource_table[] = {
91 "font",
92 "procset",
93 "file",
94 "encoding",
95 "form",
96 "pattern",
99 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
101 static int read_uint_arg(const char **pp, unsigned *res)
103 while (white_space(**pp))
104 *pp += 1;
105 if (**pp == '\0') {
106 error("missing argument");
107 return 0;
109 const char *start = *pp;
110 // XXX use strtoul
111 long n = strtol(start, (char **)pp, 10);
112 if (n == 0 && *pp == start) {
113 error("not an integer");
114 return 0;
116 if (n < 0) {
117 error("argument must not be negative");
118 return 0;
120 *res = unsigned(n);
121 return 1;
124 struct resource {
125 resource *next;
126 resource_type type;
127 string name;
128 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
129 unsigned flags;
130 string version;
131 unsigned revision;
132 char *filename;
133 int rank;
134 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
135 ~resource();
136 void print_type_and_name(FILE *outfp);
139 resource::resource(resource_type t, string &n, string &v, unsigned r)
140 : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
142 name.move(n);
143 version.move(v);
144 if (type == RESOURCE_FILE) {
145 if (name.search('\0') >= 0)
146 error("filename contains a character with code 0");
147 filename = name.extract();
151 resource::~resource()
153 a_delete filename;
156 void resource::print_type_and_name(FILE *outfp)
158 fputs(resource_table[type], outfp);
159 putc(' ', outfp);
160 print_ps_string(name, outfp);
161 if (type == RESOURCE_PROCSET) {
162 putc(' ', outfp);
163 print_ps_string(version, outfp);
164 fprintf(outfp, " %u", revision);
168 resource_manager::resource_manager()
169 : extensions(0), language_level(0), resource_list(0)
171 read_download_file();
172 string procset_name("grops");
173 extern const char *version_string;
174 extern const char *revision_string;
175 unsigned revision_uint;
176 if (!read_uint_arg(&revision_string, &revision_uint))
177 revision_uint = 0;
178 string procset_version(version_string);
179 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
180 procset_version, revision_uint);
181 procset_resource->flags |= resource::SUPPLIED;
184 resource_manager::~resource_manager()
186 while (resource_list) {
187 resource *tem = resource_list;
188 resource_list = resource_list->next;
189 delete tem;
193 resource *resource_manager::lookup_resource(resource_type type,
194 string &name,
195 string &version,
196 unsigned revision)
198 resource *r;
199 for (r = resource_list; r; r = r->next)
200 if (r->type == type
201 && r->name == name
202 && r->version == version
203 && r->revision == revision)
204 return r;
205 r = new resource(type, name, version, revision);
206 r->next = resource_list;
207 resource_list = r;
208 return r;
211 // Just a specialized version of lookup_resource().
213 resource *resource_manager::lookup_font(const char *name)
215 resource *r;
216 for (r = resource_list; r; r = r->next)
217 if (r->type == RESOURCE_FONT
218 && strlen(name) == (size_t)r->name.length()
219 && memcmp(name, r->name.contents(), r->name.length()) == 0)
220 return r;
221 string s(name);
222 r = new resource(RESOURCE_FONT, s);
223 r->next = resource_list;
224 resource_list = r;
225 return r;
228 void resource_manager::need_font(const char *name)
230 lookup_font(name)->flags |= resource::FONT_NEEDED;
233 typedef resource *Presource; // Work around g++ bug.
235 void resource_manager::document_setup(ps_output &out)
237 int nranks = 0;
238 resource *r;
239 for (r = resource_list; r; r = r->next)
240 if (r->rank >= nranks)
241 nranks = r->rank + 1;
242 if (nranks > 0) {
243 // Sort resource_list in reverse order of rank.
244 Presource *head = new Presource[nranks + 1];
245 Presource **tail = new Presource *[nranks + 1];
246 int i;
247 for (i = 0; i < nranks + 1; i++) {
248 head[i] = 0;
249 tail[i] = &head[i];
251 for (r = resource_list; r; r = r->next) {
252 i = r->rank < 0 ? 0 : r->rank + 1;
253 *tail[i] = r;
254 tail[i] = &(*tail[i])->next;
256 resource_list = 0;
257 for (i = 0; i < nranks + 1; i++)
258 if (head[i]) {
259 *tail[i] = resource_list;
260 resource_list = head[i];
262 a_delete head;
263 a_delete tail;
264 // check it
265 for (r = resource_list; r; r = r->next)
266 if (r->next)
267 assert(r->rank >= r->next->rank);
268 for (r = resource_list; r; r = r->next)
269 if (r->type == RESOURCE_FONT && r->rank >= 0)
270 supply_resource(r, -1, out.get_file());
274 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
276 int continued = 0;
277 for (resource *r = resource_list; r; r = r->next)
278 if (r->flags & flag) {
279 if (continued)
280 fputs("%%+ ", outfp);
281 else {
282 fputs(flag == resource::NEEDED
283 ? "%%DocumentNeededResources: "
284 : "%%DocumentSuppliedResources: ",
285 outfp);
286 continued = 1;
288 r->print_type_and_name(outfp);
289 putc('\n', outfp);
293 void resource_manager::print_header_comments(ps_output &out)
295 for (resource *r = resource_list; r; r = r->next)
296 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
297 supply_resource(r, 0, 0);
298 print_resources_comment(resource::NEEDED, out.get_file());
299 print_resources_comment(resource::SUPPLIED, out.get_file());
300 print_language_level_comment(out.get_file());
301 print_extensions_comment(out.get_file());
304 void resource_manager::output_prolog(ps_output &out)
306 FILE *outfp = out.get_file();
307 out.end_line();
308 char *path;
309 if (!getenv("GROPS_PROLOGUE")) {
310 string e = "GROPS_PROLOGUE";
311 e += '=';
312 e += GROPS_PROLOGUE;
313 e += '\0';
314 if (putenv(strsave(e.contents())))
315 fatal("putenv failed");
317 char *prologue = getenv("GROPS_PROLOGUE");
318 FILE *fp = font::open_file(prologue, &path);
319 if (!fp)
320 fatal("can't find `%1'", prologue);
321 fputs("%%BeginResource: ", outfp);
322 procset_resource->print_type_and_name(outfp);
323 putc('\n', outfp);
324 process_file(-1, fp, path, outfp);
325 fclose(fp);
326 a_delete path;
327 fputs("%%EndResource\n", outfp);
330 void resource_manager::import_file(const char *filename, ps_output &out)
332 out.end_line();
333 string name(filename);
334 resource *r = lookup_resource(RESOURCE_FILE, name);
335 supply_resource(r, -1, out.get_file(), 1);
338 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
339 int is_document)
341 if (r->flags & resource::BUSY) {
342 r->name += '\0';
343 fatal("loop detected in dependency graph for %1 `%2'",
344 resource_table[r->type],
345 r->name.contents());
347 r->flags |= resource::BUSY;
348 if (rank > r->rank)
349 r->rank = rank;
350 char *path;
351 FILE *fp = 0;
352 if (r->filename != 0) {
353 if (r->type == RESOURCE_FONT) {
354 fp = font::open_file(r->filename, &path);
355 if (!fp) {
356 error("can't find `%1'", r->filename);
357 a_delete r->filename;
358 r->filename = 0;
361 else {
362 errno = 0;
363 fp = include_search_path.open_file_cautious(r->filename);
364 if (!fp) {
365 error("can't open `%1': %2", r->filename, strerror(errno));
366 a_delete r->filename;
367 r->filename = 0;
369 else
370 path = r->filename;
373 if (fp) {
374 if (outfp) {
375 if (r->type == RESOURCE_FILE && is_document) {
376 fputs("%%BeginDocument: ", outfp);
377 print_ps_string(r->name, outfp);
378 putc('\n', outfp);
380 else {
381 fputs("%%BeginResource: ", outfp);
382 r->print_type_and_name(outfp);
383 putc('\n', outfp);
386 process_file(rank, fp, path, outfp);
387 fclose(fp);
388 if (r->type == RESOURCE_FONT)
389 a_delete path;
390 if (outfp) {
391 if (r->type == RESOURCE_FILE && is_document)
392 fputs("%%EndDocument\n", outfp);
393 else
394 fputs("%%EndResource\n", outfp);
396 r->flags |= resource::SUPPLIED;
398 else {
399 if (outfp) {
400 if (r->type == RESOURCE_FILE && is_document) {
401 fputs("%%IncludeDocument: ", outfp);
402 print_ps_string(r->name, outfp);
403 putc('\n', outfp);
405 else {
406 fputs("%%IncludeResource: ", outfp);
407 r->print_type_and_name(outfp);
408 putc('\n', outfp);
411 r->flags |= resource::NEEDED;
413 r->flags &= ~resource::BUSY;
416 #define PS_MAGIC "%!PS-Adobe-"
418 static int ps_get_line(string &buf, FILE *fp)
420 buf.clear();
421 int c = getc(fp);
422 if (c == EOF)
423 return 0;
424 current_lineno++;
425 while (c != '\r' && c != '\n' && c != EOF) {
426 if (!valid_input_table[c])
427 error("invalid input character code %1", int(c));
428 buf += c;
429 c = getc(fp);
431 buf += '\n';
432 buf += '\0';
433 if (c == '\r') {
434 c = getc(fp);
435 if (c != EOF && c != '\n')
436 ungetc(c, fp);
438 return 1;
441 static int read_text_arg(const char **pp, string &res)
443 res.clear();
444 while (white_space(**pp))
445 *pp += 1;
446 if (**pp == '\0') {
447 error("missing argument");
448 return 0;
450 if (**pp != '(') {
451 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
452 res += **pp;
453 return 1;
455 *pp += 1;
456 res.clear();
457 int level = 0;
458 for (;;) {
459 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
460 error("missing ')'");
461 return 0;
463 if (**pp == ')') {
464 if (level == 0) {
465 *pp += 1;
466 break;
468 res += **pp;
469 level--;
471 else if (**pp == '(') {
472 level++;
473 res += **pp;
475 else if (**pp == '\\') {
476 *pp += 1;
477 switch (**pp) {
478 case 'n':
479 res += '\n';
480 break;
481 case 'r':
482 res += '\n';
483 break;
484 case 't':
485 res += '\t';
486 break;
487 case 'b':
488 res += '\b';
489 break;
490 case 'f':
491 res += '\f';
492 break;
493 case '0':
494 case '1':
495 case '2':
496 case '3':
497 case '4':
498 case '5':
499 case '6':
500 case '7':
502 int val = **pp - '0';
503 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
504 *pp += 1;
505 val = val*8 + (**pp - '0');
506 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
507 *pp += 1;
508 val = val*8 + (**pp - '0');
512 break;
513 default:
514 res += **pp;
515 break;
518 else
519 res += **pp;
520 *pp += 1;
522 return 1;
525 resource *resource_manager::read_file_arg(const char **ptr)
527 string arg;
528 if (!read_text_arg(ptr, arg))
529 return 0;
530 return lookup_resource(RESOURCE_FILE, arg);
533 resource *resource_manager::read_font_arg(const char **ptr)
535 string arg;
536 if (!read_text_arg(ptr, arg))
537 return 0;
538 return lookup_resource(RESOURCE_FONT, arg);
541 resource *resource_manager::read_procset_arg(const char **ptr)
543 string arg;
544 if (!read_text_arg(ptr, arg))
545 return 0;
546 string version;
547 if (!read_text_arg(ptr, version))
548 return 0;
549 unsigned revision;
550 if (!read_uint_arg(ptr, &revision))
551 return 0;
552 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
555 resource *resource_manager::read_resource_arg(const char **ptr)
557 while (white_space(**ptr))
558 *ptr += 1;
559 const char *name = *ptr;
560 while (**ptr != '\0' && !white_space(**ptr))
561 *ptr += 1;
562 if (name == *ptr) {
563 error("missing resource type");
564 return 0;
566 int ri;
567 for (ri = 0; ri < NRESOURCES; ri++)
568 if (strlen(resource_table[ri]) == size_t(*ptr - name)
569 && memcmp(resource_table[ri], name, *ptr - name) == 0)
570 break;
571 if (ri >= NRESOURCES) {
572 error("unknown resource type");
573 return 0;
575 if (ri == RESOURCE_PROCSET)
576 return read_procset_arg(ptr);
577 string arg;
578 if (!read_text_arg(ptr, arg))
579 return 0;
580 return lookup_resource(resource_type(ri), arg);
583 static const char *matches_comment(string &buf, const char *comment)
585 if ((size_t)buf.length() < strlen(comment) + 3)
586 return 0;
587 if (buf[0] != '%' || buf[1] != '%')
588 return 0;
589 const char *bufp = buf.contents() + 2;
590 for (; *comment; comment++, bufp++)
591 if (*bufp != *comment)
592 return 0;
593 if (comment[-1] == ':')
594 return bufp;
595 if (*bufp == '\0' || white_space(*bufp))
596 return bufp;
597 return 0;
600 // Return 1 if the line should be copied out.
602 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
603 FILE *)
605 resource *r = read_resource_arg(&ptr);
606 if (r)
607 r->flags |= resource::SUPPLIED;
608 return 1;
611 int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
612 FILE *outfp)
614 resource *r = read_resource_arg(&ptr);
615 if (r) {
616 if (r->type == RESOURCE_FONT) {
617 if (rank >= 0)
618 supply_resource(r, rank + 1, outfp);
619 else
620 r->flags |= resource::FONT_NEEDED;
622 else
623 supply_resource(r, rank, outfp);
625 return 0;
628 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
629 FILE *)
631 resource *r = read_file_arg(&ptr);
632 if (r)
633 r->flags |= resource::SUPPLIED;
634 return 1;
637 int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
638 FILE *outfp)
640 resource *r = read_file_arg(&ptr);
641 if (r)
642 supply_resource(r, rank, outfp, 1);
643 return 0;
646 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
647 FILE *outfp)
649 resource *r = read_procset_arg(&ptr);
650 if (r) {
651 r->flags |= resource::SUPPLIED;
652 if (outfp) {
653 fputs("%%BeginResource: ", outfp);
654 r->print_type_and_name(outfp);
655 putc('\n', outfp);
658 return 0;
661 int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
662 FILE *outfp)
664 resource *r = read_procset_arg(&ptr);
665 if (r)
666 supply_resource(r, rank, outfp);
667 return 0;
670 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
671 FILE *outfp)
673 resource *r = read_file_arg(&ptr);
674 if (r) {
675 r->flags |= resource::SUPPLIED;
676 if (outfp) {
677 fputs("%%BeginResource: ", outfp);
678 r->print_type_and_name(outfp);
679 putc('\n', outfp);
682 return 0;
685 int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
686 FILE *outfp)
688 resource *r = read_file_arg(&ptr);
689 if (r)
690 supply_resource(r, rank, outfp);
691 return 0;
694 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
695 FILE *outfp)
697 resource *r = read_font_arg(&ptr);
698 if (r) {
699 r->flags |= resource::SUPPLIED;
700 if (outfp) {
701 fputs("%%BeginResource: ", outfp);
702 r->print_type_and_name(outfp);
703 putc('\n', outfp);
706 return 0;
709 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
710 FILE *outfp)
712 resource *r = read_font_arg(&ptr);
713 if (r) {
714 if (rank >= 0)
715 supply_resource(r, rank + 1, outfp);
716 else
717 r->flags |= resource::FONT_NEEDED;
719 return 0;
722 int resource_manager::change_to_end_resource(const char *, int, FILE *,
723 FILE *outfp)
725 if (outfp)
726 fputs("%%EndResource\n", outfp);
727 return 0;
730 int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
732 string buf;
733 do {
734 if (!ps_get_line(buf, fp)) {
735 error("end of file in preview section");
736 break;
738 } while (!matches_comment(buf, "EndPreview"));
739 return 0;
742 int read_one_of(const char **ptr, const char **s, int n)
744 while (white_space(**ptr))
745 *ptr += 1;
746 if (**ptr == '\0')
747 return -1;
748 const char *start = *ptr;
749 do {
750 ++(*ptr);
751 } while (**ptr != '\0' && !white_space(**ptr));
752 for (int i = 0; i < n; i++)
753 if (strlen(s[i]) == size_t(*ptr - start)
754 && memcmp(s[i], start, *ptr - start) == 0)
755 return i;
756 return -1;
759 void skip_possible_newline(FILE *fp, FILE *outfp)
761 int c = getc(fp);
762 if (c == '\r') {
763 current_lineno++;
764 if (outfp)
765 putc(c, outfp);
766 int cc = getc(fp);
767 if (cc != '\n') {
768 if (cc != EOF)
769 ungetc(cc, fp);
771 else {
772 if (outfp)
773 putc(cc, outfp);
776 else if (c == '\n') {
777 current_lineno++;
778 if (outfp)
779 putc(c, outfp);
781 else if (c != EOF)
782 ungetc(c, fp);
785 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
786 FILE *outfp)
788 while (white_space(*ptr))
789 ptr++;
790 const char *start = ptr;
791 unsigned numberof;
792 if (!read_uint_arg(&ptr, &numberof))
793 return 0;
794 static const char *types[] = { "Binary", "Hex", "ASCII" };
795 const int Binary = 0;
796 int type = 0;
797 static const char *units[] = { "Bytes", "Lines" };
798 const int Bytes = 0;
799 int unit = Bytes;
800 while (white_space(*ptr))
801 ptr++;
802 if (*ptr != '\0') {
803 type = read_one_of(&ptr, types, 3);
804 if (type < 0) {
805 error("bad data type");
806 return 0;
808 while (white_space(*ptr))
809 ptr++;
810 if (*ptr != '\0') {
811 unit = read_one_of(&ptr, units, 2);
812 if (unit < 0) {
813 error("expected `Bytes' or `Lines'");
814 return 0;
818 if (type != Binary)
819 return 1;
820 if (outfp) {
821 fputs("%%BeginData: ", outfp);
822 fputs(start, outfp);
824 if (numberof > 0) {
825 unsigned bytecount = 0;
826 unsigned linecount = 0;
827 do {
828 int c = getc(fp);
829 if (c == EOF) {
830 error("end of file within data section");
831 return 0;
833 if (outfp)
834 putc(c, outfp);
835 bytecount++;
836 if (c == '\r') {
837 int cc = getc(fp);
838 if (cc != '\n') {
839 linecount++;
840 current_lineno++;
842 if (cc != EOF)
843 ungetc(c, fp);
845 else if (c == '\n') {
846 linecount++;
847 current_lineno++;
849 } while ((unit == Bytes ? bytecount : linecount) < numberof);
851 skip_possible_newline(fp, outfp);
852 string buf;
853 if (!ps_get_line(buf, fp)) {
854 error("missing %%%%EndData line");
855 return 0;
857 if (!matches_comment(buf, "EndData"))
858 error("bad %%%%EndData line");
859 if (outfp)
860 fputs(buf.contents(), outfp);
861 return 0;
864 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
865 FILE *outfp)
867 if (!outfp)
868 return 0;
869 unsigned count;
870 if (!read_uint_arg(&ptr, &count))
871 return 0;
872 if (outfp)
873 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
874 while (count != 0) {
875 int c = getc(fp);
876 if (c == EOF) {
877 error("end of file within binary section");
878 return 0;
880 if (outfp)
881 putc(c, outfp);
882 --count;
883 if (c == '\r') {
884 int cc = getc(fp);
885 if (cc != '\n')
886 current_lineno++;
887 if (cc != EOF)
888 ungetc(cc, fp);
890 else if (c == '\n')
891 current_lineno++;
893 skip_possible_newline(fp, outfp);
894 string buf;
895 if (!ps_get_line(buf, fp)) {
896 error("missing %%%%EndBinary line");
897 return 0;
899 if (!matches_comment(buf, "EndBinary")) {
900 error("bad %%%%EndBinary line");
901 if (outfp)
902 fputs(buf.contents(), outfp);
904 else if (outfp)
905 fputs("%%EndData\n", outfp);
906 return 0;
909 static unsigned parse_extensions(const char *ptr)
911 unsigned flags = 0;
912 for (;;) {
913 while (white_space(*ptr))
914 ptr++;
915 if (*ptr == '\0')
916 break;
917 const char *name = ptr;
918 do {
919 ++ptr;
920 } while (*ptr != '\0' && !white_space(*ptr));
921 int i;
922 for (i = 0; i < NEXTENSIONS; i++)
923 if (strlen(extension_table[i]) == size_t(ptr - name)
924 && memcmp(extension_table[i], name, ptr - name) == 0) {
925 flags |= (1 << i);
926 break;
928 if (i >= NEXTENSIONS) {
929 string s(name, ptr - name);
930 s += '\0';
931 error("unknown extension `%1'", s.contents());
934 return flags;
937 // XXX if it has not been surrounded with {Begin,End}Document need to strip
938 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
940 // XXX Perhaps the decision whether to use BeginDocument or
941 // BeginResource: file should be postponed till we have seen
942 // the first line of the file.
944 void resource_manager::process_file(int rank, FILE *fp, const char *filename,
945 FILE *outfp)
947 // If none of these comments appear in the header section, and we are
948 // just analyzing the file (ie outfp is 0), then we can return immediately.
949 static const char *header_comment_table[] = {
950 "DocumentNeededResources:",
951 "DocumentSuppliedResources:",
952 "DocumentNeededFonts:",
953 "DocumentSuppliedFonts:",
954 "DocumentNeededProcSets:",
955 "DocumentSuppliedProcSets:",
956 "DocumentNeededFiles:",
957 "DocumentSuppliedFiles:",
960 const int NHEADER_COMMENTS = sizeof(header_comment_table)
961 / sizeof(header_comment_table[0]);
962 struct comment_info {
963 const char *name;
964 int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
967 static comment_info comment_table[] = {
968 { "BeginResource:", &resource_manager::do_begin_resource },
969 { "IncludeResource:", &resource_manager::do_include_resource },
970 { "BeginDocument:", &resource_manager::do_begin_document },
971 { "IncludeDocument:", &resource_manager::do_include_document },
972 { "BeginProcSet:", &resource_manager::do_begin_procset },
973 { "IncludeProcSet:", &resource_manager::do_include_procset },
974 { "BeginFont:", &resource_manager::do_begin_font },
975 { "IncludeFont:", &resource_manager::do_include_font },
976 { "BeginFile:", &resource_manager::do_begin_file },
977 { "IncludeFile:", &resource_manager::do_include_file },
978 { "EndProcSet", &resource_manager::change_to_end_resource },
979 { "EndFont", &resource_manager::change_to_end_resource },
980 { "EndFile", &resource_manager::change_to_end_resource },
981 { "BeginPreview:", &resource_manager::do_begin_preview },
982 { "BeginData:", &resource_manager::do_begin_data },
983 { "BeginBinary:", &resource_manager::do_begin_binary },
986 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
987 string buf;
988 int saved_lineno = current_lineno;
989 const char *saved_filename = current_filename;
990 current_filename = filename;
991 current_lineno = 0;
992 if (!ps_get_line(buf, fp)) {
993 current_filename = saved_filename;
994 current_lineno = saved_lineno;
995 return;
997 if ((size_t)buf.length() < sizeof(PS_MAGIC)
998 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
999 if (outfp) {
1000 do {
1001 if (!(broken_flags & STRIP_PERCENT_BANG)
1002 || buf[0] != '%' || buf[1] != '!')
1003 fputs(buf.contents(), outfp);
1004 } while (ps_get_line(buf, fp));
1007 else {
1008 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
1009 fputs(buf.contents(), outfp);
1010 int in_header = 1;
1011 int interesting = 0;
1012 int had_extensions_comment = 0;
1013 int had_language_level_comment = 0;
1014 for (;;) {
1015 if (!ps_get_line(buf, fp))
1016 break;
1017 int copy_this_line = 1;
1018 if (buf[0] == '%') {
1019 if (buf[1] == '%') {
1020 const char *ptr;
1021 int i;
1022 for (i = 0; i < NCOMMENTS; i++)
1023 if ((ptr = matches_comment(buf, comment_table[i].name))) {
1024 copy_this_line
1025 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
1026 break;
1028 if (i >= NCOMMENTS && in_header) {
1029 if ((ptr = matches_comment(buf, "EndComments")))
1030 in_header = 0;
1031 else if (!had_extensions_comment
1032 && (ptr = matches_comment(buf, "Extensions:"))) {
1033 extensions |= parse_extensions(ptr);
1034 // XXX handle possibility that next line is %%+
1035 had_extensions_comment = 1;
1037 else if (!had_language_level_comment
1038 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
1039 unsigned ll;
1040 if (read_uint_arg(&ptr, &ll) && ll > language_level)
1041 language_level = ll;
1042 had_language_level_comment = 1;
1044 else {
1045 for (i = 0; i < NHEADER_COMMENTS; i++)
1046 if (matches_comment(buf, header_comment_table[i])) {
1047 interesting = 1;
1048 break;
1052 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
1053 && (matches_comment(buf, "EndProlog")
1054 || matches_comment(buf, "Page:")
1055 || matches_comment(buf, "Trailer")))
1056 copy_this_line = 0;
1058 else if (buf[1] == '!') {
1059 if (broken_flags & STRIP_PERCENT_BANG)
1060 copy_this_line = 0;
1063 else
1064 in_header = 0;
1065 if (!outfp && !in_header && !interesting)
1066 break;
1067 if (copy_this_line && outfp)
1068 fputs(buf.contents(), outfp);
1071 current_filename = saved_filename;
1072 current_lineno = saved_lineno;
1075 void resource_manager::read_download_file()
1077 char *path = 0;
1078 FILE *fp = font::open_file("download", &path);
1079 if (!fp)
1080 fatal("can't find `download'");
1081 char buf[512];
1082 int lineno = 0;
1083 while (fgets(buf, sizeof(buf), fp)) {
1084 lineno++;
1085 char *p = strtok(buf, " \t\r\n");
1086 if (p == 0 || *p == '#')
1087 continue;
1088 char *q = strtok(0, " \t\r\n");
1089 if (!q)
1090 fatal_with_file_and_line(path, lineno, "missing filename");
1091 lookup_font(p)->filename = strsave(q);
1093 a_delete path;
1094 fclose(fp);
1097 // XXX Can we share some code with ps_output::put_string()?
1099 static void print_ps_string(const string &s, FILE *outfp)
1101 int len = s.length();
1102 const char *str = s.contents();
1103 int funny = 0;
1104 if (str[0] == '(')
1105 funny = 1;
1106 else {
1107 for (int i = 0; i < len; i++)
1108 if (str[i] <= 040 || str[i] > 0176) {
1109 funny = 1;
1110 break;
1113 if (!funny) {
1114 put_string(s, outfp);
1115 return;
1117 int level = 0;
1118 int i;
1119 for (i = 0; i < len; i++)
1120 if (str[i] == '(')
1121 level++;
1122 else if (str[i] == ')' && --level < 0)
1123 break;
1124 putc('(', outfp);
1125 for (i = 0; i < len; i++)
1126 switch (str[i]) {
1127 case '(':
1128 case ')':
1129 if (level != 0)
1130 putc('\\', outfp);
1131 putc(str[i], outfp);
1132 break;
1133 case '\\':
1134 fputs("\\\\", outfp);
1135 break;
1136 case '\n':
1137 fputs("\\n", outfp);
1138 break;
1139 case '\r':
1140 fputs("\\r", outfp);
1141 break;
1142 case '\t':
1143 fputs("\\t", outfp);
1144 break;
1145 case '\b':
1146 fputs("\\b", outfp);
1147 break;
1148 case '\f':
1149 fputs("\\f", outfp);
1150 break;
1151 default:
1152 if (str[i] < 040 || str[i] > 0176)
1153 fprintf(outfp, "\\%03o", str[i] & 0377);
1154 else
1155 putc(str[i], outfp);
1156 break;
1158 putc(')', outfp);
1161 void resource_manager::print_extensions_comment(FILE *outfp)
1163 if (extensions) {
1164 fputs("%%Extensions:", outfp);
1165 for (int i = 0; i < NEXTENSIONS; i++)
1166 if (extensions & (1 << i)) {
1167 putc(' ', outfp);
1168 fputs(extension_table[i], outfp);
1170 putc('\n', outfp);
1174 void resource_manager::print_language_level_comment(FILE *outfp)
1176 if (language_level)
1177 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);