Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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 *
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
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
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
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
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 *
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
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
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
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
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
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
}