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