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
);