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