1 /* Copyright (C) 2001-2008 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: mkromfs.c 9099 2008-09-18 18:23:22Z giles $ */
15 /* Generate source data for the %rom% IODevice */
16 /*
17  * For reasons of convenience and footprint reduction, the various postscript
18  * source files, resources and fonts required by Ghostscript can be compiled
19  * into the executable.
20  *
21  * This file takes a set of directories, and creates a compressed filesystem
22  * image that can be compiled into the executable as static data and accessed
23  * through the %rom% iodevice prefix
24  */
25 /*
26  *	Usage: mkromfs [-o outputfile] [options ...] paths
27  *	    options and paths can be interspersed and are processed in order
28  *	    options:
29  *		-o outputfile	default: obj/gsromfs.c if this option present, must be first.
30  *		-P prefix	use prefix to find path. prefix not included in %rom%
31  *		-X path		exclude the path from further processing.
32  *              -d string       directory in %rom file system (really just a prefix string on filename)
33  *		-c		compression on
34  *		-b		compression off (binary).
35  *
36  *	    Note: The tail of any path encountered will be tested so .svn on the -X
37  *		  list will exclude that path in all subsequent paths enumerated.
38  */
39 
40 #include "stdpre.h"
41 #include "stdint_.h"
42 #include "time_.h"
43 #include "gsiorom.h"
44 #include "gsmemret.h" /* for gs_memory_type_ptr_t */
45 #include "gsmalloc.h"
46 #include "gsstype.h"
47 #include "gp.h"
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 
52 #include <zlib.h>
53 
54 /*
55  * The rom file system is an array of pointers to nodes, terminated by a NULL
56  */
57 /*
58  * in memory structure of each node is:
59  *
60  *	length_of_uncompressed_file		[31-bit big-endian]
61  *						high bit is compression flag
62  *	data_block_struct[]
63  *	padded_file_name (char *)	includes as least one terminating <nul>
64  *	padded_data_blocks
65  */
66 /*
67  *	data_block_struct:
68  *	    data_length			(not including pad)	[32-bit big-endian]
69  *	    data_block_offset		(start of each block)	[32-bit big-endian]
70  */
71 
72 typedef struct romfs_inode_s {
73     unsigned long length;		/* blocks is (length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE */
74     char *name;				/* nul terminated */
75     unsigned long *data_lengths;	/* this could be short if ROMFS_BLOCKSIZE */
76 					/* is < 64k, but the cost is small to use int */
77     unsigned char **data;
78 } romfs_inode;
79 
80 typedef struct Xlist_element_s {
81 	void *next;
82 	char *path;
83     } Xlist_element;
84 
85 byte *minimal_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname);
86 byte *minimal_alloc_byte_array(gs_memory_t * mem, uint num_elements,
87 			     uint elt_size, client_name_t cname);
88 void *minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
89 	       client_name_t cname);
90 void minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname);
91 void minimal_free_string(gs_memory_t * mem, byte * data, uint nbytes, client_name_t cname);
92 
93 /*******************************************************************************
94  * The following is a REALLY minimal gs_memory_t for use by the gp_ functions
95  *******************************************************************************/
96 byte *
minimal_alloc_bytes(gs_memory_t * mem,uint size,client_name_t cname)97 minimal_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
98 {
99     return malloc(size);
100 }
101 
102 byte *
minimal_alloc_byte_array(gs_memory_t * mem,uint num_elements,uint elt_size,client_name_t cname)103 minimal_alloc_byte_array(gs_memory_t * mem, uint num_elements,
104 			     uint elt_size, client_name_t cname)
105 {
106     return malloc(num_elements * elt_size);
107 }
108 
109 void *
minimal_alloc_struct(gs_memory_t * mem,gs_memory_type_ptr_t pstype,client_name_t cname)110 minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
111 	       client_name_t cname)
112 {
113     return malloc(pstype->ssize);
114 }
115 
116 void
minimal_free_object(gs_memory_t * mem,void * data,client_name_t cname)117 minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname)
118 {
119     free(data);
120     return;
121 }
122 
123 void
minimal_free_string(gs_memory_t * mem,byte * data,uint nbytes,client_name_t cname)124 minimal_free_string(gs_memory_t * mem, byte * data, uint nbytes, client_name_t cname)
125 {
126     free(data);
127     return;
128 }
129 
130 void basic_enum_ptrs(void);
131 void basic_reloc_ptrs(void);
132 
basic_enum_ptrs()133 void basic_enum_ptrs() {
134     printf("basic_enum_ptrs is only called by a GC. Abort.\n");
135     exit(1);
136 }
137 
basic_reloc_ptrs()138 void basic_reloc_ptrs() {
139     printf("basic_reloc_ptrs is only called by a GC. Abort.\n");
140     exit(1);
141 }
142 
143 
144 const gs_malloc_memory_t minimal_memory = {
145     (gs_memory_t *)&minimal_memory, /* stable */
146     { minimal_alloc_bytes, /* alloc_bytes_immovable */
147       NULL, /* resize_object */
148       minimal_free_object, /* free_object */
149       NULL, /* stable */
150       NULL, /* status */
151       NULL, /* free_all */
152       NULL, /* consolidate_free */
153       minimal_alloc_bytes, /* alloc_bytes */
154       minimal_alloc_struct, /* alloc_struct */
155       minimal_alloc_struct, /* alloc_struct_immovable */
156       minimal_alloc_byte_array, /* alloc_byte_array */
157       minimal_alloc_byte_array, /* alloc_byte_array_immovable */
158       NULL, /* alloc_struct_array */
159       NULL, /* alloc_struct_array_immovable */
160       NULL, /* object_size */
161       NULL, /* object_type */
162       minimal_alloc_bytes, /* alloc_string */
163       minimal_alloc_bytes, /* alloc_string_immovable */
164       NULL, /* resize_string */
165       minimal_free_string, /* free_string */
166       NULL, /* register_root */
167       NULL, /* unregister_root */
168       NULL /* enable_free */
169     },
170     NULL, /* gs_lib_ctx */
171     NULL, /* head */
172     NULL, /* non_gc_memory */
173     0, /* allocated */
174     0, /* limit */
175     0, /* used */
176     0  /* max used */
177 };
178 
179 void put_uint32(FILE *out, const unsigned int q);
180 void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len);
181 void inode_clear(romfs_inode* node);
182 void inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int*totlen);
183 void process_path(char *path, const char *prefix, const char *add_prefix, Xlist_element *Xlist_head, int compression,
184 	int *inode_count, int *totlen, FILE *out);
185 
186 /* put 4 byte integer, big endian */
put_uint32(FILE * out,const unsigned int q)187 void put_uint32(FILE *out, const unsigned int q)
188 {
189 #if ARCH_IS_BIG_ENDIAN
190     fprintf (out, "0x%08x,", q);
191 #else
192     fprintf (out, "0x%02x%02x%02x%02x,", q & 0xff, (q>>8) & 0xff, (q>>16) & 0xff, (q>>24) & 0xff);
193 #endif
194 }
195 
196 /* write string as 4 character chunks, padded to 4 byte words. */
put_bytes_padded(FILE * out,unsigned char * p,unsigned int len)197 void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len)
198 {
199     int i, j=0;
200     union {
201 	uint32_t w;
202 	struct {
203 	    unsigned char c1;
204 	    unsigned char c2;
205 	    unsigned char c3;
206 	    unsigned char c4;
207 	} c;
208     } w2c;
209 
210     for (i=0; i<(len/4); i++) {
211 	j = i*4;
212 	w2c.c.c1 = p[j++];
213 	w2c.c.c2 = p[j++];
214 	w2c.c.c3 = p[j++];
215 	w2c.c.c4 = p[j++];
216 	fprintf(out, "0x%08x,", w2c.w);
217 	if ((i & 7) == 7)
218 	    fprintf(out, "\n\t");
219     }
220     w2c.w = 0;
221     switch (len - j) {
222       case 3:
223         w2c.c.c3 = p[j+2];
224       case 2:
225         w2c.c.c2 = p[j+1];
226       case 1:
227         w2c.c.c1 = p[j];
228 	fprintf(out, "0x%08x,", w2c.w);
229       default: ;
230     }
231     fprintf(out, "\n\t");
232 }
233 
234 /* clear the internal memory of an inode */
inode_clear(romfs_inode * node)235 void inode_clear(romfs_inode* node)
236 {
237     int i, blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
238 
239     if (node) {
240         if (node->data) {
241             for (i = 0; i < blocks; i++) {
242                 if (node->data[i]) free(node->data[i]);
243             }
244             free(node->data);
245         }
246         if (node->data_lengths) free(node->data_lengths);
247     }
248 }
249 
250 /* write out and inode and its file data */
251 void
inode_write(FILE * out,romfs_inode * node,int compression,int inode_count,int * totlen)252 inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen)
253 {
254     int i, offset;
255     int blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
256     int namelen = strlen(node->name) + 1;	/* include terminating <nul> */
257 
258     /* write the node header */
259     fprintf(out,"    static uint32_t node_%d[] = {\n\t", inode_count);
260     /* 4 byte file length + compression flag in high bit */
261     put_uint32(out, node->length | (compression ? 0x80000000 : 0));
262     fprintf(out, "\t/* compression_flag_bit + file length */\n\t");
263 
264     printf("writing node '%s' len=%ld", node->name, node->length);
265 #ifdef DEBUG
266     printf(" %ld blocks %s", blocks, compression ? "compressed" : "binary");
267 #endif
268     printf("\n");
269 
270     /* write out data block structures */
271     offset = 4 + (8*blocks) + ((namelen+3) & ~3);
272     *totlen += offset;			/* add in the header size */
273     for (i = 0; i < blocks; i++) {
274 	put_uint32(out, node->data_lengths[i]);
275 	put_uint32(out, offset);
276 	offset += (node->data_lengths[i]+3) & ~3;
277 	fprintf(out, "\t/* data_block_length, offset to data_block */\n\t");
278     }
279     /* write file name (path) padded to 4 byte multiple */
280     fprintf(out, "\t/* file name '%s' */\n\t", node->name);
281     put_bytes_padded(out, (unsigned char *)node->name, namelen);
282 
283     /* write out data */
284     for (i = 0; i < blocks; i++) {
285 	put_bytes_padded(out, node->data[i], node->data_lengths[i]);
286 	*totlen += (node->data_lengths[i]+3) & ~3;	/* padded block length */
287     }
288     fprintf(out, "\t0 };\t/* end-of-node */\n");
289 }
290 
291 
292 /* This relies on the gp_enumerate_* which should not return directories, nor */
293 /* should it recurse into directories (unlike Adobe's implementation)         */
process_path(char * path,const char * prefix,const char * add_prefix,Xlist_element * Xlist_head,int compression,int * inode_count,int * totlen,FILE * out)294 void process_path(char *path, const char *prefix, const char *add_prefix, Xlist_element *Xlist_head,
295 		int compression, int *inode_count, int *totlen, FILE *out)
296 {
297     int namelen, excluded, save_count=*inode_count;
298     Xlist_element *Xlist_scan;
299     char *prefixed_path;
300     char *found_path, *rom_filename;
301     file_enum *pfenum;
302     int ret, block, blocks;
303     romfs_inode *node;
304     unsigned char *ubuf, *cbuf;
305     unsigned long ulen, clen;
306     FILE *in;
307 
308     prefixed_path = malloc(1024);
309     found_path = malloc(1024);
310     rom_filename = malloc(1024);
311     ubuf = malloc(ROMFS_BLOCKSIZE);
312     cbuf = malloc(ROMFS_CBUFSIZE);
313     if (ubuf == NULL || cbuf == NULL || prefixed_path == NULL ||
314 		found_path == NULL || rom_filename == NULL) {
315 	printf("malloc fail in process_path\n");
316 	exit(1);
317     }
318     prefixed_path[0] = 0;	/* empty string */
319     strcat(prefixed_path, prefix);
320     strcat(prefixed_path, path);
321     strcat(prefixed_path, "*");
322     strcpy(rom_filename, add_prefix);
323 #if defined(__WIN32__) || defined(__OS2__)
324     {
325 	int i;
326 
327 	/* On Windows, the paths may (will) have '\' instead of '/' so we translate them */
328 	for (i=0; i<strlen(prefixed_path); i++)
329 	    if (prefixed_path[i] == '\\')
330 		prefixed_path[i] = '/';
331 	for (i=0; i<strlen(rom_filename); i++)
332 	    if (rom_filename[i] == '\\')
333 		rom_filename[i] = '/';
334     }
335 #endif
336     /* check for the file on the Xlist */
337     pfenum = gp_enumerate_files_init(prefixed_path, strlen(prefixed_path),
338 		    	(gs_memory_t *)&minimal_memory);
339     if (pfenum == NULL) {
340 	printf("gp_enumerate_files_init failed.\n");
341 	exit(1);
342     }
343     while ((namelen=gp_enumerate_files_next(pfenum, found_path, 1024)) >= 0) {
344 	excluded = 0;
345 	found_path[namelen] = 0;		/* terminate the string */
346 	/* check to see if the tail of the path we found matches one on the exclusion list */
347 	for (Xlist_scan = Xlist_head; Xlist_scan != NULL; Xlist_scan = Xlist_scan->next) {
348 	    if (strlen(found_path) >= strlen(Xlist_scan->path)) {
349 		if (strcmp(Xlist_scan->path, found_path+strlen(found_path)-strlen(Xlist_scan->path)) == 0) {
350 		    excluded = 1;
351 		    break;
352 		}
353 	    }
354 	}
355 	if (excluded)
356 	    continue;
357 
358 	/* process a file */
359 	node = calloc(1, sizeof(romfs_inode));
360 	/* get info for this file */
361 	in = fopen(found_path, "rb");
362 	if (in == NULL) {
363 	    printf("unable to open file for processing: %s\n", found_path);
364 	    continue;
365 	}
366 	/* rom_filename + strlen(add_prefix) is first char after the new prefix we want to add */
367 	/* found_path + strlen(prefix) is the file name after the -P prefix */
368 	rom_filename[strlen(add_prefix)] = 0;		/* truncate afater prefix */
369 	strcat(rom_filename, found_path + strlen(prefix));
370 	node->name = rom_filename;	/* without -P prefix, with -d add_prefix */
371 	fseek(in, 0, SEEK_END);
372 	node->length = ftell(in);
373 	blocks = (node->length+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE + 1;
374 	node->data_lengths = calloc(blocks, sizeof(*node->data_lengths));
375 	node->data = calloc(blocks, sizeof(*node->data));
376 	fclose(in);
377 	in = fopen(found_path, "rb");
378 	block = 0;
379 	while (!feof(in)) {
380 	    ulen = fread(ubuf, 1, ROMFS_BLOCKSIZE, in);
381 	    if (!ulen) break;
382 	    clen = ROMFS_CBUFSIZE;
383 	    if (compression) {
384 		/* compress data here */
385 		ret = compress(cbuf, &clen, ubuf, ulen);
386 		if (ret != Z_OK) {
387 		    printf("error compressing data block!\n");
388 		    exit(1);
389 		}
390 	    } else {
391 		memcpy(cbuf, ubuf, ulen);
392 		clen = ulen;
393 	    }
394 	    node->data_lengths[block] = clen;
395 	    node->data[block] = malloc(clen);
396 	    memcpy(node->data[block], cbuf, clen);
397 	    block++;
398 	}
399 	fclose(in);
400 	/* write out data for this file */
401 	inode_write(out, node, compression, *inode_count, totlen);
402 	/* clean up */
403 	inode_clear(node);
404 	free(node);
405 	(*inode_count)++;
406     }
407     free(cbuf);
408     free(ubuf);
409     free(found_path);
410     free(prefixed_path);
411     if (save_count == *inode_count) {
412 	printf("warning: no files found from path '%s%s'\n", prefix, path);
413     }
414 }
415 
416 int
main(int argc,char * argv[])417 main(int argc, char *argv[])
418 {
419     int i;
420     int inode_count = 0, totlen = 0;
421     FILE *out;
422     const char *outfilename = "obj/gsromfs.c";
423     const char *prefix = "";
424     const char *add_prefix = "";
425     int atarg = 1;
426     int compression = 1;			/* default to doing compression */
427     Xlist_element *Xlist_scan, *Xlist_head = NULL;
428 
429     if (argc < 2) {
430 	printf("\n"
431  		"	Usage: mkromfs [-o outputfile] [options ...] paths\n"
432  		"	    options and paths can be interspersed and are processed in order\n"
433  		"	    options:\n"
434  		"		-o outputfile	default: obj/gsromfs.c if this option present, must be first.\n"
435  		"		-P prefix	use prefix to find path. prefix not included in %%rom%%\n"
436  		"		-X path		exclude the path from further processing.\n"
437  		"		-d string       directory in %%rom file system (just a prefix string on filename)\n"
438  		"		-c		compression on\n"
439  		"		-b		compression off (binary).\n"
440  		"\n"
441  		"	    Note: The tail of any path encountered will be tested so .svn on the -X\n"
442  		"		  list will exclude that path in all subsequent paths enumerated.\n"
443 	    );
444 	exit(1);
445     }
446 
447 #ifdef DEBUG
448     printf("compression will use %d byte blocksize (zlib output buffer %d bytes)\n",
449         ROMFS_BLOCKSIZE, ROMFS_CBUFSIZE);
450 #endif /* DEBUG */
451 
452     if (argc > 3 && argv[1][0] == '-' && argv[1][1] == 'o') {
453 	/* process -o option for outputfile */
454 	outfilename = argv[2];
455 	atarg += 2;
456     }
457 #ifdef DEBUG
458     printf("   writing romfs data to '%s'\n", outfilename);
459 #endif /* DEBUG */
460     out = fopen(outfilename, "w");
461 
462     fprintf(out,"\t/* Generated data for %%rom%% device, see mkromfs.c */\n");
463 #if ARCH_IS_BIG_ENDIAN
464     fprintf(out,"\t/* this code assumes a big endian target platform */\n");
465 #else
466     fprintf(out,"\t/* this code assumes a little endian target platform */\n");
467 #endif
468     fprintf(out,"\n#include \"stdint_.h\"\n");
469     fprintf(out,"\n#include \"time_.h\"\n\n");
470     fprintf(out,"    time_t gs_romfs_buildtime = %ld;\n\n", time(NULL));
471 
472     /* process the remaining arguments (options interspersed with paths) */
473     for (; atarg < argc; atarg++) {
474 	if (argv[atarg][0] == '-') {
475 	    /* process an option */
476 	    switch (argv[atarg][1]) {
477 	      case 'b':
478 	        compression = 0;
479 	        break;
480 	      case 'c':
481 	        compression = 1;
482 	        break;
483 	      case 'd':
484 		if (++atarg == argc) {
485 		    printf("   option %s missing required argument\n", argv[atarg-1]);
486 		    exit(1);
487 		}
488 		add_prefix = argv[atarg];
489 	        break;
490 	      case 'P':
491 		if (++atarg == argc) {
492 		    printf("   option %s missing required argument\n", argv[atarg-1]);
493 		    exit(1);
494 		}
495 		prefix = argv[atarg];
496 	        break;
497 	      case 'X':
498 		if (++atarg == argc) {
499 		    printf("   option %s missing required argument\n", argv[atarg-1]);
500 		    exit(1);
501 		}
502 		Xlist_scan = malloc(sizeof(Xlist_element));
503 		if (Xlist_scan == NULL) {
504 		    exit(1);
505 		}
506 		Xlist_scan->next = Xlist_head;
507 		Xlist_head = Xlist_scan;
508 		Xlist_head->path = argv[atarg];
509 	        break;
510 	      default:
511 	        printf("  unknown option: %s \n", argv[atarg]);
512 	    }
513 	    continue;
514 	}
515 	/* process a path */
516 	process_path(argv[atarg], prefix, add_prefix, Xlist_head, compression, &inode_count, &totlen, out);
517     }
518     /* now write out the array of nodes */
519     fprintf(out, "    uint32_t *gs_romfs[] = {\n");
520     for (i=0; i<inode_count; i++)
521 	fprintf(out, "\tnode_%d,\n", i);
522     fprintf(out, "\t0 };\n");
523 
524     fclose(out);
525 
526     printf("Total %%rom%% structure size is %d bytes.\n", totlen);
527 
528     return 0;
529 }
530 
531