/* ******************************************************************************* * * Copyright (C) 1999-2004, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* * file name: gencmn.c * encoding: US-ASCII * tab size: 8 (not used) * indentation:4 * * created on: 1999nov01 * created by: Markus W. Scherer * * This program reads a list of data files and combines them * into one common, memory-mappable file. */ #include #include #include "unicode/utypes.h" #include "unicode/putil.h" #include "cmemory.h" #include "cstring.h" #include "filestrm.h" #include "toolutil.h" #include "unicode/uclean.h" #include "unewdata.h" #include "uoptions.h" #define STRING_STORE_SIZE 100000 #define MAX_FILE_COUNT 2000 #define COMMON_DATA_NAME U_ICUDATA_NAME #define DATA_TYPE "dat" /* ICU package data file format (.dat files) ------------------------------- *** Description of the data format after the usual ICU data file header (UDataInfo etc.). Format version 1 A .dat package file contains a simple Table of Contents of item names, followed by the items themselves: 1. ToC table uint32_t count; - number of items UDataOffsetTOCEntry entry[count]; - pair of uint32_t values per item: uint32_t nameOffset; - offset of the item name uint32_t dataOffset; - offset of the item data both are byte offsets from the beginning of the data 2. item name strings All item names are stored as char * strings in one block between the ToC table and the data items. 3. data items The data items are stored following the item names block. Each data item is 16-aligned. The data items are stored in the sorted order of their names. Therefore, the top of the name strings block is the offset of the first item, the length of the last item is the difference between its offset and the .dat file length, and the length of all previous items is the difference between its offset and the next one. ----------------------------------------------------------------------------- */ /* UDataInfo cf. udata.h */ static const UDataInfo dataInfo={ sizeof(UDataInfo), 0, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, sizeof(UChar), 0, {0x43, 0x6d, 0x6e, 0x44}, /* dataFormat="CmnD" */ {1, 0, 0, 0}, /* formatVersion */ {3, 0, 0, 0} /* dataVersion */ }; static uint32_t maxSize; static char stringStore[STRING_STORE_SIZE]; static uint32_t stringTop=0, basenameTotal=0; typedef struct { char *pathname, *basename; uint32_t basenameLength, basenameOffset, fileSize, fileOffset; } File; static File files[MAX_FILE_COUNT]; static uint32_t fileCount=0; static UBool embed = FALSE; /* prototypes --------------------------------------------------------------- */ static void addFile(const char *filename, UBool sourceTOC, UBool verbose); static char * allocString(uint32_t length); static int compareFiles(const void *file1, const void *file2); static char * pathToFullPath(const char *path); /* map non-tree separator (such as '\') to tree separator ('/') inplace. */ static void fixDirToTreePath(char *s); /* -------------------------------------------------------------------------- */ static UOption options[]={ /*0*/ UOPTION_HELP_H, /*1*/ UOPTION_HELP_QUESTION_MARK, /*2*/ UOPTION_VERBOSE, /*3*/ UOPTION_COPYRIGHT, /*4*/ UOPTION_DESTDIR, /*5*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG), /*6*/ UOPTION_DEF( "name", 'n', UOPT_REQUIRES_ARG), /*7*/ UOPTION_DEF( "type", 't', UOPT_REQUIRES_ARG), /*8*/ UOPTION_DEF( "source", 'S', UOPT_NO_ARG), /*9*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG), /*10*/UOPTION_SOURCEDIR, /*11*/UOPTION_DEF( "embed", 'E', UOPT_NO_ARG) }; static char *symPrefix = NULL; extern int main(int argc, char* argv[]) { static char buffer[4096]; char line[512]; FileStream *in, *file; char *s; UErrorCode errorCode=U_ZERO_ERROR; uint32_t i, fileOffset, basenameOffset, length, nread; UBool sourceTOC, verbose; const char *entrypointName = NULL; U_MAIN_INIT_ARGS(argc, argv); /* preset then read command line options */ options[4].value=u_getDataDirectory(); options[6].value=COMMON_DATA_NAME; options[7].value=DATA_TYPE; options[10].value="."; argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options); /* error handling, printing usage message */ if(argc<0) { fprintf(stderr, "error in command line argument \"%s\"\n", argv[-argc]); } else if(argc<2) { argc=-1; } if(options[11].doesOccur) { embed = TRUE; } if(argc<0 || options[0].doesOccur || options[1].doesOccur) { FILE *where = argc < 0 ? stderr : stdout; /* * Broken into chucks because the C89 standard says the minimum * required supported string length is 509 bytes. */ fprintf(where, "%csage: %s [ -h, -?, --help ] [ -v, --verbose ] [ -c, --copyright ] [ -C, --comment comment ] [ -d, --destdir dir ] [ -n, --name filename ] [ -t, --type filetype ] [ -S, --source tocfile ] [ -e, --entrypoint name ] maxsize listfile\n", argc < 0 ? 'u' : 'U', *argv); if (options[0].doesOccur || options[1].doesOccur) { fprintf(where, "\n" "Read the list file (default: standard input) and create a common data\n" "file from specified files. Omit any files larger than maxsize, if maxsize > 0.\n"); fprintf(where, "\n" "Options:\n" "\t-h, -?, --help this usage text\n" "\t-v, --verbose verbose output\n" "\t-c, --copyright include the ICU copyright notice\n" "\t-C, --comment comment include a comment string\n" "\t-d, --destdir dir destination directory\n"); fprintf(where, "\t-n, --name filename output filename, without .type extension\n" "\t (default: " COMMON_DATA_NAME ")\n" "\t-t, --type filetype type of the destination file\n" "\t (default: \"" DATA_TYPE "\")\n" "\t-S, --source tocfile write a .c source file with the table of\n" "\t contents\n" "\t-e, --entrypoint name override the c entrypoint name\n" "\t (default: \"_\")\n"); } return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR; } sourceTOC=options[8].doesOccur; verbose = options[2].doesOccur; maxSize=(uint32_t)uprv_strtoul(argv[1], NULL, 0); if(argc==2) { in=T_FileStream_stdin(); } else { in=T_FileStream_open(argv[2], "r"); if(in==NULL) { fprintf(stderr, "gencmn: unable to open input file %s\n", argv[2]); exit(U_FILE_ACCESS_ERROR); } } if (verbose) { if(sourceTOC) { printf("generating %s_%s.c (table of contents source file)\n", options[6].value, options[7].value); } else { printf("generating %s.%s (common data file with table of contents)\n", options[6].value, options[7].value); } } /* read the list of files and get their lengths */ while(T_FileStream_readLine(in, line, sizeof(line))!=NULL) { /* remove trailing newline characters */ s=line; while(*s!=0) { if(*s=='\r' || *s=='\n') { *s=0; break; } ++s; } /* check for comment */ if (*line == '#') { continue; } /* add the file */ #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) { char *t; while((t = uprv_strchr(line,U_FILE_ALT_SEP_CHAR))) { *t = U_FILE_SEP_CHAR; } } #endif addFile(getLongPathname(line), sourceTOC, verbose); } if(in!=T_FileStream_stdin()) { T_FileStream_close(in); } if(fileCount==0) { fprintf(stderr, "gencmn: no files listed in %s\n", argc==2 ? "" : argv[2]); return 0; } /* sort the files by basename */ qsort(files, fileCount, sizeof(File), compareFiles); if(!sourceTOC) { UNewDataMemory *out; /* determine the offsets of all basenames and files in this common one */ basenameOffset=4+8*fileCount; fileOffset=(basenameOffset+(basenameTotal+15))&~0xf; for(i=0; ifilename && *(s-1)!=U_FILE_SEP_CHAR) { *s++=U_FILE_SEP_CHAR; } uprv_strcpy(s, options[6].value); if(*(options[7].value)!=0) { s+=uprv_strlen(s); *s++='_'; uprv_strcpy(s, options[7].value); } s+=uprv_strlen(s); uprv_strcpy(s, ".c"); /* open the output file */ out=T_FileStream_open(filename, "w"); if(out==NULL) { fprintf(stderr, "gencmn: unable to open .c output file %s\n", filename); exit(U_FILE_ACCESS_ERROR); } /* If an entrypoint is specified, use it. */ if(options[9].doesOccur) { entrypointName = options[9].value; } else { entrypointName = options[6].value; } #if 0 if(!embed) { symPrefix = (char *) uprv_malloc(uprv_strlen(entrypointName) + 2); /* test for NULL */ if (symPrefix == NULL) { sprintf(buffer, "U_MEMORY_ALLOCATION_ERROR"); exit(U_MEMORY_ALLOCATION_ERROR); } uprv_strcpy(symPrefix, entrypointName); uprv_strcat(symPrefix, "_"); } #endif /* write the source file */ sprintf(buffer, "/*\n" " * ICU common data table of contents for %s.%s ,\n" " * Automatically generated by icu/source/tools/gencmn/gencmn .\n" " */\n\n" "#include \"unicode/utypes.h\"\n" "#include \"unicode/udata.h\"\n" "\n" "/* external symbol declarations for data */\n", options[6].value, options[7].value); T_FileStream_writeLine(out, buffer); sprintf(buffer, "extern const char\n %s%s[]", symPrefix?symPrefix:"", files[0].pathname); T_FileStream_writeLine(out, buffer); for(i=1; imaxSize) { if (verbose) { printf("%s ignored (size %ld > %ld)\n", fullPath, (long)length, (long)maxSize); } return; } files[fileCount].fileSize=length; } else { char *t; if(embed) { filename = findBasename(filename); } /* get and store the basename */ if(!embed) { /* need to include the package name */ length = (uint32_t)(uprv_strlen(filename) + 1 + uprv_strlen(options[6].value) + 1); s=allocString(length); uprv_strcpy(s, options[6].value); uprv_strcat(s, U_TREE_ENTRY_SEP_STRING); uprv_strcat(s, filename); } else { length = (uint32_t)(uprv_strlen(filename) + 1); s=allocString(length); uprv_memcpy(s, filename, length); } fixDirToTreePath(s); files[fileCount].basename=s; /* turn the basename into an entry point name and store in the pathname field */ t=files[fileCount].pathname=allocString(length); while(--length>0) { if(*s=='.' || *s=='-' || *s=='/') { *t='_'; } else { *t=*s; } ++s; ++t; } *t=0; } ++fileCount; } static char * allocString(uint32_t length) { uint32_t top=stringTop+length; char *p; if(top>STRING_STORE_SIZE) { fprintf(stderr, "gencmn: out of memory\n"); exit(U_MEMORY_ALLOCATION_ERROR); } p=stringStore+stringTop; stringTop=top; return p; } static char * pathToFullPath(const char *path) { int32_t length; int32_t newLength; char *fullPath; int32_t n; length = (uint32_t)(uprv_strlen(path) + 1); newLength = (length + 1 + (int32_t)uprv_strlen(options[10].value)); fullPath = uprv_malloc(newLength); if(options[10].doesOccur) { uprv_strcpy(fullPath, options[10].value); uprv_strcat(fullPath, U_FILE_SEP_STRING); } else { fullPath[0] = 0; } n = (int32_t)uprv_strlen(fullPath); uprv_strcat(fullPath, path); if(!embed) { #if (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) #if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) /* replace tree separator (such as '/') with file sep char (such as ':' or '\\') */ for(;fullPath[n];n++) { if(fullPath[n] == U_FILE_ALT_SEP_CHAR) { fullPath[n] = U_FILE_SEP_CHAR; } } #endif #endif #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) /* replace tree separator (such as '/') with file sep char (such as ':' or '\\') */ for(;fullPath[n];n++) { if(fullPath[n] == U_TREE_ENTRY_SEP_CHAR) { fullPath[n] = U_FILE_SEP_CHAR; } } #endif } return fullPath; } static int compareFiles(const void *file1, const void *file2) { /* sort by basename */ return uprv_strcmp(((File *)file1)->basename, ((File *)file2)->basename); } static void fixDirToTreePath(char *s) { #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) || ((U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) && (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR)) char *t; #endif #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) for(t=s;t=uprv_strchr(t,U_FILE_SEP_CHAR);) { *t = U_TREE_ENTRY_SEP_CHAR; } #endif #if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) && (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) for(t=s;t=uprv_strchr(t,U_FILE_ALT_SEP_CHAR);) { *t = U_TREE_ENTRY_SEP_CHAR; } #endif } /* * Hey, Emacs, please set the following: * * Local Variables: * indent-tabs-mode: nil * End: * */