1 /* $NetBSD: psrm.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $ */
2
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
7
8 This file is part of groff.
9
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
14
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
19
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23
24 #include "driver.h"
25 #include "stringclass.h"
26 #include "cset.h"
27
28 #include "ps.h"
29
30 #ifdef NEED_DECLARATION_PUTENV
31 extern "C" {
32 int putenv(const char *);
33 }
34 #endif /* NEED_DECLARATION_PUTENV */
35
36 #define GROPS_PROLOGUE "prologue"
37
38 static void print_ps_string(const string &s, FILE *outfp);
39
40 cset white_space("\n\r \t\f");
41 string an_empty_string;
42
43 char valid_input_table[256]= {
44 #ifndef IS_EBCDIC_HOST
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
46 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1,
52 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
53
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
62 #else
63 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71
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, 1,
79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
80 #endif
81 };
82
83 const char *extension_table[] = {
84 "DPS",
85 "CMYK",
86 "Composite",
87 "FileSystem",
88 };
89
90 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
91
92 const char *resource_table[] = {
93 "font",
94 "procset",
95 "file",
96 "encoding",
97 "form",
98 "pattern",
99 };
100
101 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
102
read_uint_arg(const char ** pp,unsigned * res)103 static int read_uint_arg(const char **pp, unsigned *res)
104 {
105 while (white_space(**pp))
106 *pp += 1;
107 if (**pp == '\0') {
108 error("missing argument");
109 return 0;
110 }
111 const char *start = *pp;
112 // XXX use strtoul
113 long n = strtol(start, (char **)pp, 10);
114 if (n == 0 && *pp == start) {
115 error("not an integer");
116 return 0;
117 }
118 if (n < 0) {
119 error("argument must not be negative");
120 return 0;
121 }
122 *res = unsigned(n);
123 return 1;
124 }
125
126 struct resource {
127 resource *next;
128 resource_type type;
129 string name;
130 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
131 unsigned flags;
132 string version;
133 unsigned revision;
134 char *filename;
135 int rank;
136 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
137 ~resource();
138 void print_type_and_name(FILE *outfp);
139 };
140
resource(resource_type t,string & n,string & v,unsigned r)141 resource::resource(resource_type t, string &n, string &v, unsigned r)
142 : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
143 {
144 name.move(n);
145 version.move(v);
146 if (type == RESOURCE_FILE) {
147 if (name.search('\0') >= 0)
148 error("filename contains a character with code 0");
149 filename = name.extract();
150 }
151 }
152
~resource()153 resource::~resource()
154 {
155 a_delete filename;
156 }
157
print_type_and_name(FILE * outfp)158 void resource::print_type_and_name(FILE *outfp)
159 {
160 fputs(resource_table[type], outfp);
161 putc(' ', outfp);
162 print_ps_string(name, outfp);
163 if (type == RESOURCE_PROCSET) {
164 putc(' ', outfp);
165 print_ps_string(version, outfp);
166 fprintf(outfp, " %u", revision);
167 }
168 }
169
resource_manager()170 resource_manager::resource_manager()
171 : extensions(0), language_level(0), resource_list(0)
172 {
173 read_download_file();
174 string procset_name("grops");
175 extern const char *version_string;
176 extern const char *revision_string;
177 unsigned revision_uint;
178 if (!read_uint_arg(&revision_string, &revision_uint))
179 revision_uint = 0;
180 string procset_version(version_string);
181 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
182 procset_version, revision_uint);
183 procset_resource->flags |= resource::SUPPLIED;
184 }
185
~resource_manager()186 resource_manager::~resource_manager()
187 {
188 while (resource_list) {
189 resource *tem = resource_list;
190 resource_list = resource_list->next;
191 delete tem;
192 }
193 }
194
lookup_resource(resource_type type,string & name,string & version,unsigned revision)195 resource *resource_manager::lookup_resource(resource_type type,
196 string &name,
197 string &version,
198 unsigned revision)
199 {
200 resource *r;
201 for (r = resource_list; r; r = r->next)
202 if (r->type == type
203 && r->name == name
204 && r->version == version
205 && r->revision == revision)
206 return r;
207 r = new resource(type, name, version, revision);
208 r->next = resource_list;
209 resource_list = r;
210 return r;
211 }
212
213 // Just a specialized version of lookup_resource().
214
lookup_font(const char * name)215 resource *resource_manager::lookup_font(const char *name)
216 {
217 resource *r;
218 for (r = resource_list; r; r = r->next)
219 if (r->type == RESOURCE_FONT
220 && strlen(name) == (size_t)r->name.length()
221 && memcmp(name, r->name.contents(), r->name.length()) == 0)
222 return r;
223 string s(name);
224 r = new resource(RESOURCE_FONT, s);
225 r->next = resource_list;
226 resource_list = r;
227 return r;
228 }
229
need_font(const char * name)230 void resource_manager::need_font(const char *name)
231 {
232 lookup_font(name)->flags |= resource::FONT_NEEDED;
233 }
234
235 typedef resource *Presource; // Work around g++ bug.
236
document_setup(ps_output & out)237 void resource_manager::document_setup(ps_output &out)
238 {
239 int nranks = 0;
240 resource *r;
241 for (r = resource_list; r; r = r->next)
242 if (r->rank >= nranks)
243 nranks = r->rank + 1;
244 if (nranks > 0) {
245 // Sort resource_list in reverse order of rank.
246 Presource *head = new Presource[nranks + 1];
247 Presource **tail = new Presource *[nranks + 1];
248 int i;
249 for (i = 0; i < nranks + 1; i++) {
250 head[i] = 0;
251 tail[i] = &head[i];
252 }
253 for (r = resource_list; r; r = r->next) {
254 i = r->rank < 0 ? 0 : r->rank + 1;
255 *tail[i] = r;
256 tail[i] = &(*tail[i])->next;
257 }
258 resource_list = 0;
259 for (i = 0; i < nranks + 1; i++)
260 if (head[i]) {
261 *tail[i] = resource_list;
262 resource_list = head[i];
263 }
264 a_delete head;
265 a_delete tail;
266 // check it
267 for (r = resource_list; r; r = r->next)
268 if (r->next)
269 assert(r->rank >= r->next->rank);
270 for (r = resource_list; r; r = r->next)
271 if (r->type == RESOURCE_FONT && r->rank >= 0)
272 supply_resource(r, -1, out.get_file());
273 }
274 }
275
print_resources_comment(unsigned flag,FILE * outfp)276 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
277 {
278 int continued = 0;
279 for (resource *r = resource_list; r; r = r->next)
280 if (r->flags & flag) {
281 if (continued)
282 fputs("%%+ ", outfp);
283 else {
284 fputs(flag == resource::NEEDED
285 ? "%%DocumentNeededResources: "
286 : "%%DocumentSuppliedResources: ",
287 outfp);
288 continued = 1;
289 }
290 r->print_type_and_name(outfp);
291 putc('\n', outfp);
292 }
293 }
294
print_header_comments(ps_output & out)295 void resource_manager::print_header_comments(ps_output &out)
296 {
297 for (resource *r = resource_list; r; r = r->next)
298 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
299 supply_resource(r, 0, 0);
300 print_resources_comment(resource::NEEDED, out.get_file());
301 print_resources_comment(resource::SUPPLIED, out.get_file());
302 print_language_level_comment(out.get_file());
303 print_extensions_comment(out.get_file());
304 }
305
output_prolog(ps_output & out)306 void resource_manager::output_prolog(ps_output &out)
307 {
308 FILE *outfp = out.get_file();
309 out.end_line();
310 char *path;
311 if (!getenv("GROPS_PROLOGUE")) {
312 string e = "GROPS_PROLOGUE";
313 e += '=';
314 e += GROPS_PROLOGUE;
315 e += '\0';
316 if (putenv(strsave(e.contents())))
317 fatal("putenv failed");
318 }
319 char *prologue = getenv("GROPS_PROLOGUE");
320 FILE *fp = font::open_file(prologue, &path);
321 if (!fp)
322 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, fp, path, outfp);
327 fclose(fp);
328 a_delete path;
329 fputs("%%EndResource\n", outfp);
330 }
331
import_file(const char * filename,ps_output & out)332 void resource_manager::import_file(const char *filename, ps_output &out)
333 {
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);
338 }
339
supply_resource(resource * r,int rank,FILE * outfp,int is_document)340 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
341 int is_document)
342 {
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());
348 }
349 r->flags |= resource::BUSY;
350 if (rank > r->rank)
351 r->rank = rank;
352 char *path = 0; // pacify compiler
353 FILE *fp = 0;
354 if (r->filename != 0) {
355 if (r->type == RESOURCE_FONT) {
356 fp = font::open_file(r->filename, &path);
357 if (!fp) {
358 error("can't find `%1'", r->filename);
359 a_delete r->filename;
360 r->filename = 0;
361 }
362 }
363 else {
364 errno = 0;
365 fp = include_search_path.open_file_cautious(r->filename);
366 if (!fp) {
367 error("can't open `%1': %2", r->filename, strerror(errno));
368 a_delete r->filename;
369 r->filename = 0;
370 }
371 else
372 path = r->filename;
373 }
374 }
375 if (fp) {
376 if (outfp) {
377 if (r->type == RESOURCE_FILE && is_document) {
378 fputs("%%BeginDocument: ", outfp);
379 print_ps_string(r->name, outfp);
380 putc('\n', outfp);
381 }
382 else {
383 fputs("%%BeginResource: ", outfp);
384 r->print_type_and_name(outfp);
385 putc('\n', outfp);
386 }
387 }
388 process_file(rank, fp, path, outfp);
389 fclose(fp);
390 if (r->type == RESOURCE_FONT)
391 a_delete path;
392 if (outfp) {
393 if (r->type == RESOURCE_FILE && is_document)
394 fputs("%%EndDocument\n", outfp);
395 else
396 fputs("%%EndResource\n", outfp);
397 }
398 r->flags |= resource::SUPPLIED;
399 }
400 else {
401 if (outfp) {
402 if (r->type == RESOURCE_FILE && is_document) {
403 fputs("%%IncludeDocument: ", outfp);
404 print_ps_string(r->name, outfp);
405 putc('\n', outfp);
406 }
407 else {
408 fputs("%%IncludeResource: ", outfp);
409 r->print_type_and_name(outfp);
410 putc('\n', outfp);
411 }
412 }
413 r->flags |= resource::NEEDED;
414 }
415 r->flags &= ~resource::BUSY;
416 }
417
418 #define PS_MAGIC "%!PS-Adobe-"
419
ps_get_line(string & buf,FILE * fp)420 static int ps_get_line(string &buf, FILE *fp)
421 {
422 buf.clear();
423 int c = getc(fp);
424 if (c == EOF)
425 return 0;
426 current_lineno++;
427 while (c != '\r' && c != '\n' && c != EOF) {
428 if (!valid_input_table[c])
429 error("invalid input character code %1", int(c));
430 buf += c;
431 c = getc(fp);
432 }
433 buf += '\n';
434 buf += '\0';
435 if (c == '\r') {
436 c = getc(fp);
437 if (c != EOF && c != '\n')
438 ungetc(c, fp);
439 }
440 return 1;
441 }
442
read_text_arg(const char ** pp,string & res)443 static int read_text_arg(const char **pp, string &res)
444 {
445 res.clear();
446 while (white_space(**pp))
447 *pp += 1;
448 if (**pp == '\0') {
449 error("missing argument");
450 return 0;
451 }
452 if (**pp != '(') {
453 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
454 res += **pp;
455 return 1;
456 }
457 *pp += 1;
458 res.clear();
459 int level = 0;
460 for (;;) {
461 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
462 error("missing ')'");
463 return 0;
464 }
465 if (**pp == ')') {
466 if (level == 0) {
467 *pp += 1;
468 break;
469 }
470 res += **pp;
471 level--;
472 }
473 else if (**pp == '(') {
474 level++;
475 res += **pp;
476 }
477 else if (**pp == '\\') {
478 *pp += 1;
479 switch (**pp) {
480 case 'n':
481 res += '\n';
482 break;
483 case 'r':
484 res += '\n';
485 break;
486 case 't':
487 res += '\t';
488 break;
489 case 'b':
490 res += '\b';
491 break;
492 case 'f':
493 res += '\f';
494 break;
495 case '0':
496 case '1':
497 case '2':
498 case '3':
499 case '4':
500 case '5':
501 case '6':
502 case '7':
503 {
504 int val = **pp - '0';
505 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
506 *pp += 1;
507 val = val*8 + (**pp - '0');
508 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
509 *pp += 1;
510 val = val*8 + (**pp - '0');
511 }
512 }
513 }
514 break;
515 default:
516 res += **pp;
517 break;
518 }
519 }
520 else
521 res += **pp;
522 *pp += 1;
523 }
524 return 1;
525 }
526
read_file_arg(const char ** ptr)527 resource *resource_manager::read_file_arg(const char **ptr)
528 {
529 string arg;
530 if (!read_text_arg(ptr, arg))
531 return 0;
532 return lookup_resource(RESOURCE_FILE, arg);
533 }
534
read_font_arg(const char ** ptr)535 resource *resource_manager::read_font_arg(const char **ptr)
536 {
537 string arg;
538 if (!read_text_arg(ptr, arg))
539 return 0;
540 return lookup_resource(RESOURCE_FONT, arg);
541 }
542
read_procset_arg(const char ** ptr)543 resource *resource_manager::read_procset_arg(const char **ptr)
544 {
545 string arg;
546 if (!read_text_arg(ptr, arg))
547 return 0;
548 string version;
549 if (!read_text_arg(ptr, version))
550 return 0;
551 unsigned revision;
552 if (!read_uint_arg(ptr, &revision))
553 return 0;
554 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
555 }
556
read_resource_arg(const char ** ptr)557 resource *resource_manager::read_resource_arg(const char **ptr)
558 {
559 while (white_space(**ptr))
560 *ptr += 1;
561 const char *name = *ptr;
562 while (**ptr != '\0' && !white_space(**ptr))
563 *ptr += 1;
564 if (name == *ptr) {
565 error("missing resource type");
566 return 0;
567 }
568 int ri;
569 for (ri = 0; ri < NRESOURCES; ri++)
570 if (strlen(resource_table[ri]) == size_t(*ptr - name)
571 && memcmp(resource_table[ri], name, *ptr - name) == 0)
572 break;
573 if (ri >= NRESOURCES) {
574 error("unknown resource type");
575 return 0;
576 }
577 if (ri == RESOURCE_PROCSET)
578 return read_procset_arg(ptr);
579 string arg;
580 if (!read_text_arg(ptr, arg))
581 return 0;
582 return lookup_resource(resource_type(ri), arg);
583 }
584
matches_comment(string & buf,const char * comment)585 static const char *matches_comment(string &buf, const char *comment)
586 {
587 if ((size_t)buf.length() < strlen(comment) + 3)
588 return 0;
589 if (buf[0] != '%' || buf[1] != '%')
590 return 0;
591 const char *bufp = buf.contents() + 2;
592 for (; *comment; comment++, bufp++)
593 if (*bufp != *comment)
594 return 0;
595 if (comment[-1] == ':')
596 return bufp;
597 if (*bufp == '\0' || white_space(*bufp))
598 return bufp;
599 return 0;
600 }
601
602 // Return 1 if the line should be copied out.
603
do_begin_resource(const char * ptr,int,FILE *,FILE *)604 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
605 FILE *)
606 {
607 resource *r = read_resource_arg(&ptr);
608 if (r)
609 r->flags |= resource::SUPPLIED;
610 return 1;
611 }
612
do_include_resource(const char * ptr,int rank,FILE *,FILE * outfp)613 int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
614 FILE *outfp)
615 {
616 resource *r = read_resource_arg(&ptr);
617 if (r) {
618 if (r->type == RESOURCE_FONT) {
619 if (rank >= 0)
620 supply_resource(r, rank + 1, outfp);
621 else
622 r->flags |= resource::FONT_NEEDED;
623 }
624 else
625 supply_resource(r, rank, outfp);
626 }
627 return 0;
628 }
629
do_begin_document(const char * ptr,int,FILE *,FILE *)630 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
631 FILE *)
632 {
633 resource *r = read_file_arg(&ptr);
634 if (r)
635 r->flags |= resource::SUPPLIED;
636 return 1;
637 }
638
do_include_document(const char * ptr,int rank,FILE *,FILE * outfp)639 int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
640 FILE *outfp)
641 {
642 resource *r = read_file_arg(&ptr);
643 if (r)
644 supply_resource(r, rank, outfp, 1);
645 return 0;
646 }
647
do_begin_procset(const char * ptr,int,FILE *,FILE * outfp)648 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
649 FILE *outfp)
650 {
651 resource *r = read_procset_arg(&ptr);
652 if (r) {
653 r->flags |= resource::SUPPLIED;
654 if (outfp) {
655 fputs("%%BeginResource: ", outfp);
656 r->print_type_and_name(outfp);
657 putc('\n', outfp);
658 }
659 }
660 return 0;
661 }
662
do_include_procset(const char * ptr,int rank,FILE *,FILE * outfp)663 int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
664 FILE *outfp)
665 {
666 resource *r = read_procset_arg(&ptr);
667 if (r)
668 supply_resource(r, rank, outfp);
669 return 0;
670 }
671
do_begin_file(const char * ptr,int,FILE *,FILE * outfp)672 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
673 FILE *outfp)
674 {
675 resource *r = read_file_arg(&ptr);
676 if (r) {
677 r->flags |= resource::SUPPLIED;
678 if (outfp) {
679 fputs("%%BeginResource: ", outfp);
680 r->print_type_and_name(outfp);
681 putc('\n', outfp);
682 }
683 }
684 return 0;
685 }
686
do_include_file(const char * ptr,int rank,FILE *,FILE * outfp)687 int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
688 FILE *outfp)
689 {
690 resource *r = read_file_arg(&ptr);
691 if (r)
692 supply_resource(r, rank, outfp);
693 return 0;
694 }
695
do_begin_font(const char * ptr,int,FILE *,FILE * outfp)696 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
697 FILE *outfp)
698 {
699 resource *r = read_font_arg(&ptr);
700 if (r) {
701 r->flags |= resource::SUPPLIED;
702 if (outfp) {
703 fputs("%%BeginResource: ", outfp);
704 r->print_type_and_name(outfp);
705 putc('\n', outfp);
706 }
707 }
708 return 0;
709 }
710
do_include_font(const char * ptr,int rank,FILE *,FILE * outfp)711 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
712 FILE *outfp)
713 {
714 resource *r = read_font_arg(&ptr);
715 if (r) {
716 if (rank >= 0)
717 supply_resource(r, rank + 1, outfp);
718 else
719 r->flags |= resource::FONT_NEEDED;
720 }
721 return 0;
722 }
723
change_to_end_resource(const char *,int,FILE *,FILE * outfp)724 int resource_manager::change_to_end_resource(const char *, int, FILE *,
725 FILE *outfp)
726 {
727 if (outfp)
728 fputs("%%EndResource\n", outfp);
729 return 0;
730 }
731
do_begin_preview(const char *,int,FILE * fp,FILE *)732 int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
733 {
734 string buf;
735 do {
736 if (!ps_get_line(buf, fp)) {
737 error("end of file in preview section");
738 break;
739 }
740 } while (!matches_comment(buf, "EndPreview"));
741 return 0;
742 }
743
read_one_of(const char ** ptr,const char ** s,int n)744 int read_one_of(const char **ptr, const char **s, int n)
745 {
746 while (white_space(**ptr))
747 *ptr += 1;
748 if (**ptr == '\0')
749 return -1;
750 const char *start = *ptr;
751 do {
752 ++(*ptr);
753 } while (**ptr != '\0' && !white_space(**ptr));
754 for (int i = 0; i < n; i++)
755 if (strlen(s[i]) == size_t(*ptr - start)
756 && memcmp(s[i], start, *ptr - start) == 0)
757 return i;
758 return -1;
759 }
760
skip_possible_newline(FILE * fp,FILE * outfp)761 void skip_possible_newline(FILE *fp, FILE *outfp)
762 {
763 int c = getc(fp);
764 if (c == '\r') {
765 current_lineno++;
766 if (outfp)
767 putc(c, outfp);
768 int cc = getc(fp);
769 if (cc != '\n') {
770 if (cc != EOF)
771 ungetc(cc, fp);
772 }
773 else {
774 if (outfp)
775 putc(cc, outfp);
776 }
777 }
778 else if (c == '\n') {
779 current_lineno++;
780 if (outfp)
781 putc(c, outfp);
782 }
783 else if (c != EOF)
784 ungetc(c, fp);
785 }
786
do_begin_data(const char * ptr,int,FILE * fp,FILE * outfp)787 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
788 FILE *outfp)
789 {
790 while (white_space(*ptr))
791 ptr++;
792 const char *start = ptr;
793 unsigned numberof;
794 if (!read_uint_arg(&ptr, &numberof))
795 return 0;
796 static const char *types[] = { "Binary", "Hex", "ASCII" };
797 const int Binary = 0;
798 int type = 0;
799 static const char *units[] = { "Bytes", "Lines" };
800 const int Bytes = 0;
801 int unit = Bytes;
802 while (white_space(*ptr))
803 ptr++;
804 if (*ptr != '\0') {
805 type = read_one_of(&ptr, types, 3);
806 if (type < 0) {
807 error("bad data type");
808 return 0;
809 }
810 while (white_space(*ptr))
811 ptr++;
812 if (*ptr != '\0') {
813 unit = read_one_of(&ptr, units, 2);
814 if (unit < 0) {
815 error("expected `Bytes' or `Lines'");
816 return 0;
817 }
818 }
819 }
820 if (type != Binary)
821 return 1;
822 if (outfp) {
823 fputs("%%BeginData: ", outfp);
824 fputs(start, outfp);
825 }
826 if (numberof > 0) {
827 unsigned bytecount = 0;
828 unsigned linecount = 0;
829 do {
830 int c = getc(fp);
831 if (c == EOF) {
832 error("end of file within data section");
833 return 0;
834 }
835 if (outfp)
836 putc(c, outfp);
837 bytecount++;
838 if (c == '\r') {
839 int cc = getc(fp);
840 if (cc != '\n') {
841 linecount++;
842 current_lineno++;
843 }
844 if (cc != EOF)
845 ungetc(c, fp);
846 }
847 else if (c == '\n') {
848 linecount++;
849 current_lineno++;
850 }
851 } while ((unit == Bytes ? bytecount : linecount) < numberof);
852 }
853 skip_possible_newline(fp, outfp);
854 string buf;
855 if (!ps_get_line(buf, fp)) {
856 error("missing %%%%EndData line");
857 return 0;
858 }
859 if (!matches_comment(buf, "EndData"))
860 error("bad %%%%EndData line");
861 if (outfp)
862 fputs(buf.contents(), outfp);
863 return 0;
864 }
865
do_begin_binary(const char * ptr,int,FILE * fp,FILE * outfp)866 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
867 FILE *outfp)
868 {
869 if (!outfp)
870 return 0;
871 unsigned count;
872 if (!read_uint_arg(&ptr, &count))
873 return 0;
874 if (outfp)
875 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
876 while (count != 0) {
877 int c = getc(fp);
878 if (c == EOF) {
879 error("end of file within binary section");
880 return 0;
881 }
882 if (outfp)
883 putc(c, outfp);
884 --count;
885 if (c == '\r') {
886 int cc = getc(fp);
887 if (cc != '\n')
888 current_lineno++;
889 if (cc != EOF)
890 ungetc(cc, fp);
891 }
892 else if (c == '\n')
893 current_lineno++;
894 }
895 skip_possible_newline(fp, outfp);
896 string buf;
897 if (!ps_get_line(buf, fp)) {
898 error("missing %%%%EndBinary line");
899 return 0;
900 }
901 if (!matches_comment(buf, "EndBinary")) {
902 error("bad %%%%EndBinary line");
903 if (outfp)
904 fputs(buf.contents(), outfp);
905 }
906 else if (outfp)
907 fputs("%%EndData\n", outfp);
908 return 0;
909 }
910
parse_extensions(const char * ptr)911 static unsigned parse_extensions(const char *ptr)
912 {
913 unsigned flags = 0;
914 for (;;) {
915 while (white_space(*ptr))
916 ptr++;
917 if (*ptr == '\0')
918 break;
919 const char *name = ptr;
920 do {
921 ++ptr;
922 } while (*ptr != '\0' && !white_space(*ptr));
923 int i;
924 for (i = 0; i < NEXTENSIONS; i++)
925 if (strlen(extension_table[i]) == size_t(ptr - name)
926 && memcmp(extension_table[i], name, ptr - name) == 0) {
927 flags |= (1 << i);
928 break;
929 }
930 if (i >= NEXTENSIONS) {
931 string s(name, ptr - name);
932 s += '\0';
933 error("unknown extension `%1'", s.contents());
934 }
935 }
936 return flags;
937 }
938
939 // XXX if it has not been surrounded with {Begin,End}Document need to strip
940 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
941
942 // XXX Perhaps the decision whether to use BeginDocument or
943 // BeginResource: file should be postponed till we have seen
944 // the first line of the file.
945
process_file(int rank,FILE * fp,const char * filename,FILE * outfp)946 void resource_manager::process_file(int rank, FILE *fp, const char *filename,
947 FILE *outfp)
948 {
949 // If none of these comments appear in the header section, and we are
950 // just analyzing the file (ie outfp is 0), then we can return immediately.
951 static const char *header_comment_table[] = {
952 "DocumentNeededResources:",
953 "DocumentSuppliedResources:",
954 "DocumentNeededFonts:",
955 "DocumentSuppliedFonts:",
956 "DocumentNeededProcSets:",
957 "DocumentSuppliedProcSets:",
958 "DocumentNeededFiles:",
959 "DocumentSuppliedFiles:",
960 };
961
962 const int NHEADER_COMMENTS = sizeof(header_comment_table)
963 / sizeof(header_comment_table[0]);
964 struct comment_info {
965 const char *name;
966 int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
967 };
968
969 static comment_info comment_table[] = {
970 { "BeginResource:", &resource_manager::do_begin_resource },
971 { "IncludeResource:", &resource_manager::do_include_resource },
972 { "BeginDocument:", &resource_manager::do_begin_document },
973 { "IncludeDocument:", &resource_manager::do_include_document },
974 { "BeginProcSet:", &resource_manager::do_begin_procset },
975 { "IncludeProcSet:", &resource_manager::do_include_procset },
976 { "BeginFont:", &resource_manager::do_begin_font },
977 { "IncludeFont:", &resource_manager::do_include_font },
978 { "BeginFile:", &resource_manager::do_begin_file },
979 { "IncludeFile:", &resource_manager::do_include_file },
980 { "EndProcSet", &resource_manager::change_to_end_resource },
981 { "EndFont", &resource_manager::change_to_end_resource },
982 { "EndFile", &resource_manager::change_to_end_resource },
983 { "BeginPreview:", &resource_manager::do_begin_preview },
984 { "BeginData:", &resource_manager::do_begin_data },
985 { "BeginBinary:", &resource_manager::do_begin_binary },
986 };
987
988 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
989 string buf;
990 int saved_lineno = current_lineno;
991 const char *saved_filename = current_filename;
992 current_filename = filename;
993 current_lineno = 0;
994 if (!ps_get_line(buf, fp)) {
995 current_filename = saved_filename;
996 current_lineno = saved_lineno;
997 return;
998 }
999 if ((size_t)buf.length() < sizeof(PS_MAGIC)
1000 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
1001 if (outfp) {
1002 do {
1003 if (!(broken_flags & STRIP_PERCENT_BANG)
1004 || buf[0] != '%' || buf[1] != '!')
1005 fputs(buf.contents(), outfp);
1006 } while (ps_get_line(buf, fp));
1007 }
1008 }
1009 else {
1010 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
1011 fputs(buf.contents(), outfp);
1012 int in_header = 1;
1013 int interesting = 0;
1014 int had_extensions_comment = 0;
1015 int had_language_level_comment = 0;
1016 for (;;) {
1017 if (!ps_get_line(buf, fp))
1018 break;
1019 int copy_this_line = 1;
1020 if (buf[0] == '%') {
1021 if (buf[1] == '%') {
1022 const char *ptr;
1023 int i;
1024 for (i = 0; i < NCOMMENTS; i++)
1025 if ((ptr = matches_comment(buf, comment_table[i].name))) {
1026 copy_this_line
1027 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
1028 break;
1029 }
1030 if (i >= NCOMMENTS && in_header) {
1031 if ((ptr = matches_comment(buf, "EndComments")))
1032 in_header = 0;
1033 else if (!had_extensions_comment
1034 && (ptr = matches_comment(buf, "Extensions:"))) {
1035 extensions |= parse_extensions(ptr);
1036 // XXX handle possibility that next line is %%+
1037 had_extensions_comment = 1;
1038 }
1039 else if (!had_language_level_comment
1040 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
1041 unsigned ll;
1042 if (read_uint_arg(&ptr, &ll) && ll > language_level)
1043 language_level = ll;
1044 had_language_level_comment = 1;
1045 }
1046 else {
1047 for (i = 0; i < NHEADER_COMMENTS; i++)
1048 if (matches_comment(buf, header_comment_table[i])) {
1049 interesting = 1;
1050 break;
1051 }
1052 }
1053 }
1054 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
1055 && (matches_comment(buf, "EndProlog")
1056 || matches_comment(buf, "Page:")
1057 || matches_comment(buf, "Trailer")))
1058 copy_this_line = 0;
1059 }
1060 else if (buf[1] == '!') {
1061 if (broken_flags & STRIP_PERCENT_BANG)
1062 copy_this_line = 0;
1063 }
1064 }
1065 else
1066 in_header = 0;
1067 if (!outfp && !in_header && !interesting)
1068 break;
1069 if (copy_this_line && outfp)
1070 fputs(buf.contents(), outfp);
1071 }
1072 }
1073 current_filename = saved_filename;
1074 current_lineno = saved_lineno;
1075 }
1076
read_download_file()1077 void resource_manager::read_download_file()
1078 {
1079 char *path = 0;
1080 FILE *fp = font::open_file("download", &path);
1081 if (!fp)
1082 fatal("can't find `download'");
1083 char buf[512];
1084 int lineno = 0;
1085 while (fgets(buf, sizeof(buf), fp)) {
1086 lineno++;
1087 char *p = strtok(buf, " \t\r\n");
1088 if (p == 0 || *p == '#')
1089 continue;
1090 char *q = strtok(0, " \t\r\n");
1091 if (!q)
1092 fatal_with_file_and_line(path, lineno, "missing filename");
1093 lookup_font(p)->filename = strsave(q);
1094 }
1095 a_delete path;
1096 fclose(fp);
1097 }
1098
1099 // XXX Can we share some code with ps_output::put_string()?
1100
print_ps_string(const string & s,FILE * outfp)1101 static void print_ps_string(const string &s, FILE *outfp)
1102 {
1103 int len = s.length();
1104 const char *str = s.contents();
1105 int funny = 0;
1106 if (str[0] == '(')
1107 funny = 1;
1108 else {
1109 for (int i = 0; i < len; i++)
1110 if (str[i] <= 040 || str[i] > 0176) {
1111 funny = 1;
1112 break;
1113 }
1114 }
1115 if (!funny) {
1116 put_string(s, outfp);
1117 return;
1118 }
1119 int level = 0;
1120 int i;
1121 for (i = 0; i < len; i++)
1122 if (str[i] == '(')
1123 level++;
1124 else if (str[i] == ')' && --level < 0)
1125 break;
1126 putc('(', outfp);
1127 for (i = 0; i < len; i++)
1128 switch (str[i]) {
1129 case '(':
1130 case ')':
1131 if (level != 0)
1132 putc('\\', outfp);
1133 putc(str[i], outfp);
1134 break;
1135 case '\\':
1136 fputs("\\\\", outfp);
1137 break;
1138 case '\n':
1139 fputs("\\n", outfp);
1140 break;
1141 case '\r':
1142 fputs("\\r", outfp);
1143 break;
1144 case '\t':
1145 fputs("\\t", outfp);
1146 break;
1147 case '\b':
1148 fputs("\\b", outfp);
1149 break;
1150 case '\f':
1151 fputs("\\f", outfp);
1152 break;
1153 default:
1154 if (str[i] < 040 || str[i] > 0176)
1155 fprintf(outfp, "\\%03o", str[i] & 0377);
1156 else
1157 putc(str[i], outfp);
1158 break;
1159 }
1160 putc(')', outfp);
1161 }
1162
print_extensions_comment(FILE * outfp)1163 void resource_manager::print_extensions_comment(FILE *outfp)
1164 {
1165 if (extensions) {
1166 fputs("%%Extensions:", outfp);
1167 for (int i = 0; i < NEXTENSIONS; i++)
1168 if (extensions & (1 << i)) {
1169 putc(' ', outfp);
1170 fputs(extension_table[i], outfp);
1171 }
1172 putc('\n', outfp);
1173 }
1174 }
1175
print_language_level_comment(FILE * outfp)1176 void resource_manager::print_language_level_comment(FILE *outfp)
1177 {
1178 if (language_level)
1179 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
1180 }
1181