diff options
Diffstat (limited to 'base/mkromfs.c')
-rw-r--r-- | base/mkromfs.c | 2638 |
1 files changed, 2638 insertions, 0 deletions
diff --git a/base/mkromfs.c b/base/mkromfs.c new file mode 100644 index 00000000..f6e4d6f9 --- /dev/null +++ b/base/mkromfs.c @@ -0,0 +1,2638 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* 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 ...] path path ... + * + * mkromfs no longer performs implicit enumeration of paths, so if a + * path is a directory to be enumerated trailing '<slash><star>' must be present. + * gcc complains about comments inside comments so we use '<slash>=/' and '<star>=*' here. + * Note: one should avoid bare "<slash><star>" which may be expanded by the shell + * to "/bin /boot /dev ...". e.g. use "<slash><star>.ttf" or "<slash><star>.ps" instead of "<slash><star>". + * + * 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/file from further processing. + * 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. + * + * -d romprefix directory in %rom% file system (a prefix string on filename) + * -c compression on + * -b compression off (binary). + * -C postscript 'compaction' on + * -B postscript 'compaction' off + * -g initfile gconfig_h + * special handling to read the 'gs_init.ps' file (from + * the current -P prefix path), and read the gconfig.h for + * psfile_ entries and combines them into a whitespace + * optimized and no comments form and writes this 'gs_init.ps' + * to the current -d destination path. This is a space and + * startup performance improvement, so also this should be + * BEFORE other stuff in the %rom% list of files (currently + * we do a sequential search in the %rom% directory). + * + * For performance reasons, it is best to turn off compression + * for the init file. Less frequently accessed files, if they + * are large should still be compressed. + * -s num_files split the output into <num_files> subfiles. + * + */ + +/* prevent gp.h redefining fopen */ +#define fopen fopen +/* prevent gp.h redefining sprintf */ +#define sprintf sprintf + +#include "stdpre.h" +#include "stdint_.h" +#include "time_.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 <ctype.h> +#include <memory.h> + +#include <zlib.h> + +int gs_log_error(int err, const char *file, int line); + +/* + * 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 <nul> + * 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 disc_length; /* length of file on disc */ + 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; + +typedef struct { + int num_splits; + int max_splits; + unsigned long *sizes; + char *outname; + char *outname_formatted; +} split_data; + +#define PATH_STR_LEN 1024 + +#ifndef ARCH_IS_BIG_ENDIAN +#define ARCH_IS_BIG_ENDIAN 0 +#endif +/* This gives what we're running on, + * whilst ARCH_IS_BIG_ENDIAN gives what we're + * building for. + */ +static inline int isbigendian(void) +{ + union { + int i; + char c[sizeof(int)]; + } u = {1}; + + return u.c[0] != 1; +} + +/* mkromfs doesn't use gp_stat, but it does link gp_misc.c which includes + call to gp_stat_impl(). Rather than major build upheaval for something not + used, just define a dummy here for Windows. + */ +#ifdef _WIN32 +int gp_stat_impl(const gs_memory_t *mem, const char *path, struct _stat64 *buf) +{ + (void)mem; + (void)path; + (void)buf; + return 0; +} +#endif + +/******************************************************************************* + * The following are non-redirected printing functions to avoid the need for + * these included from gsmisc.c (unix gp_ functions, among others, use if_debug). + *******************************************************************************/ +#include <stdarg.h> +#define PRINTF_BUF_LENGTH 1024 + +static const char msg_truncated[] = "\n*** Previous line has been truncated.\n"; + +int outprintf(const gs_memory_t *mem, const char *fmt, ...) +{ + int count; + char buf[PRINTF_BUF_LENGTH]; + va_list args; + + va_start(args, fmt); + count = vsnprintf(buf, sizeof(buf), fmt, args); + if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */ + fwrite(buf, 1, sizeof(buf) - 1, stdout); + fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stdout); + } else { + fwrite(buf, 1, count, stdout); + } + va_end(args); + return count; +} + +#ifndef GS_THREADSAFE +int errprintf_nomem(const char *fmt, ...) +{ + int count; + char buf[PRINTF_BUF_LENGTH]; + va_list args; + + va_start(args, fmt); + count = vsnprintf(buf, sizeof(buf), fmt, args); + if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */ + fwrite(buf, 1, sizeof(buf) - 1, stderr); + fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stderr); + } else { + fwrite(buf, 1, count, stderr); + } + va_end(args); + return count; +} +#endif + +int errprintf(const gs_memory_t *mem, const char *fmt, ...) +{ + int count; + char buf[PRINTF_BUF_LENGTH]; + va_list args; + + va_start(args, fmt); + count = vsnprintf(buf, sizeof(buf), fmt, args); + if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */ + fwrite(buf, 1, sizeof(buf) - 1, stderr); + fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stderr); + } else { + fwrite(buf, 1, count, stderr); + } + va_end(args); + return count; +} + + +#ifndef GS_THREADSAFE +#if __LINE__ /* compiler provides it */ +void +lprintf_file_and_line(const char *file, int line) +{ + epf("%s(%d): ", file, line); +} +#else +void +lprintf_file_only(FILE * f, const char *file) +{ + epf("%s(?): ", file); +} +#endif + +void +eprintf_program_ident(const char *program_name, + long revision_number) +{ + if (program_name) { + epf((revision_number ? "%s " : "%s"), program_name); + if (revision_number) { + int fpart = revision_number % 100; + + epf("%d.%02d", (int)(revision_number / 100), fpart); + } + epf(": "); + } +} +#endif + +void +emprintf_program_ident(const gs_memory_t *mem, + const char *program_name, + long revision_number) +{ + if (program_name) { + epfm(mem, (revision_number ? "%s " : "%s"), program_name); + if (revision_number) { + int fpart = revision_number % 100; + + epfm(mem, "%d.%02d", (int)(revision_number / 100), fpart); + } + epfm(mem, ": "); + } +} + +int +gs_log_error(int err, const char *file, int line) +{ + if (file == NULL) + errprintf_nomem("Returning error %d.\n", err); + else + errprintf_nomem("%s(%d): Returning error %d.\n", + (const char *)file, line, err); + return err; +} + +/******************************************************************************* + * The following is a REALLY minimal gs_memory_t for use by the gp_ functions + *******************************************************************************/ + +byte *minimal_alloc_bytes(gs_memory_t * mem, size_t size, client_name_t cname); +byte *minimal_alloc_byte_array(gs_memory_t * mem, size_t num_elements, + size_t 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, size_t nbytes, client_name_t cname); + +byte * +minimal_alloc_bytes(gs_memory_t * mem, size_t size, client_name_t cname) +{ + return malloc(size); +} + +byte * +minimal_alloc_byte_array(gs_memory_t * mem, size_t num_elements, + size_t 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, size_t 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 */ +}; + +int cmpstringp(const void *p1, const void *p2); +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, split_data *splits); +void process_path(char *path, const char *os_prefix, const char *rom_prefix, + Xlist_element *Xlist_head, int compression, + int compaction, int *inode_count, int *totlen, FILE *out, + split_data *splits); +FILE *prefix_open(const char *os_prefix, const char *inname); +void prefix_add(const char *prefix, const char *filename, char *prefixed_path); + +/* 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++]; + if (isbigendian() != ARCH_IS_BIG_ENDIAN) { + fprintf (out, "0x%02x%02x%02x%02x,", (w2c.w) & 0xff, (w2c.w>>8) & 0xff, (w2c.w>>16) & 0xff, (w2c.w >>24) & 0xff); + } + else { + 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]; + if (isbigendian() != ARCH_IS_BIG_ENDIAN) { + fprintf (out, "0x%02x%02x%02x%02x,", (w2c.w) & 0xff, (w2c.w>>8) & 0xff, (w2c.w>>16) & 0xff, (w2c.w >>24) & 0xff); + } + else { + 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; + + if (node) { + blocks = (node->disc_length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE; + 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); + } +} + +static unsigned long +do_inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen, int split) +{ + int i, offset; + int blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE; + int namelen = strlen(node->name) + 1; /* include terminating <nul> */ + int clen = 0; /* compressed length */ + + /* write the node header */ + fprintf(out," %s const uint32_t %snode_%d[] = {\n\t", + split ? "" : "static", + split ? "mkromfs_" : "", + 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"); + + /* 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, (unsigned char *)node->name, namelen); + + /* write out data */ + for (i = 0; i < blocks; i++) { + put_bytes_padded(out, node->data[i], node->data_lengths[i]); + clen += node->data_lengths[i]; + *totlen += (node->data_lengths[i]+3) & ~3; /* padded block length */ + } + fprintf(out, "\t0 };\t/* end-of-node */\n"); + + printf("node '%s' len=%ld", node->name, node->length); + printf(" %d blocks", blocks); + if (compression) { + printf(", compressed size=%d", clen); + } + printf("\n"); + if (compression) + return clen; + return node->length; +} + +static void +prepare_splits(split_data *splits) +{ + if (splits->num_splits) { + /* Make sure we have a properly sized size array. */ + if (splits->num_splits > splits->max_splits) { + unsigned long *sizes = realloc(splits->sizes, sizeof(unsigned long) * splits->num_splits); + if (sizes == NULL) { + fprintf(stderr, "Failed to allocate split data array\n"); + exit(1); + } + memset(&sizes[splits->max_splits], 0, sizeof(unsigned long) * (splits->num_splits - splits->max_splits)); + splits->sizes = sizes; + splits->max_splits = splits->num_splits; + } + } +} + +static void +start_file(FILE *out) +{ + 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"); +} + +/* write out an inode and its file data */ +void +inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen, split_data *splits) +{ + prepare_splits(splits); + if (splits->max_splits) { + /* Find the smallest bin to add this to. */ + FILE *out2; + int which = 0; + int i; + for (i = 1; i < splits->max_splits; i++) { + if (splits->sizes[which] > splits->sizes[i]) + which = i; + } + + sprintf(splits->outname_formatted, splits->outname, which); + if (splits->sizes[which] == 0) { + out2 = fopen(splits->outname_formatted, "w"); + start_file(out2); + } else { + out2 = fopen(splits->outname_formatted, "a"); + } + splits->sizes[which] += do_inode_write(out2, node, compression, inode_count, totlen, 1); + fclose(out2); + } else + (void)do_inode_write(out, node, compression, inode_count, totlen, 0); +} + +void +prefix_add(const char *prefix, const char *filename, char *prefixed_path) +{ + prefixed_path[0] = 0; /* empty string */ + strcat(prefixed_path, prefix); + strcat(prefixed_path, filename); +#if defined(__WIN32__) || defined(__OS2__) + /* On Windows, the paths may (will) have '\' instead of '/' so we translate them */ + { + int i; + + for (i=0; i<strlen(prefixed_path); i++) + if (prefixed_path[i] == '\\') + prefixed_path[i] = '/'; + } +#endif +} + +/* Simple ps compaction routines; strip comments, compact whitespace */ +typedef enum { + PSC_BufferIn = 0, + PSC_InComment, + PSC_InString, + PSC_InHexString, + PSC_BufferOut, + PSC_BufferCopy, +} psc_state; + +typedef int (psc_getc)(void *); +typedef void (psc_ungetc)(int, void *); +typedef int (psc_feof)(void *); + +typedef struct { + psc_state state; + int inpos; + int inmax; + int outpos; + int outend; + int outmax; + int buffercopy; + int wasascii; + unsigned char *bufferin; + unsigned char *bufferout; + psc_getc *pgetc; + psc_ungetc *unpgetc; + psc_feof *peof; + void *file; + int names; + int binary; + int noescape; + int escaping; + int paren; + int firstnum; +} pscompstate; + +static void pscompact_start(pscompstate *psc, psc_getc *myfgetc, psc_ungetc *myungetc, psc_feof *myfeof, void *myfile, int names, int binary, int firstnum) +{ + psc->state = PSC_BufferIn; + psc->bufferin = malloc(80); + psc->bufferout = malloc(80); + if ((psc->bufferin == NULL) || (psc->bufferout == NULL)) { + fprintf(stderr, "Malloc failed in ps compaction\n"); + exit(1); + } + psc->inmax = 80; + psc->outmax = 80; + psc->inpos = 0; + psc->wasascii = 0; + psc->pgetc = myfgetc; + psc->unpgetc = myungetc; + psc->peof = myfeof; + psc->file = myfile; + psc->names = names; + psc->binary = binary; + psc->noescape = 0; + psc->escaping = 0; + psc->paren = 0; + psc->firstnum = firstnum; +} + +static void pscompact_end(pscompstate *psc) +{ + free(psc->bufferin); + free(psc->bufferout); +} + +static void pscompact_copy(pscompstate *psc, int c, int n) +{ + psc->bufferout[0] = c; + psc->outend = 1; + psc->outpos = 0; + psc->buffercopy = n; + if (n == 0) + psc->state = PSC_BufferOut; + else + psc->state = PSC_BufferCopy; +} + +static void pscompact_copy2(pscompstate *psc, int c1, int c2, int n) +{ + psc->bufferout[0] = c1; + psc->bufferout[1] = c2; + psc->outend = 2; + psc->outpos = 0; + psc->buffercopy = n; + if (n == 0) + psc->state = PSC_BufferOut; + else + psc->state = PSC_BufferCopy; +} + +static void pscompact_copy3(pscompstate *psc, int c1, int c2, int c3, int n) +{ + psc->bufferout[0] = c1; + psc->bufferout[1] = c2; + psc->bufferout[2] = c3; + psc->outend = 3; + psc->outpos = 0; + psc->buffercopy = n; + if (n == 0) + psc->state = PSC_BufferOut; + else + psc->state = PSC_BufferCopy; +} + +static void pscompact_buffer(pscompstate *psc, int c) +{ + if (psc->inpos == psc->inmax) { + psc->inmax *= 2; + psc->bufferin = realloc(psc->bufferin, psc->inmax * 2); + if (psc->bufferin == NULL) { + fprintf(stderr, "Realloc failed in pscompaction\n"); + exit(1); + } + } + psc->bufferin[psc->inpos++] = c; +} + +static void pscompact_bufferatstart(pscompstate *psc, int c) +{ + if (psc->inpos == psc->inmax) { + psc->inmax *= 2; + psc->bufferin = realloc(psc->bufferin, psc->inmax * 2); + if (psc->bufferin == NULL) { + fprintf(stderr, "Realloc failed in pscompaction\n"); + exit(1); + } + } + memmove(psc->bufferin+1, psc->bufferin, psc->inpos); + psc->inpos++; + psc->bufferin[0] = c; +} + +static void pscompact_copyinout(pscompstate *psc) +{ + if (psc->outmax < psc->inpos) { + psc->outmax = psc->inmax; + psc->bufferout = realloc(psc->bufferout, psc->outmax); + if (psc->bufferout == NULL) { + fprintf(stderr, "Realloc failed in pscompaction\n"); + exit(1); + } + } + memcpy(psc->bufferout, psc->bufferin, psc->inpos); + psc->outpos = 0; + psc->outend = psc->inpos; + psc->state = PSC_BufferOut; + psc->inpos = 0; +} + +static void pscompact_copyinout_bin(pscompstate *psc) +{ + pscompact_copyinout(psc); + psc->noescape = 1; +} + +static void pscompact_hex2ascii(pscompstate *psc) +{ + int i = 0; + int o = 0; + + while (i < psc->inpos) { + int v = 0; + + if ((psc->bufferin[i] >= '0') && (psc->bufferin[i] <= '9')) { + v = (psc->bufferin[i] - '0')<<4; + } else if ((psc->bufferin[i] >= 'a') && (psc->bufferin[i] <= 'f')) { + v = (psc->bufferin[i] - 'a' + 10)<<4; + } else if ((psc->bufferin[i] >= 'A') && (psc->bufferin[i] <= 'F')) { + v = (psc->bufferin[i] - 'A' + 10)<<4; + } else { + fprintf(stderr, "Malformed hexstring in pscompaction!\n"); + exit(1); + } + i++; + + if (i == psc->inpos) { + /* End of string */ + } else if ((psc->bufferin[i] >= '0') && (psc->bufferin[i] <= '9')) { + v += psc->bufferin[i] - '0'; + } else if ((psc->bufferin[i] >= 'a') && (psc->bufferin[i] <= 'f')) { + v += psc->bufferin[i] - 'a' + 10; + } else if ((psc->bufferin[i] >= 'A') && (psc->bufferin[i] <= 'F')) { + v += psc->bufferin[i] - 'A' + 10; + } else { + fprintf(stderr, "Malformed hexstring in pscompaction!\n"); + exit(1); + } + i++; + psc->bufferin[o++] = v; + } + psc->inpos = o; +} + +static const char *pscompact_names[] = +{ + "abs", + "add", + "aload", + "anchorsearch", + "and", + "arc", + "arcn", + "arct", + "arcto", + "array", + "ashow", + "astore", + "awidthshow", + "begin", + "bind", + "bitshift", + "ceiling", + "charpath", + "clear", + "cleartomark", + "clip", + "clippath", + "closepath", + "concat", + "concatmatrix", + "copy", + "count", + "counttomark", + "currentcmykcolor", + "currentdash", + "currentdict", + "currentfile", + "currentfont", + "currentgray", + "currentgstate", + "currenthsbcolor", + "currentlinecap", + "currentlinejoin", + "currentlinewidth", + "currentmatrix", + "currentpoint", + "currentrgbcolor", + "currentshared", + "curveto", + "cvi", + "cvlit", + "cvn", + "cvr", + "cvrs", + "cvs", + "cvx", + "def", + "defineusername", + "dict", + "div", + "dtransform", + "dup", + "end", + "eoclip", + "eofill", + "eoviewclip", + "eq", + "exch", + "exec", + "exit", + "file", + "fill", + "findfont", + "flattenpath", + "floor", + "flush", + "flushfile", + "for", + "forall", + "ge", + "get", + "getinterval", + "grestore", + "gsave", + "gstate", + "gt", + "identmatrix", + "idiv", + "idtransform", + "if", + "ifelse", + "image", + "imagemask", + "index", + "ineofill", + "infill", + "initviewclip", + "inueofill", + "inufill", + "invertmatrix", + "itransform", + "known", + "le", + "length", + "lineto", + "load", + "loop", + "lt", + "makefont", + "matrix", + "maxlength", + "mod", + "moveto", + "mul", + "ne", + "neg", + "newpath", + "not", + "null", + "or", + "pathbbox", + "pathforall", + "pop", + "print", + "printobject", + "put", + "putinterval", + "rcurveto", + "read", + "readhexstring", + "readline", + "readstring", + "rectclip", + "rectfill", + "rectstroke", + "rectviewclip", + "repeat", + "restore", + "rlineto", + "rmoveto", + "roll", + "rotate", + "round", + "save", + "scale", + "scalefont", + "search", + "selectfont", + "setbbox", + "setcachedevice", + "setcachedevice2", + "setcharwidth", + "setcmykcolor", + "setdash", + "setfont", + "setgray", + "setgstate", + "sethsbcolor", + "setlinecap", + "setlinejoin", + "setlinewidth", + "setmatrix", + "setrgbcolor", + "setshared", + "shareddict", + "show", + "showpage", + "stop", + "stopped", + "store", + "string", + "stringwidth", + "stroke", + "strokepath", + "sub", + "systemdict", + "token", + "transform", + "translate", + "truncate", + "type", + "uappend", + "ucache", + "ueofill", + "ufill", + "undef", + "upath", + "userdict", + "ustroke", + "viewclip", + "viewclippath", + "where", + "widthshow", + "write", + "writehexstring", + "writeobject", + "writestring", + "wtranslation", + "xor", + "xshow", + "xyshow", + "yshow", + "FontDirectory", + "SharedFontDirectory", + "Courier%", + "Courier-Bold", + "Courier-BoldOblique", + "Courier-Oblique", + "Helvetica", + "Helvetica-Bold", + "Helvetica-BoldOblique", + "Helvetica-Oblique", + "Symbol", + "Times-Bold", + "Times-BoldItalic", + "Times-Italic", + "Times-Roman", + "execuserobject", + "currentcolor", + "currentcolorspace", + "currentglobal", + "execform", + "filter", + "findresource", + "globaldict", + "makepattern", + "setcolor", + "setcolorspace", + "setglobal", + "setpagedevice", + "setpattern" +}; + +static int pscompact_isname(pscompstate *psc, int *i) +{ + int off = 0; + int n; + + if (psc->bufferin[0] == '/') + off = 1; + for (n = 0; n < sizeof(pscompact_names)/sizeof(char *); n++) { + if (strncmp(pscompact_names[n], (const char *)&psc->bufferin[off], psc->inpos-off) == 0) { + /* Match! */ + if (off) + *i = -1-n; + else + *i = n; + return 1; + } + } + return 0; +} + +static int pscompact_isint(pscompstate *psc, int *i) +{ + int pos = 0; + + if ((psc->bufferin[0] == '+') || (psc->bufferin[0] == '-')) { + pos = 1; + } + if (pos >= psc->inpos) + return 0; + if ((psc->inpos > pos+3) && + (strncmp((const char *)&psc->bufferin[pos], "16#", 3) == 0)) { + /* hex */ + int v = 0; + pos += 3; + while (pos < psc->inpos) { + if ((psc->bufferin[pos] >= '0') && (psc->bufferin[pos] <= '9')) + v = v*16 + psc->bufferin[pos] - '0'; + else if ((psc->bufferin[pos] >= 'a') && (psc->bufferin[pos] <= 'f')) + v = v*16 + psc->bufferin[pos] - 'a' + 10; + else if ((psc->bufferin[pos] >= 'A') && (psc->bufferin[pos] <= 'F')) + v = v*16 + psc->bufferin[pos] - 'A' + 10; + else + return 0; + pos++; + } + if (psc->bufferin[0] == '-') + v = -v; + *i = v; + return 1; + } + + do { + if ((psc->bufferin[pos] < '0') || (psc->bufferin[pos] > '9')) + return 0; + pos++; + } while (pos < psc->inpos); + + if (psc->inpos == psc->inmax) { + psc->inmax *= 2; + psc->bufferin = realloc(psc->bufferin, psc->inmax); + } + psc->bufferin[psc->inpos] = 0; + *i = atoi((const char *)psc->bufferin); + + /* Check for 32bit overflow */ + if (psc->inpos > 9) { + char *end; + double d = strtod((const char *)psc->bufferin, &end); + if (d != (double)(int)*i) + return 0; + } + + return 1; +} + +static int pscompact_isfloat(pscompstate *psc, float *f) +{ + int pos = 0; + int point = 0; + + if ((psc->bufferin[0] == '+') || (psc->bufferin[0] == '-')) { + pos = 1; + } + if (pos >= psc->inpos) + return 0; + do { + if ((psc->bufferin[pos] >= '0') && (psc->bufferin[pos] <= '9')) { + /* Digits are OK */ + } else if ((psc->bufferin[pos] == '.') && (point == 0)) { + /* as are points, but only the first one */ + point = 1; + } else { + /* Anything else is a failure */ + return 0; + } + pos++; + } while (pos < psc->inpos); + + if (psc->inpos == psc->inmax) { + psc->inmax *= 2; + psc->bufferin = realloc(psc->bufferin, psc->inmax); + } + psc->bufferin[psc->inpos] = 0; + *f = atof((const char *)psc->bufferin); + return 1; +} + +static unsigned long pscompact_getcompactedblock(pscompstate *psc, unsigned char *ubuf, unsigned long ulen) +{ + unsigned char *out; + int c; + + if (ulen == 0) + return 0; + out = ubuf; + do { + switch (psc->state) { + case PSC_BufferIn: + c = psc->pgetc(psc->file); + if ((c <= 32) || (c == EOF)) { + /* Whitespace */ + if (psc->inpos == 0) { + /* Leading whitespace, just bin it */ + break; + } + } else if (c == '(') { + /* Start of a string */ + if (psc->inpos == 0) { + /* Go into string state */ + psc->state = PSC_InString; + psc->paren = 1; + break; + } + } else if (c == '>') { + /* End of a dictionary */ + if (psc->inpos == 0) { + /* Just output it (with no whitespace) */ + *out++ = c; + break; + } + } else if ((c == '{') || (c == '}') || + (c == '[') || (c == ']')) { + /* Stand alone token bytes */ + if (psc->inpos == 0) { + /* Process now and be done with it */ + *out++ = c; + psc->wasascii = 0; + break; + } + } else if ((c >= 128) && (c <= 131)) { + fprintf(stderr, "Can't compact files with binary object sequences in!"); + exit(1); + } else if ((c == 132) || (c == 133) || (c == 138) || (c == 139) || (c == 140)) { + /* 32 bit integers or reals */ + if (psc->inpos == 0) { + pscompact_copy(psc, c, 4); + break; + } + } else if ((c == 134) || (c == 135)) { + /* 16 bit integers */ + if (psc->inpos == 0) { + pscompact_copy(psc, c, 2); + break; + } + } else if ((c == 136) || (c == 141) || (c == 145) || + (c == 146) || (c == 147) || (c == 148)) { + /* 8 bit integers or bools or pool name */ + if (psc->inpos == 0) { + pscompact_copy(psc, c, 1); + break; + } + } else if (c == 137) { + /* fixed point */ + if (psc->inpos == 0) { + int r = psc->pgetc(psc->file); + if (r & 32) { + pscompact_copy2(psc, c, r, 2); + } else { + pscompact_copy2(psc, c, r, 4); + } + break; + } + } else if (c == 142) { + /* short string */ + if (psc->inpos == 0) { + int n = psc->pgetc(psc->file); + pscompact_copy2(psc, c, n, n); + break; + } + } else if (c == 143) { + /* long string */ + if (psc->inpos == 0) { + int n1 = psc->pgetc(psc->file); + int n2 = psc->pgetc(psc->file); + pscompact_copy3(psc, c, n1, n2, (n1<<8) + n2); + break; + } + } else if (c == 144) { + /* long string */ + if (psc->inpos == 0) { + int n1 = psc->pgetc(psc->file); + int n2 = psc->pgetc(psc->file); + pscompact_copy3(psc, c, n1, n2, n1 + (n2<<8)); + break; + } + } else if ((c >= 149) && (c <= 159)) { + fprintf(stderr, "Can't compact files with binary postscript byte %d in!", c); + exit(1); + } else if (c == '%') { + if (psc->inpos == 0) { + psc->state = PSC_InComment; + break; + } + } else if (c == '<') { + if (psc->inpos == 0) { + psc->state = PSC_InHexString; + break; + } + } else if ((c == '/') && + (psc->inpos > 0) && + (psc->bufferin[psc->inpos-1] != '/')) { + /* We hit a / while not in a prefix of them - stop the + * buffering. */ + } else { + /* Stick c into the buffer and continue */ + pscompact_buffer(psc, c); + break; + } + /* If we reach here, we have a complete buffer full. We need + * to write it (or something equivalent to it) out. */ + if (c > 32) { + /* Put c back into the file to process next time */ + psc->unpgetc(c, psc->file); + } + if (psc->binary) { + int i; + float f; + /* Is it a number? */ + if (pscompact_isint(psc, &i)) { + if (psc->firstnum) { + /* Don't alter the first number in a file */ + psc->firstnum = 0; + } else if ((i >= -128) && (i <= 127)) { + /* Encode as a small integer */ + psc->bufferout[0] = 136; + psc->bufferout[1] = i & 255; + psc->inpos = 0; + psc->outend = 2; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } else if ((i >= -0x8000) && (i <= 0x7FFF)) { + /* Encode as a 16 bit integer */ + psc->bufferout[0] = 135; + psc->bufferout[1] = i & 255; + psc->bufferout[2] = (i>>8) & 255; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 3; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } else { + /* Encode as a 32 bit integer */ + psc->bufferout[0] = 133; + psc->bufferout[1] = i & 255; + psc->bufferout[2] = (i>>8) & 255; + psc->bufferout[3] = (i>>16) & 255; + psc->bufferout[4] = (i>>24) & 255; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 5; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } + } else if ((sizeof(float) == 4) && pscompact_isfloat(psc, &f)) { + /* Encode as a 32 bit float */ + union { + float f; + unsigned char c[4]; + } fc; + fc.f = 1.0; + if ((fc.c[0] == 0) && (fc.c[1] == 0) && + (fc.c[2] == 0x80) && (fc.c[3] == 0x3f)) { + fc.f = f; + psc->bufferout[0] = 139; + psc->bufferout[1] = (char)fc.c[0]; + psc->bufferout[2] = (char)fc.c[1]; + psc->bufferout[3] = (char)fc.c[2]; + psc->bufferout[4] = (char)fc.c[3]; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 5; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } else if ((fc.c[0] == 0x3f) && (fc.c[1] == 0x80) && + (fc.c[2] == 0) && (fc.c[3] == 0)) { + fc.f = f; + psc->bufferout[0] = 139; + psc->bufferout[1] = (char)fc.c[3]; + psc->bufferout[2] = (char)fc.c[2]; + psc->bufferout[3] = (char)fc.c[1]; + psc->bufferout[4] = (char)fc.c[0]; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 5; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } + } + if ((psc->inpos == 4) && + (strncmp((const char *)psc->bufferin, "true", 4) == 0)) { + /* Encode as a 32 bit integer */ + psc->bufferout[0] = 141; + psc->bufferout[1] = 1; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 2; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } + if ((psc->inpos == 5) && + (strncmp((const char *)psc->bufferin, "false", 5) == 0)) { + /* Encode as a 32 bit integer */ + psc->bufferout[0] = 141; + psc->bufferout[1] = 0; + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 2; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } + if (psc->names && pscompact_isname(psc, &i)) { + /* Encode as a name lookup */ + if (i >= 0) { + /* Executable */ + psc->bufferout[0] = 146; + psc->bufferout[1] = i; + } else { + /* Literal */ + psc->bufferout[0] = 145; + psc->bufferout[1] = 1-i; + } + psc->inpos = 0; + psc->outpos = 0; + psc->outend = 2; + psc->wasascii = 0; + psc->noescape = 1; + psc->state = PSC_BufferOut; + break; + } + } + if ((psc->wasascii) && (psc->bufferin[0]!='/')) + *out++ = 32; + pscompact_copyinout(psc); + psc->wasascii = 1; + break; + case PSC_BufferOut: + { + unsigned char c = psc->bufferout[psc->outpos++]; + if (psc->noescape) { + } else if ((c == 10) && (psc->outpos < psc->outend)) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = 'n'; + psc->escaping = 0; + } + } else if (c == 9) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = 't'; + psc->escaping = 0; + } + } else if (c == 8) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = 'b'; + psc->escaping = 0; + } + } else if (c == 12) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = 'f'; + psc->escaping = 0; + } + } else if (c == 13) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = 'r'; + psc->escaping = 0; + } + } else if (c == '\\') { + if (!psc->escaping) { + psc->outpos--; + psc->escaping = 1; + } else { + psc->escaping = 0; + } + } else if ((c == ')') && (psc->outpos < psc->outend)) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = ')'; + psc->escaping = 0; + } + } else if ((c == '(') && (psc->outpos > 1)) { + if (!psc->escaping) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else { + c = '('; + psc->escaping = 0; + } + } else if (((c < 32) && (c != 10)) || (c >= 128)) { + if (psc->escaping == 0) { + c = '\\'; + psc->outpos--; + psc->escaping = 1; + } else if (psc->escaping == 1) { + c = '0' + ((c >> 6)&3); + psc->outpos--; + psc->escaping = 2; + } else if (psc->escaping == 2) { + c = '0' + ((c >> 3)&7); + psc->outpos--; + psc->escaping = 3; + } else if (psc->escaping == 3) { + c = '0' + (c&7); + psc->escaping = 0; + } + } + *out++ = c; + if (psc->outpos == psc->outend) { + psc->outpos = 0; + psc->outend = 0; + psc->noescape = 0; + psc->state = PSC_BufferIn; + } + break; + } + case PSC_BufferCopy: + if (psc->outpos < psc->outend) { + *out++ = psc->bufferout[psc->outpos++]; + break; + } + *out++ = psc->pgetc(psc->file); + psc->buffercopy--; + if (psc->buffercopy == 0) { + psc->outpos = 0; + psc->outend = 0; + psc->state = PSC_BufferIn; + } + break; + case PSC_InString: + c = psc->pgetc(psc->file); + if ((c == ')') && (--psc->paren == 0)) { + psc->wasascii = 0; + if (psc->binary) { + /* Write out the string as binary */ + if (psc->inpos < 256) { + pscompact_bufferatstart(psc, psc->inpos); + pscompact_bufferatstart(psc, 142); + pscompact_copyinout_bin(psc); + break; + } else if (psc->inpos < 65536) { + int count = psc->inpos; + pscompact_bufferatstart(psc, count>>8); + pscompact_bufferatstart(psc, count & 255); + pscompact_bufferatstart(psc, 144); + pscompact_copyinout_bin(psc); + break; + } + } + /* if all else fails, just write it out as an ascii + * string. */ + pscompact_bufferatstart(psc, '('); + pscompact_buffer(psc, ')'); + pscompact_copyinout(psc); + break; + } else if (c == '\\') { + c = psc->pgetc(psc->file); + if (c == 10) + break; + else if (c == 'b') + c = 8; + else if (c == 't') + c = 9; + else if (c == 'n') + c = 10; + else if (c == 'f') + c = 12; + else if (c == 'r') + c = 13; + else if ((c >= '0') && (c <= '7')) { + int d; + c = (c - '0'); + d = psc->pgetc(psc->file); + if ((d >= '0') && (d <= '7')) { + c = (c<<3) + (d-'0'); + d = psc->pgetc(psc->file); + if ((d >= '0') && (d <= '7')) { + c = (c<<3) + (d-'0'); + } else { + psc->unpgetc(d, psc->file); + } + } else { + psc->unpgetc(d, psc->file); + } + c &= 0xFF; + } + } else if (c == '(') { + psc->paren++; + } + pscompact_buffer(psc, c); + break; + case PSC_InComment: + /* Watch for an EOL, otherwise swallow */ + c = psc->pgetc(psc->file); + if ((c == 13) || (c == 10)) { + if ((psc->inpos >= 3) && + (strncmp((const char *)psc->bufferin, "END", 3) == 0)) { + /* Special comment to retain */ + pscompact_bufferatstart(psc, '%'); + pscompact_buffer(psc, 10); + pscompact_copyinout(psc); + break; + } + if ((psc->inpos >= 7) && + (strncmp((const char *)psc->bufferin, "NAMESOK", 7) == 0)) { + psc->names = 1; + pscompact_bufferatstart(psc, '%'); + pscompact_buffer(psc, 10); + pscompact_copyinout(psc); + break; + } + if ((psc->inpos >= 8) && + (strncmp((const char *)psc->bufferin, "BINARYOK", 8) == 0)) { + psc->binary = 1; + pscompact_bufferatstart(psc, '%'); + pscompact_buffer(psc, 10); + pscompact_copyinout(psc); + break; + } + /* Throw the buffered line away, and go back to buffering */ + psc->inpos = 0; + psc->state = PSC_BufferIn; + break; + } + pscompact_buffer(psc, c); + break; + case PSC_InHexString: + c = psc->pgetc(psc->file); + if (c == '<') { + /* Dictionary */ + pscompact_copy2(psc, '<', '<', 0); + break; + } else if (c == '~') { + /* FIXME: ASCII85 encoded! */ + fprintf(stderr, "ASCII85 encoded strings unsupported in pscompaction\n"); + exit(1); + } else if (c == '>') { + psc->wasascii = 0; + if (psc->binary) { + pscompact_hex2ascii(psc); + if (psc->inpos < 256) { + pscompact_bufferatstart(psc, psc->inpos); + pscompact_bufferatstart(psc, 142); + pscompact_copyinout_bin(psc); + } else if (psc->inpos < 65536) { + int count = psc->inpos; + pscompact_bufferatstart(psc, count>>8); + pscompact_bufferatstart(psc, count & 255); + pscompact_bufferatstart(psc, 144); + pscompact_copyinout_bin(psc); + } else { + fprintf(stderr, "HexString more than 64K in pscompaction\n"); + exit(1); + } + break; + } + /* If all else fails, write it out as an ascii hexstring + * again */ + pscompact_bufferatstart(psc, '<'); + pscompact_buffer(psc, '>'); + pscompact_copyinout(psc); + break; + } else if (c <= 32) { + /* Swallow whitespace */ + break; + } else if (((c >= 'A') && (c <= 'Z')) || + ((c >= 'a') && (c <= 'z')) || + ((c >= '0') && (c <= '9'))) { + pscompact_buffer(psc, c); + } else { + fprintf(stderr, "Unexpected char when parsing hexstring in pscompaction\n"); + exit(1); + } + break; + } + } while ((out-ubuf != ulen) && (!psc->peof(psc->file))); + return out-ubuf; +} + +int cmpstringp(const void *p1, const void *p2) +{ + /* The actual arguments to this function are "pointers to + pointers to char", but strcmp(3) arguments are "pointers + to char", hence the following cast plus dereference */ + + return strcmp(* (char * const *) p1, * (char * const *) p2); +} + +/* This relies on the gp_enumerate_* which should not return directories, nor */ +/* should it recurse into directories (unlike Adobe's implementation) */ +/* paths are checked to see if they are an ordinary file or a path */ +void process_path(char *path, const char *os_prefix, const char *rom_prefix, + Xlist_element *Xlist_head, int compression, + int compaction, int *inode_count, int *totlen, FILE *out, + split_data *splits) +{ + int i, 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; + unsigned long psc_len; + pscompstate psc = { 0 }; + unsigned long numfiles = 0; + char **foundfiles = NULL, *temp; + + prefixed_path = malloc(PATH_STR_LEN); + found_path = malloc(PATH_STR_LEN); + rom_filename = malloc(PATH_STR_LEN); + 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); + } + prefix_add(os_prefix, path, prefixed_path); + prefix_add(rom_prefix, "", rom_filename); + strcpy(rom_filename, rom_prefix); + + /* check for the file on the Xlist */ + pfenum = gp_enumerate_files_init((gs_memory_t *)&minimal_memory, prefixed_path, + strlen(prefixed_path)); + if (pfenum == NULL) { + printf("gp_enumerate_files_init failed.\n"); + exit(1); + } + while ((namelen=gp_enumerate_files_next((gs_memory_t *)&minimal_memory, 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; + + numfiles++; + temp = realloc(foundfiles, sizeof(char *) * numfiles); + if (temp == NULL) { + free(cbuf); + free(ubuf); + free(found_path); + free(foundfiles); + free(prefixed_path); + free(rom_filename); + printf("realloc failed in process_path.\n"); + exit(1); + } + foundfiles = (char **)temp; + foundfiles[numfiles - 1] = strdup(found_path); + } + + qsort(foundfiles, numfiles, sizeof(char *), cmpstringp); + + for (i = 0; i < numfiles; i++) { + char *fpath = foundfiles[i]; + + /* process a file */ + node = calloc(1, sizeof(romfs_inode)); + /* get info for this file */ + in = fopen(fpath, "rb"); + if (in == NULL) { + printf("unable to open file for processing: %s\n", fpath); + continue; + } + /* printf("compacting %s\n", fpath); */ + /* rom_filename + strlen(rom_prefix) is first char after the new prefix we want to add */ + /* fpath + strlen(os_prefix) is the file name after the -P prefix */ + rom_filename[strlen(rom_prefix)] = 0; /* truncate afater prefix */ + strcat(rom_filename, fpath + strlen(os_prefix)); + node->name = rom_filename; /* without -P prefix, with -d rom_prefix */ + fseek(in, 0, SEEK_END); + node->disc_length = 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(fpath, "rb"); + ulen = strlen(fpath); + block = 0; + psc_len = 0; + if (compaction) + pscompact_start(&psc, (psc_getc*)&fgetc, (psc_ungetc*)&ungetc, (psc_feof*)&feof, in, 1, 0, 0); + while (!feof(in)) { + if (compaction) + ulen = pscompact_getcompactedblock(&psc, ubuf, ROMFS_BLOCKSIZE); + else + ulen = fread(ubuf, 1, ROMFS_BLOCKSIZE, in); + psc_len += ulen; + 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); + if (compaction) { + /* printf("%s: Compaction saved %d bytes (before compression)\n", + * fpath, node->length - psc_len); */ + pscompact_end(&psc); + node->length = psc_len; + } + + /* write out data for this file */ + inode_write(out, node, compression, *inode_count, totlen, splits); + /* clean up */ + inode_clear(node); + free(node); + (*inode_count)++; + free(fpath); + } + free(cbuf); + free(ubuf); + free(found_path); + free(foundfiles); + free(prefixed_path); + free(rom_filename); + if (save_count == *inode_count) { + printf("warning: no files found from path '%s%s'\n", os_prefix, path); + } +} + +/* + * Utility for merging all the Ghostscript initialization files (gs_*.ps) + * into a single file. + * + * The following special constructs are recognized in the input files: + * %% Replace[%| ]<#lines> (<psfile>) + * %% Replace[%| ]<#lines> INITFILES + * '%' after Replace means that the file to be included intact. + * If the first non-comment, non-blank line in a file ends with the + * LanguageLevel 2 constructs << or <~, its section of the merged file + * will begin with + * currentobjectformat 1 setobjectformat + * and end with + * setobjectformat + * and if any line then ends with with <, the following ASCIIHex string + * will be converted to a binary token. + */ +/* Forward references */ +void merge_to_ps(const char *os_prefix, const char *inname, FILE * in, FILE * config); +int write_init(char *); +bool rl(FILE * in, char *str, int len); +void wsc(const byte *str, int len); +void ws(const byte *str, int len); +void wl(const char *str); +char *doit(char *line, bool intact); +void hex_string_to_binary(FILE *in); +void flush_buf(char *buf); +void mergefile(const char *os_prefix, const char *inname, FILE * in, FILE * config, + bool intact); +void flush_line_buf(int len); + +typedef struct in_block_s in_block_t; +struct in_block_s { + struct in_block_s *next; + unsigned char data[ROMFS_BLOCKSIZE]; +}; + +#define LINE_SIZE 1024 + +/* Globals used for gs_init processing */ +char linebuf[LINE_SIZE * 2]; /* make it plenty long to avoid overflow */ +in_block_t *in_block_head = NULL; +in_block_t *in_block_tail = NULL; +unsigned char *curr_block_p, *curr_block_end; + +typedef struct { + in_block_t *block; + int pos; + int eof; +} in_block_file; + +static int ib_getc(in_block_file *ibf) { + if ((ibf->block == in_block_tail) && + (ibf->pos == curr_block_p - in_block_tail->data)) { + ibf->eof = 1; + return -1; + } + if (ibf->pos == ROMFS_BLOCKSIZE) { + ibf->block = ibf->block->next; + ibf->pos = 0; + } + return ibf->block->data[ibf->pos++]; +} + +static void ib_ungetc(int c, in_block_file *ibf) +{ + ibf->pos--; +} + +static int ib_feof(in_block_file *ibf) +{ + return ibf->eof; +} + +static int +process_initfile(char *initfile, char *gconfig_h, const char *os_prefix, + const char *rom_prefix, int compression, int *inode_count, + int *totlen, FILE *out, split_data *splits) +{ + int ret, block, blocks; + romfs_inode *node = NULL; + unsigned char *ubuf = NULL, *cbuf = NULL; + char *prefixed_path = NULL, *rom_filename = NULL; + unsigned long clen; + FILE *in; + FILE *config; + in_block_t *in_block = NULL; + int compaction = 1; + int code = 0; + + ubuf = malloc(ROMFS_BLOCKSIZE); + cbuf = malloc(ROMFS_CBUFSIZE); + prefixed_path = malloc(1024); + rom_filename = malloc(1024); + if (ubuf == NULL || cbuf == NULL || prefixed_path == NULL || + rom_filename == NULL) { + printf("malloc fail in process_initfile\n"); + /* should free whichever buffers got allocated, but don't bother */ + code = -1; + goto done; + } + + prefix_add(os_prefix, initfile, prefixed_path); + prefix_add(rom_prefix, initfile, rom_filename); + + in = fopen(prefixed_path, "r"); + if (in == 0) { + printf("cannot open initfile at: %s\n", prefixed_path); + code = -1; + goto done; + } + config = fopen(gconfig_h, "r"); + if (config == 0) { + printf("Cannot open gconfig file %s\n", gconfig_h); + fclose(in); + code = -1; + goto done; + } + memset(linebuf, 0, sizeof(linebuf)); + node = calloc(1, sizeof(romfs_inode)); + node->name = rom_filename; /* without -P prefix, with -d rom_prefix */ + + merge_to_ps(os_prefix, initfile, in, config); + + fclose(in); + fclose(config); + + if (compaction) + { + in_block_t *comp_block_head; + in_block_t *comp_block; + pscompstate psc = {0}; + in_block_file ibf; + int ulen; + + ibf.block = in_block_head; + ibf.pos = 0; + ibf.eof = 0; + + comp_block = malloc(sizeof(*comp_block)); + comp_block_head = comp_block; + pscompact_start(&psc, (psc_getc*)&ib_getc, (psc_ungetc*)&ib_ungetc, (psc_feof*)&ib_feof, &ibf, 0, 0, 1); + do { + ulen = pscompact_getcompactedblock(&psc, comp_block->data, ROMFS_BLOCKSIZE); + comp_block->next = NULL; + if (ulen == ROMFS_BLOCKSIZE) { + comp_block->next = malloc(sizeof(*comp_block)); + comp_block = comp_block->next; + } + } while (ulen == ROMFS_BLOCKSIZE); + pscompact_end(&psc); + while (in_block_head != NULL) { + in_block = in_block_head->next; + free(in_block_head); + in_block_head = in_block; + } + in_block_head = comp_block_head; + in_block_tail = comp_block; + curr_block_p = in_block_tail->data + ulen; + } + + node->length = 0; + in_block = in_block_head; + while (in_block != NULL) { + node->length += + in_block != in_block_tail ? ROMFS_BLOCKSIZE : curr_block_p - in_block->data; + in_block = in_block->next; + } + node->disc_length = node->length; + + 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)); + block = 0; + + in_block = in_block_head; + while (in_block != NULL) { + int block_len = + in_block != in_block_tail ? ROMFS_BLOCKSIZE : curr_block_p - in_block->data; + + clen = ROMFS_CBUFSIZE; + if (compression) { + /* compress data here */ + ret = compress(cbuf, &clen, in_block->data, block_len); + if (ret != Z_OK) { + printf("error compressing data block!\n"); + code = -1; + goto done; + } + } else { + memcpy(cbuf, in_block->data, block_len); + clen = block_len; + } + node->data_lengths[block] = clen; + node->data[block] = malloc(clen); + memcpy(node->data[block], cbuf, clen); + block++; + in_block = in_block->next; + } + + /* write data for this file */ + inode_write(out, node, compression, *inode_count, totlen, splits); + /* clean up */ + inode_clear(node); + (*inode_count)++; +done: + free(node); + free(cbuf); + free(ubuf); + free(prefixed_path); + free(rom_filename); + return code; +} + +void +flush_line_buf(int len) { + int remaining_len = len; + int move_len; + int line_offset = 0; + + if (len > LINE_SIZE) { + printf("*** warning, flush_line called with len (%d) > LINE_SIZE (%d)\n", + len, LINE_SIZE); + return; + } + /* check for empty list and allocate the first block if needed */ + if (in_block_tail == NULL) { + in_block_head = in_block_tail = calloc(1, sizeof(in_block_t)); + in_block_tail->next = NULL; /* calloc really already does this */ + curr_block_p = in_block_head->data; + curr_block_end = curr_block_p + ROMFS_BLOCKSIZE; + } + /* move the data into the in_block buffer */ + do { + move_len = min(remaining_len, curr_block_end - curr_block_p); + memcpy(curr_block_p, linebuf + line_offset, move_len); + curr_block_p += move_len; + line_offset += move_len; + if (curr_block_p == curr_block_end) { + /* start a new data block appended to the list of blocks */ + in_block_tail->next = calloc(1, sizeof(in_block_t)); + in_block_tail = in_block_tail->next; + in_block_tail->next = NULL; /* calloc really already does this */ + curr_block_p = in_block_tail->data; + curr_block_end = curr_block_p + ROMFS_BLOCKSIZE; + } + remaining_len = max(0, remaining_len - move_len); + } while (remaining_len > 0); + + /* clear the line (to allow 'strlen' to work if the data is not binary */ + memset(linebuf, 0, sizeof(linebuf)); +} + +/* Read a line from the input. */ +bool +rl(FILE * in, char *str, int len) +{ + /* + * Unfortunately, we can't use fgets here, because the typical + * implementation only recognizes the EOL convention of the current + * platform. + */ + int i = 0, c = getc(in); + + if (c < 0) + return false; + while (i < len - 1) { + if (c < 0 || c == 10) /* ^J, Unix EOL */ + break; + if (c == 13) { /* ^M, Mac EOL */ + c = getc(in); + if (c != 10 && c >= 0) /* ^M^J, PC EOL */ + ungetc(c, in); + break; + } + str[i++] = c; + c = getc(in); + } + str[i] = 0; + return true; +} + +/* Write a string or a line on the output. */ +void +wsc(const byte *str, int len) +{ + int n = 0; + int i; + + if (len >= LINE_SIZE) + exit(1); + + for (i = 0; i < len; ++i) { + int c = str[i]; + + sprintf(linebuf, + (c < 32 || c >= 127 ? "%d," : + c == '\'' || c == '\\' ? "'\\%c'," : "'%c',"), + c); + flush_line_buf(strlen(linebuf)); + if (++n == 15) { + linebuf[0] = '\n'; + flush_line_buf(1); + n = 0; + } + } + if (n != 0) { + flush_line_buf(strlen(linebuf)); + linebuf[0] = '\n'; + flush_line_buf(1); + } +} +void +ws(const byte *str, int len) +{ + if (len >= LINE_SIZE) + exit(1); + + memcpy(linebuf, str, len); + flush_line_buf(len); +} + +void +wl(const char *str) +{ + ws((const byte *)str, strlen(str)); + ws((const byte *)"\n", 1); +} + +/* + * Strip whitespace and comments from a line of PostScript code as possible. + * Return a pointer to any string that remains, or NULL if none. + * Note that this may store into the string. + */ +char * +doit(char *line, bool intact) +{ + char *str = line; + char *from; + char *to; + int in_string = 0; + + if (intact) + return str; + while (*str == ' ' || *str == '\t') /* strip leading whitespace */ + ++str; + if (*str == 0) /* all whitespace */ + return NULL; + if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */ + return str; + if (str[0] == '%') /* comment line */ + return NULL; + /* + * Copy the string over itself removing: + * - All comments not within string literals; + * - Whitespace adjacent to '[' ']' '{' '}'; + * - Whitespace before '/' '(' '<'; + * - Whitespace after ')' '>'. + */ + for (to = from = str; (*to = *from) != 0; ++from, ++to) { + switch (*from) { + case '%': + if (!in_string) + break; + continue; + case ' ': + case '\t': + if (to > str && !in_string && strchr(" \t>[]{})", to[-1])) + --to; + continue; + case '(': + case '<': + case '/': + case '[': + case ']': + case '{': + case '}': + if (to > str && !in_string && strchr(" \t", to[-1])) + *--to = *from; + if (*from == '(') + ++in_string; + continue; + case ')': + --in_string; + continue; + case '\\': + if (from[1] == '\\' || from[1] == '(' || from[1] == ')') + *++to = *++from; + continue; + default: + continue; + } + break; + } + /* Strip trailing whitespace. */ + while (to > str && (to[-1] == ' ' || to[-1] == '\t')) + --to; + *to = 0; + return str; +} + +/* Copy a hex string to the output as a binary token. */ +void +hex_string_to_binary(FILE *in) +{ +#define MAX_STR 0xffff /* longest possible PostScript string token */ + byte *strbuf = (byte *)malloc(MAX_STR); + byte *q = strbuf; + int c; + bool which = false; + int len; + byte prefix[3]; + + if (strbuf == 0) { + printf("Unable to allocate string token buffer.\n"); + exit(1); + } + while ((c = getc(in)) >= 0) { + if (isxdigit(c)) { + c -= (isdigit(c) ? '0' : islower(c) ? 'a' : 'A'); + if ((which = !which)) { + if (q == strbuf + MAX_STR) { + printf("Can't handle string token > %d bytes.\n", + MAX_STR); + exit(1); + } + *q++ = c << 4; + } else + q[-1] += c; + } else if (isspace(c)) + continue; + else if (c == '>') + break; + else + printf("Unknown character in ASCIIHex string: %c\n", c); + } + len = q - strbuf; + if (len <= 255) { + prefix[0] = 142; + prefix[1] = (byte)len; + ws(prefix, 2); + } else { + prefix[0] = 143; + prefix[1] = (byte)(len >> 8); + prefix[2] = (byte)len; + ws(prefix, 3); + } + ws(strbuf, len); + free((char *)strbuf); +#undef MAX_STR +} + +/* Merge a file from input to output. */ +void +flush_buf(char *buf) +{ + if (buf[0] != 0) { + wl(buf); + buf[0] = 0; + } +} + +FILE * +prefix_open(const char *os_prefix, const char *filename) +{ + char *prefixed_path; + FILE *filep; + + prefixed_path = malloc(1024); + if (prefixed_path == NULL) { + printf("malloc problem in prefix_open\n"); + return NULL; + } + prefix_add(os_prefix, filename, prefixed_path); + printf("including: '%s'\n", prefixed_path); + filep = fopen(prefixed_path, "rb"); + free(prefixed_path); + return filep; +} + +void +mergefile(const char *os_prefix, const char *inname, FILE * in, FILE * config, + bool intact) +{ + char line[LINE_SIZE + 1]; + char buf[LINE_SIZE + 1]; + char *str; + int level = 1; + bool first = true; + + buf[0] = 0; + while (rl(in, line, LINE_SIZE)) { + char psname[LINE_SIZE + 1]; + int nlines; + + if (!strncmp(line, "%% Replace", 10) && + sscanf(line + 11, "%d %s", &nlines, psname) == 2 + ) { + bool do_intact = (line[10] == '%'); + + flush_buf(buf); + while (nlines-- > 0) + rl(in, line, LINE_SIZE); + if (psname[0] == '(') { + FILE *ps; + + psname[strlen(psname) - 1] = 0; + ps = prefix_open(os_prefix, psname + 1); + if (ps == 0) { + fprintf(stderr, "Failed to open '%s' - aborting\n", psname+1); + exit(1); + } + mergefile(os_prefix, psname + 1, ps, config, intact || do_intact); + } else if (!strcmp(psname, "INITFILES")) { + /* + * We don't want to bind config.h into geninit, so + * we parse it ourselves at execution time instead. + */ + rewind(config); + while (rl(config, psname, LINE_SIZE)) + if (!strncmp(psname, "psfile_(\"", 9)) { + FILE *ps; + char *quote = strchr(psname + 9, '"'); + if (quote == NULL) + exit(1); + + *quote = 0; + ps = prefix_open(os_prefix, psname + 9); + if (ps == 0) + exit(1); + mergefile(os_prefix, psname + 9, ps, config, false); + } + } else { + printf("Unknown %%%% Replace %d %s\n", + nlines, psname); + exit(1); + } + } else if (!strcmp(line, "currentfile closefile")) { + /* The rest of the file is debugging code, stop here. */ + break; + } else { + int len; + + str = doit(line, intact); + if (str == 0) + continue; + len = strlen(str); + if (first && len >= 2 && str[len - 2] == '<' && + (str[len - 1] == '<' || str[len - 1] == '~') + ) { + wl("currentobjectformat 1 setobjectformat\n"); + level = 2; + } + if (level > 1 && len > 0 && str[len - 1] == '<' && + (len < 2 || str[len - 2] != '<') + ) { + /* Convert a hex string to a binary token. */ + flush_buf(buf); + str[len - 1] = 0; + wl(str); + hex_string_to_binary(in); + continue; + } + if (buf[0] != '%' && /* i.e. not special retained comment */ + strlen(buf) + len < LINE_SIZE && + (strchr("(/[]{}", str[0]) || + (buf[0] != 0 && strchr(")[]{}", buf[strlen(buf) - 1]))) + ) + strcat(buf, str); + else { + flush_buf(buf); + strcpy(buf, str); + } + first = false; + } + } + flush_buf(buf); + if (level > 1) + wl("setobjectformat"); +} + +/* Merge and produce a PostScript file. */ +void +merge_to_ps(const char *os_prefix, const char *inname, FILE * in, FILE * config) +{ + char line[LINE_SIZE + 1]; + + while ((rl(in, line, LINE_SIZE), line[0])) { + sprintf(linebuf, "%s", line ); + wl(linebuf); + } + mergefile(os_prefix, inname, in, config, false); +} + +static void +make_split_name(split_data *splits, const char *filename) +{ + const char *s = filename; + const char *t = NULL; + char *u; + + while (*s) { + if (*s == '.') + t = s; + s++; + } + if (t == NULL) + t = s; + + free(splits->outname); + splits->outname = u = malloc(s-filename+4); + if (u == NULL) { + fprintf(stderr, "malloc failure while constructing split filename\n"); + exit(1); + } + memcpy(u, filename, t-filename); + u[t-filename] = 'c'; + u[t-filename+1] = '%'; + u[t-filename+2] = 'd'; + if (s-t) + memcpy(u+(t-filename)+3, t, s-t); + u[s-filename+3] = 0; + + free(splits->outname_formatted); + splits->outname_formatted = malloc(s-filename+4+32); + if (splits->outname_formatted == NULL) { + fprintf(stderr, "malloc failure while constructing split filename\n"); + exit(1); + } +} + +int +main(int argc, char *argv[]) +{ + int i; + int inode_count = 0, totlen = 0; + FILE *out; + const char *outfilename = "obj/gsromfs.c"; + const char *os_prefix = ""; + const char *rom_prefix = ""; + int atarg = 1; + int compression = 1; /* default to doing compression */ + int compaction = 0; + Xlist_element *Xlist_scan = NULL, *Xlist_head = NULL; + char pa[PATH_STR_LEN]; + time_t buildtime = 0; + char* env_source_date_epoch; + split_data splits = { 0 } ; + + memset(pa, 0x00, PATH_STR_LEN); + + 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" + " 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" + "\n" + " -d romprefix directory in %%rom file system (just a prefix string on filename)\n" + " -c compression on\n" + " -b compression off (binary).\n" + " -C postscript 'compaction' on\n" + " -B postscript 'compaction' off\n" + " -g initfile gconfig_h \n" + " special handling to read the 'gs_init.ps' file (from\n" + " the current -P prefix path), and read the gconfig.h for\n" + " psfile_ entries and combines them into a whitespace\n" + " optimized and no comments form and writes this 'gs_init.ps'\n" + " to the current -d destination path. This is a space and\n" + " startup performance improvement, so also this should be\n" + " BEFORE other stuff in the %%rom%% list of files (currently\n" + " we do a sequential search in the %%rom%% directory).\n" + "\n" + " For performance reasons, it is best to turn off compression\n" + " for the init file. Less frequently accessed files, if they\n" + " are large should still be compressed.\n" + "\n" + ); + exit(1); + } + + printf("compression will use %d byte blocksize (zlib output buffer %d bytes)\n", + ROMFS_BLOCKSIZE, ROMFS_CBUFSIZE); + + if (argc > 3 && argv[1][0] == '-' && argv[1][1] == 'o') { + /* process -o option for outputfile */ + outfilename = argv[2]; + atarg += 2; + } + printf(" writing romfs data to '%s'\n", outfilename); + out = fopen(outfilename, "w"); + + start_file(out); + + if ((env_source_date_epoch = getenv("SOURCE_DATE_EPOCH"))) { + buildtime = strtoul(env_source_date_epoch, NULL, 10); + } + if (!buildtime) + buildtime = time(NULL); + fprintf(out," time_t gs_romfs_buildtime = %ld;\n\n", buildtime); + + /* 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 'B': + compaction = 0; + break; + case 'C': + compaction = 1; + break; + case 'd': + if (++atarg == argc) { + printf(" option %s missing required argument\n", argv[atarg-1]); + exit(1); + } + rom_prefix = argv[atarg]; + break; + case 's': + if (++atarg == argc) { + printf(" option %s missing required argument\n", argv[atarg-1]); + exit(1); + } + splits.num_splits = atoi(argv[atarg]); + if (splits.num_splits <= 0) { + printf(" Invalid number of files to split to: %s\n", argv[atarg]); + exit(1); + } + make_split_name(&splits, outfilename); + break; + case 'g': + { + char initfile[PATH_STR_LEN] = {0}; + char gconfig_h[PATH_STR_LEN] = {0}; + if ((++atarg) + 1 == argc) { + printf(" option %s missing required arguments\n", argv[atarg-1]); + exit(1); + } + strncpy(initfile, argv[atarg], PATH_STR_LEN - 1); + atarg++; + strncpy(gconfig_h, argv[atarg], PATH_STR_LEN - 1); + process_initfile(initfile, gconfig_h, os_prefix, rom_prefix, compression, + &inode_count, &totlen, out, &splits); + } + break; + case 'P': + if (++atarg == argc) { + printf(" option %s missing required argument\n", argv[atarg-1]); + exit(1); + } + os_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 or file */ + strncpy(pa, argv[atarg], PATH_STR_LEN - (strlen(os_prefix) < strlen(rom_prefix) ? strlen(rom_prefix) : strlen(os_prefix))); + process_path(pa, os_prefix, rom_prefix, Xlist_head, + compression, compaction, &inode_count, &totlen, out, &splits); + } + + /* Now allow for the (probably never happening) case where we are splitting, but haven't written anything to one of the files */ + prepare_splits(&splits); + for (i = 0; i < splits.max_splits; i++) { + if (splits.sizes[i] == 0) { + FILE *out2; + sprintf(splits.outname_formatted, splits.outname, i); + out2 = fopen(splits.outname_formatted, "w"); + fprintf(out2, "const int mkromfs_dummy_chunk%d;\n", i); + fclose(out2); + } + } + + if (splits.max_splits) { + for (i=0; i<inode_count; i++) + fprintf(out, "\t extern const uint32_t mkromfs_node_%d[];\n", i); + } + + /* now write out the array of nodes */ + fprintf(out, " const uint32_t *gs_romfs[] = {\n"); + for (i=0; i<inode_count; i++) + fprintf(out, "\t%snode_%d,\n", splits.max_splits ? "mkromfs_" : "", i); + fprintf(out, "\t0 };\n"); + fclose(out); + while (Xlist_head) { + Xlist_scan = Xlist_head->next; + free(Xlist_head); + Xlist_head = Xlist_scan; + } + printf("Total %%rom%% structure size is %d bytes.\n", totlen); + + return 0; +} |