Line data Source code
1 : /* Copyright (C) 1992-2025 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 : /* This translates ps fonts in .pfb format to ASCII ps files. */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #include <assert.h> // assert()
26 : #include <errno.h> // errno
27 : #include <stdio.h>
28 : #include <stdlib.h> // exit(), EXIT_FAILURE, EXIT_SUCCESS
29 : #include <string.h> // strerror()
30 : #include <limits.h>
31 :
32 : #include <getopt.h>
33 :
34 : #include "nonposix.h"
35 :
36 : /* Binary bytes per output line. */
37 : #define BYTES_PER_LINE (64/2)
38 : #define MAX_LINE_LENGTH 78
39 : #define HEX_DIGITS "0123456789abcdef"
40 :
41 : extern const char *Version_string;
42 :
43 : static char *program_name;
44 :
45 0 : static void error(const char *s)
46 : {
47 0 : (void) fprintf(stderr, "%s: error: %s\n", program_name, s);
48 0 : }
49 :
50 0 : static void die(const char *s)
51 : {
52 0 : error(s);
53 0 : exit(EXIT_FAILURE);
54 : }
55 :
56 0 : static void usage(FILE *stream)
57 : {
58 0 : (void) fprintf(stream, "usage: %s [pfb-file]\n"
59 : "usage: %s {-v | --version}\n"
60 : "usage: %s --help\n",
61 : program_name, program_name, program_name);
62 0 : if (stdout == stream)
63 0 : fputs("\n"
64 : "Translate a PostScript Type 1 font in Printer Font Binary (PFB) format"
65 : "\n"
66 : "to Printer Font ASCII (PFA) format. See the pfbtops(1) manual page."
67 : "\n",
68 : stream);
69 0 : }
70 :
71 2 : static void get_text(int n)
72 : {
73 2 : int c = 0, c1;
74 2 : int in_string = 0;
75 2 : int is_comment = 0;
76 2 : int count = 0;
77 :
78 2215 : while (--n >= 0) {
79 2213 : c = getchar();
80 2213 : if (c == '(' && !is_comment)
81 5 : in_string++;
82 2208 : else if (c == ')' && !is_comment)
83 5 : in_string--;
84 2203 : else if (c == '%' && !in_string)
85 19 : is_comment = 1;
86 2184 : else if (c == '\\' && in_string) {
87 1 : count++;
88 1 : putchar(c);
89 1 : if (n-- == 0)
90 0 : break;
91 1 : c = getchar();
92 : /* don't split octal character representations */
93 1 : if (c >= '0' && c <= '7') {
94 1 : count++;
95 1 : putchar(c);
96 1 : if (n-- == 0)
97 0 : break;
98 1 : c = getchar();
99 1 : if (c >= '0' && c <= '7') {
100 1 : count++;
101 1 : putchar(c);
102 1 : if (n-- == 0)
103 0 : break;
104 1 : c = getchar();
105 1 : if (c >= '0' && c <= '7') {
106 1 : count++;
107 1 : putchar(c);
108 1 : if (n-- == 0)
109 0 : break;
110 1 : c = getchar();
111 : }
112 : }
113 : }
114 : }
115 2213 : if (c == EOF)
116 0 : die("end of file in text packet");
117 2213 : else if (c == '\r') {
118 0 : if (n-- == 0)
119 0 : break;
120 0 : c1 = getchar();
121 0 : if (c1 != '\n') {
122 0 : ungetc(c1, stdin);
123 0 : n++;
124 : }
125 0 : c = '\n';
126 : }
127 2213 : if (c == '\n') {
128 73 : count = 0;
129 73 : is_comment = 0;
130 : }
131 2140 : else if (count >= MAX_LINE_LENGTH) {
132 1 : if (in_string > 0) {
133 1 : count = 1;
134 1 : putchar('\\');
135 1 : putchar('\n');
136 : }
137 0 : else if (is_comment) {
138 0 : count = 2;
139 0 : putchar('\n');
140 0 : putchar('%');
141 : }
142 : else {
143 : /* split at the next whitespace character */
144 0 : while (c != ' ' && c != '\t' && c != '\f') {
145 0 : putchar(c);
146 0 : if (n-- == 0)
147 0 : break;
148 0 : c = getchar();
149 : }
150 0 : count = 0;
151 0 : putchar('\n');
152 0 : continue;
153 : }
154 : }
155 2213 : count++;
156 2213 : putchar(c);
157 : }
158 2 : if (c != '\n')
159 0 : putchar('\n');
160 2 : }
161 :
162 1 : static void get_binary(int n)
163 : {
164 : int c;
165 1 : int count = 0;
166 :
167 12157 : while (--n >= 0) {
168 12156 : c = getchar();
169 12156 : if (c == EOF)
170 0 : die("end of file in binary packet");
171 12156 : if (count >= BYTES_PER_LINE) {
172 379 : putchar('\n');
173 379 : count = 0;
174 : }
175 12156 : count++;
176 12156 : putchar(HEX_DIGITS[(c >> 4) & 0xf]);
177 12156 : putchar(HEX_DIGITS[c & 0xf]);
178 : }
179 1 : putchar('\n');
180 1 : }
181 :
182 1 : int main(int argc, char **argv)
183 : {
184 : int opt;
185 : static const struct option long_options[] = {
186 : { "help", no_argument, NULL, CHAR_MAX + 1 },
187 : { "version", no_argument, NULL, 'v' },
188 : { NULL, 0, NULL, 0 }
189 : };
190 :
191 1 : program_name = argv[0];
192 :
193 1 : while ((opt = getopt_long(argc, argv, ":v", long_options, NULL))
194 : != EOF) {
195 0 : switch (opt) {
196 0 : case 'v':
197 0 : printf("GNU pfbtops (groff) version %s\n", Version_string);
198 0 : exit(EXIT_SUCCESS);
199 : break;
200 0 : case CHAR_MAX + 1: /* --help */
201 0 : usage(stdout);
202 0 : exit(EXIT_SUCCESS);
203 : break;
204 0 : case '?':
205 0 : if (optopt != 0) {
206 0 : char errbuf[] = "unrecognized command-line option 'X'";
207 0 : errbuf[(strchr(errbuf, 'X') - errbuf)] = optopt;
208 0 : error(errbuf);
209 : }
210 : else
211 : // We can't use `error()` because we have no idea how big the
212 : // user-specified long option is.
213 0 : (void) fprintf(stderr, "%s: error: unrecognized command-line"
214 : " option '%s'\n", program_name,
215 0 : argv[(optind - 1)]);
216 0 : usage(stderr);
217 0 : exit(2);
218 : break;
219 : // in case we ever accept options that take arguments
220 0 : case ':':
221 0 : fprintf(stderr, "%s: error: command-line option '%c' requires an"
222 0 : " argument\n", program_name, (char) optopt);
223 0 : usage(stderr);
224 0 : exit(2);
225 : break;
226 0 : default:
227 0 : assert(0 == "unhandled case of command-line option");
228 : }
229 : }
230 :
231 1 : if (argc - optind > 1) {
232 0 : usage(stderr);
233 0 : exit(2);
234 : }
235 1 : const char *file = argv[optind];
236 1 : if (argc > optind && !freopen(file, "r", stdin)) {
237 0 : fprintf(stderr, "%s: error: unable to open file '%s': %s\n",
238 0 : program_name, file, strerror(errno));
239 0 : exit(EXIT_FAILURE);
240 : }
241 : SET_BINARY(fileno(stdin));
242 3 : for (;;) {
243 : int type, c, i;
244 : long n;
245 :
246 4 : c = getchar();
247 4 : if (c != 0x80)
248 0 : die("first byte of packet not 0x80");
249 4 : type = getchar();
250 4 : if (type == 3)
251 1 : break;
252 3 : if (type != 1 && type != 2)
253 0 : die("bad packet type");
254 3 : n = 0;
255 15 : for (i = 0; i < 4; i++) {
256 12 : c = getchar();
257 12 : if (c == EOF)
258 0 : die("end of file in packet header");
259 12 : n |= (long)c << (i << 3);
260 : }
261 3 : if (n < 0)
262 0 : die("negative packet length");
263 3 : if (type == 1)
264 2 : get_text(n);
265 : else
266 1 : get_binary(n);
267 : }
268 1 : exit(EXIT_SUCCESS);
269 : }
270 :
271 : // Local Variables:
272 : // fill-column: 72
273 : // mode: C
274 : // End:
275 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|