Home | History | Annotate | Download | only in common
      1 /*
      2 ******************************************************************************
      3 *
      4 *   Copyright (C) 2009-2012, International Business Machines
      5 *   Corporation and others.  All Rights Reserved.
      6 *
      7 ******************************************************************************
      8 *
      9 *  FILE NAME : icuplug.c
     10 *
     11 *   Date         Name        Description
     12 *   10/29/2009   sl          New.
     13 ******************************************************************************
     14 */
     15 
     16 #include "unicode/icuplug.h"
     17 #include "icuplugimp.h"
     18 #include "cstring.h"
     19 #include "cmemory.h"
     20 #include "putilimp.h"
     21 #include "ucln.h"
     22 #include <stdio.h>
     23 #ifdef __MVS__  /* defined by z/OS compiler */
     24 #define _POSIX_SOURCE
     25 #include <cics.h> /* 12 Nov 2011 JAM iscics() function */
     26 #endif
     27 
     28 #ifndef UPLUG_TRACE
     29 #define UPLUG_TRACE 0
     30 #endif
     31 
     32 #if UPLUG_TRACE
     33 #include <stdio.h>
     34 #define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x
     35 #endif
     36 
     37 /**
     38  * Internal structure of an ICU plugin.
     39  */
     40 
     41 struct UPlugData {
     42   UPlugEntrypoint  *entrypoint; /**< plugin entrypoint */
     43   uint32_t structSize;    /**< initialized to the size of this structure */
     44   uint32_t token;         /**< must be U_PLUG_TOKEN */
     45   void *lib;              /**< plugin library, or NULL */
     46   char libName[UPLUG_NAME_MAX];   /**< library name */
     47   char sym[UPLUG_NAME_MAX];        /**< plugin symbol, or NULL */
     48   char config[UPLUG_NAME_MAX];     /**< configuration data */
     49   void *context;          /**< user context data */
     50   char name[UPLUG_NAME_MAX];   /**< name of plugin */
     51   UPlugLevel  level; /**< level of plugin */
     52   UBool   awaitingLoad; /**< TRUE if the plugin is awaiting a load call */
     53   UBool   dontUnload; /**< TRUE if plugin must stay resident (leak plugin and lib) */
     54   UErrorCode pluginStatus; /**< status code of plugin */
     55 };
     56 
     57 
     58 
     59 #define UPLUG_LIBRARY_INITIAL_COUNT 8
     60 #define UPLUG_PLUGIN_INITIAL_COUNT 12
     61 
     62 /**
     63  * Remove an item
     64  * @param list the full list
     65  * @param listSize the number of entries in the list
     66  * @param memberSize the size of one member
     67  * @param itemToRemove the item number of the member
     68  * @return the new listsize
     69  */
     70 static int32_t uplug_removeEntryAt(void *list, int32_t listSize, int32_t memberSize, int32_t itemToRemove) {
     71   uint8_t *bytePtr = (uint8_t *)list;
     72 
     73   /* get rid of some bad cases first */
     74   if(listSize<1) {
     75     return listSize;
     76   }
     77 
     78   /* is there anything to move? */
     79   if(listSize > itemToRemove+1) {
     80     memmove(bytePtr+(itemToRemove*memberSize), bytePtr+((itemToRemove+1)*memberSize), memberSize);
     81   }
     82 
     83   return listSize-1;
     84 }
     85 
     86 
     87 
     88 
     89 #if U_ENABLE_DYLOAD
     90 /**
     91  * Library management. Internal.
     92  * @internal
     93  */
     94 struct UPlugLibrary;
     95 
     96 /**
     97  * Library management. Internal.
     98  * @internal
     99  */
    100 typedef struct UPlugLibrary {
    101   void *lib;                           /**< library ptr */
    102   char name[UPLUG_NAME_MAX]; /**< library name */
    103   uint32_t ref;                        /**< reference count */
    104 } UPlugLibrary;
    105 
    106 static UPlugLibrary   staticLibraryList[UPLUG_LIBRARY_INITIAL_COUNT];
    107 static UPlugLibrary * libraryList = staticLibraryList;
    108 static int32_t libraryCount = 0;
    109 static int32_t libraryMax = UPLUG_LIBRARY_INITIAL_COUNT;
    110 
    111 /**
    112  * Search for a library. Doesn't lock
    113  * @param libName libname to search for
    114  * @return the library's struct
    115  */
    116 static int32_t searchForLibraryName(const char *libName) {
    117   int32_t i;
    118 
    119   for(i=0;i<libraryCount;i++) {
    120     if(!uprv_strcmp(libName, libraryList[i].name)) {
    121       return i;
    122     }
    123   }
    124   return -1;
    125 }
    126 
    127 static int32_t searchForLibrary(void *lib) {
    128   int32_t i;
    129 
    130   for(i=0;i<libraryCount;i++) {
    131     if(lib==libraryList[i].lib) {
    132       return i;
    133     }
    134   }
    135   return -1;
    136 }
    137 
    138 U_INTERNAL char * U_EXPORT2
    139 uplug_findLibrary(void *lib, UErrorCode *status) {
    140   int32_t libEnt;
    141   char *ret = NULL;
    142   if(U_FAILURE(*status)) {
    143     return NULL;
    144   }
    145   libEnt = searchForLibrary(lib);
    146   if(libEnt!=-1) {
    147     ret = libraryList[libEnt].name;
    148   } else {
    149     *status = U_MISSING_RESOURCE_ERROR;
    150   }
    151   return ret;
    152 }
    153 
    154 U_INTERNAL void * U_EXPORT2
    155 uplug_openLibrary(const char *libName, UErrorCode *status) {
    156   int32_t libEntry = -1;
    157   void *lib = NULL;
    158 
    159   if(U_FAILURE(*status)) return NULL;
    160 
    161   libEntry = searchForLibraryName(libName);
    162   if(libEntry == -1) {
    163     libEntry = libraryCount++;
    164     if(libraryCount >= libraryMax) {
    165       /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */
    166       *status = U_MEMORY_ALLOCATION_ERROR;
    167 #if UPLUG_TRACE
    168       DBG((stderr, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax));
    169 #endif
    170       return NULL;
    171     }
    172     /* Some operating systems don't want
    173        DL operations from multiple threads. */
    174     libraryList[libEntry].lib = uprv_dl_open(libName, status);
    175 #if UPLUG_TRACE
    176     DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
    177 #endif
    178 
    179     if(libraryList[libEntry].lib == NULL || U_FAILURE(*status)) {
    180       /* cleanup. */
    181       libraryList[libEntry].lib = NULL; /* failure with open */
    182       libraryList[libEntry].name[0] = 0;
    183 #if UPLUG_TRACE
    184       DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
    185 #endif
    186       /* no need to free - just won't increase the count. */
    187       libraryCount--;
    188     } else { /* is it still there? */
    189       /* link it in */
    190       uprv_strncpy(libraryList[libEntry].name,libName,UPLUG_NAME_MAX);
    191       libraryList[libEntry].ref=1;
    192       lib = libraryList[libEntry].lib;
    193     }
    194 
    195   } else {
    196     lib = libraryList[libEntry].lib;
    197     libraryList[libEntry].ref++;
    198   }
    199   return lib;
    200 }
    201 
    202 U_INTERNAL void U_EXPORT2
    203 uplug_closeLibrary(void *lib, UErrorCode *status) {
    204   int32_t i;
    205 
    206 #if UPLUG_TRACE
    207   DBG((stderr, "uplug_closeLibrary(%p,%s) list %p\n", lib, u_errorName(*status), (void*)libraryList));
    208 #endif
    209   if(U_FAILURE(*status)) return;
    210 
    211   for(i=0;i<libraryCount;i++) {
    212     if(lib==libraryList[i].lib) {
    213       if(--(libraryList[i].ref) == 0) {
    214         uprv_dl_close(libraryList[i].lib, status);
    215         libraryCount = uplug_removeEntryAt(libraryList, libraryCount, sizeof(*libraryList), i);
    216       }
    217       return;
    218     }
    219   }
    220   *status = U_INTERNAL_PROGRAM_ERROR; /* could not find the entry! */
    221 }
    222 
    223 #endif
    224 
    225 static UPlugData pluginList[UPLUG_PLUGIN_INITIAL_COUNT];
    226 static int32_t pluginCount = 0;
    227 
    228 
    229 
    230 
    231 static int32_t uplug_pluginNumber(UPlugData* d) {
    232   UPlugData *pastPlug = &pluginList[pluginCount];
    233   if(d<=pluginList) {
    234     return 0;
    235   } else if(d>=pastPlug) {
    236     return pluginCount;
    237   } else {
    238     return (d-pluginList)/sizeof(pluginList[0]);
    239   }
    240 }
    241 
    242 
    243 U_CAPI UPlugData * U_EXPORT2
    244 uplug_nextPlug(UPlugData *prior) {
    245   if(prior==NULL) {
    246     return pluginList;
    247   } else {
    248     UPlugData *nextPlug = &prior[1];
    249     UPlugData *pastPlug = &pluginList[pluginCount];
    250 
    251     if(nextPlug>=pastPlug) {
    252       return NULL;
    253     } else {
    254       return nextPlug;
    255     }
    256   }
    257 }
    258 
    259 
    260 
    261 /**
    262  * Call the plugin with some params
    263  */
    264 static void uplug_callPlug(UPlugData *plug, UPlugReason reason, UErrorCode *status) {
    265   UPlugTokenReturn token;
    266   if(plug==NULL||U_FAILURE(*status)) {
    267     return;
    268   }
    269   token = (*(plug->entrypoint))(plug, reason, status);
    270   if(token!=UPLUG_TOKEN) {
    271     *status = U_INTERNAL_PROGRAM_ERROR;
    272   }
    273 }
    274 
    275 
    276 static void uplug_unloadPlug(UPlugData *plug, UErrorCode *status) {
    277   if(plug->awaitingLoad) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    278     *status = U_INTERNAL_PROGRAM_ERROR;
    279     return;
    280   }
    281   if(U_SUCCESS(plug->pluginStatus)) {
    282     /* Don't unload a plug which has a failing load status - means it didn't actually load. */
    283     uplug_callPlug(plug, UPLUG_REASON_UNLOAD, status);
    284   }
    285 }
    286 
    287 static void uplug_queryPlug(UPlugData *plug, UErrorCode *status) {
    288   if(!plug->awaitingLoad || !(plug->level == UPLUG_LEVEL_UNKNOWN) ) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    289     *status = U_INTERNAL_PROGRAM_ERROR;
    290     return;
    291   }
    292   plug->level = UPLUG_LEVEL_INVALID;
    293   uplug_callPlug(plug, UPLUG_REASON_QUERY, status);
    294   if(U_SUCCESS(*status)) {
    295     if(plug->level == UPLUG_LEVEL_INVALID) {
    296       plug->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
    297       plug->awaitingLoad = FALSE;
    298     }
    299   } else {
    300     plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
    301     plug->awaitingLoad = FALSE;
    302   }
    303 }
    304 
    305 
    306 static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) {
    307   if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    308     *status = U_INTERNAL_PROGRAM_ERROR;
    309     return;
    310   }
    311   uplug_callPlug(plug, UPLUG_REASON_LOAD, status);
    312   plug->awaitingLoad = FALSE;
    313   if(!U_SUCCESS(*status)) {
    314     plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
    315   }
    316 }
    317 
    318 static UPlugData *uplug_allocateEmptyPlug(UErrorCode *status)
    319 {
    320   UPlugData *plug = NULL;
    321 
    322   if(U_FAILURE(*status)) {
    323     return NULL;
    324   }
    325 
    326   if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) {
    327     *status = U_MEMORY_ALLOCATION_ERROR;
    328     return NULL;
    329   }
    330 
    331   plug = &pluginList[pluginCount++];
    332 
    333   plug->token = UPLUG_TOKEN;
    334   plug->structSize = sizeof(UPlugData);
    335   plug->name[0]=0;
    336   plug->level = UPLUG_LEVEL_UNKNOWN; /* initialize to null state */
    337   plug->awaitingLoad = TRUE;
    338   plug->dontUnload = FALSE;
    339   plug->pluginStatus = U_ZERO_ERROR;
    340   plug->libName[0] = 0;
    341   plug->config[0]=0;
    342   plug->sym[0]=0;
    343   plug->lib=NULL;
    344   plug->entrypoint=NULL;
    345 
    346 
    347   return plug;
    348 }
    349 
    350 static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName,
    351                                      UErrorCode *status) {
    352   UPlugData *plug;
    353 
    354   if(U_FAILURE(*status)) {
    355     return NULL;
    356   }
    357 
    358   plug = uplug_allocateEmptyPlug(status);
    359   if(config!=NULL) {
    360     uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
    361   } else {
    362     plug->config[0] = 0;
    363   }
    364 
    365   if(symName!=NULL) {
    366     uprv_strncpy(plug->sym, symName, UPLUG_NAME_MAX);
    367   } else {
    368     plug->sym[0] = 0;
    369   }
    370 
    371   plug->entrypoint = entrypoint;
    372   plug->lib = lib;
    373   uplug_queryPlug(plug, status);
    374 
    375   return plug;
    376 }
    377 
    378 static void uplug_deallocatePlug(UPlugData *plug, UErrorCode *status) {
    379   UErrorCode subStatus = U_ZERO_ERROR;
    380   if(!plug->dontUnload) {
    381 #if U_ENABLE_DYLOAD
    382     uplug_closeLibrary(plug->lib, &subStatus);
    383 #endif
    384   }
    385   plug->lib = NULL;
    386   if(U_SUCCESS(*status) && U_FAILURE(subStatus)) {
    387     *status = subStatus;
    388   }
    389   /* shift plugins up and decrement count. */
    390   if(U_SUCCESS(*status)) {
    391     /* all ok- remove. */
    392     pluginCount = uplug_removeEntryAt(pluginList, pluginCount, sizeof(plug[0]), uplug_pluginNumber(plug));
    393   } else {
    394     /* not ok- leave as a message. */
    395     plug->awaitingLoad=FALSE;
    396     plug->entrypoint=0;
    397     plug->dontUnload=TRUE;
    398   }
    399 }
    400 
    401 static void uplug_doUnloadPlug(UPlugData *plugToRemove, UErrorCode *status) {
    402   if(plugToRemove != NULL) {
    403     uplug_unloadPlug(plugToRemove, status);
    404     uplug_deallocatePlug(plugToRemove, status);
    405   }
    406 }
    407 
    408 U_CAPI void U_EXPORT2
    409 uplug_removePlug(UPlugData *plug, UErrorCode *status)  {
    410   UPlugData *cursor = NULL;
    411   UPlugData *plugToRemove = NULL;
    412   if(U_FAILURE(*status)) return;
    413 
    414   for(cursor=pluginList;cursor!=NULL;) {
    415     if(cursor==plug) {
    416       plugToRemove = plug;
    417       cursor=NULL;
    418     } else {
    419       cursor = uplug_nextPlug(cursor);
    420     }
    421   }
    422 
    423   uplug_doUnloadPlug(plugToRemove, status);
    424 }
    425 
    426 
    427 
    428 
    429 U_CAPI void U_EXPORT2
    430 uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload)
    431 {
    432   data->dontUnload = dontUnload;
    433 }
    434 
    435 
    436 U_CAPI void U_EXPORT2
    437 uplug_setPlugLevel(UPlugData *data, UPlugLevel level) {
    438   data->level = level;
    439 }
    440 
    441 
    442 U_CAPI UPlugLevel U_EXPORT2
    443 uplug_getPlugLevel(UPlugData *data) {
    444   return data->level;
    445 }
    446 
    447 
    448 U_CAPI void U_EXPORT2
    449 uplug_setPlugName(UPlugData *data, const char *name) {
    450   uprv_strncpy(data->name, name, UPLUG_NAME_MAX);
    451 }
    452 
    453 
    454 U_CAPI const char * U_EXPORT2
    455 uplug_getPlugName(UPlugData *data) {
    456   return data->name;
    457 }
    458 
    459 
    460 U_CAPI const char * U_EXPORT2
    461 uplug_getSymbolName(UPlugData *data) {
    462   return data->sym;
    463 }
    464 
    465 U_CAPI const char * U_EXPORT2
    466 uplug_getLibraryName(UPlugData *data, UErrorCode *status) {
    467   if(data->libName[0]) {
    468     return data->libName;
    469   } else {
    470 #if U_ENABLE_DYLOAD
    471     return uplug_findLibrary(data->lib, status);
    472 #else
    473     return NULL;
    474 #endif
    475   }
    476 }
    477 
    478 U_CAPI void * U_EXPORT2
    479 uplug_getLibrary(UPlugData *data) {
    480   return data->lib;
    481 }
    482 
    483 U_CAPI void * U_EXPORT2
    484 uplug_getContext(UPlugData *data) {
    485   return data->context;
    486 }
    487 
    488 
    489 U_CAPI void U_EXPORT2
    490 uplug_setContext(UPlugData *data, void *context) {
    491   data->context = context;
    492 }
    493 
    494 U_CAPI const char* U_EXPORT2
    495 uplug_getConfiguration(UPlugData *data) {
    496   return data->config;
    497 }
    498 
    499 U_INTERNAL UPlugData* U_EXPORT2
    500 uplug_getPlugInternal(int32_t n) {
    501   if(n <0 || n >= pluginCount) {
    502     return NULL;
    503   } else {
    504     return &(pluginList[n]);
    505   }
    506 }
    507 
    508 
    509 U_CAPI UErrorCode U_EXPORT2
    510 uplug_getPlugLoadStatus(UPlugData *plug) {
    511   return plug->pluginStatus;
    512 }
    513 
    514 
    515 
    516 
    517 /**
    518  * Initialize a plugin fron an entrypoint and library - but don't load it.
    519  */
    520 static UPlugData* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *sym,
    521                                                          UErrorCode *status) {
    522   UPlugData *plug = NULL;
    523 
    524   plug = uplug_allocatePlug(entrypoint, config, lib, sym, status);
    525 
    526   if(U_SUCCESS(*status)) {
    527     return plug;
    528   } else {
    529     uplug_deallocatePlug(plug, status);
    530     return NULL;
    531   }
    532 }
    533 
    534 U_CAPI UPlugData* U_EXPORT2
    535 uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status) {
    536   UPlugData* plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, NULL, NULL, status);
    537   uplug_loadPlug(plug, status);
    538   return plug;
    539 }
    540 
    541 #if U_ENABLE_DYLOAD
    542 
    543 static UPlugData*
    544 uplug_initErrorPlug(const char *libName, const char *sym, const char *config, const char *nameOrError, UErrorCode loadStatus, UErrorCode *status)
    545 {
    546   UPlugData *plug = uplug_allocateEmptyPlug(status);
    547   if(U_FAILURE(*status)) return NULL;
    548 
    549   plug->pluginStatus = loadStatus;
    550   plug->awaitingLoad = FALSE; /* Won't load. */
    551   plug->dontUnload = TRUE; /* cannot unload. */
    552 
    553   if(sym!=NULL) {
    554     uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX);
    555   }
    556 
    557   if(libName!=NULL) {
    558     uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX);
    559   }
    560 
    561   if(nameOrError!=NULL) {
    562     uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX);
    563   }
    564 
    565   if(config!=NULL) {
    566     uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
    567   }
    568 
    569   return plug;
    570 }
    571 
    572 /**
    573  * Fetch a plugin from DLL, and then initialize it from a library- but don't load it.
    574  */
    575 static UPlugData*
    576 uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
    577   void *lib = NULL;
    578   UPlugData *plug = NULL;
    579   if(U_FAILURE(*status)) { return NULL; }
    580   lib = uplug_openLibrary(libName, status);
    581   if(lib!=NULL && U_SUCCESS(*status)) {
    582     UPlugEntrypoint *entrypoint = NULL;
    583     entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status);
    584 
    585     if(entrypoint!=NULL&&U_SUCCESS(*status)) {
    586       plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status);
    587       if(plug!=NULL&&U_SUCCESS(*status)) {
    588         plug->lib = lib; /* plug takes ownership of library */
    589         lib = NULL; /* library is now owned by plugin. */
    590       }
    591     } else {
    592       UErrorCode subStatus = U_ZERO_ERROR;
    593       plug = uplug_initErrorPlug(libName,sym,config,"ERROR: Could not load entrypoint",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
    594     }
    595     if(lib!=NULL) { /* still need to close the lib */
    596       UErrorCode subStatus = U_ZERO_ERROR;
    597       uplug_closeLibrary(lib, &subStatus); /* don't care here */
    598     }
    599   } else {
    600     UErrorCode subStatus = U_ZERO_ERROR;
    601     plug = uplug_initErrorPlug(libName,sym,config,"ERROR: could not load library",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
    602   }
    603   return plug;
    604 }
    605 
    606 U_CAPI UPlugData* U_EXPORT2
    607 uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
    608   UPlugData *plug = NULL;
    609   if(U_FAILURE(*status)) { return NULL; }
    610   plug = uplug_initPlugFromLibrary(libName, sym, config, status);
    611   uplug_loadPlug(plug, status);
    612 
    613   return plug;
    614 }
    615 
    616 #endif
    617 
    618 U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() {
    619   if(cmemory_inUse()) {
    620     return UPLUG_LEVEL_HIGH;
    621   } else {
    622     return UPLUG_LEVEL_LOW;
    623   }
    624 }
    625 
    626 static UBool U_CALLCONV uplug_cleanup(void)
    627 {
    628   int32_t i;
    629 
    630   UPlugData *pluginToRemove;
    631   /* cleanup plugs */
    632   for(i=0;i<pluginCount;i++) {
    633     UErrorCode subStatus = U_ZERO_ERROR;
    634     pluginToRemove = &pluginList[i];
    635     /* unload and deallocate */
    636     uplug_doUnloadPlug(pluginToRemove, &subStatus);
    637   }
    638   /* close other held libs? */
    639   return TRUE;
    640 }
    641 
    642 #if U_ENABLE_DYLOAD
    643 
    644 static void uplug_loadWaitingPlugs(UErrorCode *status) {
    645   int32_t i;
    646   UPlugLevel currentLevel = uplug_getCurrentLevel();
    647 
    648   if(U_FAILURE(*status)) {
    649     return;
    650   }
    651 #if UPLUG_TRACE
    652   DBG((stderr,  "uplug_loadWaitingPlugs() Level: %d\n", currentLevel));
    653 #endif
    654   /* pass #1: low level plugs */
    655   for(i=0;i<pluginCount;i++) {
    656     UErrorCode subStatus = U_ZERO_ERROR;
    657     UPlugData *pluginToLoad = &pluginList[i];
    658     if(pluginToLoad->awaitingLoad) {
    659       if(pluginToLoad->level == UPLUG_LEVEL_LOW) {
    660         if(currentLevel > UPLUG_LEVEL_LOW) {
    661           pluginToLoad->pluginStatus = U_PLUGIN_TOO_HIGH;
    662         } else {
    663           UPlugLevel newLevel;
    664           uplug_loadPlug(pluginToLoad, &subStatus);
    665           newLevel = uplug_getCurrentLevel();
    666           if(newLevel > currentLevel) {
    667             pluginToLoad->pluginStatus = U_PLUGIN_CHANGED_LEVEL_WARNING;
    668             currentLevel = newLevel;
    669           }
    670         }
    671         pluginToLoad->awaitingLoad = FALSE;
    672       }
    673     }
    674   }
    675   for(i=0;i<pluginCount;i++) {
    676     UErrorCode subStatus = U_ZERO_ERROR;
    677     UPlugData *pluginToLoad = &pluginList[i];
    678 
    679     if(pluginToLoad->awaitingLoad) {
    680       if(pluginToLoad->level == UPLUG_LEVEL_INVALID) {
    681         pluginToLoad->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
    682       } else if(pluginToLoad->level == UPLUG_LEVEL_UNKNOWN) {
    683         pluginToLoad->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
    684       } else {
    685         uplug_loadPlug(pluginToLoad, &subStatus);
    686       }
    687       pluginToLoad->awaitingLoad = FALSE;
    688     }
    689   }
    690 
    691 #if UPLUG_TRACE
    692   DBG((stderr,  " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel()));
    693 #endif
    694 }
    695 
    696 /* Name of the plugin config file */
    697 static char plugin_file[2048] = "";
    698 #endif
    699 
    700 U_INTERNAL const char* U_EXPORT2
    701 uplug_getPluginFile() {
    702 #if U_ENABLE_DYLOAD
    703   return plugin_file;
    704 #else
    705   return NULL;
    706 #endif
    707 }
    708 
    709 
    710 U_CAPI void U_EXPORT2
    711 uplug_init(UErrorCode *status) {
    712 #if !U_ENABLE_DYLOAD
    713   (void)status; /* unused */
    714 #else
    715   const char *plugin_dir;
    716 
    717   if(U_FAILURE(*status)) return;
    718   plugin_dir = getenv("ICU_PLUGINS");
    719 
    720 #if defined(DEFAULT_ICU_PLUGINS)
    721   if(plugin_dir == NULL || !*plugin_dir) {
    722     plugin_dir = DEFAULT_ICU_PLUGINS;
    723   }
    724 #endif
    725 
    726 #if UPLUG_TRACE
    727   DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir));
    728 #endif
    729 
    730   if(plugin_dir != NULL && *plugin_dir) {
    731     FILE *f;
    732 
    733 
    734 #ifdef OS390BATCH
    735 /* There are potentially a lot of ways to implement a plugin directory on OS390/zOS  */
    736 /* Keeping in mind that unauthorized file access is logged, monitored, and enforced  */
    737 /* I've chosen to open a DDNAME if BATCH and leave it alone for (presumably) UNIX    */
    738 /* System Services.  Alternative techniques might be allocating a member in          */
    739 /* SYS1.PARMLIB or setting an environment variable "ICU_PLUGIN_PATH" (?).  The       */
    740 /* DDNAME can be connected to a file in the HFS if need be.                          */
    741 
    742     uprv_strncpy(plugin_file,"//DD:ICUPLUG", 2047);        /* JAM 20 Oct 2011 */
    743 #else
    744     uprv_strncpy(plugin_file, plugin_dir, 2047);
    745     uprv_strncat(plugin_file, U_FILE_SEP_STRING,2047);
    746     uprv_strncat(plugin_file, "icuplugins",2047);
    747     uprv_strncat(plugin_file, U_ICU_VERSION_SHORT ,2047);
    748     uprv_strncat(plugin_file, ".txt" ,2047);
    749 #endif
    750 
    751 #if UPLUG_TRACE
    752     DBG((stderr, "pluginfile= %s\n", plugin_file));
    753 #endif
    754 
    755 #ifdef __MVS__
    756     if (iscics()) /* 12 Nov 2011 JAM */
    757     {
    758         f = NULL;
    759     }
    760     else
    761 #endif
    762     {
    763          f = fopen(plugin_file, "r");
    764     }
    765 
    766     if(f != NULL) {
    767       char linebuf[1024];
    768       char *p, *libName=NULL, *symName=NULL, *config=NULL;
    769       int32_t line = 0;
    770 
    771 
    772       while(fgets(linebuf,1023,f)) {
    773         line++;
    774 
    775         if(!*linebuf || *linebuf=='#') {
    776           continue;
    777         } else {
    778           p = linebuf;
    779           while(*p&&isspace((int)*p))
    780             p++;
    781           if(!*p || *p=='#') continue;
    782           libName = p;
    783           while(*p&&!isspace((int)*p)) {
    784             p++;
    785           }
    786           if(!*p || *p=='#') continue; /* no tab after libname */
    787           *p=0; /* end of libname */
    788           p++;
    789           while(*p&&isspace((int)*p)) {
    790             p++;
    791           }
    792           if(!*p||*p=='#') continue; /* no symname after libname +tab */
    793           symName = p;
    794           while(*p&&!isspace((int)*p)) {
    795             p++;
    796           }
    797 
    798           if(*p) { /* has config */
    799             *p=0;
    800             ++p;
    801             while(*p&&isspace((int)*p)) {
    802               p++;
    803             }
    804             if(*p) {
    805               config = p;
    806             }
    807           }
    808 
    809           /* chop whitespace at the end of the config */
    810           if(config!=NULL&&*config!=0) {
    811             p = config+strlen(config);
    812             while(p>config&&isspace((int)*(--p))) {
    813               *p=0;
    814             }
    815           }
    816 
    817           /* OK, we're good. */
    818           {
    819             UErrorCode subStatus = U_ZERO_ERROR;
    820             UPlugData *plug = uplug_initPlugFromLibrary(libName, symName, config, &subStatus);
    821             if(U_FAILURE(subStatus) && U_SUCCESS(*status)) {
    822               *status = subStatus;
    823             }
    824 #if UPLUG_TRACE
    825             DBG((stderr, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName, symName, config));
    826             DBG((stderr, " -> %p, %s\n", (void*)plug, u_errorName(subStatus)));
    827 #else
    828             (void)plug; /* unused */
    829 #endif
    830           }
    831         }
    832       }
    833       fclose(f);
    834     } else {
    835 #if UPLUG_TRACE
    836       DBG((stderr, "Can't open plugin file %s\n", plugin_file));
    837 #endif
    838     }
    839   }
    840   uplug_loadWaitingPlugs(status);
    841 #endif /* U_ENABLE_DYLOAD */
    842   ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup);
    843 }
    844