1 /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
2 Written by James Clark (jjc@jclark.com)
4 This file is part of groff.
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.
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
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/>. */
20 #include "stringclass.h"
25 #ifdef NEED_DECLARATION_PUTENV
27 int putenv(const char *);
29 #endif /* NEED_DECLARATION_PUTENV */
31 #define GROPS_PROLOGUE "prologue"
33 static void print_ps_string(const string &s, FILE *outfp);
35 cset white_space("\n\r \t\f");
36 string an_empty_string;
38 char valid_input_table[256]= {
39 #ifndef IS_EBCDIC_HOST
40 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
41 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
42 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
43 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
44 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
45 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
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, 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,
58 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
59 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
63 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
64 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
65 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
67 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
68 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
69 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
72 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
73 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
78 const char *extension_table[] = {
85 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
87 // this must stay in sync with 'resource_type' in 'ps.h'
88 const char *resource_table[] = {
98 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
100 static int read_uint_arg(const char **pp, unsigned *res)
102 while (white_space(**pp))
105 error("missing argument");
108 const char *start = *pp;
110 long n = strtol(start, (char **)pp, 10);
111 if (n == 0 && *pp == start) {
112 error("not an integer");
116 error("argument must not be negative");
127 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
133 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
135 void print_type_and_name(FILE *outfp);
138 resource::resource(resource_type t, string &n, string &v, unsigned r)
139 : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
143 if (type == RESOURCE_FILE) {
144 if (name.search('\0') >= 0)
145 error("filename contains a character with code 0");
146 filename = name.extract();
150 resource::~resource()
155 void resource::print_type_and_name(FILE *outfp)
157 fputs(resource_table[type], outfp);
159 print_ps_string(name, outfp);
160 if (type == RESOURCE_PROCSET) {
162 print_ps_string(version, outfp);
163 fprintf(outfp, " %u", revision);
167 resource_manager::resource_manager()
168 : extensions(0), language_level(0), resource_list(0)
170 read_download_file();
171 string procset_name("grops");
172 extern const char *version_string;
173 extern const char *revision_string;
174 unsigned revision_uint;
175 if (!read_uint_arg(&revision_string, &revision_uint))
177 string procset_version(version_string);
178 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
179 procset_version, revision_uint);
180 procset_resource->flags |= resource::SUPPLIED;
183 resource_manager::~resource_manager()
185 while (resource_list) {
186 resource *tem = resource_list;
187 resource_list = resource_list->next;
192 resource *resource_manager::lookup_resource(resource_type type,
198 for (r = resource_list; r; r = r->next)
201 && r->version == version
202 && r->revision == revision)
204 r = new resource(type, name, version, revision);
205 r->next = resource_list;
210 // Just a specialized version of lookup_resource().
212 resource *resource_manager::lookup_font(const char *name)
215 for (r = resource_list; r; r = r->next)
216 if (r->type == RESOURCE_FONT
217 && strlen(name) == (size_t)r->name.length()
218 && memcmp(name, r->name.contents(), r->name.length()) == 0)
221 r = new resource(RESOURCE_FONT, s);
222 r->next = resource_list;
227 void resource_manager::need_font(const char *name)
229 lookup_font(name)->flags |= resource::FONT_NEEDED;
232 typedef resource *Presource; // Work around g++ bug.
234 void resource_manager::document_setup(ps_output &out)
238 for (r = resource_list; r; r = r->next)
239 if (r->rank >= nranks)
240 nranks = r->rank + 1;
242 // Sort resource_list in reverse order of rank.
243 Presource *head = new Presource[nranks + 1];
244 Presource **tail = new Presource *[nranks + 1];
246 for (i = 0; i < nranks + 1; i++) {
250 for (r = resource_list; r; r = r->next) {
251 i = r->rank < 0 ? 0 : r->rank + 1;
253 tail[i] = &(*tail[i])->next;
256 for (i = 0; i < nranks + 1; i++)
258 *tail[i] = resource_list;
259 resource_list = head[i];
264 for (r = resource_list; r; r = r->next)
266 assert(r->rank >= r->next->rank);
267 for (r = resource_list; r; r = r->next)
268 if (r->type == RESOURCE_FONT && r->rank >= 0)
269 supply_resource(r, -1, out.get_file());
273 void resource_manager::print_resources_comment(unsigned flag,
277 for (resource *r = resource_list; r; r = r->next)
278 if (r->flags & flag) {
280 fputs("%%+ ", outfp);
282 fputs(flag == resource::NEEDED
283 ? "%%DocumentNeededResources: "
284 : "%%DocumentSuppliedResources: ",
288 r->print_type_and_name(outfp);
293 void resource_manager::print_header_comments(ps_output &out)
295 for (resource *r = resource_list; r; r = r->next)
296 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
297 supply_resource(r, 0, 0);
298 print_resources_comment(resource::NEEDED, out.get_file());
299 print_resources_comment(resource::SUPPLIED, out.get_file());
300 print_language_level_comment(out.get_file());
301 print_extensions_comment(out.get_file());
304 void resource_manager::output_prolog(ps_output &out)
306 FILE *outfp = out.get_file();
309 if (!getenv("GROPS_PROLOGUE")) {
310 string e = "GROPS_PROLOGUE";
314 if (putenv(strsave(e.contents())))
315 fatal("putenv failed");
317 char *prologue = getenv("GROPS_PROLOGUE");
318 FILE *fp = font::open_file(prologue, &path);
320 fatal("failed to open PostScript prologue '%1': %2", prologue,
322 fputs("%%BeginResource: ", outfp);
323 procset_resource->print_type_and_name(outfp);
325 process_file(-1, fp, path, outfp);
328 fputs("%%EndResource\n", outfp);
331 void resource_manager::import_file(const char *filename, ps_output &out)
334 string name(filename);
335 resource *r = lookup_resource(RESOURCE_FILE, name);
336 supply_resource(r, -1, out.get_file(), 1);
339 void resource_manager::supply_resource(resource *r, int rank,
340 FILE *outfp, int is_document)
342 if (r->flags & resource::BUSY) {
344 fatal("loop detected in dependency graph for %1 '%2'",
345 resource_table[r->type],
348 r->flags |= resource::BUSY;
351 char *path = 0 /* nullptr */;
352 FILE *fp = 0 /* nullptr */;
353 if (r->filename != 0 /* nullptr */) {
354 if (r->type == RESOURCE_FONT) {
355 fp = font::open_file(r->filename, &path);
357 error("failed to open PostScript resource '%1': %2",
358 r->filename, strerror(errno));
359 delete[] r->filename;
360 r->filename = 0 /* nullptr */;
365 fp = include_search_path.open_file_cautious(r->filename);
367 error("can't open '%1': %2", r->filename, strerror(errno));
368 delete[] r->filename;
369 r->filename = 0 /* nullptr */;
377 if (r->type == RESOURCE_FILE && is_document) {
378 fputs("%%BeginDocument: ", outfp);
379 print_ps_string(r->name, outfp);
383 fputs("%%BeginResource: ", outfp);
384 r->print_type_and_name(outfp);
388 process_file(rank, fp, path, outfp);
390 if (r->type == RESOURCE_FONT)
393 if (r->type == RESOURCE_FILE && is_document)
394 fputs("%%EndDocument\n", outfp);
396 fputs("%%EndResource\n", outfp);
398 r->flags |= resource::SUPPLIED;
402 if (r->type == RESOURCE_FILE && is_document) {
403 fputs("%%IncludeDocument: ", outfp);
404 print_ps_string(r->name, outfp);
408 fputs("%%IncludeResource: ", outfp);
409 r->print_type_and_name(outfp);
413 r->flags |= resource::NEEDED;
415 r->flags &= ~resource::BUSY;
418 #define PS_MAGIC "%!PS-Adobe-"
420 static int ps_get_line(string &buf, FILE *fp)
427 while (c != '\r' && c != '\n' && c != EOF) {
428 if (!valid_input_table[c])
429 error("invalid input character code %1", int(c));
437 if (c != EOF && c != '\n')
443 static int read_text_arg(const char **pp, string &res)
446 while (white_space(**pp))
449 error("missing argument");
453 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
461 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
462 error("missing ')'");
473 else if (**pp == '(') {
477 else if (**pp == '\\') {
504 int val = **pp - '0';
505 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
507 val = val*8 + (**pp - '0');
508 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
510 val = val*8 + (**pp - '0');
527 resource *resource_manager::read_file_arg(const char **ptr)
530 if (!read_text_arg(ptr, arg))
532 return lookup_resource(RESOURCE_FILE, arg);
535 resource *resource_manager::read_font_arg(const char **ptr)
538 if (!read_text_arg(ptr, arg))
540 return lookup_resource(RESOURCE_FONT, arg);
543 resource *resource_manager::read_procset_arg(const char **ptr)
546 if (!read_text_arg(ptr, arg))
549 if (!read_text_arg(ptr, version))
552 if (!read_uint_arg(ptr, &revision))
554 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
557 resource *resource_manager::read_resource_arg(const char **ptr)
559 while (white_space(**ptr))
561 const char *name = *ptr;
562 while (**ptr != '\0' && !white_space(**ptr))
565 error("missing resource type");
569 for (ri = 0; ri < NRESOURCES; ri++)
570 if (strlen(resource_table[ri]) == size_t(*ptr - name)
571 && strncasecmp(resource_table[ri], name, *ptr - name) == 0)
573 if (ri >= NRESOURCES) {
574 error("unknown resource type");
577 if (ri == RESOURCE_PROCSET)
578 return read_procset_arg(ptr);
580 if (!read_text_arg(ptr, arg))
582 return lookup_resource(resource_type(ri), arg);
585 static const char *matches_comment(string &buf, const char *comment)
587 if ((size_t)buf.length() < strlen(comment) + 3)
589 if (buf[0] != '%' || buf[1] != '%')
591 const char *bufp = buf.contents() + 2;
592 for (; *comment; comment++, bufp++)
593 if (*bufp != *comment)
595 if (comment[-1] == ':')
597 if (*bufp == '\0' || white_space(*bufp))
602 // Return 1 if the line should be copied out.
604 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
607 resource *r = read_resource_arg(&ptr);
609 r->flags |= resource::SUPPLIED;
613 int resource_manager::do_include_resource(const char *ptr, int rank,
616 resource *r = read_resource_arg(&ptr);
618 if (r->type == RESOURCE_FONT) {
620 supply_resource(r, rank + 1, outfp);
622 r->flags |= resource::FONT_NEEDED;
625 supply_resource(r, rank, outfp);
630 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
633 resource *r = read_file_arg(&ptr);
635 r->flags |= resource::SUPPLIED;
639 int resource_manager::do_include_document(const char *ptr, int rank,
642 resource *r = read_file_arg(&ptr);
644 supply_resource(r, rank, outfp, 1);
648 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
651 resource *r = read_procset_arg(&ptr);
653 r->flags |= resource::SUPPLIED;
655 fputs("%%BeginResource: ", outfp);
656 r->print_type_and_name(outfp);
663 int resource_manager::do_include_procset(const char *ptr, int rank,
666 resource *r = read_procset_arg(&ptr);
668 supply_resource(r, rank, outfp);
672 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
675 resource *r = read_file_arg(&ptr);
677 r->flags |= resource::SUPPLIED;
679 fputs("%%BeginResource: ", outfp);
680 r->print_type_and_name(outfp);
687 int resource_manager::do_include_file(const char *ptr, int rank,
690 resource *r = read_file_arg(&ptr);
692 supply_resource(r, rank, outfp);
696 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
699 resource *r = read_font_arg(&ptr);
701 r->flags |= resource::SUPPLIED;
703 fputs("%%BeginResource: ", outfp);
704 r->print_type_and_name(outfp);
711 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
714 resource *r = read_font_arg(&ptr);
717 supply_resource(r, rank + 1, outfp);
719 r->flags |= resource::FONT_NEEDED;
724 int resource_manager::change_to_end_resource(const char *, int, FILE *,
728 fputs("%%EndResource\n", outfp);
732 int resource_manager::do_begin_preview(const char *, int, FILE *fp,
737 if (!ps_get_line(buf, fp)) {
738 error("end of file in preview section");
741 } while (!matches_comment(buf, "EndPreview"));
745 int read_one_of(const char **ptr, const char **s, int n)
747 while (white_space(**ptr))
751 const char *start = *ptr;
754 } while (**ptr != '\0' && !white_space(**ptr));
755 for (int i = 0; i < n; i++)
756 if (strlen(s[i]) == size_t(*ptr - start)
757 && memcmp(s[i], start, *ptr - start) == 0)
762 void skip_possible_newline(FILE *fp, FILE *outfp)
779 else if (c == '\n') {
788 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
791 while (white_space(*ptr))
793 const char *start = ptr;
795 if (!read_uint_arg(&ptr, &numberof))
797 static const char *types[] = { "Binary", "Hex", "ASCII" };
798 const int Binary = 0;
800 static const char *units[] = { "Bytes", "Lines" };
803 while (white_space(*ptr))
806 type = read_one_of(&ptr, types, 3);
808 error("bad data type");
811 while (white_space(*ptr))
814 unit = read_one_of(&ptr, units, 2);
816 error("expected 'Bytes' or 'Lines'");
824 fputs("%%BeginData: ", outfp);
828 unsigned bytecount = 0;
829 unsigned linecount = 0;
833 error("end of file within data section");
848 else if (c == '\n') {
852 } while ((unit == Bytes ? bytecount : linecount) < numberof);
854 skip_possible_newline(fp, outfp);
856 if (!ps_get_line(buf, fp)) {
857 error("missing %%%%EndData line");
860 if (!matches_comment(buf, "EndData"))
861 error("bad %%%%EndData line");
863 fputs(buf.contents(), outfp);
867 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
873 if (!read_uint_arg(&ptr, &count))
876 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
880 error("end of file within binary section");
896 skip_possible_newline(fp, outfp);
898 if (!ps_get_line(buf, fp)) {
899 error("missing %%%%EndBinary line");
902 if (!matches_comment(buf, "EndBinary")) {
903 error("bad %%%%EndBinary line");
905 fputs(buf.contents(), outfp);
908 fputs("%%EndData\n", outfp);
912 static unsigned parse_extensions(const char *ptr)
916 while (white_space(*ptr))
920 const char *name = ptr;
923 } while (*ptr != '\0' && !white_space(*ptr));
925 for (i = 0; i < NEXTENSIONS; i++)
926 if (strlen(extension_table[i]) == size_t(ptr - name)
927 && memcmp(extension_table[i], name, ptr - name) == 0) {
931 if (i >= NEXTENSIONS) {
932 string s(name, ptr - name);
934 error("unknown extension '%1'", s.contents());
940 // XXX if it has not been surrounded with {Begin,End}Document need to
941 // strip out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
943 // XXX Perhaps the decision whether to use BeginDocument or
944 // BeginResource: file should be postponed till we have seen
945 // the first line of the file.
947 void resource_manager::process_file(int rank, FILE *fp,
948 const char *filename, FILE *outfp)
950 // If none of these comments appear in the header section, and we are
951 // just analyzing the file (i.e., outfp is 0), then we can return
953 static const char *header_comment_table[] = {
954 "DocumentNeededResources:",
955 "DocumentSuppliedResources:",
956 "DocumentNeededFonts:",
957 "DocumentSuppliedFonts:",
958 "DocumentNeededProcSets:",
959 "DocumentSuppliedProcSets:",
960 "DocumentNeededFiles:",
961 "DocumentSuppliedFiles:",
964 const int NHEADER_COMMENTS = sizeof(header_comment_table)
965 / sizeof(header_comment_table[0]);
966 struct comment_info {
968 int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
971 static const comment_info comment_table[] = {
972 { "BeginResource:", &resource_manager::do_begin_resource },
973 { "IncludeResource:", &resource_manager::do_include_resource },
974 { "BeginDocument:", &resource_manager::do_begin_document },
975 { "IncludeDocument:", &resource_manager::do_include_document },
976 { "BeginProcSet:", &resource_manager::do_begin_procset },
977 { "IncludeProcSet:", &resource_manager::do_include_procset },
978 { "BeginFont:", &resource_manager::do_begin_font },
979 { "IncludeFont:", &resource_manager::do_include_font },
980 { "BeginFile:", &resource_manager::do_begin_file },
981 { "IncludeFile:", &resource_manager::do_include_file },
982 { "EndProcSet", &resource_manager::change_to_end_resource },
983 { "EndFont", &resource_manager::change_to_end_resource },
984 { "EndFile", &resource_manager::change_to_end_resource },
985 { "BeginPreview:", &resource_manager::do_begin_preview },
986 { "BeginData:", &resource_manager::do_begin_data },
987 { "BeginBinary:", &resource_manager::do_begin_binary },
990 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
992 int saved_lineno = current_lineno;
993 const char *saved_filename = current_filename;
994 current_filename = filename;
996 if (!ps_get_line(buf, fp)) {
997 current_filename = saved_filename;
998 current_lineno = saved_lineno;
1001 if ((size_t)buf.length() < sizeof(PS_MAGIC)
1002 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
1005 if (!(broken_flags & STRIP_PERCENT_BANG)
1006 || buf[0] != '%' || buf[1] != '!')
1007 fputs(buf.contents(), outfp);
1008 } while (ps_get_line(buf, fp));
1012 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
1013 fputs(buf.contents(), outfp);
1015 int interesting = 0;
1016 int had_extensions_comment = 0;
1017 int had_language_level_comment = 0;
1019 if (!ps_get_line(buf, fp))
1021 int copy_this_line = 1;
1022 if (buf[0] == '%') {
1023 if (buf[1] == '%') {
1026 for (i = 0; i < NCOMMENTS; i++)
1027 if ((ptr = matches_comment(buf, comment_table[i].name))) {
1029 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
1032 if (i >= NCOMMENTS && in_header) {
1033 if ((ptr = matches_comment(buf, "EndComments")))
1035 else if (!had_extensions_comment
1036 && (ptr = matches_comment(buf, "Extensions:"))) {
1037 extensions |= parse_extensions(ptr);
1038 // XXX handle possibility that next line is %%+
1039 had_extensions_comment = 1;
1041 else if (!had_language_level_comment
1042 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
1044 if (read_uint_arg(&ptr, &ll) && ll > language_level)
1045 language_level = ll;
1046 had_language_level_comment = 1;
1049 for (i = 0; i < NHEADER_COMMENTS; i++)
1050 if (matches_comment(buf, header_comment_table[i])) {
1056 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
1057 && (matches_comment(buf, "EndProlog")
1058 || matches_comment(buf, "Page:")
1059 || matches_comment(buf, "Trailer")))
1062 else if (buf[1] == '!') {
1063 if (broken_flags & STRIP_PERCENT_BANG)
1069 if (!outfp && !in_header && !interesting)
1071 if (copy_this_line && outfp)
1072 fputs(buf.contents(), outfp);
1075 current_filename = saved_filename;
1076 current_lineno = saved_lineno;
1079 void resource_manager::read_download_file()
1081 char *path = 0 /* nullptr */;
1082 FILE *fp = font::open_file("download", &path);
1083 if (0 /* nullptr */ == fp)
1084 fatal("failed to open 'download' file: %1", strerror(errno));
1087 while (fgets(buf, sizeof(buf), fp)) {
1089 char *p = strtok(buf, " \t\r\n");
1090 if (p == 0 /* nullptr */ || *p == '#')
1092 char *q = strtok(0 /* nullptr */, " \t\r\n");
1094 fatal_with_file_and_line(path, lineno, "file name missing for"
1096 lookup_font(p)->filename = strsave(q);
1102 // XXX Can we share some code with ps_output::put_string()?
1104 static void print_ps_string(const string &s, FILE *outfp)
1106 int len = s.length();
1107 const char *str = s.contents();
1112 for (int i = 0; i < len; i++)
1113 if (str[i] <= 040 || str[i] > 0176) {
1119 put_string(s, outfp);
1124 for (i = 0; i < len; i++)
1127 else if (str[i] == ')' && --level < 0)
1130 for (i = 0; i < len; i++)
1136 putc(str[i], outfp);
1139 fputs("\\\\", outfp);
1142 fputs("\\n", outfp);
1145 fputs("\\r", outfp);
1148 fputs("\\t", outfp);
1151 fputs("\\b", outfp);
1154 fputs("\\f", outfp);
1157 if (str[i] < 040 || str[i] > 0176)
1158 fprintf(outfp, "\\%03o", str[i] & 0377);
1160 putc(str[i], outfp);
1166 void resource_manager::print_extensions_comment(FILE *outfp)
1169 fputs("%%Extensions:", outfp);
1170 for (int i = 0; i < NEXTENSIONS; i++)
1171 if (extensions & (1 << i)) {
1173 fputs(extension_table[i], outfp);
1179 void resource_manager::print_language_level_comment(FILE *outfp)
1182 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
1189 // vim: set cindent noexpandtab shiftwidth=2 textwidth=72: