1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html |
3 | /* |
4 | ****************************************************************************** |
5 | * |
6 | * Copyright (C) 2009-2015, International Business Machines |
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" |
19 | |
20 | |
21 | #if UCONFIG_ENABLE_PLUGINS |
22 | |
23 | |
24 | #include "icuplugimp.h" |
25 | #include "cstring.h" |
26 | #include "cmemory.h" |
27 | #include "putilimp.h" |
28 | #include "ucln.h" |
29 | #include <stdio.h> |
30 | #ifdef __MVS__ /* defined by z/OS compiler */ |
31 | #define _POSIX_SOURCE |
32 | #include <cics.h> /* 12 Nov 2011 JAM iscics() function */ |
33 | #endif |
34 | #include "charstr.h" |
35 | |
36 | using namespace icu; |
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 nullptr */ |
56 | char libName[UPLUG_NAME_MAX]; /**< library name */ |
57 | char sym[UPLUG_NAME_MAX]; /**< plugin symbol, or nullptr */ |
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_CAPI char * U_EXPORT2 |
149 | uplug_findLibrary(void *lib, UErrorCode *status) { |
150 | int32_t libEnt; |
151 | char *ret = nullptr; |
152 | if(U_FAILURE(*status)) { |
153 | return nullptr; |
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_CAPI void * U_EXPORT2 |
165 | uplug_openLibrary(const char *libName, UErrorCode *status) { |
166 | int32_t libEntry = -1; |
167 | void *lib = nullptr; |
168 | |
169 | if(U_FAILURE(*status)) return nullptr; |
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 nullptr; |
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 == nullptr || U_FAILURE(*status)) { |
190 | /* cleanup. */ |
191 | libraryList[libEntry].lib = nullptr; /* 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_CAPI 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==nullptr) { |
256 | return pluginList; |
257 | } else { |
258 | UPlugData *nextPlug = &prior[1]; |
259 | UPlugData *pastPlug = &pluginList[pluginCount]; |
260 | |
261 | if(nextPlug>=pastPlug) { |
262 | return nullptr; |
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==nullptr||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't been 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't been 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) { |
317 | if(U_FAILURE(*status)) { |
318 | return; |
319 | } |
320 | if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) { /* shouldn't happen. Plugin hasn't been 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 = nullptr; |
334 | |
335 | if(U_FAILURE(*status)) { |
336 | return nullptr; |
337 | } |
338 | |
339 | if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) { |
340 | *status = U_MEMORY_ALLOCATION_ERROR; |
341 | return nullptr; |
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=nullptr; |
357 | plug->entrypoint=nullptr; |
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) { |
365 | UPlugData *plug = uplug_allocateEmptyPlug(status); |
366 | if(U_FAILURE(*status)) { |
367 | return nullptr; |
368 | } |
369 | |
370 | if(config!=nullptr) { |
371 | uprv_strncpy(plug->config, config, UPLUG_NAME_MAX); |
372 | } else { |
373 | plug->config[0] = 0; |
374 | } |
375 | |
376 | if(symName!=nullptr) { |
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 = nullptr; |
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 != nullptr) { |
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 = nullptr; |
422 | UPlugData *plugToRemove = nullptr; |
423 | if(U_FAILURE(*status)) return; |
424 | |
425 | for(cursor=pluginList;cursor!=nullptr;) { |
426 | if(cursor==plug) { |
427 | plugToRemove = plug; |
428 | cursor=nullptr; |
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 nullptr; |
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_CAPI UPlugData* U_EXPORT2 |
511 | uplug_getPlugInternal(int32_t n) { |
512 | if(n <0 || n >= pluginCount) { |
513 | return nullptr; |
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 from 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 = nullptr; |
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 nullptr; |
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, nullptr, nullptr, status); |
548 | uplug_loadPlug(plug, status); |
549 | return plug; |
550 | } |
551 | |
552 | #if U_ENABLE_DYLOAD |
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 nullptr; |
559 | |
560 | plug->pluginStatus = loadStatus; |
561 | plug->awaitingLoad = false; /* Won't load. */ |
562 | plug->dontUnload = true; /* cannot unload. */ |
563 | |
564 | if(sym!=nullptr) { |
565 | uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX); |
566 | } |
567 | |
568 | if(libName!=nullptr) { |
569 | uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX); |
570 | } |
571 | |
572 | if(nameOrError!=nullptr) { |
573 | uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX); |
574 | } |
575 | |
576 | if(config!=nullptr) { |
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 | */ |
586 | static UPlugData* |
587 | uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) { |
588 | void *lib = nullptr; |
589 | UPlugData *plug = nullptr; |
590 | if(U_FAILURE(*status)) { return nullptr; } |
591 | lib = uplug_openLibrary(libName, status); |
592 | if(lib!=nullptr && U_SUCCESS(*status)) { |
593 | UPlugEntrypoint *entrypoint = nullptr; |
594 | entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status); |
595 | |
596 | if(entrypoint!=nullptr&&U_SUCCESS(*status)) { |
597 | plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status); |
598 | if(plug!=nullptr&&U_SUCCESS(*status)) { |
599 | plug->lib = lib; /* plug takes ownership of library */ |
600 | lib = nullptr; /* 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==nullptr)?U_MISSING_RESOURCE_ERROR:*status,&subStatus); |
605 | } |
606 | if(lib!=nullptr) { /* 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==nullptr)?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 = nullptr; |
620 | if(U_FAILURE(*status)) { return nullptr; } |
621 | plug = uplug_initPlugFromLibrary(libName, sym, config, status); |
622 | uplug_loadPlug(plug, status); |
623 | |
624 | return plug; |
625 | } |
626 | |
627 | #endif |
628 | |
629 | static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW; |
630 | |
631 | U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() { |
632 | return gCurrentLevel; |
633 | } |
634 | |
635 | static UBool U_CALLCONV uplug_cleanup() |
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? */ |
648 | gCurrentLevel = UPLUG_LEVEL_LOW; |
649 | return true; |
650 | } |
651 | |
652 | #if U_ENABLE_DYLOAD |
653 | |
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 | } |
684 | } |
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 | |
706 | /* Name of the plugin config file */ |
707 | static char plugin_file[2048] = "" ; |
708 | #endif |
709 | |
710 | U_CAPI const char* U_EXPORT2 |
711 | uplug_getPluginFile() { |
712 | #if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO |
713 | return plugin_file; |
714 | #else |
715 | return nullptr; |
716 | #endif |
717 | } |
718 | |
719 | |
720 | // uplug_init() is called first thing from u_init(). |
721 | |
722 | U_CAPI void U_EXPORT2 |
723 | uplug_init(UErrorCode *status) { |
724 | #if !U_ENABLE_DYLOAD |
725 | (void)status; /* unused */ |
726 | #elif !UCONFIG_NO_FILE_IO |
727 | CharString plugin_dir; |
728 | const char *env = getenv("ICU_PLUGINS" ); |
729 | |
730 | if(U_FAILURE(*status)) return; |
731 | if(env != nullptr) { |
732 | plugin_dir.append(env, -1, *status); |
733 | } |
734 | if(U_FAILURE(*status)) return; |
735 | |
736 | #if defined(DEFAULT_ICU_PLUGINS) |
737 | if(plugin_dir.isEmpty()) { |
738 | plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status); |
739 | } |
740 | #endif |
741 | |
742 | #if UPLUG_TRACE |
743 | DBG((stderr, "ICU_PLUGINS=%s\n" , plugin_dir.data())); |
744 | #endif |
745 | |
746 | if(!plugin_dir.isEmpty()) { |
747 | FILE *f; |
748 | |
749 | CharString pluginFile; |
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 | |
758 | pluginFile.append("//DD:ICUPLUG" , -1, *status); /* JAM 20 Oct 2011 */ |
759 | #else |
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); |
765 | #endif |
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 | pluginFile.extract(plugin_file, sizeof(plugin_file), *status); |
786 | |
787 | #if UPLUG_TRACE |
788 | DBG((stderr, "pluginfile= %s len %d/%d\n" , plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file))); |
789 | #endif |
790 | |
791 | #ifdef __MVS__ |
792 | if (iscics()) /* 12 Nov 2011 JAM */ |
793 | { |
794 | f = nullptr; |
795 | } |
796 | else |
797 | #endif |
798 | { |
799 | f = fopen(pluginFile.data(), "r" ); |
800 | } |
801 | |
802 | if(f != nullptr) { |
803 | char linebuf[1024]; |
804 | char *p, *libName=nullptr, *symName=nullptr, *config=nullptr; |
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; |
815 | while(*p&&isspace((int)*p)) |
816 | p++; |
817 | if(!*p || *p=='#') continue; |
818 | libName = p; |
819 | while(*p&&!isspace((int)*p)) { |
820 | p++; |
821 | } |
822 | if(!*p || *p=='#') continue; /* no tab after libname */ |
823 | *p=0; /* end of libname */ |
824 | p++; |
825 | while(*p&&isspace((int)*p)) { |
826 | p++; |
827 | } |
828 | if(!*p||*p=='#') continue; /* no symname after libname +tab */ |
829 | symName = p; |
830 | while(*p&&!isspace((int)*p)) { |
831 | p++; |
832 | } |
833 | |
834 | if(*p) { /* has config */ |
835 | *p=0; |
836 | ++p; |
837 | while(*p&&isspace((int)*p)) { |
838 | p++; |
839 | } |
840 | if(*p) { |
841 | config = p; |
842 | } |
843 | } |
844 | |
845 | /* chop whitespace at the end of the config */ |
846 | if(config!=nullptr&&*config!=0) { |
847 | p = config+strlen(config); |
848 | while(p>config&&isspace((int)*(--p))) { |
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 | } |
869 | fclose(f); |
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 */ |
878 | gCurrentLevel = UPLUG_LEVEL_HIGH; |
879 | ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup); |
880 | } |
881 | |
882 | #endif |
883 | |
884 | |
885 | |