Sync-to-go: update copyright for 2015
[s-roff.git] / src / dev-ps / psrm.cpp
blobbb467a018f7c305b162d9483d7147d39da29b9ad
1 /*@
2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000 - 2004
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 * This 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.
23 #include "config.h"
24 #include "ps-config.h"
26 #include "cset.h"
27 #include "driver.h"
28 #include "file_case.h"
29 #include "stringclass.h"
31 #include "ps.h"
33 #ifdef NEED_DECLARATION_PUTENV /* FIXME */
34 extern "C" {
35 int putenv(const char *);
37 #endif /* NEED_DECLARATION_PUTENV */
39 static void print_ps_string(const string &s, FILE *outfp);
41 cset white_space("\n\r \t\f");
42 string an_empty_string;
44 char valid_input_table[256]= {
45 #ifndef IS_EBCDIC_HOST
46 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1,
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, 0,
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
62 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
63 #else
64 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
65 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
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,
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, 1,
79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
81 #endif
84 const char *extension_table[] = {
85 "DPS",
86 "CMYK",
87 "Composite",
88 "FileSystem",
91 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
93 const char *resource_table[] = {
94 "font",
95 "procset",
96 "file",
97 "encoding",
98 "form",
99 "pattern",
102 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
104 static int read_uint_arg(const char **pp, unsigned *res)
106 while (white_space(**pp))
107 *pp += 1;
108 if (**pp == '\0') {
109 error("missing argument");
110 return 0;
112 const char *start = *pp;
113 // XXX use strtoul
114 long n = strtol(start, (char **)pp, 10);
115 if (n == 0 && *pp == start) {
116 error("not an integer");
117 return 0;
119 if (n < 0) {
120 error("argument must not be negative");
121 return 0;
123 *res = unsigned(n);
124 return 1;
127 class resource
129 public:
130 resource *next;
131 resource_type type;
132 string name;
133 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
134 unsigned flags;
135 string version;
136 unsigned revision;
137 char *filename;
138 int rank;
139 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
140 ~resource();
141 void print_type_and_name(FILE *outfp);
144 resource::resource(resource_type t, string &n, string &v, unsigned r)
145 : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
147 name.move(n);
148 version.move(v);
149 if (type == RESOURCE_FILE) {
150 if (name.search('\0') >= 0)
151 error("filename contains a character with code 0");
152 filename = name.extract();
156 resource::~resource()
158 a_delete filename;
161 void resource::print_type_and_name(FILE *outfp)
163 fputs(resource_table[type], outfp);
164 putc(' ', outfp);
165 print_ps_string(name, outfp);
166 if (type == RESOURCE_PROCSET) {
167 putc(' ', outfp);
168 print_ps_string(version, outfp);
169 fprintf(outfp, " %u", revision);
173 resource_manager::resource_manager()
174 : extensions(0), language_level(0), resource_list(0)
176 read_download_file();
177 string procset_name(L_D_PS);
178 extern const char *version_string; /* FIXME */
179 extern const char *revision_string; /* FIXME */
180 unsigned revision_uint;
181 if (!read_uint_arg(&revision_string, &revision_uint))
182 revision_uint = 0;
183 string procset_version(version_string);
184 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
185 procset_version, revision_uint);
186 procset_resource->flags |= resource::SUPPLIED;
189 resource_manager::~resource_manager()
191 while (resource_list) {
192 resource *tem = resource_list;
193 resource_list = resource_list->next;
194 delete tem;
198 resource *resource_manager::lookup_resource(resource_type type,
199 string &name,
200 string &version,
201 unsigned revision)
203 resource *r;
204 for (r = resource_list; r; r = r->next)
205 if (r->type == type
206 && r->name == name
207 && r->version == version
208 && r->revision == revision)
209 return r;
210 r = new resource(type, name, version, revision);
211 r->next = resource_list;
212 resource_list = r;
213 return r;
216 // Just a specialized version of lookup_resource().
218 resource *resource_manager::lookup_font(const char *name)
220 resource *r;
221 for (r = resource_list; r; r = r->next)
222 if (r->type == RESOURCE_FONT
223 && strlen(name) == (size_t)r->name.length()
224 && memcmp(name, r->name.contents(), r->name.length()) == 0)
225 return r;
226 string s(name);
227 r = new resource(RESOURCE_FONT, s);
228 r->next = resource_list;
229 resource_list = r;
230 return r;
233 void resource_manager::need_font(const char *name)
235 lookup_font(name)->flags |= resource::FONT_NEEDED;
238 typedef resource *Presource; // Work around g++ bug.
240 void resource_manager::document_setup(ps_output &out)
242 int nranks = 0;
243 resource *r;
244 for (r = resource_list; r; r = r->next)
245 if (r->rank >= nranks)
246 nranks = r->rank + 1;
247 if (nranks > 0) {
248 // Sort resource_list in reverse order of rank.
249 Presource *head = new Presource[nranks + 1];
250 Presource **tail = new Presource *[nranks + 1];
251 int i;
252 for (i = 0; i < nranks + 1; i++) {
253 head[i] = 0;
254 tail[i] = &head[i];
256 for (r = resource_list; r; r = r->next) {
257 i = r->rank < 0 ? 0 : r->rank + 1;
258 *tail[i] = r;
259 tail[i] = &(*tail[i])->next;
261 resource_list = 0;
262 for (i = 0; i < nranks + 1; i++)
263 if (head[i]) {
264 *tail[i] = resource_list;
265 resource_list = head[i];
267 a_delete head;
268 a_delete tail;
269 // check it
270 for (r = resource_list; r; r = r->next)
271 if (r->next)
272 assert(r->rank >= r->next->rank);
273 for (r = resource_list; r; r = r->next)
274 if (r->type == RESOURCE_FONT && r->rank >= 0)
275 supply_resource(r, -1, out.get_file());
279 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
281 int continued = 0;
282 for (resource *r = resource_list; r; r = r->next)
283 if (r->flags & flag) {
284 if (continued)
285 fputs("%%+ ", outfp);
286 else {
287 fputs(flag == resource::NEEDED
288 ? "%%DocumentNeededResources: "
289 : "%%DocumentSuppliedResources: ",
290 outfp);
291 continued = 1;
293 r->print_type_and_name(outfp);
294 putc('\n', outfp);
298 void resource_manager::print_header_comments(ps_output &out)
300 for (resource *r = resource_list; r; r = r->next)
301 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
302 supply_resource(r, 0, 0);
303 print_resources_comment(resource::NEEDED, out.get_file());
304 print_resources_comment(resource::SUPPLIED, out.get_file());
305 print_language_level_comment(out.get_file());
306 print_extensions_comment(out.get_file());
309 void resource_manager::output_prolog(ps_output &out)
311 FILE *outfp = out.get_file();
312 out.end_line();
313 char const *prologue;
314 if ((prologue = getenv(U_D_PS_PROLOGUE)) == NULL) {
315 string e = U_D_PS_PROLOGUE;
316 e += '=';
317 e += (prologue = PROLOGUE_DEFAULT);
318 e += '\0';
319 if (putenv(strsave(e.contents())))
320 fatal("putenv failed");
323 file_case *fcp = font::open_file(prologue);
324 if (fcp == NULL)
325 fatal("can't find `%1'", prologue);
327 fputs("%%BeginResource: ", outfp);
328 procset_resource->print_type_and_name(outfp);
329 putc('\n', outfp);
330 process_file(-1, fcp, outfp);
331 fputs("%%EndResource\n", outfp);
333 delete fcp;
336 void resource_manager::import_file(const char *filename, ps_output &out)
338 out.end_line();
339 string name(filename);
340 resource *r = lookup_resource(RESOURCE_FILE, name);
341 supply_resource(r, -1, out.get_file(), 1);
344 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
345 int is_document)
347 if (r->flags & resource::BUSY) {
348 r->name += '\0';
349 fatal("loop detected in dependency graph for %1 `%2'",
350 resource_table[r->type],
351 r->name.contents());
353 r->flags |= resource::BUSY;
354 if (rank > r->rank)
355 r->rank = rank;
357 file_case *fcp = NULL;
358 if (r->filename != NULL) {
359 if (r->type == RESOURCE_FONT) {
360 if ((fcp = font::open_file(r->filename)) == NULL)
361 error("can't find `%1'", r->filename);
362 } else {
363 if ((fcp = include_search_path.open_file_cautious(r->filename)) == NULL)
364 error("can't open `%1': %2", r->filename, strerror(errno));
366 if (fcp == NULL) {
367 a_delete r->filename;
368 r->filename = NULL;
372 if (fcp != NULL) {
373 if (outfp) {
374 if (r->type == RESOURCE_FILE && is_document) {
375 fputs("%%BeginDocument: ", outfp);
376 print_ps_string(r->name, outfp);
377 putc('\n', outfp);
378 } else {
379 fputs("%%BeginResource: ", outfp);
380 r->print_type_and_name(outfp);
381 putc('\n', outfp);
384 process_file(rank, fcp, outfp);
385 delete fcp;
387 if (outfp) {
388 if (r->type == RESOURCE_FILE && is_document)
389 fputs("%%EndDocument\n", outfp);
390 else
391 fputs("%%EndResource\n", outfp);
393 r->flags |= resource::SUPPLIED;
394 } else {
395 if (outfp) {
396 if (r->type == RESOURCE_FILE && is_document) {
397 fputs("%%IncludeDocument: ", outfp);
398 print_ps_string(r->name, outfp);
399 putc('\n', outfp);
400 } else {
401 fputs("%%IncludeResource: ", outfp);
402 r->print_type_and_name(outfp);
403 putc('\n', outfp);
406 r->flags |= resource::NEEDED;
408 r->flags &= ~resource::BUSY;
411 static int ps_get_line(string &buf, file_case *fcp)
413 buf.clear();
414 int c = fcp->get_c();
415 if (c == EOF)
416 return 0;
417 current_lineno++;
418 while (c != '\r' && c != '\n' && c != EOF) {
419 if (!valid_input_table[c])
420 error("invalid input character code %1", int(c));
421 buf += c;
422 c = fcp->get_c();
424 buf += '\n';
425 buf += '\0';
426 if (c == '\r') {
427 c = fcp->get_c();
428 if (c != EOF && c != '\n')
429 fcp->unget_c(c);
431 return 1;
434 static int read_text_arg(const char **pp, string &res)
436 res.clear();
437 while (white_space(**pp))
438 *pp += 1;
439 if (**pp == '\0') {
440 error("missing argument");
441 return 0;
443 if (**pp != '(') {
444 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
445 res += **pp;
446 return 1;
448 *pp += 1;
449 res.clear();
450 int level = 0;
451 for (;;) {
452 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
453 error("missing ')'");
454 return 0;
456 if (**pp == ')') {
457 if (level == 0) {
458 *pp += 1;
459 break;
461 res += **pp;
462 level--;
464 else if (**pp == '(') {
465 level++;
466 res += **pp;
468 else if (**pp == '\\') {
469 *pp += 1;
470 switch (**pp) {
471 case 'n':
472 res += '\n';
473 break;
474 case 'r':
475 res += '\n';
476 break;
477 case 't':
478 res += '\t';
479 break;
480 case 'b':
481 res += '\b';
482 break;
483 case 'f':
484 res += '\f';
485 break;
486 case '0':
487 case '1':
488 case '2':
489 case '3':
490 case '4':
491 case '5':
492 case '6':
493 case '7':
495 int val = **pp - '0';
496 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
497 *pp += 1;
498 val = val*8 + (**pp - '0');
499 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
500 *pp += 1;
501 val = val*8 + (**pp - '0');
505 break;
506 default:
507 res += **pp;
508 break;
511 else
512 res += **pp;
513 *pp += 1;
515 return 1;
518 resource *resource_manager::read_file_arg(const char **ptr)
520 string arg;
521 if (!read_text_arg(ptr, arg))
522 return 0;
523 return lookup_resource(RESOURCE_FILE, arg);
526 resource *resource_manager::read_font_arg(const char **ptr)
528 string arg;
529 if (!read_text_arg(ptr, arg))
530 return 0;
531 return lookup_resource(RESOURCE_FONT, arg);
534 resource *resource_manager::read_procset_arg(const char **ptr)
536 string arg;
537 if (!read_text_arg(ptr, arg))
538 return 0;
539 string version;
540 if (!read_text_arg(ptr, version))
541 return 0;
542 unsigned revision;
543 if (!read_uint_arg(ptr, &revision))
544 return 0;
545 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
548 resource *resource_manager::read_resource_arg(const char **ptr)
550 while (white_space(**ptr))
551 *ptr += 1;
552 const char *name = *ptr;
553 while (**ptr != '\0' && !white_space(**ptr))
554 *ptr += 1;
555 if (name == *ptr) {
556 error("missing resource type");
557 return 0;
559 int ri;
560 for (ri = 0; ri < NRESOURCES; ri++)
561 if (strlen(resource_table[ri]) == size_t(*ptr - name)
562 && memcmp(resource_table[ri], name, *ptr - name) == 0)
563 break;
564 if (ri >= NRESOURCES) {
565 error("unknown resource type");
566 return 0;
568 if (ri == RESOURCE_PROCSET)
569 return read_procset_arg(ptr);
570 string arg;
571 if (!read_text_arg(ptr, arg))
572 return 0;
573 return lookup_resource(resource_type(ri), arg);
576 static const char *matches_comment(string &buf, const char *comment)
578 if ((size_t)buf.length() < strlen(comment) + 3)
579 return 0;
580 if (buf[0] != '%' || buf[1] != '%')
581 return 0;
582 const char *bufp = buf.contents() + 2;
583 for (; *comment; comment++, bufp++)
584 if (*bufp != *comment)
585 return 0;
586 if (comment[-1] == ':')
587 return bufp;
588 if (*bufp == '\0' || white_space(*bufp))
589 return bufp;
590 return 0;
593 // Return 1 if the line should be copied out.
595 int resource_manager::do_begin_resource(const char *ptr, int, file_case *,
596 FILE *)
598 resource *r = read_resource_arg(&ptr);
599 if (r)
600 r->flags |= resource::SUPPLIED;
601 return 1;
604 int resource_manager::do_include_resource(const char *ptr, int rank,
605 file_case *, FILE *outfp)
607 resource *r = read_resource_arg(&ptr);
608 if (r) {
609 if (r->type == RESOURCE_FONT) {
610 if (rank >= 0)
611 supply_resource(r, rank + 1, outfp);
612 else
613 r->flags |= resource::FONT_NEEDED;
615 else
616 supply_resource(r, rank, outfp);
618 return 0;
621 int resource_manager::do_begin_document(const char *ptr, int, file_case *,
622 FILE *)
624 resource *r = read_file_arg(&ptr);
625 if (r)
626 r->flags |= resource::SUPPLIED;
627 return 1;
630 int resource_manager::do_include_document(const char *ptr, int rank,
631 file_case *, FILE *outfp)
633 resource *r = read_file_arg(&ptr);
634 if (r)
635 supply_resource(r, rank, outfp, 1);
636 return 0;
639 int resource_manager::do_begin_procset(const char *ptr, int, file_case *,
640 FILE *outfp)
642 resource *r = read_procset_arg(&ptr);
643 if (r) {
644 r->flags |= resource::SUPPLIED;
645 if (outfp) {
646 fputs("%%BeginResource: ", outfp);
647 r->print_type_and_name(outfp);
648 putc('\n', outfp);
651 return 0;
654 int resource_manager::do_include_procset(const char *ptr, int rank,
655 file_case *, FILE *outfp)
657 resource *r = read_procset_arg(&ptr);
658 if (r)
659 supply_resource(r, rank, outfp);
660 return 0;
663 int resource_manager::do_begin_file(const char *ptr, int, file_case *,
664 FILE *outfp)
666 resource *r = read_file_arg(&ptr);
667 if (r) {
668 r->flags |= resource::SUPPLIED;
669 if (outfp) {
670 fputs("%%BeginResource: ", outfp);
671 r->print_type_and_name(outfp);
672 putc('\n', outfp);
675 return 0;
678 int resource_manager::do_include_file(const char *ptr, int rank, file_case *,
679 FILE *outfp)
681 resource *r = read_file_arg(&ptr);
682 if (r)
683 supply_resource(r, rank, outfp);
684 return 0;
687 int resource_manager::do_begin_font(const char *ptr, int, file_case *,
688 FILE *outfp)
690 resource *r = read_font_arg(&ptr);
691 if (r) {
692 r->flags |= resource::SUPPLIED;
693 if (outfp) {
694 fputs("%%BeginResource: ", outfp);
695 r->print_type_and_name(outfp);
696 putc('\n', outfp);
699 return 0;
702 int resource_manager::do_include_font(const char *ptr, int rank, file_case *,
703 FILE *outfp)
705 resource *r = read_font_arg(&ptr);
706 if (r) {
707 if (rank >= 0)
708 supply_resource(r, rank + 1, outfp);
709 else
710 r->flags |= resource::FONT_NEEDED;
712 return 0;
715 int resource_manager::change_to_end_resource(const char *, int, file_case *,
716 FILE *outfp)
718 if (outfp)
719 fputs("%%EndResource\n", outfp);
720 return 0;
723 int resource_manager::do_begin_preview(const char *, int, file_case *fcp,
724 FILE *)
726 string buf;
727 do {
728 if (!ps_get_line(buf, fcp)) {
729 error("end of file in preview section");
730 break;
732 } while (!matches_comment(buf, "EndPreview"));
733 return 0;
736 int read_one_of(const char **ptr, const char **s, int n)
738 while (white_space(**ptr))
739 *ptr += 1;
740 if (**ptr == '\0')
741 return -1;
742 const char *start = *ptr;
743 do {
744 ++(*ptr);
745 } while (**ptr != '\0' && !white_space(**ptr));
746 for (int i = 0; i < n; i++)
747 if (strlen(s[i]) == size_t(*ptr - start)
748 && memcmp(s[i], start, *ptr - start) == 0)
749 return i;
750 return -1;
753 void skip_possible_newline(file_case *fcp, FILE *outfp)
755 int c = fcp->get_c();
756 if (c == '\r') {
757 current_lineno++;
758 if (outfp)
759 putc(c, outfp);
760 int cc = fcp->get_c();
761 if (cc != '\n') {
762 if (cc != EOF)
763 fcp->unget_c(cc);
765 else {
766 if (outfp)
767 putc(cc, outfp);
770 else if (c == '\n') {
771 current_lineno++;
772 if (outfp)
773 putc(c, outfp);
775 else if (c != EOF)
776 fcp->unget_c(c);
779 int resource_manager::do_begin_data(const char *ptr, int, file_case *fcp,
780 FILE *outfp)
782 while (white_space(*ptr))
783 ptr++;
784 const char *start = ptr;
785 unsigned numberof;
786 if (!read_uint_arg(&ptr, &numberof))
787 return 0;
788 static const char *types[] = { "Binary", "Hex", "ASCII" };
789 const int Binary = 0;
790 int type = 0;
791 static const char *units[] = { "Bytes", "Lines" };
792 const int Bytes = 0;
793 int unit = Bytes;
794 while (white_space(*ptr))
795 ptr++;
796 if (*ptr != '\0') {
797 type = read_one_of(&ptr, types, 3);
798 if (type < 0) {
799 error("bad data type");
800 return 0;
802 while (white_space(*ptr))
803 ptr++;
804 if (*ptr != '\0') {
805 unit = read_one_of(&ptr, units, 2);
806 if (unit < 0) {
807 error("expected `Bytes' or `Lines'");
808 return 0;
812 if (type != Binary)
813 return 1;
814 if (outfp) {
815 fputs("%%BeginData: ", outfp);
816 fputs(start, outfp);
818 if (numberof > 0) {
819 unsigned bytecount = 0;
820 unsigned linecount = 0;
821 do {
822 int c = fcp->get_c();
823 if (c == EOF) {
824 error("end of file within data section");
825 return 0;
827 if (outfp)
828 putc(c, outfp);
829 bytecount++;
830 if (c == '\r') {
831 int cc = fcp->get_c();
832 if (cc != '\n') {
833 linecount++;
834 current_lineno++;
836 if (cc != EOF)
837 fcp->unget_c(cc);
839 else if (c == '\n') {
840 linecount++;
841 current_lineno++;
843 } while ((unit == Bytes ? bytecount : linecount) < numberof);
845 skip_possible_newline(fcp, outfp);
846 string buf;
847 if (!ps_get_line(buf, fcp)) {
848 error("missing %%%%EndData line");
849 return 0;
851 if (!matches_comment(buf, "EndData"))
852 error("bad %%%%EndData line");
853 if (outfp)
854 fputs(buf.contents(), outfp);
855 return 0;
858 int resource_manager::do_begin_binary(const char *ptr, int, file_case *fcp,
859 FILE *outfp)
861 if (!outfp)
862 return 0;
863 unsigned count;
864 if (!read_uint_arg(&ptr, &count))
865 return 0;
866 if (outfp)
867 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
868 while (count != 0) {
869 int c = fcp->get_c();
870 if (c == EOF) {
871 error("end of file within binary section");
872 return 0;
874 if (outfp)
875 putc(c, outfp);
876 --count;
877 if (c == '\r') {
878 int cc = fcp->get_c();
879 if (cc != '\n')
880 current_lineno++;
881 if (cc != EOF)
882 fcp->unget_c(cc);
884 else if (c == '\n')
885 current_lineno++;
887 skip_possible_newline(fcp, outfp);
888 string buf;
889 if (!ps_get_line(buf, fcp)) {
890 error("missing %%%%EndBinary line");
891 return 0;
893 if (!matches_comment(buf, "EndBinary")) {
894 error("bad %%%%EndBinary line");
895 if (outfp)
896 fputs(buf.contents(), outfp);
898 else if (outfp)
899 fputs("%%EndData\n", outfp);
900 return 0;
903 static unsigned parse_extensions(const char *ptr)
905 unsigned flags = 0;
906 for (;;) {
907 while (white_space(*ptr))
908 ptr++;
909 if (*ptr == '\0')
910 break;
911 const char *name = ptr;
912 do {
913 ++ptr;
914 } while (*ptr != '\0' && !white_space(*ptr));
915 int i;
916 for (i = 0; i < NEXTENSIONS; i++)
917 if (strlen(extension_table[i]) == size_t(ptr - name)
918 && memcmp(extension_table[i], name, ptr - name) == 0) {
919 flags |= (1 << i);
920 break;
922 if (i >= NEXTENSIONS) {
923 string s(name, ptr - name);
924 s += '\0';
925 error("unknown extension `%1'", s.contents());
928 return flags;
931 // XXX if it has not been surrounded with {Begin,End}Document need to strip
932 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
934 // XXX Perhaps the decision whether to use BeginDocument or
935 // BeginResource: file should be postponed till we have seen
936 // the first line of the file.
938 void resource_manager::process_file(int rank, file_case *fcp, FILE *outfp)
940 // If none of these comments appear in the header section, and we are
941 // just analyzing the file (ie outfp is 0), then we can return immediately.
942 static const char *header_comment_table[] = {
943 "DocumentNeededResources:",
944 "DocumentSuppliedResources:",
945 "DocumentNeededFonts:",
946 "DocumentSuppliedFonts:",
947 "DocumentNeededProcSets:",
948 "DocumentSuppliedProcSets:",
949 "DocumentNeededFiles:",
950 "DocumentSuppliedFiles:",
953 const int NHEADER_COMMENTS = sizeof(header_comment_table)
954 / sizeof(header_comment_table[0]);
955 struct comment_info {
956 const char *name;
957 int (resource_manager::*proc)(const char *, int, file_case *, FILE *);
960 static comment_info comment_table[] = {
961 { "BeginResource:", &resource_manager::do_begin_resource },
962 { "IncludeResource:", &resource_manager::do_include_resource },
963 { "BeginDocument:", &resource_manager::do_begin_document },
964 { "IncludeDocument:", &resource_manager::do_include_document },
965 { "BeginProcSet:", &resource_manager::do_begin_procset },
966 { "IncludeProcSet:", &resource_manager::do_include_procset },
967 { "BeginFont:", &resource_manager::do_begin_font },
968 { "IncludeFont:", &resource_manager::do_include_font },
969 { "BeginFile:", &resource_manager::do_begin_file },
970 { "IncludeFile:", &resource_manager::do_include_file },
971 { "EndProcSet", &resource_manager::change_to_end_resource },
972 { "EndFont", &resource_manager::change_to_end_resource },
973 { "EndFile", &resource_manager::change_to_end_resource },
974 { "BeginPreview:", &resource_manager::do_begin_preview },
975 { "BeginData:", &resource_manager::do_begin_data },
976 { "BeginBinary:", &resource_manager::do_begin_binary },
979 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
980 string buf;
981 int saved_lineno = current_lineno;
982 const char *saved_filename = current_filename;
983 current_filename = fcp->path();
984 current_lineno = 0;
985 if (!ps_get_line(buf, fcp)) {
986 current_filename = saved_filename;
987 current_lineno = saved_lineno;
988 return;
990 if ((size_t)buf.length() < sizeof(PS_MAGIC)
991 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
992 if (outfp) {
993 do {
994 if (!(broken_flags & STRIP_PERCENT_BANG)
995 || buf[0] != '%' || buf[1] != '!')
996 fputs(buf.contents(), outfp);
997 } while (ps_get_line(buf, fcp));
1000 else {
1001 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
1002 fputs(buf.contents(), outfp);
1003 int in_header = 1;
1004 int interesting = 0;
1005 int had_extensions_comment = 0;
1006 int had_language_level_comment = 0;
1007 for (;;) {
1008 if (!ps_get_line(buf, fcp))
1009 break;
1010 int copy_this_line = 1;
1011 if (buf[0] == '%') {
1012 if (buf[1] == '%') {
1013 const char *ptr;
1014 int i;
1015 for (i = 0; i < NCOMMENTS; i++)
1016 if ((ptr = matches_comment(buf, comment_table[i].name))) {
1017 copy_this_line
1018 = (this->*(comment_table[i].proc))(ptr, rank, fcp, outfp);
1019 break;
1021 if (i >= NCOMMENTS && in_header) {
1022 if ((ptr = matches_comment(buf, "EndComments")))
1023 in_header = 0;
1024 else if (!had_extensions_comment
1025 && (ptr = matches_comment(buf, "Extensions:"))) {
1026 extensions |= parse_extensions(ptr);
1027 // XXX handle possibility that next line is %%+
1028 had_extensions_comment = 1;
1030 else if (!had_language_level_comment
1031 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
1032 unsigned ll;
1033 if (read_uint_arg(&ptr, &ll) && ll > language_level)
1034 language_level = ll;
1035 had_language_level_comment = 1;
1037 else {
1038 for (i = 0; i < NHEADER_COMMENTS; i++)
1039 if (matches_comment(buf, header_comment_table[i])) {
1040 interesting = 1;
1041 break;
1045 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
1046 && (matches_comment(buf, "EndProlog")
1047 || matches_comment(buf, "Page:")
1048 || matches_comment(buf, "Trailer")))
1049 copy_this_line = 0;
1051 else if (buf[1] == '!') {
1052 if (broken_flags & STRIP_PERCENT_BANG)
1053 copy_this_line = 0;
1056 else
1057 in_header = 0;
1058 if (!outfp && !in_header && !interesting)
1059 break;
1060 if (copy_this_line && outfp)
1061 fputs(buf.contents(), outfp);
1064 current_filename = saved_filename;
1065 current_lineno = saved_lineno;
1068 void resource_manager::read_download_file()
1070 file_case *fcp = font::open_file("download");
1071 if (fcp == NULL)
1072 fatal("can't find `download'");
1074 char buf[512];
1075 for (int lineno = 1; fcp->get_line(buf, sizeof(buf)) != NULL; ++lineno) {
1076 char *p = strtok(buf, " \t\r\n");
1077 if (p == NULL || *p == '#')
1078 continue;
1079 char *q = strtok(0, " \t\r\n");
1080 if (q == NULL)
1081 fatal_with_file_and_line(fcp->path(), lineno, "missing filename");
1082 lookup_font(p)->filename = strsave(q);
1085 delete fcp;
1088 // XXX Can we share some code with ps_output::put_string()?
1090 static void print_ps_string(const string &s, FILE *outfp)
1092 int len = s.length();
1093 const char *str = s.contents();
1094 int funny = 0;
1095 if (str[0] == '(')
1096 funny = 1;
1097 else {
1098 for (int i = 0; i < len; i++)
1099 if (str[i] <= 040 || str[i] > 0176) {
1100 funny = 1;
1101 break;
1104 if (!funny) {
1105 put_string(s, outfp);
1106 return;
1108 int level = 0;
1109 int i;
1110 for (i = 0; i < len; i++)
1111 if (str[i] == '(')
1112 level++;
1113 else if (str[i] == ')' && --level < 0)
1114 break;
1115 putc('(', outfp);
1116 for (i = 0; i < len; i++)
1117 switch (str[i]) {
1118 case '(':
1119 case ')':
1120 if (level != 0)
1121 putc('\\', outfp);
1122 putc(str[i], outfp);
1123 break;
1124 case '\\':
1125 fputs("\\\\", outfp);
1126 break;
1127 case '\n':
1128 fputs("\\n", outfp);
1129 break;
1130 case '\r':
1131 fputs("\\r", outfp);
1132 break;
1133 case '\t':
1134 fputs("\\t", outfp);
1135 break;
1136 case '\b':
1137 fputs("\\b", outfp);
1138 break;
1139 case '\f':
1140 fputs("\\f", outfp);
1141 break;
1142 default:
1143 if (str[i] < 040 || str[i] > 0176)
1144 fprintf(outfp, "\\%03o", str[i] & 0377);
1145 else
1146 putc(str[i], outfp);
1147 break;
1149 putc(')', outfp);
1152 void resource_manager::print_extensions_comment(FILE *outfp)
1154 if (extensions) {
1155 fputs("%%Extensions:", outfp);
1156 for (int i = 0; i < NEXTENSIONS; i++)
1157 if (extensions & (1 << i)) {
1158 putc(' ', outfp);
1159 fputs(extension_table[i], outfp);
1161 putc('\n', outfp);
1165 void resource_manager::print_language_level_comment(FILE *outfp)
1167 if (language_level)
1168 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
1171 // s-it2-mode