2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include "stringclass.h"
27 #define PROLOGUE "prologue"
29 static void print_ps_string(const string &s, FILE *outfp);
31 cset white_space("\n\r \t");
32 string an_empty_string;
34 const char *extension_table[] = {
41 const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
43 const char *resource_table[] = {
52 const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
58 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
64 resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
66 void print_type_and_name(FILE *outfp);
69 resource::resource(resource_type t, string &n, string &v, unsigned r)
70 : type(t), revision(r), flags (0), filename(0), rank(-1), next(0)
74 if (type == RESOURCE_FILE) {
75 if (name.search('\0') >= 0)
76 error("filename contains a character with code 0");
77 filename = name.extract();
86 void resource::print_type_and_name(FILE *outfp)
88 fputs(resource_table[type], outfp);
90 print_ps_string(name, outfp);
91 if (type == RESOURCE_PROCSET) {
93 print_ps_string(version, outfp);
94 fprintf(outfp, " %u", revision);
98 resource_manager::resource_manager()
99 : resource_list(0), extensions(0), language_level(0)
101 read_download_file();
102 string procset_name("grops");
103 extern const char *version_string;
104 string procset_version(version_string);
105 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
107 procset_resource->flags |= resource::SUPPLIED;
110 resource_manager::~resource_manager()
112 while (resource_list) {
113 resource *tem = resource_list;
114 resource_list = resource_list->next;
119 resource *resource_manager::lookup_resource(resource_type type,
125 for (r = resource_list; r; r = r->next)
128 && r->version == version
129 && r->revision == revision)
131 r = new resource(type, name, version, revision);
132 r->next = resource_list;
137 // Just a specialized version of lookup_resource().
139 resource *resource_manager::lookup_font(const char *name)
142 for (r = resource_list; r; r = r->next)
143 if (r->type == RESOURCE_FONT
144 && strlen(name) == r->name.length()
145 && memcmp(name, r->name.contents(), r->name.length()) == 0)
148 r = new resource(RESOURCE_FONT, s);
149 r->next = resource_list;
154 void resource_manager::need_font(const char *name)
156 lookup_font(name)->flags |= resource::FONT_NEEDED;
159 typedef resource *Presource; // Work around g++ bug.
161 void resource_manager::document_setup(ps_output &out)
165 for (r = resource_list; r; r = r->next)
166 if (r->rank >= nranks)
167 nranks = r->rank + 1;
169 // Sort resource_list in reverse order of rank.
170 Presource *head = new Presource[nranks + 1];
171 Presource **tail = new Presource *[nranks + 1];
173 for (i = 0; i < nranks + 1; i++) {
177 for (r = resource_list; r; r = r->next) {
178 i = r->rank < 0 ? 0 : r->rank + 1;
180 tail[i] = &(*tail[i])->next;
183 for (i = 0; i < nranks + 1; i++)
185 *tail[i] = resource_list;
186 resource_list = head[i];
191 for (r = resource_list; r; r = r->next)
193 assert(r->rank >= r->next->rank);
194 for (r = resource_list; r; r = r->next)
195 if (r->type == RESOURCE_FONT && r->rank >= 0)
196 supply_resource(r, -1, out.get_file());
200 void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
203 for (resource *r = resource_list; r; r = r->next)
204 if (r->flags & flag) {
206 fputs("%%+ ", outfp);
208 fputs(flag == resource::NEEDED
209 ? "%%DocumentNeededResources: "
210 : "%%DocumentSuppliedResources: ",
214 r->print_type_and_name(outfp);
219 void resource_manager::print_header_comments(ps_output &out)
221 for (resource *r = resource_list; r; r = r->next)
222 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
223 supply_resource(r, 0, 0);
224 print_resources_comment(resource::NEEDED, out.get_file());
225 print_resources_comment(resource::SUPPLIED, out.get_file());
226 print_language_level_comment(out.get_file());
227 print_extensions_comment(out.get_file());
230 void resource_manager::output_prolog(ps_output &out)
232 FILE *outfp = out.get_file();
235 FILE *fp = font::open_file(PROLOGUE, &path);
237 fatal("can't find `%1'", PROLOGUE);
238 fputs("%%BeginResource: ", outfp);
239 procset_resource->print_type_and_name(outfp);
241 process_file(-1, fp, path, outfp);
244 fputs("%%EndResource\n", outfp);
247 void resource_manager::import_file(const char *filename, ps_output &out)
250 string name(filename);
251 resource *r = lookup_resource(RESOURCE_FILE, name);
252 supply_resource(r, -1, out.get_file(), 1);
255 void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
258 if (r->flags & resource::BUSY) {
260 fatal("loop detected in dependency graph for %1 `%2'",
261 resource_table[r->type],
264 r->flags |= resource::BUSY;
269 if (r->filename != 0) {
270 if (r->type == RESOURCE_FONT) {
271 fp = font::open_file(r->filename, &path);
273 error("can't find `%1'", r->filename);
274 a_delete r->filename;
280 fp = fopen(r->filename, "r");
282 error("can't open `%1': %2", r->filename, strerror(errno));
283 a_delete r->filename;
292 if (r->type == RESOURCE_FILE && is_document) {
293 fputs("%%BeginDocument: ", outfp);
294 print_ps_string(r->name, outfp);
298 fputs("%%BeginResource: ", outfp);
299 r->print_type_and_name(outfp);
303 process_file(rank, fp, path, outfp);
305 if (r->type == RESOURCE_FONT)
308 if (r->type == RESOURCE_FILE && is_document)
309 fputs("%%EndDocument\n", outfp);
311 fputs("%%EndResource\n", outfp);
313 r->flags |= resource::SUPPLIED;
317 if (r->type == RESOURCE_FILE && is_document) {
318 fputs("%%IncludeDocument: ", outfp);
319 print_ps_string(r->name, outfp);
323 fputs("%%IncludeResource: ", outfp);
324 r->print_type_and_name(outfp);
328 r->flags |= resource::NEEDED;
330 r->flags &= ~resource::BUSY;
334 #define PS_LINE_MAX 255
335 #define PS_MAGIC "%!PS-Adobe-"
337 static int ps_get_line(char *buf, FILE *fp)
347 while (c != '\r' && c != '\n' && c != EOF) {
348 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
349 error("illegal input character code %1", int(c));
350 else if (i < PS_LINE_MAX)
354 error("PostScript file non-conforming "
355 "because length of line exceeds 255");
363 if (c != EOF && c != '\n')
369 static int read_text_arg(const char **pp, string &res)
372 while (white_space(**pp))
375 error("missing argument");
379 for (; **pp != '\0' && !white_space(**pp); *pp += 1)
387 if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
388 error("missing ')'");
399 else if (**pp == '(') {
403 else if (**pp == '\\') {
430 int val = **pp - '0';
431 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
433 val = val*8 + (**pp - '0');
434 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
436 val = val*8 + (**pp - '0');
453 static int read_uint_arg(const char **pp, unsigned *res)
455 while (white_space(**pp))
458 error("missing argument");
461 const char *start = *pp;
463 long n = strtol(start, (char **)pp, 10);
464 if (n == 0 && *pp == start) {
465 error("not an integer");
469 error("argument must not be negative");
476 resource *resource_manager::read_file_arg(const char **ptr)
479 if (!read_text_arg(ptr, arg))
481 return lookup_resource(RESOURCE_FILE, arg);
484 resource *resource_manager::read_font_arg(const char **ptr)
487 if (!read_text_arg(ptr, arg))
489 return lookup_resource(RESOURCE_FONT, arg);
492 resource *resource_manager::read_procset_arg(const char **ptr)
495 if (!read_text_arg(ptr, arg))
498 if (!read_text_arg(ptr, version))
501 if (!read_uint_arg(ptr, &revision))
503 return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
506 resource *resource_manager::read_resource_arg(const char **ptr)
508 while (white_space(**ptr))
510 const char *name = *ptr;
511 while (**ptr != '\0' && !white_space(**ptr))
514 error("missing resource type");
518 for (ri = 0; ri < NRESOURCES; ri++)
519 if (strlen(resource_table[ri]) == *ptr - name
520 && memcmp(resource_table[ri], name, *ptr - name) == 0)
522 if (ri >= NRESOURCES) {
523 error("unknown resource type");
526 if (ri == RESOURCE_PROCSET)
527 return read_procset_arg(ptr);
529 if (!read_text_arg(ptr, arg))
531 return lookup_resource(resource_type(ri), arg);
534 static const char *matches_comment(const char *buf, const char *comment)
536 if (buf[0] != '%' || buf[1] != '%')
538 for (buf += 2; *comment; comment++, buf++)
539 if (*buf != *comment)
541 if (comment[-1] == ':')
543 if (*buf == '\0' || white_space(*buf))
548 // Return 1 if the line should be copied out.
550 int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
553 resource *r = read_resource_arg(&ptr);
555 r->flags |= resource::SUPPLIED;
559 int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
562 resource *r = read_resource_arg(&ptr);
564 if (r->type == RESOURCE_FONT) {
566 supply_resource(r, rank + 1, outfp);
568 r->flags |= resource::FONT_NEEDED;
571 supply_resource(r, rank, outfp);
576 int resource_manager::do_begin_document(const char *ptr, int, FILE *,
579 resource *r = read_file_arg(&ptr);
581 r->flags |= resource::SUPPLIED;
585 int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
588 resource *r = read_file_arg(&ptr);
590 supply_resource(r, rank, outfp, 1);
594 int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
597 resource *r = read_procset_arg(&ptr);
599 r->flags |= resource::SUPPLIED;
601 fputs("%%BeginResource: ", outfp);
602 r->print_type_and_name(outfp);
609 int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
612 resource *r = read_procset_arg(&ptr);
614 supply_resource(r, rank, outfp);
618 int resource_manager::do_begin_file(const char *ptr, int, FILE *,
621 resource *r = read_file_arg(&ptr);
623 r->flags |= resource::SUPPLIED;
625 fputs("%%BeginResource: ", outfp);
626 r->print_type_and_name(outfp);
633 int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
636 resource *r = read_file_arg(&ptr);
638 supply_resource(r, rank, outfp);
642 int resource_manager::do_begin_font(const char *ptr, int, FILE *,
645 resource *r = read_font_arg(&ptr);
647 r->flags |= resource::SUPPLIED;
649 fputs("%%BeginResource: ", outfp);
650 r->print_type_and_name(outfp);
657 int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
660 resource *r = read_font_arg(&ptr);
663 supply_resource(r, rank + 1, outfp);
665 r->flags |= resource::FONT_NEEDED;
670 int resource_manager::change_to_end_resource(const char *, int, FILE *,
674 fputs("%%EndResource\n", outfp);
678 int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
680 char buf[PS_LINE_MAX + 2];
682 if (!ps_get_line(buf, fp)) {
683 error("end of file in preview section");
686 } while (!matches_comment(buf, "EndPreview"));
690 int read_one_of(const char **ptr, const char **s, int n)
692 while (white_space(**ptr))
696 const char *start = *ptr;
699 } while (**ptr != '\0' && !white_space(**ptr));
700 for (int i = 0; i < n; i++)
701 if (strlen(s[i]) == *ptr - start
702 && memcmp(s[i], start, *ptr - start) == 0)
707 int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
710 while (white_space(*ptr))
712 const char *start = ptr;
714 if (!read_uint_arg(&ptr, &numberof))
716 static const char *types[] = { "Binary", "Hex", "ASCII" };
717 const int Binary = 0;
719 static const char *units[] = { "Bytes", "Lines" };
722 while (white_space(*ptr))
725 type = read_one_of(&ptr, types, 3);
727 error("bad data type");
730 while (white_space(*ptr))
733 unit = read_one_of(&ptr, units, 2);
735 error("expected `Bytes' or `Lines'");
743 fputs("%%BeginData: ", outfp);
747 unsigned bytecount = 0;
748 unsigned linecount = 0;
752 error("end of file within data section");
767 else if (c == '\n') {
771 } while ((unit == Bytes ? bytecount : linecount) < numberof);
773 char buf[PS_LINE_MAX + 2];
774 if (!ps_get_line(buf, fp)) {
775 error("missing %%%%EndData line");
778 if (!matches_comment(buf, "EndData"))
779 error("bad %%%%EndData line");
785 int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
791 if (!read_uint_arg(&ptr, &count))
794 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
798 error("end of file within binary section");
814 char buf[PS_LINE_MAX + 2];
815 if (!ps_get_line(buf, fp)) {
816 error("missing %%%%EndBinary line");
819 if (!matches_comment(buf, "EndBinary")) {
820 error("bad %%%%EndBinary line");
825 fputs("%%EndData\n", outfp);
829 static unsigned parse_extensions(const char *ptr)
833 while (white_space(*ptr))
837 const char *name = ptr;
840 } while (*ptr != '\0' && !white_space(*ptr));
842 for (i = 0; i < NEXTENSIONS; i++)
843 if (strlen(extension_table[i]) == ptr - name
844 && memcmp(extension_table[i], name, ptr - name) == 0) {
848 if (i >= NEXTENSIONS) {
849 string s(name, ptr - name);
851 error("unknown extension `%1'", s.contents());
857 // XXX if it has not been surrounded with {Begin,End}Document need to strip
858 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
860 // XXX Perhaps the decision whether to use BeginDocument or
861 // BeginResource: file should be postponed till we have seen
862 // the first line of the file.
864 void resource_manager::process_file(int rank, FILE *fp, const char *filename,
867 // If none of these comments appear in the header section, and we are
868 // just analyzing the file (ie outfp is 0), then we can return immediately.
869 static const char *header_comment_table[] = {
870 "DocumentNeededResources:",
871 "DocumentSuppliedResources:",
872 "DocumentNeededFonts:",
873 "DocumentSuppliedFonts:",
874 "DocumentNeededProcSets:",
875 "DocumentSuppliedProcSets:",
876 "DocumentNeededFiles:",
877 "DocumentSuppliedFiles:",
880 const int NHEADER_COMMENTS = (sizeof(header_comment_table)
881 / sizeof(header_comment_table[0]));
882 struct comment_info {
884 int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
887 static comment_info comment_table[] = {
888 { "BeginResource:", &resource_manager::do_begin_resource },
889 { "IncludeResource:", &resource_manager::do_include_resource },
890 { "BeginDocument:", &resource_manager::do_begin_document },
891 { "IncludeDocument:", &resource_manager::do_include_document },
892 { "BeginProcSet:", &resource_manager::do_begin_procset },
893 { "IncludeProcSet:", &resource_manager::do_include_procset },
894 { "BeginFont:", &resource_manager::do_begin_font },
895 { "IncludeFont:", &resource_manager::do_include_font },
896 { "BeginFile:", &resource_manager::do_begin_file },
897 { "IncludeFile:", &resource_manager::do_include_file },
898 { "EndProcSet", &resource_manager::change_to_end_resource },
899 { "EndFont", &resource_manager::change_to_end_resource },
900 { "EndFile", &resource_manager::change_to_end_resource },
901 { "BeginPreview:", &resource_manager::do_begin_preview },
902 { "BeginData:", &resource_manager::do_begin_data },
903 { "BeginBinary:", &resource_manager::do_begin_binary },
906 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
907 char buf[PS_LINE_MAX + 2];
908 int saved_lineno = current_lineno;
909 const char *saved_filename = current_filename;
910 current_filename = filename;
912 if (!ps_get_line(buf, fp)) {
913 current_filename = saved_filename;
914 current_lineno = saved_lineno;
917 if (strlen(buf) < sizeof(PS_MAGIC) - 1
918 || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
921 if (!(broken_flags & STRIP_PERCENT_BANG)
922 || buf[0] != '%' || buf[1] != '!')
924 } while (ps_get_line(buf, fp));
928 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
932 int had_extensions_comment = 0;
933 int had_language_level_comment = 0;
935 if (!ps_get_line(buf, fp))
937 int copy_this_line = 1;
942 for (i = 0; i < NCOMMENTS; i++)
943 if (ptr = matches_comment(buf, comment_table[i].name)) {
945 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
948 if (i >= NCOMMENTS && in_header) {
949 if (ptr = matches_comment(buf, "EndComments"))
951 else if (!had_extensions_comment
952 && (ptr = matches_comment(buf, "Extensions:"))) {
953 extensions |= parse_extensions(ptr);
954 // XXX handle possibility that next line is %%+
955 had_extensions_comment = 1;
957 else if (!had_language_level_comment
958 && (ptr = matches_comment(buf, "LanguageLevel:"))) {
960 if (read_uint_arg(&ptr, &ll) && ll > language_level)
962 had_language_level_comment = 1;
965 for (int i = 0; i < NHEADER_COMMENTS; i++)
966 if (matches_comment(buf, header_comment_table[i])) {
972 if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
973 && (matches_comment(buf, "EndProlog")
974 || matches_comment(buf, "Page:")
975 || matches_comment(buf, "Trailer")))
978 else if (buf[1] == '!') {
979 if (broken_flags & STRIP_PERCENT_BANG)
985 if (!outfp && !in_header && !interesting)
987 if (copy_this_line && outfp)
991 current_filename = saved_filename;
992 current_lineno = saved_lineno;
995 void resource_manager::read_download_file()
998 FILE *fp = font::open_file("download", &path);
1000 fatal("can't find `download'");
1003 while (fgets(buf, sizeof(buf), fp)) {
1005 char *p = strtok(buf, " \t\r\n");
1006 if (p == 0 || *p == '#')
1008 char *q = strtok(0, " \t\r\n");
1010 fatal_with_file_and_line(path, lineno, "missing filename");
1011 lookup_font(p)->filename = strsave(q);
1017 // XXX Can we share some code with ps_output::put_string()?
1019 static void print_ps_string(const string &s, FILE *outfp)
1021 int len = s.length();
1022 const char *str = s.contents();
1027 for (int i = 0; i < len; i++)
1028 if (str[i] <= 040 || str[i] > 0176) {
1034 put_string(s, outfp);
1039 for (i = 0; i < len; i++)
1042 else if (str[i] == ')' && --level < 0)
1045 for (i = 0; i < len; i++)
1051 putc(str[i], outfp);
1054 fputs("\\\\", outfp);
1057 fputs("\\n", outfp);
1060 fputs("\\r", outfp);
1063 fputs("\\t", outfp);
1066 fputs("\\b", outfp);
1069 fputs("\\f", outfp);
1072 if (str[i] < 040 || str[i] > 0176)
1073 fprintf(outfp, "\\%03o", str[i] & 0377);
1075 putc(str[i], outfp);
1081 void resource_manager::print_extensions_comment(FILE *outfp)
1084 fputs("%%Extensions:", outfp);
1085 for (int i = 0; i < NEXTENSIONS; i++)
1086 if (extensions & (1 << i)) {
1088 fputs(extension_table[i], outfp);
1094 void resource_manager::print_language_level_comment(FILE *outfp)
1097 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);