1 /* Copyright (C) 1995, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: geninit.c,v 1.8 2003/07/01 04:37:20 alexcher Exp $ */
18 /*
19 * Utility for merging all the Ghostscript initialization files (gs_*.ps)
20 * into a single file, optionally converting them to C data. Usage:
21 * geninit [-(I|i) <prefix>] <init-file.ps> <gconfig.h> <merged-init-file.ps>
22 * geninit [-(I|i) <prefix>] <init-file.ps> <gconfig.h> -c <merged-init-file.c>
23 *
24 * The following special constructs are recognized in the input files:
25 * %% Replace[%| ]<#lines> (<psfile>)
26 * %% Replace[%| ]<#lines> INITFILES
27 * '%' after Replace means that the file to be included intact.
28 * If the first non-comment, non-blank line in a file ends with the
29 * LanguageLevel 2 constructs << or <~, its section of the merged file
30 * will begin with
31 * currentobjectformat 1 setobjectformat
32 * and end with
33 * setobjectformat
34 * and if any line then ends with with <, the following ASCIIHex string
35 * will be converted to a binary token.
36 */
37 #include "stdpre.h"
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdlib.h> /* for exit() */
41 #include <string.h>
42 #include <memory.h>
43
44 /* Forward references */
45 private FILE *prefix_open(const char *prefix, const char *inname);
46 private void merge_to_c(const char *prefix, const char *inname, FILE * in,
47 FILE * config, FILE * out);
48 private void merge_to_ps(const char *prefix, const char *inname, FILE * in,
49 FILE * config, FILE * out);
50
51 #define LINE_SIZE 128
52
53 int
main(int argc,char * argv[])54 main(int argc, char *argv[])
55 {
56 int arg_c = argc;
57 char **arg_v = argv;
58 const char *fin;
59 FILE *in;
60 const char *fconfig;
61 FILE *config;
62 const char *fout;
63 FILE *out;
64 const char *prefix = "";
65 bool to_c = false;
66
67 if (arg_c >= 2 && (!strcmp(arg_v[1], "-I") || !strcmp(arg_v[1], "-i"))) {
68 prefix = arg_v[2];
69 arg_c -= 2;
70 arg_v += 2;
71 }
72 if (arg_c == 4)
73 fin = arg_v[1], fconfig = arg_v[2], fout = arg_v[3];
74 else if (arg_c == 5 && !strcmp(arg_v[3], "-c"))
75 fin = arg_v[1], fconfig = arg_v[2], fout = arg_v[4], to_c = true;
76 else {
77 fprintf(stderr, "\
78 Usage: geninit [-(I|i) lib/] gs_init.ps gconfig.h gs_xinit.ps\n\
79 or geninit [-(I|i) lib/] gs_init.ps gconfig.h -c gs_init.c\n");
80 exit(1);
81 }
82 in = prefix_open(prefix, fin);
83 if (in == 0)
84 exit(1);
85 config = fopen(fconfig, "r");
86 if (config == 0) {
87 fprintf(stderr, "Cannot open %s for reading.\n", fconfig);
88 fclose(in);
89 exit(1);
90 }
91 out = fopen(fout, "w");
92 if (out == 0) {
93 fprintf(stderr, "Cannot open %s for writing.\n", fout);
94 fclose(config);
95 fclose(in);
96 exit(1);
97 }
98 if (to_c)
99 merge_to_c(prefix, fin, in, config, out);
100 else
101 merge_to_ps(prefix, fin, in, config, out);
102 fclose(out);
103 return 0;
104 }
105
106 /* Translate a path from Postscript notation to platform notation. */
107
108 private void
translate_path(char * path)109 translate_path(char *path)
110 {
111 /*
112 * It looks that we only need to do for Mac OS.
113 * We don't support 16-bit DOS/Windows, which wants '\'.
114 * Win32 API supports '/'.
115 */
116 #ifdef __MACOS__
117 if (path[0] == '.' && path[1] == '.' && path[2] == '/') {
118 char *p;
119
120 path[0] = path[1] = ':';
121 for (p = path + 2; p[1] ; p++)
122 *p = (p[1] == '/' ? ':' : p[1]);
123 *p = 0;
124 }
125 #endif
126 }
127
128 /* Open a file with a name prefix. */
129 private FILE *
prefix_open(const char * prefix,const char * inname)130 prefix_open(const char *prefix, const char *inname)
131 {
132 char fname[LINE_SIZE + 1];
133 int prefix_length = strlen(prefix);
134 FILE *f;
135
136 if (prefix_length + strlen(inname) > LINE_SIZE) {
137 fprintf(stderr, "File name > %d characters, too long.\n",
138 LINE_SIZE);
139 exit(1);
140 }
141 strcpy(fname, prefix);
142 strcat(fname, inname);
143 translate_path(fname + prefix_length);
144 f = fopen(fname, "r");
145 if (f == NULL)
146 fprintf(stderr, "Cannot open %s for reading.\n", fname);
147 return f;
148 }
149
150 /* Read a line from the input. */
151 private bool
rl(FILE * in,char * str,int len)152 rl(FILE * in, char *str, int len)
153 {
154 /*
155 * Unfortunately, we can't use fgets here, because the typical
156 * implementation only recognizes the EOL convention of the current
157 * platform.
158 */
159 int i = 0, c = getc(in);
160
161 if (c < 0)
162 return false;
163 while (i < len - 1) {
164 if (c < 0 || c == 10) /* ^J, Unix EOL */
165 break;
166 if (c == 13) { /* ^M, Mac EOL */
167 c = getc(in);
168 if (c != 10 && c >= 0) /* ^M^J, PC EOL */
169 ungetc(c, in);
170 break;
171 }
172 str[i++] = c;
173 c = getc(in);
174 }
175 str[i] = 0;
176 return true;
177 }
178
179 /* Write a string or a line on the output. */
180 private void
wsc(FILE * out,const byte * str,int len)181 wsc(FILE * out, const byte *str, int len)
182 {
183 int n = 0;
184 const byte *p = str;
185 int i;
186
187 for (i = 0; i < len; ++i) {
188 int c = str[i];
189
190 fprintf(out,
191 (c < 32 || c >= 127 ? "%d," :
192 c == '\'' || c == '\\' ? "'\\%c'," : "'%c',"),
193 c);
194 if (++n == 15) {
195 fputs("\n", out);
196 n = 0;
197 }
198 }
199 if (n != 0)
200 fputc('\n', out);
201 }
202 private void
ws(FILE * out,const byte * str,int len,bool to_c)203 ws(FILE * out, const byte *str, int len, bool to_c)
204 {
205 if (to_c)
206 wsc(out, str, len);
207 else
208 fwrite(str, 1, len, out);
209 }
210 private void
wl(FILE * out,const char * str,bool to_c)211 wl(FILE * out, const char *str, bool to_c)
212 {
213 ws(out, (const byte *)str, strlen(str), to_c);
214 ws(out, (const byte *)"\n", 1, to_c);
215 }
216
217 /*
218 * Strip whitespace and comments from a line of PostScript code as possible.
219 * Return a pointer to any string that remains, or NULL if none.
220 * Note that this may store into the string.
221 */
222 private char *
doit(char * line,bool intact)223 doit(char *line, bool intact)
224 {
225 char *str = line;
226 char *from;
227 char *to;
228 int in_string = 0;
229
230 if (intact)
231 return str;
232 while (*str == ' ' || *str == '\t') /* strip leading whitespace */
233 ++str;
234 if (*str == 0) /* all whitespace */
235 return NULL;
236 if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
237 return str;
238 if (str[0] == '%') /* comment line */
239 return NULL;
240 /*
241 * Copy the string over itself removing:
242 * - All comments not within string literals;
243 * - Whitespace adjacent to '[' ']' '{' '}';
244 * - Whitespace before '/' '(' '<';
245 * - Whitespace after ')' '>'.
246 */
247 for (to = from = str; (*to = *from) != 0; ++from, ++to) {
248 switch (*from) {
249 case '%':
250 if (!in_string)
251 break;
252 continue;
253 case ' ':
254 case '\t':
255 if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
256 --to;
257 continue;
258 case '(':
259 case '<':
260 case '/':
261 case '[':
262 case ']':
263 case '{':
264 case '}':
265 if (to > str && !in_string && strchr(" \t", to[-1]))
266 *--to = *from;
267 if (*from == '(')
268 ++in_string;
269 continue;
270 case ')':
271 --in_string;
272 continue;
273 case '\\':
274 if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
275 *++to = *++from;
276 continue;
277 default:
278 continue;
279 }
280 break;
281 }
282 /* Strip trailing whitespace. */
283 while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
284 --to;
285 *to = 0;
286 return str;
287 }
288
289 /* Copy a hex string to the output as a binary token. */
290 private void
hex_string_to_binary(FILE * out,FILE * in,bool to_c)291 hex_string_to_binary(FILE *out, FILE *in, bool to_c)
292 {
293 #define MAX_STR 0xffff /* longest possible PostScript string token */
294 byte *strbuf = (byte *)malloc(MAX_STR);
295 byte *q = strbuf;
296 int c, digit;
297 bool which = false;
298 int len;
299 byte prefix[3];
300
301 if (strbuf == 0) {
302 fprintf(stderr, "Unable to allocate string token buffer.\n");
303 exit(1);
304 }
305 while ((c = getc(in)) >= 0) {
306 if (isxdigit(c)) {
307 c -= (isdigit(c) ? '0' : islower(c) ? 'a' : 'A');
308 if ((which = !which)) {
309 if (q == strbuf + MAX_STR) {
310 fprintf(stderr, "Can't handle string token > %d bytes.\n",
311 MAX_STR);
312 exit(1);
313 }
314 *q++ = c << 4;
315 } else
316 q[-1] += c;
317 } else if (isspace(c))
318 continue;
319 else if (c == '>')
320 break;
321 else
322 fprintf(stderr, "Unknown character in ASCIIHex string: %c\n", c);
323 }
324 len = q - strbuf;
325 if (len <= 255) {
326 prefix[0] = 142;
327 prefix[1] = (byte)len;
328 ws(out, prefix, 2, to_c);
329 } else {
330 prefix[0] = 143;
331 prefix[1] = (byte)(len >> 8);
332 prefix[2] = (byte)len;
333 ws(out, prefix, 3, to_c);
334 }
335 ws(out, strbuf, len, to_c);
336 free((char *)strbuf);
337 #undef MAX_STR
338 }
339
340 /* Merge a file from input to output. */
341 private void
flush_buf(FILE * out,char * buf,bool to_c)342 flush_buf(FILE * out, char *buf, bool to_c)
343 {
344 if (buf[0] != 0) {
345 wl(out, buf, to_c);
346 buf[0] = 0;
347 }
348 }
349 private void
mergefile(const char * prefix,const char * inname,FILE * in,FILE * config,FILE * out,bool to_c,bool intact)350 mergefile(const char *prefix, const char *inname, FILE * in, FILE * config,
351 FILE * out, bool to_c, bool intact)
352 {
353 char line[LINE_SIZE + 1];
354 char buf[LINE_SIZE + 1];
355 char *str;
356 int level = 1;
357 bool first = true;
358
359 buf[0] = 0;
360 while (rl(in, line, LINE_SIZE)) {
361 char psname[LINE_SIZE + 1];
362 int nlines;
363
364 if (!strncmp(line, "%% Replace", 10) &&
365 sscanf(line + 11, "%d %s", &nlines, psname) == 2
366 ) {
367 bool do_intact = (line[10] == '%');
368
369 flush_buf(out, buf, to_c);
370 while (nlines-- > 0)
371 rl(in, line, LINE_SIZE);
372 if (psname[0] == '(') {
373 FILE *ps;
374
375 psname[strlen(psname) - 1] = 0;
376 ps = prefix_open(prefix, psname + 1);
377 if (ps == 0)
378 exit(1);
379 mergefile(prefix, psname + 1, ps, config, out, to_c, intact || do_intact);
380 } else if (!strcmp(psname, "INITFILES")) {
381 /*
382 * We don't want to bind config.h into geninit, so
383 * we parse it ourselves at execution time instead.
384 */
385 rewind(config);
386 while (rl(config, psname, LINE_SIZE))
387 if (!strncmp(psname, "psfile_(\"", 9)) {
388 FILE *ps;
389 char *quote = strchr(psname + 9, '"');
390
391 *quote = 0;
392 ps = prefix_open(prefix, psname + 9);
393 if (ps == 0)
394 exit(1);
395 mergefile(prefix, psname + 9, ps, config, out, to_c, false);
396 }
397 } else {
398 fprintf(stderr, "Unknown %%%% Replace %d %s\n",
399 nlines, psname);
400 exit(1);
401 }
402 } else if (!strcmp(line, "currentfile closefile")) {
403 /* The rest of the file is debugging code, stop here. */
404 break;
405 } else {
406 int len;
407
408 str = doit(line, intact);
409 if (str == 0)
410 continue;
411 len = strlen(str);
412 if (first && len >= 2 && str[len - 2] == '<' &&
413 (str[len - 1] == '<' || str[len - 1] == '~')
414 ) {
415 wl(out, "currentobjectformat 1 setobjectformat\n", to_c);
416 level = 2;
417 }
418 if (level > 1 && len > 0 && str[len - 1] == '<' &&
419 (len < 2 || str[len - 2] != '<')
420 ) {
421 /* Convert a hex string to a binary token. */
422 flush_buf(out, buf, to_c);
423 str[len - 1] = 0;
424 wl(out, str, to_c);
425 hex_string_to_binary(out, in, to_c);
426 continue;
427 }
428 if (buf[0] != '%' && /* i.e. not special retained comment */
429 strlen(buf) + len < LINE_SIZE &&
430 (strchr("(/[]{}", str[0]) ||
431 (buf[0] != 0 && strchr(")[]{}", buf[strlen(buf) - 1])))
432 )
433 strcat(buf, str);
434 else {
435 flush_buf(out, buf, to_c);
436 strcpy(buf, str);
437 }
438 first = false;
439 }
440 }
441 flush_buf(out, buf, to_c);
442 if (level > 1)
443 wl(out, "setobjectformat", to_c);
444 fprintf(stderr, "%s: %ld bytes, output pos = %ld\n",
445 inname, ftell(in), ftell(out));
446 fclose(in);
447 }
448
449 /* Merge and produce a C file. */
450 private void
merge_to_c(const char * prefix,const char * inname,FILE * in,FILE * config,FILE * out)451 merge_to_c(const char *prefix, const char *inname, FILE * in, FILE * config,
452 FILE * out)
453 {
454 char line[LINE_SIZE + 1];
455
456 fputs("/*\n", out);
457 while ((rl(in, line, LINE_SIZE), line[0]))
458 fprintf(out, "%s\n", line);
459 fputs("*/\n", out);
460 fputs("\n", out);
461 fputs("/* Pre-compiled interpreter initialization string. */\n", out);
462 fputs("\n", out);
463 fputs("const unsigned char gs_init_string[] = {\n", out);
464 mergefile(prefix, inname, in, config, out, true, false);
465 fputs("10};\n", out);
466 fputs("const unsigned int gs_init_string_sizeof = sizeof(gs_init_string);\n", out);
467 }
468
469 /* Merge and produce a PostScript file. */
470 private void
merge_to_ps(const char * prefix,const char * inname,FILE * in,FILE * config,FILE * out)471 merge_to_ps(const char *prefix, const char *inname, FILE * in, FILE * config,
472 FILE * out)
473 {
474 char line[LINE_SIZE + 1];
475
476 while ((rl(in, line, LINE_SIZE), line[0]))
477 fprintf(out, "%s\n", line);
478 mergefile(prefix, inname, in, config, out, false, false);
479 }
480