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