/* Copyright (C) 2001-2006 artofcode LLC.
All Rights Reserved.
This file is part of GNU ghostscript
GNU ghostscript is free software; you can redistribute it and/or
modify it under the terms of the version 2 of the GNU General Public
License as published by the Free Software Foundation.
GNU ghostscript is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
ghostscript; see the file COPYING. If not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* $Id: mkromfs.c,v 1.3 2007/08/01 14:26:40 jemarch Exp $ */
/* Generate source data for the %rom% IODevice */
/*
* For reasons of convenience and footprint reduction, the various postscript
* source files, resources and fonts required by Ghostscript can be compiled
* into the executable.
*
* This file takes a set of directories, and creates a compressed filesystem
* image that can be compiled into the executable as static data and accessed
* through the %rom% iodevice prefix
*/
/*
* Usage: mkromfs [-o outputfile] [options ...] paths
* options and paths can be interspersed and are processed in order
* options:
* -o outputfile default: obj/gsromfs.c if this option present, must be first.
* -P prefix use prefix to find path. prefix not included in %rom%
* -X path exclude the path from further processing.
* -d string directory in %rom file system (really just a prefix string on filename)
* -c compression on
* -b compression off (binary).
*
* Note: The tail of any path encountered will be tested so .svn on the -X
* list will exclude that path in all subsequent paths enumerated.
*/
#include "stdpre.h"
#include "stdint_.h"
#include "gsiorom.h"
#include "gsmemret.h" /* for gs_memory_type_ptr_t */
#include "gsmalloc.h"
#include "gsstype.h"
#include "gp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "time_.h"
#include <zlib.h>
/*
* The rom file system is an array of pointers to nodes, terminated by a NULL
*/
/*
* in memory structure of each node is:
*
* length_of_uncompressed_file [31-bit big-endian]
* high bit is compression flag
* data_block_struct[]
* padded_file_name (char *) includes as least one terminating
* padded_data_blocks
*/
/*
* data_block_struct:
* data_length (not including pad) [32-bit big-endian]
* data_block_offset (start of each block) [32-bit big-endian]
*/
typedef struct romfs_inode_s {
unsigned long length; /* blocks is (length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE */
char *name; /* nul terminated */
unsigned long *data_lengths; /* this could be short if ROMFS_BLOCKSIZE */
/* is < 64k, but the cost is small to use int */
unsigned char **data;
} romfs_inode;
typedef struct Xlist_element_s {
void *next;
char *path;
} Xlist_element;
byte *minimal_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname);
byte *minimal_alloc_byte_array(gs_memory_t * mem, uint num_elements,
uint elt_size, client_name_t cname);
void *minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
client_name_t cname);
void minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname);
void minimal_free_string(gs_memory_t * mem, byte * data, uint nbytes, client_name_t cname);
/*******************************************************************************
* The following is a REALLY minimal gs_memory_t for use by the gp_ functions
*******************************************************************************/
byte *
minimal_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
{
return malloc(size);
}
byte *
minimal_alloc_byte_array(gs_memory_t * mem, uint num_elements,
uint elt_size, client_name_t cname)
{
return malloc(num_elements * elt_size);
}
void *
minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
client_name_t cname)
{
return malloc(pstype->ssize);
}
void
minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname)
{
free(data);
return;
}
void
minimal_free_string(gs_memory_t * mem, byte * data, uint nbytes, client_name_t cname)
{
free(data);
return;
}
void basic_enum_ptrs(void);
void basic_reloc_ptrs(void);
void basic_enum_ptrs() {
printf("basic_enum_ptrs is only called by a GC. Abort.\n");
exit(1);
}
void basic_reloc_ptrs() {
printf("basic_reloc_ptrs is only called by a GC. Abort.\n");
exit(1);
}
const gs_malloc_memory_t minimal_memory = {
(gs_memory_t *)&minimal_memory, /* stable */
{ minimal_alloc_bytes, /* alloc_bytes_immovable */
NULL, /* resize_object */
minimal_free_object, /* free_object */
NULL, /* stable */
NULL, /* status */
NULL, /* free_all */
NULL, /* consolidate_free */
minimal_alloc_bytes, /* alloc_bytes */
minimal_alloc_struct, /* alloc_struct */
minimal_alloc_struct, /* alloc_struct_immovable */
minimal_alloc_byte_array, /* alloc_byte_array */
minimal_alloc_byte_array, /* alloc_byte_array_immovable */
NULL, /* alloc_struct_array */
NULL, /* alloc_struct_array_immovable */
NULL, /* object_size */
NULL, /* object_type */
minimal_alloc_bytes, /* alloc_string */
minimal_alloc_bytes, /* alloc_string_immovable */
NULL, /* resize_string */
minimal_free_string, /* free_string */
NULL, /* register_root */
NULL, /* unregister_root */
NULL /* enable_free */
},
NULL, /* gs_lib_ctx */
NULL, /* head */
NULL, /* non_gc_memory */
0, /* allocated */
0, /* limit */
0, /* used */
0 /* max used */
};
void put_uint32(FILE *out, const unsigned int q);
void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len);
void inode_clear(romfs_inode* node);
void inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int*totlen);
void process_path(char *path, const char *prefix, const char *add_prefix, Xlist_element *Xlist_head, int compression,
int *inode_count, int *totlen, FILE *out);
/* put 4 byte integer, big endian */
void put_uint32(FILE *out, const unsigned int q)
{
#if ARCH_IS_BIG_ENDIAN
fprintf (out, "0x%08x,", q);
#else
fprintf (out, "0x%02x%02x%02x%02x,", q & 0xff, (q>>8) & 0xff, (q>>16) & 0xff, (q>>24) & 0xff);
#endif
}
/* write string as 4 character chunks, padded to 4 byte words. */
void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len)
{
int i, j=0;
union {
uint32_t w;
struct {
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
} c;
} w2c;
for (i=0; i<(len/4); i++) {
j = i*4;
w2c.c.c1 = p[j++];
w2c.c.c2 = p[j++];
w2c.c.c3 = p[j++];
w2c.c.c4 = p[j++];
fprintf(out, "0x%08x,", w2c.w);
if ((i & 7) == 7)
fprintf(out, "\n\t");
}
w2c.w = 0;
switch (len - j) {
case 3:
w2c.c.c3 = p[j+2];
case 2:
w2c.c.c2 = p[j+1];
case 1:
w2c.c.c1 = p[j];
fprintf(out, "0x%08x,", w2c.w);
default: ;
}
fprintf(out, "\n\t");
}
/* clear the internal memory of an inode */
void inode_clear(romfs_inode* node)
{
int i, blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
if (node) {
if (node->data) {
for (i = 0; i < blocks; i++) {
if (node->data[i]) free(node->data[i]);
}
free(node->data);
}
if (node->data_lengths) free(node->data_lengths);
}
}
/* write out and inode and its file data */
void
inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen)
{
int i, offset;
int blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
int namelen = strlen(node->name) + 1; /* include terminating */
/* write the node header */
fprintf(out," static uint32_t node_%d[] = {\n\t", inode_count);
/* 4 byte file length + compression flag in high bit */
put_uint32(out, node->length | (compression ? 0x80000000 : 0));
fprintf(out, "\t/* compression_flag_bit + file length */\n\t");
printf("writing node '%s' len=%ld", node->name, node->length);
#ifdef DEBUG
printf(" %ld blocks %s", blocks, compression ? "compressed" : "binary");
#endif
printf("\n");
/* write out data block structures */
offset = 4 + (8*blocks) + ((namelen+3) & ~3);
*totlen += offset; /* add in the header size */
for (i = 0; i < blocks; i++) {
put_uint32(out, node->data_lengths[i]);
put_uint32(out, offset);
offset += (node->data_lengths[i]+3) & ~3;
fprintf(out, "\t/* data_block_length, offset to data_block */\n\t");
}
/* write file name (path) padded to 4 byte multiple */
fprintf(out, "\t/* file name '%s' */\n\t", node->name);
put_bytes_padded(out, node->name, namelen);
/* write out data */
for (i = 0; i < blocks; i++) {
put_bytes_padded(out, node->data[i], node->data_lengths[i]);
*totlen += (node->data_lengths[i]+3) & ~3; /* padded block length */
}
fprintf(out, "\t0 };\t/* end-of-node */\n");
}
/* This relies on the gp_enumerate_* which should not return directories, nor */
/* should it recurse into directories (unlike Adobe's implementation) */
void process_path(char *path, const char *prefix, const char *add_prefix, Xlist_element *Xlist_head,
int compression, int *inode_count, int *totlen, FILE *out)
{
int namelen, excluded, save_count=*inode_count;
Xlist_element *Xlist_scan;
char *prefixed_path;
char *found_path, *rom_filename;
file_enum *pfenum;
int ret, block, blocks;
romfs_inode *node;
unsigned char *ubuf, *cbuf;
unsigned long ulen, clen;
FILE *in;
prefixed_path = malloc(1024);
found_path = malloc(1024);
rom_filename = malloc(1024);
ubuf = malloc(ROMFS_BLOCKSIZE);
cbuf = malloc(ROMFS_CBUFSIZE);
if (ubuf == NULL || cbuf == NULL || prefixed_path == NULL ||
found_path == NULL || rom_filename == NULL) {
printf("malloc fail in process_path\n");
exit(1);
}
prefixed_path[0] = 0; /* empty string */
strcat(prefixed_path, prefix);
strcat(prefixed_path, path);
strcat(prefixed_path, "*");
strcpy(rom_filename, add_prefix);
#ifdef __WIN32__
{
int i;
/* On Windows, the paths may (will) have '\' instead of '/' so we translate them */
for (i=0; i<strlen(prefixed_path); i++)
if (prefixed_path[i] == '\\')
prefixed_path[i] = '/';
for (i=0; i<strlen(rom_filename); i++)
if (rom_filename[i] == '\\')
rom_filename[i] = '/';
}
#endif
/* check for the file on the Xlist */
pfenum = gp_enumerate_files_init(prefixed_path, strlen(prefixed_path),
(gs_memory_t *)&minimal_memory);
if (pfenum == NULL) {
printf("gp_enumerate_files_init failed.\n");
exit(1);
}
while ((namelen=gp_enumerate_files_next(pfenum, found_path, 1024)) >= 0) {
excluded = 0;
found_path[namelen] = 0; /* terminate the string */
/* check to see if the tail of the path we found matches one on the exclusion list */
for (Xlist_scan = Xlist_head; Xlist_scan != NULL; Xlist_scan = Xlist_scan->next) {
if (strlen(found_path) >= strlen(Xlist_scan->path)) {
if (strcmp(Xlist_scan->path, found_path+strlen(found_path)-strlen(Xlist_scan->path)) == 0) {
excluded = 1;
break;
}
}
}
if (excluded)
continue;
/* process a file */
node = calloc(1, sizeof(romfs_inode));
/* get info for this file */
in = fopen(found_path, "rb");
if (in == NULL) {
printf("unable to open file for processing: %s\n", found_path);
continue;
}
/* rom_filename + strlen(add_prefix) is first char after the new prefix we want to add */
/* found_path + strlen(prefix) is the file name after the -P prefix */
rom_filename[strlen(add_prefix)] = 0; /* truncate afater prefix */
strcat(rom_filename, found_path + strlen(prefix));
node->name = rom_filename; /* without -P prefix, with -d add_prefix */
fseek(in, 0, SEEK_END);
node->length = ftell(in);
blocks = (node->length+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE + 1;
node->data_lengths = calloc(blocks, sizeof(*node->data_lengths));
node->data = calloc(blocks, sizeof(*node->data));
fclose(in);
in = fopen(found_path, "rb");
block = 0;
while (!feof(in)) {
ulen = fread(ubuf, 1, ROMFS_BLOCKSIZE, in);
if (!ulen) break;
clen = ROMFS_CBUFSIZE;
if (compression) {
/* compress data here */
ret = compress(cbuf, &clen, ubuf, ulen);
if (ret != Z_OK) {
printf("error compressing data block!\n");
exit(1);
}
} else {
memcpy(cbuf, ubuf, ulen);
clen = ulen;
}
node->data_lengths[block] = clen;
node->data[block] = malloc(clen);
memcpy(node->data[block], cbuf, clen);
block++;
}
fclose(in);
/* write out data for this file */
inode_write(out, node, compression, *inode_count, totlen);
/* clean up */
inode_clear(node);
free(node);
(*inode_count)++;
}
free(cbuf);
free(ubuf);
free(found_path);
free(prefixed_path);
if (save_count == *inode_count) {
printf("warning: no files found from path '%s%s'\n", prefix, path);
}
}
int
main(int argc, char *argv[])
{
int i;
int inode_count = 0, totlen = 0;
FILE *out;
const char *outfilename = "obj/gsromfs.c";
const char *prefix = "";
const char *add_prefix = "";
int atarg = 1;
int compression = 1; /* default to doing compression */
Xlist_element *Xlist_scan, *Xlist_head = NULL;
if (argc < 2) {
printf("\n"
" Usage: mkromfs [-o outputfile] [options ...] paths\n"
" options and paths can be interspersed and are processed in order\n"
" options:\n"
" -o outputfile default: obj/gsromfs.c if this option present, must be first.\n"
" -P prefix use prefix to find path. prefix not included in %%rom%%\n"
" -X path exclude the path from further processing.\n"
" -d string directory in %%rom file system (just a prefix string on filename)\n"
" -c compression on\n"
" -b compression off (binary).\n"
"\n"
" Note: The tail of any path encountered will be tested so .svn on the -X\n"
" list will exclude that path in all subsequent paths enumerated.\n"
);
exit(1);
}
#ifdef DEBUG
printf("compression will use %d byte blocksize (zlib output buffer %d bytes)\n",
ROMFS_BLOCKSIZE, ROMFS_CBUFSIZE);
#endif /* DEBUG */
if (argc > 3 && argv[1][0] == '-' && argv[1][1] == 'o') {
/* process -o option for outputfile */
outfilename = argv[2];
atarg += 2;
}
#ifdef DEBUG
printf(" writing romfs data to '%s'\n", outfilename);
#endif /* DEBUG */
out = fopen(outfilename, "w");
fprintf(out,"\t/* Generated data for %%rom%% device, see mkromfs.c */\n");
#if ARCH_IS_BIG_ENDIAN
fprintf(out,"\t/* this code assumes a big endian target platform */\n");
#else
fprintf(out,"\t/* this code assumes a little endian target platform */\n");
#endif
fprintf(out,"\n#include \"stdint_.h\"\n");
fprintf(out,"\n#include \"time_.h\"\n\n");
fprintf(out," time_t gs_romfs_buildtime = %ld;\n\n", time(NULL));
/* process the remaining arguments (options interspersed with paths) */
for (; atarg < argc; atarg++) {
if (argv[atarg][0] == '-') {
/* process an option */
switch (argv[atarg][1]) {
case 'b':
compression = 0;
break;
case 'c':
compression = 1;
break;
case 'd':
if (++atarg == argc) {
printf(" option %s missing required argument\n", argv[atarg-1]);
exit(1);
}
add_prefix = argv[atarg];
break;
case 'P':
if (++atarg == argc) {
printf(" option %s missing required argument\n", argv[atarg-1]);
exit(1);
}
prefix = argv[atarg];
break;
case 'X':
if (++atarg == argc) {
printf(" option %s missing required argument\n", argv[atarg-1]);
exit(1);
}
Xlist_scan = malloc(sizeof(Xlist_element));
if (Xlist_scan == NULL) {
exit(1);
}
Xlist_scan->next = Xlist_head;
Xlist_head = Xlist_scan;
Xlist_head->path = argv[atarg];
break;
default:
printf(" unknown option: %s \n", argv[atarg]);
}
continue;
}
/* process a path */
process_path(argv[atarg], prefix, add_prefix, Xlist_head, compression, &inode_count, &totlen, out);
}
/* now write out the array of nodes */
fprintf(out, " uint32_t *gs_romfs[] = {\n");
for (i=0; i<inode_count; i++)
fprintf(out, "\tnode_%d,\n", i);
fprintf(out, "\t0 };\n");
fclose(out);
printf("Total %%rom%% structure size is %d bytes.\n", totlen);
return 0;
}
|