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