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