/****************************************************************************** * * Copyright (C) 2000-2003, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* * file name: pkgdata.c * encoding: ANSI X3.4 (1968) * tab size: 8 (not used) * indentation:4 * * created on: 2000may15 * created by: Steven \u24C7 Loomis * * This program packages the ICU data into different forms * (DLL, common data, etc.) */ #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" #if U_HAVE_POPEN # include #endif U_CDECL_BEGIN #include "pkgtypes.h" #include "makefile.h" U_CDECL_END static int executeMakefile(const UPKGOptions *o); static void loadLists(UPKGOptions *o, UErrorCode *status); /* always have this fcn, just might not do anything */ static void fillInMakefileFromICUConfig(UOption *option); /* This sets the modes that are available */ static struct { const char *name, *alt_name; UPKGMODE *fcn; const char *desc; } modes[] = { { "files", 0, pkg_mode_files, "Uses raw data files (no effect). Installation copies all files to the target location." }, #ifdef WIN32 { "dll", "library", pkg_mode_windows, "Generates one common data file and one shared library, .dll"}, { "common", "archive", pkg_mode_windows, "Generates just the common file, .dat"}, { "static", "static", pkg_mode_windows, "Generates one statically linked library, " LIB_PREFIX "" UDATA_LIB_SUFFIX } #else /*#ifdef WIN32*/ #ifdef UDATA_SO_SUFFIX { "dll", "library", pkg_mode_dll, "Generates one shared library, " UDATA_SO_SUFFIX }, #endif { "common", "archive", pkg_mode_common, "Generates one common data file, .dat" }, { "static", "static", pkg_mode_static, "Generates one statically linked library, " LIB_PREFIX "" UDATA_LIB_SUFFIX } #endif /*#ifdef WIN32*/ }; static UOption options[]={ /*00*/ UOPTION_DEF( "name", 'p', UOPT_REQUIRES_ARG), /*01*/ UOPTION_DEF( "bldopt", 'O', UOPT_REQUIRES_ARG), /* on Win32 it is release or debug */ /*02*/ UOPTION_DEF( "mode", 'm', UOPT_REQUIRES_ARG), /*03*/ UOPTION_HELP_H, /* -h */ /*04*/ UOPTION_HELP_QUESTION_MARK, /* -? */ /*05*/ UOPTION_VERBOSE, /* -v */ /*06*/ UOPTION_COPYRIGHT, /* -c */ /*07*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG), /*08*/ UOPTION_DESTDIR, /* -d */ /*09*/ UOPTION_DEF( "clean", 'k', UOPT_NO_ARG), /*10*/ UOPTION_DEF( "nooutput",'n', UOPT_NO_ARG), /*11*/ UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG), /*12*/ UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG), /*13*/ UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG), /*14*/ UOPTION_SOURCEDIR , /*15*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG), /*16*/ UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG), /*17*/ UOPTION_DEF( 0, 'M', UOPT_REQUIRES_ARG), /*18*/ UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG), /*19*/ UOPTION_DEF( "numerictmp", 'N', UOPT_NO_ARG) }; const char options_help[][160]={ "Set the data name", #ifdef WIN32 "The directory where the ICU is located (e.g. which contains the bin directory)", #else "Specify options for the builder. (Autdetected if icu-config is available)", #endif "Specify the mode of building (see below; default: common)", "This usage text", "This usage text", "Make the output verbose", "Use the standard ICU copyright", "Use a custom comment (instead of the copyright)", "Specify the destination directory for files", "Clean out generated & temporary files", "Suppress output of data, just list files to be created", "Force rebuilding of all data", "Specify temporary dir (default: output dir)", "Install the data (specify target)", "Specify a custom source directory", "Specify a custom entrypoint name (default: short name)", "Specify a version when packaging in DLL or static mode", "Pass the next argument to make(1)", "Add package to all file names if not present", "Use short numeric temporary file names such as t1234.c" }; const char *progname = "PKGDATA"; int main(int argc, char* argv[]) { FileStream *out; UPKGOptions o; CharList *tail; UBool needsHelp = FALSE; UErrorCode status = U_ZERO_ERROR; char tmp[1024]; int32_t i; U_MAIN_INIT_ARGS(argc, argv); progname = argv[0]; options[2].value = "common"; options[17].value = ""; /* read command line options */ argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options); /* error handling, printing usage message */ /* I've decided to simply print an error and quit. This tool has too many options to just display them all of the time. */ if(options[3].doesOccur || options[4].doesOccur) { needsHelp = TRUE; } else { if(!needsHelp && argc<0) { fprintf(stderr, "%s: error in command line argument \"%s\"\n", progname, argv[-argc]); fprintf(stderr, "Run '%s --help' for help.\n", progname); return 1; } if(!options[1].doesOccur) { /* Try to fill in from icu-config or equivalent */ fillInMakefileFromICUConfig(&options[1]); } #ifdef WIN32 else { fprintf(stderr, "Warning: You are using the deprecated -O option\n" "\tYou can fix this warning by installing pkgdata, gencmn and genccode\n" "\tinto the same directory and not specifying the -O option to pkgdata.\n"); } #endif if(!options[1].doesOccur) { fprintf(stderr, " required parameter is missing: -O is required \n"); fprintf(stderr, "Run '%s --help' for help.\n", progname); return 1; } if(!options[0].doesOccur) /* -O we already have - don't report it. */ { fprintf(stderr, " required parameter -p is missing \n"); fprintf(stderr, "Run '%s --help' for help.\n", progname); return 1; } if(argc == 1) { fprintf(stderr, "No input files specified.\n" "Run '%s --help' for help.\n", progname); return 1; } } /* end !needsHelp */ if(argc<0 || needsHelp ) { fprintf(stderr, "usage: %s [-options] [-] [packageFile] \n" "\tProduce packaged ICU data from the given list(s) of files.\n" "\t'-' by itself means to read from stdin.\n" "\tpackageFile is a text file containing the list of files to package.\n", progname); fprintf(stderr, "\n options:\n"); for(i=0;i<(sizeof(options)/sizeof(options[0]));i++) { fprintf(stderr, "%-5s -%c %s%-10s %s\n", (i<2?"[REQ]":""), options[i].shortName, options[i].longName ? "or --" : " ", options[i].longName ? options[i].longName : "", options_help[i]); } fprintf(stderr, "modes: (-m option)\n"); for(i=0;i<(sizeof(modes)/sizeof(modes[0]));i++) { fprintf(stderr, " %-9s ", modes[i].name); if (modes[i].alt_name) { fprintf(stderr, "/ %-9s", modes[i].alt_name); } else { fprintf(stderr, " "); } fprintf(stderr, " %s\n", modes[i].desc); } return 1; } /* OK, fill in the options struct */ uprv_memset(&o, 0, sizeof(o)); o.mode = options[2].value; o.version = options[16].doesOccur ? options[16].value : 0; o.makeArgs = options[17].value; o.fcn = NULL; for(i=0;iinstall ? "INSTALLTO=" : "", o->install ? o->install : "", o->makeFile, o->clean ? "clean" : "", o->rebuild ? "rebuild" : "", o->install ? "install" : "", o->makeArgs); #elif OS400 sprintf(cmd, "CALL GNU/GMAKE PARM(%s%s%s '-f' '%s' %s %s %s %s)", o->install ? "'INSTALLTO=" : "", o->install ? o->install : "", o->install ? "'" : "", o->makeFile, o->clean ? "'clean'" : "", o->rebuild ? "'rebuild'" : "", o->install ? "'install'" : "", o->makeArgs); #else sprintf(cmd, "%s %s%s -f %s %s %s %s %s", make, o->install ? "INSTALLTO=" : "", o->install ? o->install : "", o->makeFile, o->clean ? "clean" : "", o->rebuild ? "rebuild" : "", o->install ? "install" : "", o->makeArgs); #endif if(o->verbose) { puts(cmd); } rc = system(cmd); if(rc < 0) { fprintf(stderr, "# Failed, rc=%d\n", rc); } return rc < 128 ? rc : (rc >> 8); } static void loadLists(UPKGOptions *o, UErrorCode *status) { CharList *l, *tail = NULL, *tail2 = NULL; FileStream *in; char line[16384]; char *linePtr, *lineNext; const uint32_t lineMax = 16300; char tmp[1024], tmp2[1024]; char pkgPrefix[1024]; int32_t pkgPrefixLen; const char *baseName; char *s; int32_t ln; UBool fixPrefix; fixPrefix = options[18].doesOccur; strcpy(pkgPrefix, o->shortName); strcat(pkgPrefix, "_"); pkgPrefixLen=uprv_strlen(pkgPrefix); for(l = o->fileListFiles; l; l = l->next) { if(o->verbose) { fprintf(stdout, "# Reading %s..\n", l->str); } /* TODO: stdin */ in = T_FileStream_open(l->str, "r"); if(!in) { fprintf(stderr, "Error opening <%s>.\n", l->str); *status = U_FILE_ACCESS_ERROR; return; } ln = 0; while(T_FileStream_readLine(in, line, sizeof(line))!=NULL) { ln++; if(uprv_strlen(line)>lineMax) { fprintf(stderr, "%s:%d - line too long (over %d chars)\n", l->str, ln, lineMax); exit(1); } /* remove spaces at the beginning */ linePtr = line; while(isspace(*linePtr)) { linePtr++; } s=linePtr; /* remove trailing newline characters */ while(*s!=0) { if(*s=='\r' || *s=='\n') { *s=0; break; } ++s; } if((*linePtr == 0) || (*linePtr == '#')) { continue; /* comment or empty line */ } /* Now, process the line */ lineNext = NULL; while(linePtr && *linePtr) { while(*linePtr == ' ') { linePtr++; } /* Find the next */ if(linePtr[0] == '"') { lineNext = uprv_strchr(linePtr+1, '"'); if(lineNext == NULL) { fprintf(stderr, "%s:%d - missing trailing double quote (\")\n", l->str, ln); exit(1); } else { lineNext++; if(*lineNext) { if(*lineNext != ' ') { fprintf(stderr, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n", l->str, ln, lineNext-line, (*lineNext)?*lineNext:'0'); exit(1); } *lineNext = 0; lineNext++; } } } else { lineNext = uprv_strchr(linePtr, ' '); if(lineNext) { *lineNext = 0; /* terminate at space */ lineNext++; } } /* add the file */ s = (char*)getLongPathname(linePtr); baseName = findBasename(s); if(s != baseName) { /* s was something 'long' with a path */ if(fixPrefix && uprv_strncmp(pkgPrefix, baseName, pkgPrefixLen)) { /* path don't have the prefix, add package prefix to short and longname */ uprv_strcpy(tmp, pkgPrefix); uprv_strcpy(tmp+pkgPrefixLen, baseName); uprv_strncpy(tmp2, s, uprv_strlen(s)-uprv_strlen(baseName)); /* should be: dirpath only, ending in sep */ tmp2[uprv_strlen(s)-uprv_strlen(baseName)]=0; uprv_strcat(tmp2, pkgPrefix); uprv_strcat(tmp2, baseName); o->files = pkg_appendToList(o->files, &tail, uprv_strdup(tmp)); o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(tmp2)); } else { /* paths already have the prefix */ o->files = pkg_appendToList(o->files, &tail, uprv_strdup(baseName)); o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(s)); } } else { /* s was just a basename, we want to prepend source dir*/ /* check for prefix of package */ uprv_strcpy(tmp, o->srcDir); uprv_strcat(tmp, o->srcDir[uprv_strlen(o->srcDir)-1]==U_FILE_SEP_CHAR?"":U_FILE_SEP_STRING); if(fixPrefix && strncmp(pkgPrefix,s, pkgPrefixLen)) { /* didn't have the prefix - add it */ uprv_strcat(tmp, pkgPrefix); /* make up a new basename */ uprv_strcpy(tmp2, pkgPrefix); uprv_strcat(tmp2, s); o->files = pkg_appendToList(o->files, &tail, uprv_strdup(tmp2)); } else { o->files = pkg_appendToList(o->files, &tail, uprv_strdup(baseName)); } uprv_strcat(tmp, s); o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(tmp)); } linePtr = lineNext; } } T_FileStream_close(in); } } /* Try calling icu-config directly to get information */ void fillInMakefileFromICUConfig(UOption *option) { #if U_HAVE_POPEN FILE *p; size_t n; static char buf[512] = ""; static const char cmd[] = "icu-config --incfile"; if(options[5].doesOccur) { /* informational */ fprintf(stderr, "%s: No -O option found, trying '%s'.\n", progname, cmd); } p = popen(cmd, "r"); if(p == NULL) { fprintf(stderr, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname); return; } n = fread(buf, 1, 511, p); pclose(p); if(n<=0) { fprintf(stderr,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname); return; } if(buf[strlen(buf)-1]=='\n') { buf[strlen(buf)-1]=0; } if(buf[0] == 0) { fprintf(stderr, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname); return; } if(options[5].doesOccur) { /* informational */ fprintf(stderr, "%s: icu-config: using '-O %s'\n", progname, buf); } option->value = buf; option->doesOccur = TRUE; #else /* ! U_HAVE_POPEN */ #ifdef WIN32 char pathbuffer[_MAX_PATH] = {0}; char *fullEXEpath = NULL; char *pathstuff = NULL; if (strchr(progname, U_FILE_SEP_CHAR) != NULL || strchr(progname, U_FILE_ALT_SEP_CHAR) != NULL) { /* pkgdata was executed with relative path */ fullEXEpath = _fullpath(pathbuffer, progname, sizeof(pathbuffer)); pathstuff = (char *)options[1].value; if (fullEXEpath) { pathstuff = strrchr(fullEXEpath, U_FILE_SEP_CHAR); if (pathstuff) { pathstuff[1] = 0; uprv_memmove(fullEXEpath + 2, fullEXEpath, uprv_strlen(fullEXEpath)+1); fullEXEpath[0] = PKGDATA_DERIVED_PATH; fullEXEpath[1] = ':'; option->value = uprv_strdup(fullEXEpath); option->doesOccur = TRUE; } } } else { /* pkgdata was executed from the path */ /* Search for file in PATH environment variable: */ _searchenv("pkgdata.exe", "PATH", pathbuffer ); if( *pathbuffer != '\0' ) { fullEXEpath = pathbuffer; pathstuff = strrchr(pathbuffer, U_FILE_SEP_CHAR); if (pathstuff) { pathstuff[1] = 0; uprv_memmove(fullEXEpath + 2, fullEXEpath, uprv_strlen(fullEXEpath)+1); fullEXEpath[0] = PKGDATA_DERIVED_PATH; fullEXEpath[1] = ':'; option->value = uprv_strdup(fullEXEpath); option->doesOccur = TRUE; } } } /* else can't determine the path */ #endif /* no popen available */ /* Put other OS specific ways to search for the Makefile.inc type information or else fail.. */ #endif }