]>
Commit | Line | Data |
---|---|---|
729e4ab9 A |
1 | /* |
2 | ****************************************************************************** | |
3 | * | |
b331163b | 4 | * Copyright (C) 2009-2015, International Business Machines |
729e4ab9 A |
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" | |
2ca993e8 A |
17 | |
18 | ||
19 | #if UCONFIG_ENABLE_PLUGINS | |
20 | ||
21 | ||
729e4ab9 A |
22 | #include "icuplugimp.h" |
23 | #include "cstring.h" | |
24 | #include "cmemory.h" | |
25 | #include "putilimp.h" | |
26 | #include "ucln.h" | |
27 | #include <stdio.h> | |
4388f060 A |
28 | #ifdef __MVS__ /* defined by z/OS compiler */ |
29 | #define _POSIX_SOURCE | |
30 | #include <cics.h> /* 12 Nov 2011 JAM iscics() function */ | |
31 | #endif | |
b331163b A |
32 | #include "charstr.h" |
33 | ||
34 | using namespace icu; | |
729e4ab9 A |
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 | */ | |
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 | */ | |
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 | ||
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 | |
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 | |
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 | |
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 | ||
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 | |
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 | */ | |
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 | ||
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 | ||
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 | ||
314 | static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) { | |
2ca993e8 A |
315 | if(U_FAILURE(*status)) { |
316 | return; | |
317 | } | |
729e4ab9 A |
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 | ||
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 | ||
361 | static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName, | |
362 | UErrorCode *status) { | |
2ca993e8 | 363 | UPlugData *plug = uplug_allocateEmptyPlug(status); |
729e4ab9 A |
364 | if(U_FAILURE(*status)) { |
365 | return NULL; | |
366 | } | |
367 | ||
729e4ab9 A |
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 | ||
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 | ||
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 | |
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 | |
439 | uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload) | |
440 | { | |
441 | data->dontUnload = dontUnload; | |
442 | } | |
443 | ||
444 | ||
445 | U_CAPI void U_EXPORT2 | |
446 | uplug_setPlugLevel(UPlugData *data, UPlugLevel level) { | |
447 | data->level = level; | |
448 | } | |
449 | ||
450 | ||
451 | U_CAPI UPlugLevel U_EXPORT2 | |
452 | uplug_getPlugLevel(UPlugData *data) { | |
453 | return data->level; | |
454 | } | |
455 | ||
456 | ||
457 | U_CAPI void U_EXPORT2 | |
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 | |
464 | uplug_getPlugName(UPlugData *data) { | |
465 | return data->name; | |
466 | } | |
467 | ||
468 | ||
469 | U_CAPI const char * U_EXPORT2 | |
470 | uplug_getSymbolName(UPlugData *data) { | |
471 | return data->sym; | |
472 | } | |
473 | ||
474 | U_CAPI const char * U_EXPORT2 | |
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 | |
488 | uplug_getLibrary(UPlugData *data) { | |
489 | return data->lib; | |
490 | } | |
491 | ||
492 | U_CAPI void * U_EXPORT2 | |
493 | uplug_getContext(UPlugData *data) { | |
494 | return data->context; | |
495 | } | |
496 | ||
497 | ||
498 | U_CAPI void U_EXPORT2 | |
499 | uplug_setContext(UPlugData *data, void *context) { | |
500 | data->context = context; | |
501 | } | |
502 | ||
503 | U_CAPI const char* U_EXPORT2 | |
504 | uplug_getConfiguration(UPlugData *data) { | |
505 | return data->config; | |
506 | } | |
507 | ||
508 | U_INTERNAL UPlugData* U_EXPORT2 | |
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 | |
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 | */ | |
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 | |
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 | ||
51004dcb | 550 | #if U_ENABLE_DYLOAD |
729e4ab9 A |
551 | |
552 | static UPlugData* | |
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 | */ | |
729e4ab9 A |
584 | static UPlugData* |
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; | |
4388f060 A |
592 | entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status); |
593 | ||
729e4ab9 A |
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 | |
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 | ||
b331163b A |
627 | static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW; |
628 | ||
729e4ab9 | 629 | U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() { |
b331163b | 630 | return gCurrentLevel; |
729e4ab9 A |
631 | } |
632 | ||
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? */ | |
b331163b | 646 | gCurrentLevel = UPLUG_LEVEL_LOW; |
729e4ab9 A |
647 | return TRUE; |
648 | } | |
649 | ||
51004dcb A |
650 | #if U_ENABLE_DYLOAD |
651 | ||
729e4ab9 A |
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 | } | |
4388f060 | 682 | } |
729e4ab9 A |
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 | ||
729e4ab9 A |
704 | /* Name of the plugin config file */ |
705 | static char plugin_file[2048] = ""; | |
706 | #endif | |
707 | ||
708 | U_INTERNAL const char* U_EXPORT2 | |
709 | uplug_getPluginFile() { | |
b331163b | 710 | #if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO |
729e4ab9 A |
711 | return plugin_file; |
712 | #else | |
713 | return NULL; | |
714 | #endif | |
715 | } | |
716 | ||
717 | ||
b331163b A |
718 | // uplug_init() is called first thing from u_init(). |
719 | ||
729e4ab9 A |
720 | U_CAPI void U_EXPORT2 |
721 | uplug_init(UErrorCode *status) { | |
722 | #if !U_ENABLE_DYLOAD | |
723 | (void)status; /* unused */ | |
b331163b A |
724 | #elif !UCONFIG_NO_FILE_IO |
725 | CharString plugin_dir; | |
726 | const char *env = getenv("ICU_PLUGINS"); | |
729e4ab9 A |
727 | |
728 | if(U_FAILURE(*status)) return; | |
b331163b A |
729 | if(env != NULL) { |
730 | plugin_dir.append(env, -1, *status); | |
731 | } | |
732 | if(U_FAILURE(*status)) return; | |
729e4ab9 A |
733 | |
734 | #if defined(DEFAULT_ICU_PLUGINS) | |
b331163b A |
735 | if(plugin_dir.isEmpty()) { |
736 | plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status); | |
729e4ab9 A |
737 | } |
738 | #endif | |
739 | ||
740 | #if UPLUG_TRACE | |
b331163b | 741 | DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir.data())); |
729e4ab9 A |
742 | #endif |
743 | ||
b331163b | 744 | if(!plugin_dir.isEmpty()) { |
729e4ab9 A |
745 | FILE *f; |
746 | ||
b331163b | 747 | CharString pluginFile; |
4388f060 A |
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 | ||
b331163b | 756 | pluginFile.append("//DD:ICUPLUG", -1, *status); /* JAM 20 Oct 2011 */ |
4388f060 | 757 | #else |
b331163b A |
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); | |
4388f060 | 763 | #endif |
b331163b A |
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)); | |
729e4ab9 A |
784 | |
785 | #if UPLUG_TRACE | |
b331163b | 786 | DBG((stderr, "pluginfile= %s len %d/%d\n", plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file))); |
729e4ab9 A |
787 | #endif |
788 | ||
4388f060 A |
789 | #ifdef __MVS__ |
790 | if (iscics()) /* 12 Nov 2011 JAM */ | |
791 | { | |
792 | f = NULL; | |
793 | } | |
794 | else | |
795 | #endif | |
796 | { | |
b331163b | 797 | f = fopen(pluginFile.data(), "r"); |
4388f060 | 798 | } |
729e4ab9 A |
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; | |
4388f060 | 813 | while(*p&&isspace((int)*p)) |
729e4ab9 A |
814 | p++; |
815 | if(!*p || *p=='#') continue; | |
816 | libName = p; | |
4388f060 | 817 | while(*p&&!isspace((int)*p)) { |
729e4ab9 A |
818 | p++; |
819 | } | |
820 | if(!*p || *p=='#') continue; /* no tab after libname */ | |
821 | *p=0; /* end of libname */ | |
822 | p++; | |
4388f060 | 823 | while(*p&&isspace((int)*p)) { |
729e4ab9 A |
824 | p++; |
825 | } | |
826 | if(!*p||*p=='#') continue; /* no symname after libname +tab */ | |
827 | symName = p; | |
4388f060 | 828 | while(*p&&!isspace((int)*p)) { |
729e4ab9 A |
829 | p++; |
830 | } | |
831 | ||
832 | if(*p) { /* has config */ | |
833 | *p=0; | |
834 | ++p; | |
4388f060 | 835 | while(*p&&isspace((int)*p)) { |
729e4ab9 A |
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); | |
4388f060 | 846 | while(p>config&&isspace((int)*(--p))) { |
729e4ab9 A |
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 | } | |
4388f060 | 867 | fclose(f); |
729e4ab9 A |
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 */ | |
b331163b | 876 | gCurrentLevel = UPLUG_LEVEL_HIGH; |
729e4ab9 A |
877 | ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup); |
878 | } | |
2ca993e8 A |
879 | |
880 | #endif | |
881 | ||
882 |