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