1/******************************************************************************
2 *   Copyright (C) 2000-2010, International Business Machines
3 *   Corporation and others.  All Rights Reserved.
4 *******************************************************************************
5 *   file name:  pkgdata.c
6 *   encoding:   ANSI X3.4 (1968)
7 *   tab size:   8 (not used)
8 *   indentation:4
9 *
10 *   created on: 2000may15
11 *   created by: Steven \u24C7 Loomis
12 *
13 *   This program packages the ICU data into different forms
14 *   (DLL, common data, etc.)
15 */
16
17/*
18 * We define _XOPEN_SOURCE so that we can get popen and pclose.
19 */
20#if !defined(_XOPEN_SOURCE)
21#if __STDC_VERSION__ >= 199901L
22/* It is invalid to compile an XPG3, XPG4, XPG4v2 or XPG5 application using c99 on Solaris */
23#define _XOPEN_SOURCE 600
24#else
25#define _XOPEN_SOURCE 4
26#endif
27#endif
28
29
30#include "unicode/utypes.h"
31
32#if U_HAVE_POPEN
33#if defined(U_CYGWIN) && defined(__STRICT_ANSI__)
34/* popen/pclose aren't defined in strict ANSI on Cygwin */
35#undef __STRICT_ANSI__
36#endif
37#endif
38
39#include "unicode/putil.h"
40#include "cmemory.h"
41#include "cstring.h"
42#include "filestrm.h"
43#include "toolutil.h"
44#include "unicode/uclean.h"
45#include "unewdata.h"
46#include "uoptions.h"
47#include "putilimp.h"
48#include "package.h"
49#include "pkg_icu.h"
50#include "pkg_genc.h"
51#include "pkg_gencmn.h"
52#include "flagparser.h"
53#include "filetools.h"
54
55
56#if U_HAVE_POPEN
57# include <unistd.h>
58#endif
59#include <stdio.h>
60#include <stdlib.h>
61
62U_CDECL_BEGIN
63#include "pkgtypes.h"
64U_CDECL_END
65
66#ifdef U_WINDOWS
67#ifdef __GNUC__
68#define WINDOWS_WITH_GNUC
69#else
70#define WINDOWS_WITH_MSVC
71#endif
72#endif
73#if !defined(WINDOWS_WITH_MSVC) && !defined(U_LINUX)
74#define BUILD_DATA_WITHOUT_ASSEMBLY
75#endif
76#if defined(WINDOWS_WITH_MSVC) || defined(U_LINUX)
77#define CAN_WRITE_OBJ_CODE
78#endif
79#if defined(U_CYGWIN) || defined(CYGWINMSVC)
80#define USING_CYGWIN
81#endif
82
83/*
84 * When building the data library without assembly,
85 * some platforms use a single c code file for all of
86 * the data to generate the final data library. This can
87 * increase the performance of the pkdata tool.
88 */
89#if defined(OS400)
90#define USE_SINGLE_CCODE_FILE
91#endif
92
93/* Need to fix the file seperator character when using MinGW. */
94#if defined(WINDOWS_WITH_GNUC) || defined(USING_CYGWIN)
95#define PKGDATA_FILE_SEP_STRING "/"
96#else
97#define PKGDATA_FILE_SEP_STRING U_FILE_SEP_STRING
98#endif
99
100#define LARGE_BUFFER_MAX_SIZE 2048
101#define SMALL_BUFFER_MAX_SIZE 512
102
103static void loadLists(UPKGOptions *o, UErrorCode *status);
104
105static int32_t pkg_executeOptions(UPKGOptions *o);
106
107#ifdef WINDOWS_WITH_MSVC
108static int32_t pkg_createWindowsDLL(const char mode, const char *gencFilePath, UPKGOptions *o);
109#endif
110static int32_t pkg_createSymLinks(const char *targetDir, UBool specialHandling=FALSE);
111static int32_t pkg_installLibrary(const char *installDir, const char *dir);
112static int32_t pkg_installFileMode(const char *installDir, const char *srcDir, const char *fileListName);
113static int32_t pkg_installCommonMode(const char *installDir, const char *fileName);
114
115#ifdef BUILD_DATA_WITHOUT_ASSEMBLY
116static int32_t pkg_createWithoutAssemblyCode(UPKGOptions *o, const char *targetDir, const char mode);
117#endif
118
119static int32_t pkg_createWithAssemblyCode(const char *targetDir, const char mode, const char *gencFilePath);
120static int32_t pkg_generateLibraryFile(const char *targetDir, const char mode, const char *objectFile, char *command = NULL);
121static int32_t pkg_archiveLibrary(const char *targetDir, const char *version, UBool reverseExt);
122static void createFileNames(UPKGOptions *o, const char mode, const char *version_major, const char *version, const char *libName, const UBool reverseExt);
123static int32_t initializePkgDataFlags(UPKGOptions *o);
124
125static int32_t pkg_getOptionsFromICUConfig(UBool verbose, UOption *option);
126static int runCommand(const char* command, UBool specialHandling=FALSE);
127
128enum {
129    NAME,
130    BLDOPT,
131    MODE,
132    HELP,
133    HELP_QUESTION_MARK,
134    VERBOSE,
135    COPYRIGHT,
136    COMMENT,
137    DESTDIR,
138    REBUILD,
139    TEMPDIR,
140    INSTALL,
141    SOURCEDIR,
142    ENTRYPOINT,
143    REVISION,
144    FORCE_PREFIX,
145    LIBNAME,
146    QUIET
147};
148
149/* This sets the modes that are available */
150static struct {
151    const char *name, *alt_name;
152    const char *desc;
153} modes[] = {
154        { "files", 0,           "Uses raw data files (no effect). Installation copies all files to the target location." },
155#ifdef U_WINDOWS
156        { "dll",    "library",  "Generates one common data file and one shared library, <package>.dll"},
157        { "common", "archive",  "Generates just the common file, <package>.dat"},
158        { "static", "static",   "Generates one statically linked library, " LIB_PREFIX "<package>" UDATA_LIB_SUFFIX }
159#else
160#ifdef UDATA_SO_SUFFIX
161        { "dll",    "library",  "Generates one shared library, <package>" UDATA_SO_SUFFIX },
162#endif
163        { "common", "archive",  "Generates one common data file, <package>.dat" },
164        { "static", "static",   "Generates one statically linked library, " LIB_PREFIX "<package>" UDATA_LIB_SUFFIX }
165#endif
166};
167
168static UOption options[]={
169    /*00*/    UOPTION_DEF( "name",    'p', UOPT_REQUIRES_ARG),
170    /*01*/    UOPTION_DEF( "bldopt",  'O', UOPT_REQUIRES_ARG), /* on Win32 it is release or debug */
171    /*02*/    UOPTION_DEF( "mode",    'm', UOPT_REQUIRES_ARG),
172    /*03*/    UOPTION_HELP_H,                                   /* -h */
173    /*04*/    UOPTION_HELP_QUESTION_MARK,                       /* -? */
174    /*05*/    UOPTION_VERBOSE,                                  /* -v */
175    /*06*/    UOPTION_COPYRIGHT,                                /* -c */
176    /*07*/    UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG),
177    /*08*/    UOPTION_DESTDIR,                                  /* -d */
178    /*11*/    UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG),
179    /*12*/    UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG),
180    /*13*/    UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG),
181    /*14*/    UOPTION_SOURCEDIR ,
182    /*15*/    UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG),
183    /*16*/    UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG),
184    /*17*/    UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG),
185    /*18*/    UOPTION_DEF( "libname", 'L', UOPT_REQUIRES_ARG),
186    /*19*/    UOPTION_DEF( "quiet", 'q', UOPT_NO_ARG)
187};
188
189enum {
190    GENCCODE_ASSEMBLY_TYPE,
191    SO_EXT,
192    SOBJ_EXT,
193    A_EXT,
194    LIBPREFIX,
195    LIB_EXT_ORDER,
196    COMPILER,
197    LIBFLAGS,
198    GENLIB,
199    LDICUDTFLAGS,
200    LD_SONAME,
201    RPATH_FLAGS,
202    BIR_FLAGS,
203    AR,
204    ARFLAGS,
205    RANLIB,
206    INSTALL_CMD,
207    PKGDATA_FLAGS_SIZE
208};
209static char **pkgDataFlags = NULL;
210
211enum {
212    LIB_FILE,
213    LIB_FILE_VERSION_MAJOR,
214    LIB_FILE_VERSION,
215    LIB_FILE_VERSION_TMP,
216#ifdef U_CYGWIN
217    LIB_FILE_CYGWIN,
218	LIB_FILE_CYGWIN_VERSION,
219#endif
220    LIB_FILENAMES_SIZE
221};
222static char libFileNames[LIB_FILENAMES_SIZE][256];
223
224static UPKGOptions  *pkg_checkFlag(UPKGOptions *o);
225
226const char options_help[][320]={
227    "Set the data name",
228#ifdef U_MAKE_IS_NMAKE
229    "The directory where the ICU is located (e.g. <ICUROOT> which contains the bin directory)",
230#else
231    "Specify options for the builder.",
232#endif
233    "Specify the mode of building (see below; default: common)",
234    "This usage text",
235    "This usage text",
236    "Make the output verbose",
237    "Use the standard ICU copyright",
238    "Use a custom comment (instead of the copyright)",
239    "Specify the destination directory for files",
240    "Force rebuilding of all data",
241    "Specify temporary dir (default: output dir)",
242    "Install the data (specify target)",
243    "Specify a custom source directory",
244    "Specify a custom entrypoint name (default: short name)",
245    "Specify a version when packaging in dll or static mode",
246    "Add package to all file names if not present",
247    "Library name to build (if different than package name)",
248    "Quite mode. (e.g. Do not output a readme file for static libraries)"
249};
250
251const char  *progname = "PKGDATA";
252
253int
254main(int argc, char* argv[]) {
255    int result = 0;
256    /* FileStream  *out; */
257    UPKGOptions  o;
258    CharList    *tail;
259    UBool        needsHelp = FALSE;
260    UErrorCode   status = U_ZERO_ERROR;
261    /* char         tmp[1024]; */
262    uint32_t i;
263    int32_t n;
264
265    U_MAIN_INIT_ARGS(argc, argv);
266
267    progname = argv[0];
268
269    options[MODE].value = "common";
270
271    /* read command line options */
272    argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
273
274    /* error handling, printing usage message */
275    /* I've decided to simply print an error and quit. This tool has too
276    many options to just display them all of the time. */
277
278    if(options[HELP].doesOccur || options[HELP_QUESTION_MARK].doesOccur) {
279        needsHelp = TRUE;
280    }
281    else {
282        if(!needsHelp && argc<0) {
283            fprintf(stderr,
284                "%s: error in command line argument \"%s\"\n",
285                progname,
286                argv[-argc]);
287            fprintf(stderr, "Run '%s --help' for help.\n", progname);
288            return 1;
289        }
290
291
292#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN)
293        if(!options[BLDOPT].doesOccur && uprv_strcmp(options[MODE].value, "common") != 0) {
294          if (pkg_getOptionsFromICUConfig(options[VERBOSE].doesOccur, &options[BLDOPT]) != 0) {
295                fprintf(stderr, " required parameter is missing: -O is required for static and shared builds.\n");
296                fprintf(stderr, "Run '%s --help' for help.\n", progname);
297                return 1;
298            }
299        }
300#else
301        if(options[BLDOPT].doesOccur) {
302            fprintf(stdout, "Warning: You are using the -O option which is not needed for MSVC build on Windows.\n");
303        }
304#endif
305
306        if(!options[NAME].doesOccur) /* -O we already have - don't report it. */
307        {
308            fprintf(stderr, " required parameter -p is missing \n");
309            fprintf(stderr, "Run '%s --help' for help.\n", progname);
310            return 1;
311        }
312
313        if(argc == 1) {
314            fprintf(stderr,
315                "No input files specified.\n"
316                "Run '%s --help' for help.\n", progname);
317            return 1;
318        }
319    }   /* end !needsHelp */
320
321    if(argc<0 || needsHelp  ) {
322        fprintf(stderr,
323            "usage: %s [-options] [-] [packageFile] \n"
324            "\tProduce packaged ICU data from the given list(s) of files.\n"
325            "\t'-' by itself means to read from stdin.\n"
326            "\tpackageFile is a text file containing the list of files to package.\n",
327            progname);
328
329        fprintf(stderr, "\n options:\n");
330        for(i=0;i<(sizeof(options)/sizeof(options[0]));i++) {
331            fprintf(stderr, "%-5s -%c %s%-10s  %s\n",
332                (i<1?"[REQ]":""),
333                options[i].shortName,
334                options[i].longName ? "or --" : "     ",
335                options[i].longName ? options[i].longName : "",
336                options_help[i]);
337        }
338
339        fprintf(stderr, "modes: (-m option)\n");
340        for(i=0;i<(sizeof(modes)/sizeof(modes[0]));i++) {
341            fprintf(stderr, "   %-9s ", modes[i].name);
342            if (modes[i].alt_name) {
343                fprintf(stderr, "/ %-9s", modes[i].alt_name);
344            } else {
345                fprintf(stderr, "           ");
346            }
347            fprintf(stderr, "  %s\n", modes[i].desc);
348        }
349        return 1;
350    }
351
352    /* OK, fill in the options struct */
353    uprv_memset(&o, 0, sizeof(o));
354
355    o.mode      = options[MODE].value;
356    o.version   = options[REVISION].doesOccur ? options[REVISION].value : 0;
357
358    o.shortName = options[NAME].value;
359    {
360        int32_t len = (int32_t)uprv_strlen(o.shortName);
361        char *csname, *cp;
362        const char *sp;
363
364        cp = csname = (char *) uprv_malloc((len + 1 + 1) * sizeof(*o.cShortName));
365        if (*(sp = o.shortName)) {
366            *cp++ = isalpha(*sp) ? * sp : '_';
367            for (++sp; *sp; ++sp) {
368                *cp++ = isalnum(*sp) ? *sp : '_';
369            }
370        }
371        *cp = 0;
372
373        o.cShortName = csname;
374    }
375
376    if(options[LIBNAME].doesOccur) { /* get libname from shortname, or explicit -L parameter */
377      o.libName = options[LIBNAME].value;
378    } else {
379      o.libName = o.shortName;
380    }
381
382    if(options[QUIET].doesOccur) {
383      o.quiet = TRUE;
384    } else {
385      o.quiet = FALSE;
386    }
387
388    o.verbose   = options[VERBOSE].doesOccur;
389
390
391#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN) /* on UNIX, we'll just include the file... */
392    if (options[BLDOPT].doesOccur) {
393        o.options   = options[BLDOPT].value;
394    } else {
395        o.options = NULL;
396    }
397#endif
398    if(options[COPYRIGHT].doesOccur) {
399        o.comment = U_COPYRIGHT_STRING;
400    } else if (options[COMMENT].doesOccur) {
401        o.comment = options[COMMENT].value;
402    }
403
404    if( options[DESTDIR].doesOccur ) {
405        o.targetDir = options[DESTDIR].value;
406    } else {
407        o.targetDir = ".";  /* cwd */
408    }
409
410    o.rebuild   = options[REBUILD].doesOccur;
411
412    if( options[TEMPDIR].doesOccur ) {
413        o.tmpDir    = options[TEMPDIR].value;
414    } else {
415        o.tmpDir    = o.targetDir;
416    }
417
418    if( options[INSTALL].doesOccur ) {
419        o.install  = options[INSTALL].value;
420    } else {
421        o.install = NULL;
422    }
423
424    if( options[SOURCEDIR].doesOccur ) {
425        o.srcDir   = options[SOURCEDIR].value;
426    } else {
427        o.srcDir   = ".";
428    }
429
430    if( options[ENTRYPOINT].doesOccur ) {
431        o.entryName = options[ENTRYPOINT].value;
432    } else {
433        o.entryName = o.cShortName;
434    }
435
436    /* OK options are set up. Now the file lists. */
437    tail = NULL;
438    for( n=1; n<argc; n++) {
439        o.fileListFiles = pkg_appendToList(o.fileListFiles, &tail, uprv_strdup(argv[n]));
440    }
441
442    /* load the files */
443    loadLists(&o, &status);
444    if( U_FAILURE(status) ) {
445        fprintf(stderr, "error loading input file lists: %s\n", u_errorName(status));
446        return 2;
447    }
448
449    result = pkg_executeOptions(&o);
450
451    if (pkgDataFlags != NULL) {
452        for (n = 0; n < PKGDATA_FLAGS_SIZE; n++) {
453            if (pkgDataFlags[n] != NULL) {
454                uprv_free(pkgDataFlags[n]);
455            }
456        }
457        uprv_free(pkgDataFlags);
458    }
459
460    if (o.cShortName != NULL) {
461        uprv_free((char *)o.cShortName);
462    }
463    if (o.fileListFiles != NULL) {
464        pkg_deleteList(o.fileListFiles);
465    }
466    if (o.filePaths != NULL) {
467        pkg_deleteList(o.filePaths);
468    }
469    if (o.files != NULL) {
470        pkg_deleteList(o.files);
471    }
472
473    return result;
474}
475
476static int runCommand(const char* command, UBool specialHandling) {
477    char *cmd = NULL;
478    char cmdBuffer[SMALL_BUFFER_MAX_SIZE];
479    int32_t len = strlen(command);
480
481    if (len == 0) {
482        return 0;
483    }
484
485    if (!specialHandling) {
486#if defined(USING_CYGWIN) || defined(OS400)
487#define CMD_PADDING_SIZE 20
488        if ((len + CMD_PADDING_SIZE) >= SMALL_BUFFER_MAX_SIZE) {
489            cmd = (char *)uprv_malloc(len + CMD_PADDING_SIZE);
490        } else {
491            cmd = cmdBuffer;
492        }
493#ifdef USING_CYGWIN
494        sprintf(cmd, "bash -c \"%s\"", command);
495
496#elif defined(OS400)
497        sprintf(cmd, "QSH CMD('%s')", command);
498#endif
499#else
500        goto normal_command_mode;
501#endif
502    } else {
503normal_command_mode:
504        cmd = (char *)command;
505    }
506
507    printf("pkgdata: %s\n", cmd);
508    int result = system(cmd);
509    if (result != 0) {
510        printf("-- return status = %d\n", result);
511    }
512
513    if (cmd != cmdBuffer && cmd != command) {
514        uprv_free(cmd);
515    }
516
517    return result;
518}
519
520#define LN_CMD "ln -s"
521#define RM_CMD "rm -f"
522
523#define MODE_COMMON 'c'
524#define MODE_STATIC 's'
525#define MODE_DLL    'd'
526#define MODE_FILES  'f'
527
528static int32_t pkg_executeOptions(UPKGOptions *o) {
529    int32_t result = 0;
530
531    const char mode = o->mode[0];
532    char targetDir[SMALL_BUFFER_MAX_SIZE] = "";
533    char tmpDir[SMALL_BUFFER_MAX_SIZE] = "";
534    char datFileName[SMALL_BUFFER_MAX_SIZE] = "";
535    char datFileNamePath[LARGE_BUFFER_MAX_SIZE] = "";
536    char checkLibFile[LARGE_BUFFER_MAX_SIZE] = "";
537
538    initializePkgDataFlags(o);
539
540    if (mode == MODE_FILES) {
541        /* Copy the raw data to the installation directory. */
542        if (o->install != NULL) {
543            uprv_strcpy(targetDir, o->install);
544            if (o->shortName != NULL) {
545                uprv_strcat(targetDir, PKGDATA_FILE_SEP_STRING);
546                uprv_strcat(targetDir, o->shortName);
547            }
548
549            if(o->verbose) {
550              fprintf(stdout, "# Install: Files mode, copying files to %s..\n", targetDir);
551            }
552            result = pkg_installFileMode(targetDir, o->srcDir, o->fileListFiles->str);
553        }
554        return result;
555    } else /* if (mode == MODE_COMMON || mode == MODE_STATIC || mode == MODE_DLL) */ {
556        uprv_strcpy(targetDir, o->targetDir);
557        uprv_strcat(targetDir, PKGDATA_FILE_SEP_STRING);
558
559        uprv_strcpy(tmpDir, o->tmpDir);
560        uprv_strcat(tmpDir, PKGDATA_FILE_SEP_STRING);
561
562        uprv_strcpy(datFileNamePath, tmpDir);
563
564        uprv_strcpy(datFileName, o->shortName);
565        uprv_strcat(datFileName, UDATA_CMN_SUFFIX);
566
567        uprv_strcat(datFileNamePath, datFileName);
568
569        if(o->verbose) {
570          fprintf(stdout, "# Writing package file %s ..\n", datFileNamePath);
571        }
572        result = writePackageDatFile(datFileNamePath, o->comment, o->srcDir, o->fileListFiles->str, NULL, U_CHARSET_FAMILY ? 'e' :  U_IS_BIG_ENDIAN ? 'b' : 'l');
573        if (result != 0) {
574            fprintf(stderr,"Error writing package dat file.\n");
575            return result;
576        }
577
578        if (mode == MODE_COMMON) {
579            char targetFileNamePath[LARGE_BUFFER_MAX_SIZE] = "";
580
581            uprv_strcpy(targetFileNamePath, targetDir);
582            uprv_strcat(targetFileNamePath, datFileName);
583
584            if (T_FileStream_file_exists(targetFileNamePath)) {
585                if ((result = remove(targetFileNamePath)) != 0) {
586                    fprintf(stderr, "Unable to remove old dat file: %s\n", targetFileNamePath);
587                    return result;
588                }
589            }
590
591            /* Move the dat file created to the target directory. */
592            result = rename(datFileNamePath, targetFileNamePath);
593
594            if(o->verbose) {
595              fprintf(stdout, "# Moving package file to %s ..\n", targetFileNamePath);
596            }
597            if (result != 0) {
598                fprintf(stderr, "Unable to move dat file (%s) to target location (%s).\n", datFileNamePath, targetFileNamePath);
599            }
600
601            if (o->install != NULL) {
602                result = pkg_installCommonMode(o->install, targetFileNamePath);
603            }
604
605            return result;
606        } else /* if (mode[0] == MODE_STATIC || mode[0] == MODE_DLL) */ {
607            char gencFilePath[SMALL_BUFFER_MAX_SIZE] = "";
608            char version_major[10] = "";
609            UBool reverseExt = FALSE;
610
611#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN)
612            /* Get the version major number. */
613            if (o->version != NULL) {
614                for (uint32_t i = 0;i < sizeof(version_major);i++) {
615                    if (o->version[i] == '.') {
616                        version_major[i] = 0;
617                        break;
618                    }
619                    version_major[i] = o->version[i];
620                }
621            }
622
623#ifndef OS400
624            /* Certain platforms have different library extension ordering. (e.g. libicudata.##.so vs libicudata.so.##)
625             * reverseExt is FALSE if the suffix should be the version number.
626             */
627            if (pkgDataFlags[LIB_EXT_ORDER][uprv_strlen(pkgDataFlags[LIB_EXT_ORDER])-1] == pkgDataFlags[SO_EXT][uprv_strlen(pkgDataFlags[SO_EXT])-1]) {
628                reverseExt = TRUE;
629            }
630#endif
631            /* Using the base libName and version number, generate the library file names. */
632            createFileNames(o, mode, version_major, o->version, o->libName, reverseExt);
633
634            if ((o->version!=NULL || (mode==MODE_STATIC)) && o->rebuild == FALSE) {
635                /* Check to see if a previous built data library file exists and check if it is the latest. */
636                sprintf(checkLibFile, "%s%s", targetDir, libFileNames[LIB_FILE_VERSION]);
637                if (T_FileStream_file_exists(checkLibFile)) {
638                    if (isFileModTimeLater(checkLibFile, o->srcDir, TRUE) && isFileModTimeLater(checkLibFile, o->options)) {
639                        if (o->install != NULL) {
640                          if(o->verbose) {
641                            fprintf(stdout, "# Installing already-built library into %s\n", o->install);
642                          }
643                          result = pkg_installLibrary(o->install, targetDir);
644                        } else {
645                          if(o->verbose) {
646                            printf("# Not rebuilding %s - up to date.\n", checkLibFile);
647                          }
648                        }
649                        return result;
650                    } else if (o->verbose && (o->install!=NULL)) {
651                      fprintf(stdout, "# Not installing up-to-date library %s into %s\n", checkLibFile, o->install);
652                    }
653                } else if(o->verbose && (o->install!=NULL)) {
654                  fprintf(stdout, "# Not installing missing %s into %s\n", checkLibFile, o->install);
655                }
656            }
657
658            pkg_checkFlag(o);
659#endif
660
661            if (pkgDataFlags[GENCCODE_ASSEMBLY_TYPE][0] != 0) {
662                const char* genccodeAssembly = pkgDataFlags[GENCCODE_ASSEMBLY_TYPE];
663
664                if(o->verbose) {
665                  fprintf(stdout, "# Generating assembly code %s of type %s ..\n", gencFilePath, genccodeAssembly);
666                }
667
668                /* Offset genccodeAssembly by 3 because "-a " */
669                if (genccodeAssembly &&
670                    (uprv_strlen(genccodeAssembly)>3) &&
671                    checkAssemblyHeaderName(genccodeAssembly+3)) {
672                    writeAssemblyCode(datFileNamePath, o->tmpDir, o->entryName, NULL, gencFilePath);
673
674                    result = pkg_createWithAssemblyCode(targetDir, mode, gencFilePath);
675                    if (result != 0) {
676                        fprintf(stderr, "Error generating assembly code for data.\n");
677                        return result;
678                    } else if (mode == MODE_STATIC) {
679                      if(o->install != NULL) {
680                        if(o->verbose) {
681                          fprintf(stdout, "# Installing static library into %s\n", o->install);
682                        }
683                        result = pkg_installLibrary(o->install, targetDir);
684                      }
685                      return result;
686                    }
687                } else {
688                    fprintf(stderr,"Assembly type \"%s\" is unknown.\n", genccodeAssembly);
689                    return -1;
690                }
691            } else {
692                if(o->verbose) {
693                  fprintf(stdout, "# Writing object code to %s ..\n", gencFilePath);
694                }
695#ifdef CAN_WRITE_OBJ_CODE
696                writeObjectCode(datFileNamePath, o->tmpDir, o->entryName, NULL, NULL, gencFilePath);
697#ifdef U_LINUX
698                result = pkg_generateLibraryFile(targetDir, mode, gencFilePath);
699#elif defined(WINDOWS_WITH_MSVC)
700                result = pkg_createWindowsDLL(mode, gencFilePath, o);
701#endif
702#elif defined(BUILD_DATA_WITHOUT_ASSEMBLY)
703                result = pkg_createWithoutAssemblyCode(o, targetDir, mode);
704#endif
705                if (result != 0) {
706                    fprintf(stderr, "Error generating package data.\n");
707                    return result;
708                }
709            }
710#ifndef U_WINDOWS
711            if(mode != MODE_STATIC) {
712                /* Certain platforms uses archive library. (e.g. AIX) */
713                if(o->verbose) {
714                  fprintf(stdout, "# Creating data archive library file ..\n");
715                }
716                result = pkg_archiveLibrary(targetDir, o->version, reverseExt);
717                if (result != 0) {
718                    fprintf(stderr, "Error creating data archive library file.\n");
719                   return result;
720                }
721#ifndef OS400
722                /* Create symbolic links for the final library file. */
723                result = pkg_createSymLinks(targetDir);
724                if (result != 0) {
725                    fprintf(stderr, "Error creating symbolic links of the data library file.\n");
726                    return result;
727                }
728#endif
729            } /* !MODE_STATIC */
730#endif
731
732#if !defined(U_WINDOWS) || defined(USING_CYGWIN)
733            /* Install the libraries if option was set. */
734            if (o->install != NULL) {
735                if(o->verbose) {
736                  fprintf(stdout, "# Installing library file to %s ..\n", o->install);
737                }
738                result = pkg_installLibrary(o->install, targetDir);
739                if (result != 0) {
740                    fprintf(stderr, "Error installing the data library.\n");
741                    return result;
742                }
743            }
744#endif
745        }
746    }
747    return result;
748}
749
750/* Initialize the pkgDataFlags with the option file given. */
751static int32_t initializePkgDataFlags(UPKGOptions *o) {
752    UErrorCode status = U_ZERO_ERROR;
753    int32_t result = 0;
754    int32_t currentBufferSize = SMALL_BUFFER_MAX_SIZE;
755    int32_t tmpResult = 0;
756
757    /* Initialize pkgdataFlags */
758    pkgDataFlags = (char**)uprv_malloc(sizeof(char*) * PKGDATA_FLAGS_SIZE);
759
760    /* If we run out of space, allocate more */
761#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN)
762    do {
763#endif
764        if (pkgDataFlags != NULL) {
765            for (int32_t i = 0; i < PKGDATA_FLAGS_SIZE; i++) {
766                pkgDataFlags[i] = (char*)uprv_malloc(sizeof(char) * currentBufferSize);
767                if (pkgDataFlags[i] != NULL) {
768                    pkgDataFlags[i][0] = 0;
769                } else {
770                    fprintf(stderr,"Error allocating memory for pkgDataFlags.\n");
771                    return -1;
772                }
773            }
774        } else {
775            fprintf(stderr,"Error allocating memory for pkgDataFlags.\n");
776            return -1;
777        }
778
779        if (o->options == NULL) {
780            return result;
781        }
782
783#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN)
784        /* Read in options file. */
785        if(o->verbose) {
786          fprintf(stdout, "# Reading options file %s\n", o->options);
787        }
788        status = U_ZERO_ERROR;
789        tmpResult = parseFlagsFile(o->options, pkgDataFlags, currentBufferSize, (int32_t)PKGDATA_FLAGS_SIZE, &status);
790        if (status == U_BUFFER_OVERFLOW_ERROR) {
791            for (int32_t i = 0; i < PKGDATA_FLAGS_SIZE; i++) {
792                uprv_free(pkgDataFlags[i]);
793            }
794            currentBufferSize = tmpResult;
795        } else if (U_FAILURE(status)) {
796            fprintf(stderr,"Unable to open or read \"%s\" option file. status = %s\n", o->options, u_errorName(status));
797            return -1;
798        }
799#endif
800        if(o->verbose) {
801            fprintf(stdout, "# pkgDataFlags=");
802            for(int32_t i=0;i<PKGDATA_FLAGS_SIZE && pkgDataFlags[i][0];i++) {
803                fprintf(stdout, "%c \"%s\"", (i>0)?',':' ',pkgDataFlags[i]);
804            }
805            fprintf(stdout, "\n");
806        }
807#if !defined(WINDOWS_WITH_MSVC) || defined(USING_CYGWIN)
808    } while (status == U_BUFFER_OVERFLOW_ERROR);
809#endif
810
811    return result;
812}
813
814
815/*
816 * Given the base libName and version numbers, generate the libary file names and store it in libFileNames.
817 * Depending on the configuration, the library name may either end with version number or shared object suffix.
818 */
819static void createFileNames(UPKGOptions *o, const char mode, const char *version_major, const char *version, const char *libName, UBool reverseExt) {
820        sprintf(libFileNames[LIB_FILE], "%s%s",
821                pkgDataFlags[LIBPREFIX],
822                libName);
823
824        if(o->verbose) {
825          fprintf(stdout, "# libFileName[LIB_FILE] = %s\n", libFileNames[LIB_FILE]);
826        }
827
828        if (version != NULL) {
829#ifdef U_CYGWIN
830            sprintf(libFileNames[LIB_FILE_CYGWIN], "cyg%s.%s",
831                    libName,
832                    pkgDataFlags[SO_EXT]);
833            sprintf(libFileNames[LIB_FILE_CYGWIN_VERSION], "cyg%s%s.%s",
834                    libName,
835                    version_major,
836                    pkgDataFlags[SO_EXT]);
837
838            uprv_strcat(pkgDataFlags[SO_EXT], ".");
839            uprv_strcat(pkgDataFlags[SO_EXT], pkgDataFlags[A_EXT]);
840
841#elif defined(OS400) || defined(_AIX)
842            sprintf(libFileNames[LIB_FILE_VERSION_TMP], "%s.%s",
843                    libFileNames[LIB_FILE],
844                    pkgDataFlags[SOBJ_EXT]);
845#else
846            sprintf(libFileNames[LIB_FILE_VERSION_TMP], "%s%s%s.%s",
847                    libFileNames[LIB_FILE],
848                    pkgDataFlags[LIB_EXT_ORDER][0] == '.' ? "." : "",
849                    reverseExt ? version : pkgDataFlags[SOBJ_EXT],
850                    reverseExt ? pkgDataFlags[SOBJ_EXT] : version);
851#endif
852            sprintf(libFileNames[LIB_FILE_VERSION_MAJOR], "%s%s%s.%s",
853                    libFileNames[LIB_FILE],
854                    pkgDataFlags[LIB_EXT_ORDER][0] == '.' ? "." : "",
855                    reverseExt ? version_major : pkgDataFlags[SO_EXT],
856                    reverseExt ? pkgDataFlags[SO_EXT] : version_major);
857
858            sprintf(libFileNames[LIB_FILE_VERSION], "%s%s%s.%s",
859                    libFileNames[LIB_FILE],
860                    pkgDataFlags[LIB_EXT_ORDER][0] == '.' ? "." : "",
861                    reverseExt ? version : pkgDataFlags[SO_EXT],
862                    reverseExt ? pkgDataFlags[SO_EXT] : version);
863
864            if(o->verbose) {
865              fprintf(stdout, "# libFileName[LIB_FILE_VERSION] = %s\n", libFileNames[LIB_FILE_VERSION]);
866            }
867
868#ifdef U_CYGWIN
869            /* Cygwin only deals with the version major number. */
870            uprv_strcpy(libFileNames[LIB_FILE_VERSION_TMP], libFileNames[LIB_FILE_VERSION_MAJOR]);
871#endif
872        }
873        if(mode == MODE_STATIC) {
874            sprintf(libFileNames[LIB_FILE_VERSION], "%s.%s", libFileNames[LIB_FILE], pkgDataFlags[A_EXT]);
875            libFileNames[LIB_FILE_VERSION_MAJOR][0]=0;
876            if(o->verbose) {
877              fprintf(stdout, "# libFileName[LIB_FILE_VERSION] = %s  (static)\n", libFileNames[LIB_FILE_VERSION]);
878            }
879        }
880}
881
882/* Create the symbolic links for the final library file. */
883static int32_t pkg_createSymLinks(const char *targetDir, UBool specialHandling) {
884    int32_t result = 0;
885    char cmd[LARGE_BUFFER_MAX_SIZE];
886    char name1[SMALL_BUFFER_MAX_SIZE]; /* symlink file name */
887    char name2[SMALL_BUFFER_MAX_SIZE]; /* file name to symlink */
888
889#ifndef USING_CYGWIN
890    /* No symbolic link to make. */
891    if (uprv_strlen(libFileNames[LIB_FILE_VERSION]) == 0 || uprv_strlen(libFileNames[LIB_FILE_VERSION_MAJOR]) == 0 ||
892        uprv_strcmp(libFileNames[LIB_FILE_VERSION], libFileNames[LIB_FILE_VERSION_MAJOR]) == 0) {
893        return result;
894    }
895
896    sprintf(cmd, "cd %s && %s %s && %s %s %s",
897            targetDir,
898            RM_CMD,
899            libFileNames[LIB_FILE_VERSION_MAJOR],
900            LN_CMD,
901            libFileNames[LIB_FILE_VERSION],
902            libFileNames[LIB_FILE_VERSION_MAJOR]);
903    result = runCommand(cmd);
904    if (result != 0) {
905        return result;
906    }
907#endif
908
909    if (specialHandling) {
910#ifdef U_CYGWIN
911        sprintf(name1, "%s", libFileNames[LIB_FILE_CYGWIN]);
912        sprintf(name2, "%s", libFileNames[LIB_FILE_CYGWIN_VERSION]);
913#else
914        goto normal_symlink_mode;
915#endif
916    } else {
917normal_symlink_mode:
918        sprintf(name1, "%s.%s", libFileNames[LIB_FILE], pkgDataFlags[SO_EXT]);
919        sprintf(name2, "%s", libFileNames[LIB_FILE_VERSION]);
920    }
921
922    sprintf(cmd, "cd %s && %s %s && %s %s %s",
923            targetDir,
924            RM_CMD,
925            name1,
926            LN_CMD,
927            name2,
928            name1);
929
930     result = runCommand(cmd);
931
932    return result;
933}
934
935static int32_t pkg_installLibrary(const char *installDir, const char *targetDir) {
936    int32_t result = 0;
937    char cmd[SMALL_BUFFER_MAX_SIZE];
938
939    sprintf(cmd, "cd %s && %s %s %s%s%s",
940            targetDir,
941            pkgDataFlags[INSTALL_CMD],
942            libFileNames[LIB_FILE_VERSION],
943            installDir, PKGDATA_FILE_SEP_STRING, libFileNames[LIB_FILE_VERSION]
944            );
945
946    result = runCommand(cmd);
947
948    if (result != 0) {
949        return result;
950    }
951
952#ifdef CYGWINMSVC
953    sprintf(cmd, "cd %s && %s %s.lib %s",
954            targetDir,
955            pkgDataFlags[INSTALL_CMD],
956            libFileNames[LIB_FILE],
957            installDir
958            );
959    result = runCommand(cmd);
960
961    if (result != 0) {
962        return result;
963    }
964#elif defined (U_CYGWIN)
965    sprintf(cmd, "cd %s && %s %s %s",
966            targetDir,
967            pkgDataFlags[INSTALL_CMD],
968            libFileNames[LIB_FILE_CYGWIN_VERSION],
969            installDir
970            );
971    result = runCommand(cmd);
972
973    if (result != 0) {
974        return result;
975    }
976#endif
977
978    return pkg_createSymLinks(installDir, TRUE);
979}
980
981static int32_t pkg_installCommonMode(const char *installDir, const char *fileName) {
982    int32_t result = 0;
983    char cmd[SMALL_BUFFER_MAX_SIZE] = "";
984
985    if (!T_FileStream_file_exists(installDir)) {
986        UErrorCode status = U_ZERO_ERROR;
987
988        uprv_mkdir(installDir, &status);
989        if (U_FAILURE(status)) {
990            fprintf(stderr, "Error creating installation directory: %s\n", installDir);
991            return -1;
992        }
993    }
994#ifndef U_WINDOWS_WITH_MSVC
995    sprintf(cmd, "%s %s %s", pkgDataFlags[INSTALL_CMD], fileName, installDir);
996#else
997    sprintf(cmd, "%s %s %s %s", WIN_INSTALL_CMD, fileName, installDir, WIN_INSTALL_CMD_FLAGS);
998#endif
999
1000    result = runCommand(cmd);
1001    if (result != 0) {
1002        fprintf(stderr, "Failed to install data file with command: %s\n", cmd);
1003    }
1004
1005    return result;
1006}
1007
1008#ifdef U_WINDOWS_MSVC
1009/* Copy commands for installing the raw data files on Windows. */
1010#define WIN_INSTALL_CMD "xcopy"
1011#define WIN_INSTALL_CMD_FLAGS "/E /Y /K"
1012#endif
1013static int32_t pkg_installFileMode(const char *installDir, const char *srcDir, const char *fileListName) {
1014    int32_t result = 0;
1015    char cmd[SMALL_BUFFER_MAX_SIZE] = "";
1016
1017    if (!T_FileStream_file_exists(installDir)) {
1018        UErrorCode status = U_ZERO_ERROR;
1019
1020        uprv_mkdir(installDir, &status);
1021        if (U_FAILURE(status)) {
1022            fprintf(stderr, "Error creating installation directory: %s\n", installDir);
1023            return -1;
1024        }
1025    }
1026#ifndef U_WINDOWS_WITH_MSVC
1027    char buffer[SMALL_BUFFER_MAX_SIZE] = "";
1028
1029    FileStream *f = T_FileStream_open(fileListName, "r");
1030    if (f != NULL) {
1031        for(;;) {
1032            if (T_FileStream_readLine(f, buffer, SMALL_BUFFER_MAX_SIZE) != NULL) {
1033                /* Remove new line character. */
1034                buffer[uprv_strlen(buffer)-1] = 0;
1035
1036                sprintf(cmd, "%s %s%s%s %s%s%s",
1037                        pkgDataFlags[INSTALL_CMD],
1038                        srcDir, PKGDATA_FILE_SEP_STRING, buffer,
1039                        installDir, PKGDATA_FILE_SEP_STRING, buffer);
1040
1041                result = runCommand(cmd);
1042                if (result != 0) {
1043                    fprintf(stderr, "Failed to install data file with command: %s\n", cmd);
1044                    break;
1045                }
1046            } else {
1047                if (!T_FileStream_eof(f)) {
1048                    fprintf(stderr, "Failed to read line from file: %s\n", fileListName);
1049                    result = -1;
1050                }
1051                break;
1052            }
1053        }
1054        T_FileStream_close(f);
1055    } else {
1056        result = -1;
1057        fprintf(stderr, "Unable to open list file: %s\n", fileListName);
1058    }
1059#else
1060    sprintf(cmd, "%s %s %s %s", WIN_INSTALL_CMD, srcDir, installDir, WIN_INSTALL_CMD_FLAGS);
1061    result = runCommand(cmd);
1062    if (result != 0) {
1063        fprintf(stderr, "Failed to install data file with command: %s\n", cmd);
1064    }
1065#endif
1066
1067    return result;
1068}
1069
1070/* Archiving of the library file may be needed depending on the platform and options given.
1071 * If archiving is not needed, copy over the library file name.
1072 */
1073static int32_t pkg_archiveLibrary(const char *targetDir, const char *version, UBool reverseExt) {
1074    int32_t result = 0;
1075    char cmd[LARGE_BUFFER_MAX_SIZE];
1076
1077    /* If the shared object suffix and the final object suffix is different and the final object suffix and the
1078     * archive file suffix is the same, then the final library needs to be archived.
1079     */
1080    if (uprv_strcmp(pkgDataFlags[SOBJ_EXT], pkgDataFlags[SO_EXT]) != 0 && uprv_strcmp(pkgDataFlags[A_EXT], pkgDataFlags[SO_EXT]) == 0) {
1081        sprintf(libFileNames[LIB_FILE_VERSION], "%s%s%s.%s",
1082                libFileNames[LIB_FILE],
1083                pkgDataFlags[LIB_EXT_ORDER][0] == '.' ? "." : "",
1084                reverseExt ? version : pkgDataFlags[SO_EXT],
1085                reverseExt ? pkgDataFlags[SO_EXT] : version);
1086
1087        sprintf(cmd, "%s %s %s%s %s%s",
1088                pkgDataFlags[AR],
1089                pkgDataFlags[ARFLAGS],
1090                targetDir,
1091                libFileNames[LIB_FILE_VERSION],
1092                targetDir,
1093                libFileNames[LIB_FILE_VERSION_TMP]);
1094
1095        result = runCommand(cmd);
1096        if (result != 0) {
1097            return result;
1098        }
1099
1100        sprintf(cmd, "%s %s%s",
1101            pkgDataFlags[RANLIB],
1102            targetDir,
1103            libFileNames[LIB_FILE_VERSION]);
1104
1105        result = runCommand(cmd);
1106        if (result != 0) {
1107            return result;
1108        }
1109
1110        /* Remove unneeded library file. */
1111        sprintf(cmd, "%s %s%s",
1112                RM_CMD,
1113                targetDir,
1114                libFileNames[LIB_FILE_VERSION_TMP]);
1115
1116        result = runCommand(cmd);
1117        if (result != 0) {
1118            return result;
1119        }
1120
1121    } else {
1122        uprv_strcpy(libFileNames[LIB_FILE_VERSION], libFileNames[LIB_FILE_VERSION_TMP]);
1123    }
1124
1125    return result;
1126}
1127
1128/*
1129 * Using the compiler information from the configuration file set by -O option, generate the library file.
1130 * command may be given to allow for a larger buffer for cmd.
1131 */
1132static int32_t pkg_generateLibraryFile(const char *targetDir, const char mode, const char *objectFile, char *command) {
1133    int32_t result = 0;
1134    char *cmd = NULL;
1135    UBool freeCmd = FALSE;
1136
1137    /* This is necessary because if packaging is done without assembly code, objectFile might be extremely large
1138     * containing many object files and so the calling function should supply a command buffer that is large
1139     * enough to handle this. Otherwise, use the default size.
1140     */
1141    if (command != NULL) {
1142        cmd = command;
1143    } else {
1144        if ((cmd = (char *)uprv_malloc(sizeof(char) * LARGE_BUFFER_MAX_SIZE)) == NULL) {
1145            fprintf(stderr, "Unable to allocate memory for command.\n");
1146            return -1;
1147        }
1148        freeCmd = TRUE;
1149    }
1150
1151    if (mode == MODE_STATIC) {
1152        sprintf(cmd, "%s %s %s%s %s",
1153                pkgDataFlags[AR],
1154                pkgDataFlags[ARFLAGS],
1155                targetDir,
1156                libFileNames[LIB_FILE_VERSION],
1157                objectFile);
1158
1159        result = runCommand(cmd);
1160        if (result == 0) {
1161            sprintf(cmd, "%s %s%s",
1162                    pkgDataFlags[RANLIB],
1163                    targetDir,
1164                    libFileNames[LIB_FILE_VERSION]);
1165
1166            result = runCommand(cmd);
1167        }
1168    } else /* if (mode == MODE_DLL) */ {
1169#ifdef U_CYGWIN
1170        sprintf(cmd, "%s%s%s %s -o %s%s %s %s%s %s %s",
1171                pkgDataFlags[GENLIB],
1172                targetDir,
1173                libFileNames[LIB_FILE_VERSION_TMP],
1174                pkgDataFlags[LDICUDTFLAGS],
1175                targetDir, libFileNames[LIB_FILE_CYGWIN_VERSION],
1176#else
1177        sprintf(cmd, "%s %s -o %s%s %s %s%s %s %s",
1178                pkgDataFlags[GENLIB],
1179                pkgDataFlags[LDICUDTFLAGS],
1180                targetDir,
1181                libFileNames[LIB_FILE_VERSION_TMP],
1182#endif
1183                objectFile,
1184                pkgDataFlags[LD_SONAME],
1185                pkgDataFlags[LD_SONAME][0] == 0 ? "" : libFileNames[LIB_FILE_VERSION_MAJOR],
1186                pkgDataFlags[RPATH_FLAGS],
1187                pkgDataFlags[BIR_FLAGS]);
1188
1189        /* Generate the library file. */
1190        result = runCommand(cmd);
1191    }
1192
1193    if (freeCmd) {
1194        uprv_free(cmd);
1195    }
1196
1197    return result;
1198}
1199
1200static int32_t pkg_createWithAssemblyCode(const char *targetDir, const char mode, const char *gencFilePath) {
1201    char tempObjectFile[SMALL_BUFFER_MAX_SIZE] = "";
1202    char *cmd;
1203    int32_t result = 0;
1204
1205    int32_t length = 0;
1206
1207    /* Remove the ending .s and replace it with .o for the new object file. */
1208    uprv_strcpy(tempObjectFile, gencFilePath);
1209    tempObjectFile[uprv_strlen(tempObjectFile)-1] = 'o';
1210
1211    length = uprv_strlen(pkgDataFlags[COMPILER]) + uprv_strlen(pkgDataFlags[LIBFLAGS])
1212                    + uprv_strlen(tempObjectFile) + uprv_strlen(gencFilePath) + 10;
1213
1214    cmd = (char *)uprv_malloc(sizeof(char) * length);
1215    if (cmd == NULL) {
1216        return -1;
1217    }
1218
1219    /* Generate the object file. */
1220    sprintf(cmd, "%s %s -o %s %s",
1221            pkgDataFlags[COMPILER],
1222            pkgDataFlags[LIBFLAGS],
1223            tempObjectFile,
1224            gencFilePath);
1225
1226    result = runCommand(cmd);
1227    uprv_free(cmd);
1228    if (result != 0) {
1229        return result;
1230    }
1231
1232    return pkg_generateLibraryFile(targetDir, mode, tempObjectFile);
1233}
1234
1235#ifdef BUILD_DATA_WITHOUT_ASSEMBLY
1236/*
1237 * Generation of the data library without assembly code needs to compile each data file
1238 * individually and then link it all together.
1239 * Note: Any update to the directory structure of the data needs to be reflected here.
1240 */
1241enum {
1242    DATA_PREFIX_BRKITR,
1243    DATA_PREFIX_COLL,
1244    DATA_PREFIX_CURR,
1245    DATA_PREFIX_LANG,
1246    DATA_PREFIX_RBNF,
1247    DATA_PREFIX_REGION,
1248    DATA_PREFIX_TRANSLIT,
1249    DATA_PREFIX_ZONE,
1250    DATA_PREFIX_LENGTH
1251};
1252
1253const static char DATA_PREFIX[DATA_PREFIX_LENGTH][10] = {
1254        "brkitr",
1255        "coll",
1256        "curr",
1257        "lang",
1258        "rbnf",
1259        "region",
1260        "translit",
1261        "zone"
1262};
1263
1264static int32_t pkg_createWithoutAssemblyCode(UPKGOptions *o, const char *targetDir, const char mode) {
1265    int32_t result = 0;
1266    CharList *list = o->filePaths;
1267    CharList *listNames = o->files;
1268    int32_t listSize = pkg_countCharList(list);
1269    char *buffer;
1270    char *cmd;
1271    char gencmnFile[SMALL_BUFFER_MAX_SIZE] = "";
1272    char tempObjectFile[SMALL_BUFFER_MAX_SIZE] = "";
1273#ifdef USE_SINGLE_CCODE_FILE
1274    char icudtAll[SMALL_BUFFER_MAX_SIZE] = "";
1275
1276    sprintf(icudtAll, "%s%s%sall.c",
1277            o->tmpDir,
1278            PKGDATA_FILE_SEP_STRING,
1279            libFileNames[LIB_FILE]);
1280    /* Remove previous icudtall.c file. */
1281    if (T_FileStream_file_exists(icudtAll) && (result = remove(icudtAll)) != 0) {
1282        fprintf(stderr, "Unable to remove old icudtall file: %s\n", icudtAll);
1283        return result;
1284    }
1285#endif
1286
1287    if (list == NULL || listNames == NULL) {
1288        /* list and listNames should never be NULL since we are looping through the CharList with
1289         * the given size.
1290         */
1291        return -1;
1292    }
1293
1294    if ((cmd = (char *)uprv_malloc((listSize + 2) * SMALL_BUFFER_MAX_SIZE)) == NULL) {
1295        fprintf(stderr, "Unable to allocate memory for cmd.\n");
1296        return -1;
1297    } else if ((buffer = (char *)uprv_malloc((listSize + 1) * SMALL_BUFFER_MAX_SIZE)) == NULL) {
1298        fprintf(stderr, "Unable to allocate memory for buffer.\n");
1299        uprv_free(cmd);
1300        return -1;
1301    }
1302
1303    for (int32_t i = 0; i < (listSize + 1); i++) {
1304        const char *file ;
1305        const char *name;
1306
1307        if (i == 0) {
1308            /* The first iteration calls the gencmn function and initailizes the buffer. */
1309            createCommonDataFile(o->tmpDir, o->shortName, o->entryName, NULL, o->srcDir, o->comment, o->fileListFiles->str, 0, TRUE, o->verbose, gencmnFile);
1310            buffer[0] = 0;
1311#ifdef USE_SINGLE_CCODE_FILE
1312            uprv_strcpy(tempObjectFile, gencmnFile);
1313            tempObjectFile[uprv_strlen(tempObjectFile) - 1] = 'o';
1314
1315            sprintf(cmd, "%s %s -o %s %s"
1316                        pkgDataFlags[COMPILER],
1317                        pkgDataFlags[LIBFLAGS],
1318                        tempObjectFile,
1319                        gencmnFile);
1320
1321            result = runCommand(cmd);
1322            if (result != 0) {
1323                break;
1324            }
1325
1326            sprintf(buffer, "%s",tempObjectFile);
1327#endif
1328        } else {
1329            char newName[SMALL_BUFFER_MAX_SIZE];
1330            char dataName[SMALL_BUFFER_MAX_SIZE];
1331            char dataDirName[SMALL_BUFFER_MAX_SIZE];
1332            const char *pSubstring;
1333            file = list->str;
1334            name = listNames->str;
1335
1336            newName[0] = dataName[0] = 0;
1337            for (int32_t n = 0; n < DATA_PREFIX_LENGTH; n++) {
1338                dataDirName[0] = 0;
1339                sprintf(dataDirName, "%s%s", DATA_PREFIX[n], PKGDATA_FILE_SEP_STRING);
1340                /* If the name contains a prefix (indicating directory), alter the new name accordingly. */
1341                pSubstring = uprv_strstr(name, dataDirName);
1342                if (pSubstring != NULL) {
1343                    char newNameTmp[SMALL_BUFFER_MAX_SIZE] = "";
1344                    const char *p = name + uprv_strlen(dataDirName);
1345                    for (int32_t i = 0;;i++) {
1346                        if (p[i] == '.') {
1347                            newNameTmp[i] = '_';
1348                            continue;
1349                        }
1350                        newNameTmp[i] = p[i];
1351                        if (p[i] == 0) {
1352                            break;
1353                        }
1354                    }
1355                    sprintf(newName, "%s_%s",
1356                            DATA_PREFIX[n],
1357                            newNameTmp);
1358                    sprintf(dataName, "%s_%s",
1359                            o->shortName,
1360                            DATA_PREFIX[n]);
1361                }
1362                if (newName[0] != 0) {
1363                    break;
1364                }
1365            }
1366
1367            writeCCode(file, o->tmpDir, dataName[0] != 0 ? dataName : o->shortName, newName[0] != 0 ? newName : NULL, gencmnFile);
1368#ifdef USE_SINGLE_CCODE_FILE
1369            sprintf(cmd, "cat %s >> %s", gencmnFile, icudtAll);
1370
1371            result = runCommand(cmd);
1372            if (result != 0) {
1373                break;
1374            } else {
1375                /* Remove the c code file after concatenating it to icudtall.c file. */
1376                if ((result = remove(gencmnFile)) != 0) {
1377                    fprintf(stderr, "Unable to remove c code file: %s\n", gencmnFile);
1378                    return result;
1379                }
1380            }
1381#endif
1382        }
1383
1384#ifndef USE_SINGLE_CCODE_FILE
1385        uprv_strcpy(tempObjectFile, gencmnFile);
1386        tempObjectFile[uprv_strlen(tempObjectFile) - 1] = 'o';
1387
1388        sprintf(cmd, "%s %s -o %s %s",
1389                    pkgDataFlags[COMPILER],
1390                    pkgDataFlags[LIBFLAGS],
1391                    tempObjectFile,
1392                    gencmnFile);
1393        result = runCommand(cmd);
1394        if (result != 0) {
1395            break;
1396        }
1397
1398        uprv_strcat(buffer, " ");
1399        uprv_strcat(buffer, tempObjectFile);
1400
1401#endif
1402
1403        if (i > 0) {
1404            list = list->next;
1405            listNames = listNames->next;
1406        }
1407    }
1408
1409#ifdef USE_SINGLE_CCODE_FILE
1410    uprv_strcpy(tempObjectFile, icudtAll);
1411    tempObjectFile[uprv_strlen(tempObjectFile) - 1] = 'o';
1412
1413    sprintf(cmd, "%s %s -o %s %s",
1414        pkgDataFlags[COMPILER],
1415        pkgDataFlags[LIBFLAGS],
1416        tempObjectFile,
1417        icudtAll);
1418
1419    result = runCommand(cmd);
1420    if (result == 0) {
1421        uprv_strcat(buffer, " ");
1422        uprv_strcat(buffer, tempObjectFile);
1423    }
1424#endif
1425
1426    if (result == 0) {
1427        /* Generate the library file. */
1428        result = pkg_generateLibraryFile(targetDir, mode, buffer, cmd);
1429    }
1430
1431    uprv_free(buffer);
1432    uprv_free(cmd);
1433
1434    return result;
1435}
1436#endif
1437
1438#ifdef WINDOWS_WITH_MSVC
1439#define LINK_CMD "link.exe /nologo /release /out:"
1440#define LINK_FLAGS "/DLL /NOENTRY /MANIFEST:NO  /base:0x4ad00000 /implib:"
1441#define LIB_CMD "LIB.exe /nologo /out:"
1442#define LIB_FILE "icudt.lib"
1443#define LIB_EXT UDATA_LIB_SUFFIX
1444#define DLL_EXT UDATA_SO_SUFFIX
1445
1446static int32_t pkg_createWindowsDLL(const char mode, const char *gencFilePath, UPKGOptions *o) {
1447    char cmd[LARGE_BUFFER_MAX_SIZE];
1448    if (mode == MODE_STATIC) {
1449        char staticLibFilePath[SMALL_BUFFER_MAX_SIZE] = "";
1450
1451        uprv_strcpy(staticLibFilePath, o->tmpDir);
1452        uprv_strcat(staticLibFilePath, PKGDATA_FILE_SEP_STRING);
1453
1454        uprv_strcat(staticLibFilePath, o->entryName);
1455        uprv_strcat(staticLibFilePath, LIB_EXT);
1456
1457        sprintf(cmd, "%s\"%s\" \"%s\"",
1458                LIB_CMD,
1459                staticLibFilePath,
1460                gencFilePath);
1461    } else if (mode == MODE_DLL) {
1462        char dllFilePath[SMALL_BUFFER_MAX_SIZE] = "";
1463        char libFilePath[SMALL_BUFFER_MAX_SIZE] = "";
1464        char resFilePath[SMALL_BUFFER_MAX_SIZE] = "";
1465        char tmpResFilePath[SMALL_BUFFER_MAX_SIZE] = "";
1466
1467#ifdef CYGWINMSVC
1468        uprv_strcpy(dllFilePath, o->targetDir);
1469#else
1470        uprv_strcpy(dllFilePath, o->srcDir);
1471#endif
1472        uprv_strcat(dllFilePath, PKGDATA_FILE_SEP_STRING);
1473        uprv_strcpy(libFilePath, dllFilePath);
1474
1475#ifdef CYGWINMSVC
1476        uprv_strcat(libFilePath, o->libName);
1477        uprv_strcat(libFilePath, ".lib");
1478
1479        uprv_strcat(dllFilePath, o->libName);
1480        uprv_strcat(dllFilePath, o->version);
1481#else
1482        if (strstr(o->libName, "icudt")) {
1483            uprv_strcat(libFilePath, LIB_FILE);
1484        } else {
1485            uprv_strcat(libFilePath, o->libName);
1486            uprv_strcat(libFilePath, ".lib");
1487        }
1488        uprv_strcat(dllFilePath, o->entryName);
1489#endif
1490        uprv_strcat(dllFilePath, DLL_EXT);
1491
1492        uprv_strcpy(tmpResFilePath, o->tmpDir);
1493        uprv_strcat(tmpResFilePath, PKGDATA_FILE_SEP_STRING);
1494        uprv_strcat(tmpResFilePath, ICUDATA_RES_FILE);
1495
1496        if (T_FileStream_file_exists(tmpResFilePath)) {
1497            sprintf(resFilePath, "\"%s\"", tmpResFilePath);
1498        }
1499
1500        /* Check if dll file and lib file exists and that it is not newer than genc file. */
1501        if (!o->rebuild && (T_FileStream_file_exists(dllFilePath) && isFileModTimeLater(dllFilePath, gencFilePath)) &&
1502            (T_FileStream_file_exists(libFilePath) && isFileModTimeLater(libFilePath, gencFilePath))) {
1503          if(o->verbose) {
1504            printf("# Not rebuilding %s - up to date.\n", gencFilePath);
1505          }
1506          return 0;
1507        }
1508
1509        sprintf(cmd, "%s\"%s\" %s\"%s\" \"%s\" %s",
1510                LINK_CMD,
1511                dllFilePath,
1512                LINK_FLAGS,
1513                libFilePath,
1514                gencFilePath,
1515                resFilePath
1516                );
1517    }
1518
1519    return runCommand(cmd, TRUE);
1520}
1521#endif
1522
1523static UPKGOptions *pkg_checkFlag(UPKGOptions *o) {
1524#ifdef U_AIX
1525    /* AIX needs a map file. */
1526    char *flag = NULL;
1527    int32_t length = 0;
1528    char tmpbuffer[SMALL_BUFFER_MAX_SIZE];
1529    const char MAP_FILE_EXT[] = ".map";
1530    FileStream *f = NULL;
1531    char mapFile[SMALL_BUFFER_MAX_SIZE] = "";
1532    int32_t start = -1;
1533    int32_t count = 0;
1534
1535    flag = pkgDataFlags[BIR_FLAGS];
1536    length = uprv_strlen(pkgDataFlags[BIR_FLAGS]);
1537
1538    for (int32_t i = 0; i < length; i++) {
1539        if (flag[i] == MAP_FILE_EXT[count]) {
1540            if (count == 0) {
1541                start = i;
1542            }
1543            count++;
1544        } else {
1545            count = 0;
1546        }
1547
1548        if (count == uprv_strlen(MAP_FILE_EXT)) {
1549            break;
1550        }
1551    }
1552
1553    if (start >= 0) {
1554        int32_t index = 0;
1555        for (int32_t i = 0;;i++) {
1556            if (i == start) {
1557                for (int32_t n = 0;;n++) {
1558                    if (o->shortName[n] == 0) {
1559                        break;
1560                    }
1561                    tmpbuffer[index++] = o->shortName[n];
1562                }
1563            }
1564
1565            tmpbuffer[index++] = flag[i];
1566
1567            if (flag[i] == 0) {
1568                break;
1569            }
1570        }
1571
1572        uprv_memset(flag, 0, length);
1573        uprv_strcpy(flag, tmpbuffer);
1574
1575        uprv_strcpy(mapFile, o->shortName);
1576        uprv_strcat(mapFile, MAP_FILE_EXT);
1577
1578        f = T_FileStream_open(mapFile, "w");
1579        if (f == NULL) {
1580            fprintf(stderr,"Unable to create map file: %s.\n", mapFile);
1581        } else {
1582            sprintf(tmpbuffer, "%s%s ", o->entryName, UDATA_CMN_INTERMEDIATE_SUFFIX);
1583
1584            T_FileStream_writeLine(f, tmpbuffer);
1585
1586            T_FileStream_close(f);
1587        }
1588    }
1589#elif defined(U_CYGWIN)
1590    /* Cygwin needs to change flag options. */
1591    char *flag = NULL;
1592    int32_t length = 0;
1593
1594    flag = pkgDataFlags[GENLIB];
1595    length = uprv_strlen(pkgDataFlags[GENLIB]);
1596
1597    int32_t position = length - 1;
1598
1599    for(;position >= 0;position--) {
1600        if (flag[position] == '=') {
1601            position++;
1602            break;
1603        }
1604    }
1605
1606    uprv_memset(flag + position, 0, length - position);
1607#elif defined(OS400)
1608    /* OS400 needs to fix the ld options (swap single quote with double quote) */
1609    char *flag = NULL;
1610    int32_t length = 0;
1611
1612    flag = pkgDataFlags[GENLIB];
1613    length = uprv_strlen(pkgDataFlags[GENLIB]);
1614
1615    int32_t position = length - 1;
1616
1617    for(int32_t i = 0; i < length; i++) {
1618        if (flag[i] == '\'') {
1619            flag[i] = '\"';
1620        }
1621    }
1622#endif
1623    // Don't really need a return value, just need to stop compiler warnings about
1624    // the unused parameter 'o' on platforms where it is not otherwise used.
1625    return o;
1626}
1627
1628static void loadLists(UPKGOptions *o, UErrorCode *status)
1629{
1630    CharList   *l, *tail = NULL, *tail2 = NULL;
1631    FileStream *in;
1632    char        line[16384];
1633    char       *linePtr, *lineNext;
1634    const uint32_t   lineMax = 16300;
1635    char        tmp[1024];
1636    char       *s;
1637    int32_t     ln=0; /* line number */
1638
1639    for(l = o->fileListFiles; l; l = l->next) {
1640        if(o->verbose) {
1641            fprintf(stdout, "# pkgdata: Reading %s..\n", l->str);
1642        }
1643        /* TODO: stdin */
1644        in = T_FileStream_open(l->str, "r"); /* open files list */
1645
1646        if(!in) {
1647            fprintf(stderr, "Error opening <%s>.\n", l->str);
1648            *status = U_FILE_ACCESS_ERROR;
1649            return;
1650        }
1651
1652        while(T_FileStream_readLine(in, line, sizeof(line))!=NULL) { /* for each line */
1653            ln++;
1654            if(uprv_strlen(line)>lineMax) {
1655                fprintf(stderr, "%s:%d - line too long (over %d chars)\n", l->str, (int)ln, (int)lineMax);
1656                exit(1);
1657            }
1658            /* remove spaces at the beginning */
1659            linePtr = line;
1660            while(isspace(*linePtr)) {
1661                linePtr++;
1662            }
1663            s=linePtr;
1664            /* remove trailing newline characters */
1665            while(*s!=0) {
1666                if(*s=='\r' || *s=='\n') {
1667                    *s=0;
1668                    break;
1669                }
1670                ++s;
1671            }
1672            if((*linePtr == 0) || (*linePtr == '#')) {
1673                continue; /* comment or empty line */
1674            }
1675
1676            /* Now, process the line */
1677            lineNext = NULL;
1678
1679            while(linePtr && *linePtr) { /* process space-separated items */
1680                while(*linePtr == ' ') {
1681                    linePtr++;
1682                }
1683                /* Find the next quote */
1684                if(linePtr[0] == '"')
1685                {
1686                    lineNext = uprv_strchr(linePtr+1, '"');
1687                    if(lineNext == NULL) {
1688                        fprintf(stderr, "%s:%d - missing trailing double quote (\")\n",
1689                            l->str, (int)ln);
1690                        exit(1);
1691                    } else {
1692                        lineNext++;
1693                        if(*lineNext) {
1694                            if(*lineNext != ' ') {
1695                                fprintf(stderr, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n",
1696                                    l->str, (int)ln, (int)(lineNext-line), (*lineNext)?*lineNext:'0');
1697                                exit(1);
1698                            }
1699                            *lineNext = 0;
1700                            lineNext++;
1701                        }
1702                    }
1703                } else {
1704                    lineNext = uprv_strchr(linePtr, ' ');
1705                    if(lineNext) {
1706                        *lineNext = 0; /* terminate at space */
1707                        lineNext++;
1708                    }
1709                }
1710
1711                /* add the file */
1712                s = (char*)getLongPathname(linePtr);
1713
1714                /* normal mode.. o->files is just the bare list without package names */
1715                o->files = pkg_appendToList(o->files, &tail, uprv_strdup(linePtr));
1716                if(uprv_pathIsAbsolute(s)) {
1717                    fprintf(stderr, "pkgdata: Error: absolute path encountered. Old style paths are not supported. Use relative paths such as 'fur.res' or 'translit%cfur.res'.\n\tBad path: '%s'\n", U_FILE_SEP_CHAR, s);
1718                    exit(U_ILLEGAL_ARGUMENT_ERROR);
1719                }
1720                uprv_strcpy(tmp, o->srcDir);
1721                uprv_strcat(tmp, o->srcDir[uprv_strlen(o->srcDir)-1] == U_FILE_SEP_CHAR ? "" :PKGDATA_FILE_SEP_STRING);
1722                uprv_strcat(tmp, s);
1723                o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(tmp));
1724                linePtr = lineNext;
1725            } /* for each entry on line */
1726        } /* for each line */
1727        T_FileStream_close(in);
1728    } /* for each file list file */
1729}
1730
1731/* Try calling icu-config directly to get the option file. */
1732 static int32_t pkg_getOptionsFromICUConfig(UBool verbose, UOption *option) {
1733#if U_HAVE_POPEN
1734    FILE *p = NULL;
1735    size_t n;
1736    static char buf[512] = "";
1737    char cmdBuf[1024];
1738    UErrorCode status = U_ZERO_ERROR;
1739    const char cmd[] = "icu-config --incpkgdatafile";
1740
1741    /* #1 try the same path where pkgdata was called from. */
1742    findDirname(progname, cmdBuf, 1024, &status);
1743    if(U_SUCCESS(status)) {
1744      uprv_strncat(cmdBuf, U_FILE_SEP_STRING, 1024);
1745      uprv_strncat(cmdBuf, cmd, 1024);
1746
1747      if(verbose) {
1748        fprintf(stdout, "# Calling icu-config: %s\n", cmdBuf);
1749      }
1750      p = popen(cmdBuf, "r");
1751    }
1752
1753    if(p == NULL) {
1754      if(verbose) {
1755        fprintf(stdout, "# Calling icu-config: %s\n", cmd);
1756      }
1757      p = popen(cmd, "r");
1758    }
1759
1760    if(p == NULL)
1761    {
1762        fprintf(stderr, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname);
1763        return -1;
1764    }
1765
1766    n = fread(buf, 1, 511, p);
1767
1768    pclose(p);
1769
1770    if(n<=0)
1771    {
1772        fprintf(stderr,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname);
1773        return -1;
1774    }
1775
1776    for (int32_t length = strlen(buf) - 1; length >= 0; length--) {
1777        if (buf[length] == '\n' || buf[length] == ' ') {
1778            buf[length] = 0;
1779        } else {
1780            break;
1781        }
1782    }
1783
1784    if(buf[strlen(buf)-1]=='\n')
1785    {
1786        buf[strlen(buf)-1]=0;
1787    }
1788
1789    if(buf[0] == 0)
1790    {
1791        fprintf(stderr, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname);
1792        return -1;
1793    }
1794
1795    if(verbose) {
1796      fprintf(stdout, "# icu-config said: %s\n", buf);
1797    }
1798
1799    option->value = buf;
1800    option->doesOccur = TRUE;
1801
1802    return 0;
1803#endif
1804    return -1;
1805}
1806