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, 675 Mass Ave, Cambridge, MA 02139, 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
,
124 for (resource
*r
= resource_list
; r
; r
= r
->next
)
127 && r
->version
== version
128 && r
->revision
== revision
)
130 r
= new resource(type
, name
, version
, revision
);
131 r
->next
= resource_list
;
136 // Just a specialized version of lookup_resource().
138 resource
*resource_manager::lookup_font(const char *name
)
140 for (resource
*r
= resource_list
; r
; r
= r
->next
)
141 if (r
->type
== RESOURCE_FONT
142 && strlen(name
) == r
->name
.length()
143 && memcmp(name
, r
->name
.contents(), r
->name
.length()) == 0)
146 r
= new resource(RESOURCE_FONT
, s
);
147 r
->next
= resource_list
;
152 void resource_manager::need_font(const char *name
)
154 lookup_font(name
)->flags
|= resource::FONT_NEEDED
;
157 typedef resource
*Presource
; // Work around g++ bug.
159 void resource_manager::document_setup(ps_output
&out
)
162 for (resource
*r
= resource_list
; r
; r
= r
->next
)
163 if (r
->rank
>= nranks
)
164 nranks
= r
->rank
+ 1;
166 // Sort resource_list in reverse order of rank.
167 Presource
*head
= new Presource
[nranks
+ 1];
168 Presource
**tail
= new Presource
*[nranks
+ 1];
169 for (int i
= 0; i
< nranks
+ 1; i
++) {
173 for (r
= resource_list
; r
; r
= r
->next
) {
174 i
= r
->rank
< 0 ? 0 : r
->rank
+ 1;
176 tail
[i
] = &(*tail
[i
])->next
;
179 for (i
= 0; i
< nranks
+ 1; i
++)
181 *tail
[i
] = resource_list
;
182 resource_list
= head
[i
];
187 for (r
= resource_list
; r
; r
= r
->next
)
189 assert(r
->rank
>= r
->next
->rank
);
190 for (r
= resource_list
; r
; r
= r
->next
)
191 if (r
->type
== RESOURCE_FONT
&& r
->rank
>= 0)
192 supply_resource(r
, -1, out
.get_file());
196 void resource_manager::print_resources_comment(unsigned flag
, FILE *outfp
)
199 for (resource
*r
= resource_list
; r
; r
= r
->next
)
200 if (r
->flags
& flag
) {
202 fputs("%%+ ", outfp
);
204 fputs(flag
== resource::NEEDED
205 ? "%%DocumentNeededResources: "
206 : "%%DocumentSuppliedResources: ",
210 r
->print_type_and_name(outfp
);
215 void resource_manager::print_header_comments(ps_output
&out
)
217 for (resource
*r
= resource_list
; r
; r
= r
->next
)
218 if (r
->type
== RESOURCE_FONT
&& (r
->flags
& resource::FONT_NEEDED
))
219 supply_resource(r
, 0, 0);
220 print_resources_comment(resource::NEEDED
, out
.get_file());
221 print_resources_comment(resource::SUPPLIED
, out
.get_file());
222 print_language_level_comment(out
.get_file());
223 print_extensions_comment(out
.get_file());
226 void resource_manager::output_prolog(ps_output
&out
)
228 FILE *outfp
= out
.get_file();
231 FILE *fp
= font::open_file(PROLOGUE
, &path
);
233 fatal("can't find `%1'", PROLOGUE
);
234 fputs("%%BeginResource: ", outfp
);
235 procset_resource
->print_type_and_name(outfp
);
237 process_file(-1, fp
, path
, outfp
);
240 fputs("%%EndResource\n", outfp
);
243 void resource_manager::import_file(const char *filename
, ps_output
&out
)
246 string
name(filename
);
247 resource
*r
= lookup_resource(RESOURCE_FILE
, name
);
248 supply_resource(r
, -1, out
.get_file(), 1);
251 void resource_manager::supply_resource(resource
*r
, int rank
, FILE *outfp
,
254 if (r
->flags
& resource::BUSY
) {
256 fatal("loop detected in dependency graph for %1 `%2'",
257 resource_table
[r
->type
],
260 r
->flags
|= resource::BUSY
;
265 if (r
->filename
!= 0) {
266 if (r
->type
== RESOURCE_FONT
) {
267 fp
= font::open_file(r
->filename
, &path
);
269 error("can't find `%1'", r
->filename
);
270 a_delete r
->filename
;
276 fp
= fopen(r
->filename
, "r");
278 error("can't open `%1': %2", r
->filename
, strerror(errno
));
279 a_delete r
->filename
;
288 if (r
->type
== RESOURCE_FILE
&& is_document
) {
289 fputs("%%BeginDocument: ", outfp
);
290 print_ps_string(r
->name
, outfp
);
294 fputs("%%BeginResource: ", outfp
);
295 r
->print_type_and_name(outfp
);
299 process_file(rank
, fp
, path
, outfp
);
301 if (r
->type
== RESOURCE_FONT
)
304 if (r
->type
== RESOURCE_FILE
&& is_document
)
305 fputs("%%EndDocument\n", outfp
);
307 fputs("%%EndResource\n", outfp
);
309 r
->flags
|= resource::SUPPLIED
;
313 if (r
->type
== RESOURCE_FILE
&& is_document
) {
314 fputs("%%IncludeDocument: ", outfp
);
315 print_ps_string(r
->name
, outfp
);
319 fputs("%%IncludeResource: ", outfp
);
320 r
->print_type_and_name(outfp
);
324 r
->flags
|= resource::NEEDED
;
326 r
->flags
&= ~resource::BUSY
;
330 #define PS_LINE_MAX 255
331 #define PS_MAGIC "%!PS-Adobe-"
333 static int ps_get_line(char *buf
, FILE *fp
)
343 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
344 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
345 error("illegal input character code %1", int(c
));
346 else if (i
< PS_LINE_MAX
)
350 error("PostScript file non-conforming "
351 "because length of line exceeds 255");
359 if (c
!= EOF
&& c
!= '\n')
365 static int read_text_arg(const char **pp
, string
&res
)
368 while (white_space(**pp
))
371 error("missing argument");
375 for (; **pp
!= '\0' && !white_space(**pp
); *pp
+= 1)
383 if (**pp
== '\0' || **pp
== '\r' || **pp
== '\n') {
384 error("missing ')'");
395 else if (**pp
== '(') {
399 else if (**pp
== '\\') {
426 int val
= **pp
- '0';
427 if ((*pp
)[1] >= '0' && (*pp
)[1] <= '7') {
429 val
= val
*8 + (**pp
- '0');
430 if ((*pp
)[1] >= '0' && (*pp
)[1] <= '7') {
432 val
= val
*8 + (**pp
- '0');
449 static int read_uint_arg(const char **pp
, unsigned *res
)
451 while (white_space(**pp
))
454 error("missing argument");
457 const char *start
= *pp
;
459 long n
= strtol(start
, (char **)pp
, 10);
460 if (n
== 0 && *pp
== start
) {
461 error("not an integer");
465 error("argument must not be negative");
472 resource
*resource_manager::read_file_arg(const char **ptr
)
475 if (!read_text_arg(ptr
, arg
))
477 return lookup_resource(RESOURCE_FILE
, arg
);
480 resource
*resource_manager::read_font_arg(const char **ptr
)
483 if (!read_text_arg(ptr
, arg
))
485 return lookup_resource(RESOURCE_FONT
, arg
);
488 resource
*resource_manager::read_procset_arg(const char **ptr
)
491 if (!read_text_arg(ptr
, arg
))
494 if (!read_text_arg(ptr
, version
))
497 if (!read_uint_arg(ptr
, &revision
))
499 return lookup_resource(RESOURCE_PROCSET
, arg
, version
, revision
);
502 resource
*resource_manager::read_resource_arg(const char **ptr
)
504 while (white_space(**ptr
))
506 const char *name
= *ptr
;
507 while (**ptr
!= '\0' && !white_space(**ptr
))
510 error("missing resource type");
513 for (int ri
= 0; ri
< NRESOURCES
; ri
++)
514 if (strlen(resource_table
[ri
]) == *ptr
- name
515 && memcmp(resource_table
[ri
], name
, *ptr
- name
) == 0)
517 if (ri
>= NRESOURCES
) {
518 error("unknown resource type");
521 if (ri
== RESOURCE_PROCSET
)
522 return read_procset_arg(ptr
);
524 if (!read_text_arg(ptr
, arg
))
526 return lookup_resource(resource_type(ri
), arg
);
529 static const char *matches_comment(const char *buf
, const char *comment
)
531 if (buf
[0] != '%' || buf
[1] != '%')
533 for (buf
+= 2; *comment
; comment
++, buf
++)
534 if (*buf
!= *comment
)
536 if (comment
[-1] == ':')
538 if (*buf
== '\0' || white_space(*buf
))
543 // Return 1 if the line should be copied out.
545 int resource_manager::do_begin_resource(const char *ptr
, int, FILE *,
548 resource
*r
= read_resource_arg(&ptr
);
550 r
->flags
|= resource::SUPPLIED
;
554 int resource_manager::do_include_resource(const char *ptr
, int rank
, FILE *,
557 resource
*r
= read_resource_arg(&ptr
);
559 if (r
->type
== RESOURCE_FONT
) {
561 supply_resource(r
, rank
+ 1, outfp
);
563 r
->flags
|= resource::FONT_NEEDED
;
566 supply_resource(r
, rank
, outfp
);
571 int resource_manager::do_begin_document(const char *ptr
, int, FILE *,
574 resource
*r
= read_file_arg(&ptr
);
576 r
->flags
|= resource::SUPPLIED
;
580 int resource_manager::do_include_document(const char *ptr
, int rank
, FILE *,
583 resource
*r
= read_file_arg(&ptr
);
585 supply_resource(r
, rank
, outfp
, 1);
589 int resource_manager::do_begin_procset(const char *ptr
, int, FILE *,
592 resource
*r
= read_procset_arg(&ptr
);
594 r
->flags
|= resource::SUPPLIED
;
596 fputs("%%BeginResource: ", outfp
);
597 r
->print_type_and_name(outfp
);
604 int resource_manager::do_include_procset(const char *ptr
, int rank
, FILE *,
607 resource
*r
= read_procset_arg(&ptr
);
609 supply_resource(r
, rank
, outfp
);
613 int resource_manager::do_begin_file(const char *ptr
, int, FILE *,
616 resource
*r
= read_file_arg(&ptr
);
618 r
->flags
|= resource::SUPPLIED
;
620 fputs("%%BeginResource: ", outfp
);
621 r
->print_type_and_name(outfp
);
628 int resource_manager::do_include_file(const char *ptr
, int rank
, FILE *,
631 resource
*r
= read_file_arg(&ptr
);
633 supply_resource(r
, rank
, outfp
);
637 int resource_manager::do_begin_font(const char *ptr
, int, FILE *,
640 resource
*r
= read_font_arg(&ptr
);
642 r
->flags
|= resource::SUPPLIED
;
644 fputs("%%BeginResource: ", outfp
);
645 r
->print_type_and_name(outfp
);
652 int resource_manager::do_include_font(const char *ptr
, int rank
, FILE *,
655 resource
*r
= read_font_arg(&ptr
);
658 supply_resource(r
, rank
+ 1, outfp
);
660 r
->flags
|= resource::FONT_NEEDED
;
665 int resource_manager::change_to_end_resource(const char *, int, FILE *,
669 fputs("%%EndResource\n", outfp
);
673 int resource_manager::do_begin_preview(const char *, int, FILE *fp
, FILE *)
675 char buf
[PS_LINE_MAX
+ 2];
677 if (!ps_get_line(buf
, fp
)) {
678 error("end of file in preview section");
681 } while (!matches_comment(buf
, "EndPreview"));
685 int read_one_of(const char **ptr
, const char **s
, int n
)
687 while (white_space(**ptr
))
691 const char *start
= *ptr
;
694 } while (**ptr
!= '\0' && !white_space(**ptr
));
695 for (int i
= 0; i
< n
; i
++)
696 if (strlen(s
[i
]) == *ptr
- start
697 && memcmp(s
[i
], start
, *ptr
- start
) == 0)
702 int resource_manager::do_begin_data(const char *ptr
, int, FILE *fp
,
705 while (white_space(*ptr
))
707 const char *start
= ptr
;
709 if (!read_uint_arg(&ptr
, &numberof
))
711 static const char *types
[] = { "Binary", "Hex", "ASCII" };
712 const int Binary
= 0;
714 static const char *units
[] = { "Bytes", "Lines" };
717 while (white_space(*ptr
))
720 type
= read_one_of(&ptr
, types
, 3);
722 error("bad data type");
725 while (white_space(*ptr
))
728 unit
= read_one_of(&ptr
, units
, 2);
730 error("expected `Bytes' or `Lines'");
738 fputs("%%BeginData: ", outfp
);
742 unsigned bytecount
= 0;
743 unsigned linecount
= 0;
747 error("end of file within data section");
762 else if (c
== '\n') {
766 } while ((unit
== Bytes
? bytecount
: linecount
) < numberof
);
768 char buf
[PS_LINE_MAX
+ 2];
769 if (!ps_get_line(buf
, fp
)) {
770 error("missing %%%%EndData line");
773 if (!matches_comment(buf
, "EndData"))
774 error("bad %%%%EndData line");
780 int resource_manager::do_begin_binary(const char *ptr
, int, FILE *fp
,
786 if (!read_uint_arg(&ptr
, &count
))
789 fprintf(outfp
, "%%%%BeginData: %u Binary Bytes\n", count
);
793 error("end of file within binary section");
809 char buf
[PS_LINE_MAX
+ 2];
810 if (!ps_get_line(buf
, fp
)) {
811 error("missing %%%%EndBinary line");
814 if (!matches_comment(buf
, "EndBinary")) {
815 error("bad %%%%EndBinary line");
820 fputs("%%EndData\n", outfp
);
824 static unsigned parse_extensions(const char *ptr
)
828 while (white_space(*ptr
))
832 const char *name
= ptr
;
835 } while (*ptr
!= '\0' && !white_space(*ptr
));
836 for (int i
= 0; i
< NEXTENSIONS
; i
++)
837 if (strlen(extension_table
[i
]) == ptr
- name
838 && memcmp(extension_table
[i
], name
, ptr
- name
) == 0) {
842 if (i
>= NEXTENSIONS
) {
843 string
s(name
, ptr
- name
);
845 error("unknown extension `%1'", s
.contents());
851 // XXX if it has not been surrounded with {Begin,End}Document need to strip
852 // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
854 // XXX Perhaps the decision whether to use BeginDocument or
855 // BeginResource: file should be postponed till we have seen
856 // the first line of the file.
858 void resource_manager::process_file(int rank
, FILE *fp
, const char *filename
,
861 // If none of these comments appear in the header section, and we are
862 // just analyzing the file (ie outfp is 0), then we can return immediately.
863 static const char *header_comment_table
[] = {
864 "DocumentNeededResources:",
865 "DocumentSuppliedResources:",
866 "DocumentNeededFonts:",
867 "DocumentSuppliedFonts:",
868 "DocumentNeededProcSets:",
869 "DocumentSuppliedProcSets:",
870 "DocumentNeededFiles:",
871 "DocumentSuppliedFiles:",
874 const int NHEADER_COMMENTS
= (sizeof(header_comment_table
)
875 / sizeof(header_comment_table
[0]));
876 struct comment_info
{
878 int (resource_manager::*proc
)(const char *, int, FILE *, FILE *);
881 static comment_info comment_table
[] = {
882 "BeginResource:", &resource_manager::do_begin_resource
,
883 "IncludeResource:", &resource_manager::do_include_resource
,
884 "BeginDocument:", &resource_manager::do_begin_document
,
885 "IncludeDocument:", &resource_manager::do_include_document
,
886 "BeginProcSet:", &resource_manager::do_begin_procset
,
887 "IncludeProcSet:", &resource_manager::do_include_procset
,
888 "BeginFont:", &resource_manager::do_begin_font
,
889 "IncludeFont:", &resource_manager::do_include_font
,
890 "BeginFile:", &resource_manager::do_begin_file
,
891 "IncludeFile:", &resource_manager::do_include_file
,
892 "EndProcSet", &resource_manager::change_to_end_resource
,
893 "EndFont", &resource_manager::change_to_end_resource
,
894 "EndFile", &resource_manager::change_to_end_resource
,
895 "BeginPreview:", &resource_manager::do_begin_preview
,
896 "BeginData:", &resource_manager::do_begin_data
,
897 "BeginBinary:", &resource_manager::do_begin_binary
,
900 const int NCOMMENTS
= sizeof(comment_table
)/sizeof(comment_table
[0]);
901 char buf
[PS_LINE_MAX
+ 2];
902 int saved_lineno
= current_lineno
;
903 const char *saved_filename
= current_filename
;
904 current_filename
= filename
;
906 if (!ps_get_line(buf
, fp
)) {
907 current_filename
= saved_filename
;
908 current_lineno
= saved_lineno
;
911 if (strlen(buf
) < sizeof(PS_MAGIC
) - 1
912 || memcmp(buf
, PS_MAGIC
, sizeof(PS_MAGIC
) - 1) != 0) {
915 if (!(broken_flags
& STRIP_PERCENT_BANG
)
916 || buf
[0] != '%' || buf
[1] != '!')
918 } while (ps_get_line(buf
, fp
));
922 if (!(broken_flags
& STRIP_PERCENT_BANG
) && outfp
)
926 int had_extensions_comment
= 0;
927 int had_language_level_comment
= 0;
929 if (!ps_get_line(buf
, fp
))
931 int copy_this_line
= 1;
935 for (int i
= 0; i
< NCOMMENTS
; i
++)
936 if (ptr
= matches_comment(buf
, comment_table
[i
].name
)) {
938 = (this->*(comment_table
[i
].proc
))(ptr
, rank
, fp
, outfp
);
941 if (i
>= NCOMMENTS
&& in_header
) {
942 if (ptr
= matches_comment(buf
, "EndComments"))
944 else if (!had_extensions_comment
945 && (ptr
= matches_comment(buf
, "Extensions:"))) {
946 extensions
|= parse_extensions(ptr
);
947 // XXX handle possibility that next line is %%+
948 had_extensions_comment
= 1;
950 else if (!had_language_level_comment
951 && (ptr
= matches_comment(buf
, "LanguageLevel:"))) {
953 if (read_uint_arg(&ptr
, &ll
) && ll
> language_level
)
955 had_language_level_comment
= 1;
958 for (int i
= 0; i
< NHEADER_COMMENTS
; i
++)
959 if (matches_comment(buf
, header_comment_table
[i
])) {
965 if ((broken_flags
& STRIP_STRUCTURE_COMMENTS
)
966 && (matches_comment(buf
, "EndProlog")
967 || matches_comment(buf
, "Page:")
968 || matches_comment(buf
, "Trailer")))
971 else if (buf
[1] == '!') {
972 if (broken_flags
& STRIP_PERCENT_BANG
)
978 if (!outfp
&& !in_header
&& !interesting
)
980 if (copy_this_line
&& outfp
)
984 current_filename
= saved_filename
;
985 current_lineno
= saved_lineno
;
988 void resource_manager::read_download_file()
991 FILE *fp
= font::open_file("download", &path
);
993 fatal("can't find `download'");
996 while (fgets(buf
, sizeof(buf
), fp
)) {
998 char *p
= strtok(buf
, " \t\r\n");
999 if (p
== 0 || *p
== '#')
1001 char *q
= strtok(0, " \t\r\n");
1003 fatal_with_file_and_line(path
, lineno
, "missing filename");
1004 lookup_font(p
)->filename
= strsave(q
);
1010 // XXX Can we share some code with ps_output::put_string()?
1012 static void print_ps_string(const string
&s
, FILE *outfp
)
1014 int len
= s
.length();
1015 const char *str
= s
.contents();
1020 for (int i
= 0; i
< len
; i
++)
1021 if (str
[i
] <= 040 || str
[i
] > 0176) {
1027 put_string(s
, outfp
);
1031 for (int i
= 0; i
< len
; i
++)
1034 else if (str
[i
] == ')' && --level
< 0)
1037 for (i
= 0; i
< len
; i
++)
1043 putc(str
[i
], outfp
);
1046 fputs("\\\\", outfp
);
1049 fputs("\\n", outfp
);
1052 fputs("\\r", outfp
);
1055 fputs("\\t", outfp
);
1058 fputs("\\b", outfp
);
1061 fputs("\\f", outfp
);
1064 if (str
[i
] < 040 || str
[i
] > 0176)
1065 fprintf(outfp
, "\\%03o", str
[i
] & 0377);
1067 putc(str
[i
], outfp
);
1073 void resource_manager::print_extensions_comment(FILE *outfp
)
1076 fputs("%%Extensions:", outfp
);
1077 for (int i
= 0; i
< NEXTENSIONS
; i
++)
1078 if (extensions
& (1 << i
)) {
1080 fputs(extension_table
[i
], outfp
);
1086 void resource_manager::print_language_level_comment(FILE *outfp
)
1089 fprintf(outfp
, "%%%%LanguageLevel: %u\n", language_level
);