2 ******************************************************************************
4 * Copyright (C) 2009-2010, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 ******************************************************************************
9 * FILE NAME : icuplug.c
11 * Date Name Description
13 ******************************************************************************
16 #include "unicode/icuplug.h"
17 #include "icuplugimp.h"
30 #define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x
34 * Internal structure of an ICU plugin.
38 UPlugEntrypoint
*entrypoint
; /**< plugin entrypoint */
39 uint32_t structSize
; /**< initialized to the size of this structure */
40 uint32_t token
; /**< must be U_PLUG_TOKEN */
41 void *lib
; /**< plugin library, or NULL */
42 char libName
[UPLUG_NAME_MAX
]; /**< library name */
43 char sym
[UPLUG_NAME_MAX
]; /**< plugin symbol, or NULL */
44 char config
[UPLUG_NAME_MAX
]; /**< configuration data */
45 void *context
; /**< user context data */
46 char name
[UPLUG_NAME_MAX
]; /**< name of plugin */
47 UPlugLevel level
; /**< level of plugin */
48 UBool awaitingLoad
; /**< TRUE if the plugin is awaiting a load call */
49 UBool dontUnload
; /**< TRUE if plugin must stay resident (leak plugin and lib) */
50 UErrorCode pluginStatus
; /**< status code of plugin */
55 #define UPLUG_LIBRARY_INITIAL_COUNT 8
56 #define UPLUG_PLUGIN_INITIAL_COUNT 12
60 * @param list the full list
61 * @param listSize the number of entries in the list
62 * @param memberSize the size of one member
63 * @param itemToRemove the item number of the member
64 * @return the new listsize
66 static int32_t uplug_removeEntryAt(void *list
, int32_t listSize
, int32_t memberSize
, int32_t itemToRemove
) {
67 uint8_t *bytePtr
= (uint8_t *)list
;
69 /* get rid of some bad cases first */
74 /* is there anything to move? */
75 if(listSize
> itemToRemove
+1) {
76 memmove(bytePtr
+(itemToRemove
*memberSize
), bytePtr
+((itemToRemove
+1)*memberSize
), memberSize
);
87 * Library management. Internal.
93 * Library management. Internal.
96 typedef struct UPlugLibrary
{
97 void *lib
; /**< library ptr */
98 char name
[UPLUG_NAME_MAX
]; /**< library name */
99 uint32_t ref
; /**< reference count */
102 static UPlugLibrary staticLibraryList
[UPLUG_LIBRARY_INITIAL_COUNT
];
103 static UPlugLibrary
* libraryList
= staticLibraryList
;
104 static int32_t libraryCount
= 0;
105 static int32_t libraryMax
= UPLUG_LIBRARY_INITIAL_COUNT
;
108 * Search for a library. Doesn't lock
109 * @param libName libname to search for
110 * @return the library's struct
112 static int32_t searchForLibraryName(const char *libName
) {
115 for(i
=0;i
<libraryCount
;i
++) {
116 if(!uprv_strcmp(libName
, libraryList
[i
].name
)) {
123 static int32_t searchForLibrary(void *lib
) {
126 for(i
=0;i
<libraryCount
;i
++) {
127 if(lib
==libraryList
[i
].lib
) {
134 U_INTERNAL
char * U_EXPORT2
135 uplug_findLibrary(void *lib
, UErrorCode
*status
) {
138 if(U_FAILURE(*status
)) {
141 libEnt
= searchForLibrary(lib
);
143 ret
= libraryList
[libEnt
].name
;
145 *status
= U_MISSING_RESOURCE_ERROR
;
150 U_INTERNAL
void * U_EXPORT2
151 uplug_openLibrary(const char *libName
, UErrorCode
*status
) {
152 int32_t libEntry
= -1;
155 if(U_FAILURE(*status
)) return NULL
;
157 libEntry
= searchForLibraryName(libName
);
159 libEntry
= libraryCount
++;
160 if(libraryCount
>= libraryMax
) {
161 /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */
162 *status
= U_MEMORY_ALLOCATION_ERROR
;
164 DBG((stderr
, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax
));
168 /* Some operating systems don't want
169 DL operations from multiple threads. */
170 libraryList
[libEntry
].lib
= uprv_dl_open(libName
, status
);
172 DBG((stderr
, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName
, u_errorName(*status
), libEntry
, lib
));
175 if(libraryList
[libEntry
].lib
== NULL
|| U_FAILURE(*status
)) {
177 libraryList
[libEntry
].lib
= NULL
; /* failure with open */
178 libraryList
[libEntry
].name
[0] = 0;
180 DBG((stderr
, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName
, u_errorName(*status
), libEntry
, lib
));
182 /* no need to free - just won't increase the count. */
184 } else { /* is it still there? */
186 uprv_strncpy(libraryList
[libEntry
].name
,libName
,UPLUG_NAME_MAX
);
187 libraryList
[libEntry
].ref
=1;
188 lib
= libraryList
[libEntry
].lib
;
192 lib
= libraryList
[libEntry
].lib
;
193 libraryList
[libEntry
].ref
++;
198 U_INTERNAL
void U_EXPORT2
199 uplug_closeLibrary(void *lib
, UErrorCode
*status
) {
203 DBG((stderr
, "uplug_closeLibrary(%p,%s) list %p\n", lib
, u_errorName(*status
), (void*)libraryList
));
205 if(U_FAILURE(*status
)) return;
207 for(i
=0;i
<libraryCount
;i
++) {
208 if(lib
==libraryList
[i
].lib
) {
209 if(--(libraryList
[i
].ref
) == 0) {
210 uprv_dl_close(libraryList
[i
].lib
, status
);
211 libraryCount
= uplug_removeEntryAt(libraryList
, libraryCount
, sizeof(*libraryList
), i
);
216 *status
= U_INTERNAL_PROGRAM_ERROR
; /* could not find the entry! */
221 static UPlugData pluginList
[UPLUG_PLUGIN_INITIAL_COUNT
];
222 static int32_t pluginCount
= 0;
227 static int32_t uplug_pluginNumber(UPlugData
* d
) {
228 UPlugData
*pastPlug
= &pluginList
[pluginCount
];
231 } else if(d
>=pastPlug
) {
234 return (d
-pluginList
)/sizeof(pluginList
[0]);
239 U_CAPI UPlugData
* U_EXPORT2
240 uplug_nextPlug(UPlugData
*prior
) {
244 UPlugData
*nextPlug
= &prior
[1];
245 UPlugData
*pastPlug
= &pluginList
[pluginCount
];
247 if(nextPlug
>=pastPlug
) {
258 * Call the plugin with some params
260 static void uplug_callPlug(UPlugData
*plug
, UPlugReason reason
, UErrorCode
*status
) {
261 UPlugTokenReturn token
;
262 if(plug
==NULL
||U_FAILURE(*status
)) {
265 token
= (*(plug
->entrypoint
))(plug
, reason
, status
);
266 if(token
!=UPLUG_TOKEN
) {
267 *status
= U_INTERNAL_PROGRAM_ERROR
;
272 static void uplug_unloadPlug(UPlugData
*plug
, UErrorCode
*status
) {
273 if(plug
->awaitingLoad
) { /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
274 *status
= U_INTERNAL_PROGRAM_ERROR
;
277 if(U_SUCCESS(plug
->pluginStatus
)) {
278 /* Don't unload a plug which has a failing load status - means it didn't actually load. */
279 uplug_callPlug(plug
, UPLUG_REASON_UNLOAD
, status
);
283 static void uplug_queryPlug(UPlugData
*plug
, UErrorCode
*status
) {
284 if(!plug
->awaitingLoad
|| !(plug
->level
== UPLUG_LEVEL_UNKNOWN
) ) { /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
285 *status
= U_INTERNAL_PROGRAM_ERROR
;
288 plug
->level
= UPLUG_LEVEL_INVALID
;
289 uplug_callPlug(plug
, UPLUG_REASON_QUERY
, status
);
290 if(U_SUCCESS(*status
)) {
291 if(plug
->level
== UPLUG_LEVEL_INVALID
) {
292 plug
->pluginStatus
= U_PLUGIN_DIDNT_SET_LEVEL
;
293 plug
->awaitingLoad
= FALSE
;
296 plug
->pluginStatus
= U_INTERNAL_PROGRAM_ERROR
;
297 plug
->awaitingLoad
= FALSE
;
302 static void uplug_loadPlug(UPlugData
*plug
, UErrorCode
*status
) {
303 if(!plug
->awaitingLoad
|| (plug
->level
< UPLUG_LEVEL_LOW
) ) { /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
304 *status
= U_INTERNAL_PROGRAM_ERROR
;
307 uplug_callPlug(plug
, UPLUG_REASON_LOAD
, status
);
308 plug
->awaitingLoad
= FALSE
;
309 if(!U_SUCCESS(*status
)) {
310 plug
->pluginStatus
= U_INTERNAL_PROGRAM_ERROR
;
314 static UPlugData
*uplug_allocateEmptyPlug(UErrorCode
*status
)
316 UPlugData
*plug
= NULL
;
318 if(U_FAILURE(*status
)) {
322 if(pluginCount
== UPLUG_PLUGIN_INITIAL_COUNT
) {
323 *status
= U_MEMORY_ALLOCATION_ERROR
;
327 plug
= &pluginList
[pluginCount
++];
329 plug
->token
= UPLUG_TOKEN
;
330 plug
->structSize
= sizeof(UPlugData
);
332 plug
->level
= UPLUG_LEVEL_UNKNOWN
; /* initialize to null state */
333 plug
->awaitingLoad
= TRUE
;
334 plug
->dontUnload
= FALSE
;
335 plug
->pluginStatus
= U_ZERO_ERROR
;
336 plug
->libName
[0] = 0;
340 plug
->entrypoint
=NULL
;
346 static UPlugData
*uplug_allocatePlug(UPlugEntrypoint
*entrypoint
, const char *config
, void *lib
, const char *symName
,
347 UErrorCode
*status
) {
350 if(U_FAILURE(*status
)) {
354 plug
= uplug_allocateEmptyPlug(status
);
356 uprv_strncpy(plug
->config
, config
, UPLUG_NAME_MAX
);
362 uprv_strncpy(plug
->sym
, symName
, UPLUG_NAME_MAX
);
367 plug
->entrypoint
= entrypoint
;
369 uplug_queryPlug(plug
, status
);
374 static void uplug_deallocatePlug(UPlugData
*plug
, UErrorCode
*status
) {
375 UErrorCode subStatus
= U_ZERO_ERROR
;
376 if(!plug
->dontUnload
) {
378 uplug_closeLibrary(plug
->lib
, &subStatus
);
382 if(U_SUCCESS(*status
) && U_FAILURE(subStatus
)) {
385 /* shift plugins up and decrement count. */
386 if(U_SUCCESS(*status
)) {
387 /* all ok- remove. */
388 pluginCount
= uplug_removeEntryAt(pluginList
, pluginCount
, sizeof(plug
[0]), uplug_pluginNumber(plug
));
390 /* not ok- leave as a message. */
391 plug
->awaitingLoad
=FALSE
;
393 plug
->dontUnload
=TRUE
;
397 static void uplug_doUnloadPlug(UPlugData
*plugToRemove
, UErrorCode
*status
) {
398 if(plugToRemove
!= NULL
) {
399 uplug_unloadPlug(plugToRemove
, status
);
400 uplug_deallocatePlug(plugToRemove
, status
);
404 U_CAPI
void U_EXPORT2
405 uplug_removePlug(UPlugData
*plug
, UErrorCode
*status
) {
406 UPlugData
*cursor
= NULL
;
407 UPlugData
*plugToRemove
= NULL
;
408 if(U_FAILURE(*status
)) return;
410 for(cursor
=pluginList
;cursor
!=NULL
;) {
415 cursor
= uplug_nextPlug(cursor
);
419 uplug_doUnloadPlug(plugToRemove
, status
);
425 U_CAPI
void U_EXPORT2
426 uplug_setPlugNoUnload(UPlugData
*data
, UBool dontUnload
)
428 data
->dontUnload
= dontUnload
;
432 U_CAPI
void U_EXPORT2
433 uplug_setPlugLevel(UPlugData
*data
, UPlugLevel level
) {
438 U_CAPI UPlugLevel U_EXPORT2
439 uplug_getPlugLevel(UPlugData
*data
) {
444 U_CAPI
void U_EXPORT2
445 uplug_setPlugName(UPlugData
*data
, const char *name
) {
446 uprv_strncpy(data
->name
, name
, UPLUG_NAME_MAX
);
450 U_CAPI
const char * U_EXPORT2
451 uplug_getPlugName(UPlugData
*data
) {
456 U_CAPI
const char * U_EXPORT2
457 uplug_getSymbolName(UPlugData
*data
) {
461 U_CAPI
const char * U_EXPORT2
462 uplug_getLibraryName(UPlugData
*data
, UErrorCode
*status
) {
463 if(data
->libName
[0]) {
464 return data
->libName
;
467 return uplug_findLibrary(data
->lib
, status
);
474 U_CAPI
void * U_EXPORT2
475 uplug_getLibrary(UPlugData
*data
) {
479 U_CAPI
void * U_EXPORT2
480 uplug_getContext(UPlugData
*data
) {
481 return data
->context
;
485 U_CAPI
void U_EXPORT2
486 uplug_setContext(UPlugData
*data
, void *context
) {
487 data
->context
= context
;
490 U_CAPI
const char* U_EXPORT2
491 uplug_getConfiguration(UPlugData
*data
) {
495 U_INTERNAL UPlugData
* U_EXPORT2
496 uplug_getPlugInternal(int32_t n
) {
497 if(n
<0 || n
>= pluginCount
) {
500 return &(pluginList
[n
]);
505 U_CAPI UErrorCode U_EXPORT2
506 uplug_getPlugLoadStatus(UPlugData
*plug
) {
507 return plug
->pluginStatus
;
514 * Initialize a plugin fron an entrypoint and library - but don't load it.
516 static UPlugData
* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint
*entrypoint
, const char *config
, void *lib
, const char *sym
,
517 UErrorCode
*status
) {
518 UPlugData
*plug
= NULL
;
520 plug
= uplug_allocatePlug(entrypoint
, config
, lib
, sym
, status
);
522 if(U_SUCCESS(*status
)) {
525 uplug_deallocatePlug(plug
, status
);
530 U_CAPI UPlugData
* U_EXPORT2
531 uplug_loadPlugFromEntrypoint(UPlugEntrypoint
*entrypoint
, const char *config
, UErrorCode
*status
) {
532 UPlugData
* plug
= uplug_initPlugFromEntrypointAndLibrary(entrypoint
, config
, NULL
, NULL
, status
);
533 uplug_loadPlug(plug
, status
);
539 uplug_initErrorPlug(const char *libName
, const char *sym
, const char *config
, const char *nameOrError
, UErrorCode loadStatus
, UErrorCode
*status
)
541 UPlugData
*plug
= uplug_allocateEmptyPlug(status
);
542 if(U_FAILURE(*status
)) return NULL
;
544 plug
->pluginStatus
= loadStatus
;
545 plug
->awaitingLoad
= FALSE
; /* Won't load. */
546 plug
->dontUnload
= TRUE
; /* cannot unload. */
549 uprv_strncpy(plug
->sym
, sym
, UPLUG_NAME_MAX
);
553 uprv_strncpy(plug
->libName
, libName
, UPLUG_NAME_MAX
);
556 if(nameOrError
!=NULL
) {
557 uprv_strncpy(plug
->name
, nameOrError
, UPLUG_NAME_MAX
);
561 uprv_strncpy(plug
->config
, config
, UPLUG_NAME_MAX
);
568 * Fetch a plugin from DLL, and then initialize it from a library- but don't load it.
574 uplug_initPlugFromLibrary(const char *libName
, const char *sym
, const char *config
, UErrorCode
*status
) {
576 UPlugData
*plug
= NULL
;
577 if(U_FAILURE(*status
)) { return NULL
; }
578 lib
= uplug_openLibrary(libName
, status
);
579 if(lib
!=NULL
&& U_SUCCESS(*status
)) {
580 UPlugEntrypoint
*entrypoint
= NULL
;
582 * ISO forbids the following cast.
583 * See: http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
585 entrypoint
= (UPlugEntrypoint
*)uprv_dl_sym(lib
, sym
, status
);
587 if(entrypoint
!=NULL
&&U_SUCCESS(*status
)) {
588 plug
= uplug_initPlugFromEntrypointAndLibrary(entrypoint
, config
, lib
, sym
, status
);
589 if(plug
!=NULL
&&U_SUCCESS(*status
)) {
590 plug
->lib
= lib
; /* plug takes ownership of library */
591 lib
= NULL
; /* library is now owned by plugin. */
594 UErrorCode subStatus
= U_ZERO_ERROR
;
595 plug
= uplug_initErrorPlug(libName
,sym
,config
,"ERROR: Could not load entrypoint",(lib
==NULL
)?U_MISSING_RESOURCE_ERROR
:*status
,&subStatus
);
597 if(lib
!=NULL
) { /* still need to close the lib */
598 UErrorCode subStatus
= U_ZERO_ERROR
;
599 uplug_closeLibrary(lib
, &subStatus
); /* don't care here */
602 UErrorCode subStatus
= U_ZERO_ERROR
;
603 plug
= uplug_initErrorPlug(libName
,sym
,config
,"ERROR: could not load library",(lib
==NULL
)?U_MISSING_RESOURCE_ERROR
:*status
,&subStatus
);
608 U_CAPI UPlugData
* U_EXPORT2
609 uplug_loadPlugFromLibrary(const char *libName
, const char *sym
, const char *config
, UErrorCode
*status
) {
610 UPlugData
*plug
= NULL
;
611 if(U_FAILURE(*status
)) { return NULL
; }
612 plug
= uplug_initPlugFromLibrary(libName
, sym
, config
, status
);
613 uplug_loadPlug(plug
, status
);
620 U_CAPI UPlugLevel U_EXPORT2
uplug_getCurrentLevel() {
621 if(cmemory_inUse()) {
622 return UPLUG_LEVEL_HIGH
;
624 return UPLUG_LEVEL_LOW
;
628 static UBool U_CALLCONV
uplug_cleanup(void)
632 UPlugData
*pluginToRemove
;
634 for(i
=0;i
<pluginCount
;i
++) {
635 UErrorCode subStatus
= U_ZERO_ERROR
;
636 pluginToRemove
= &pluginList
[i
];
637 /* unload and deallocate */
638 uplug_doUnloadPlug(pluginToRemove
, &subStatus
);
640 /* close other held libs? */
644 static void uplug_loadWaitingPlugs(UErrorCode
*status
) {
646 UPlugLevel currentLevel
= uplug_getCurrentLevel();
648 if(U_FAILURE(*status
)) {
652 DBG((stderr
, "uplug_loadWaitingPlugs() Level: %d\n", currentLevel
));
654 /* pass #1: low level plugs */
655 for(i
=0;i
<pluginCount
;i
++) {
656 UErrorCode subStatus
= U_ZERO_ERROR
;
657 UPlugData
*pluginToLoad
= &pluginList
[i
];
658 if(pluginToLoad
->awaitingLoad
) {
659 if(pluginToLoad
->level
== UPLUG_LEVEL_LOW
) {
660 if(currentLevel
> UPLUG_LEVEL_LOW
) {
661 pluginToLoad
->pluginStatus
= U_PLUGIN_TOO_HIGH
;
664 uplug_loadPlug(pluginToLoad
, &subStatus
);
665 newLevel
= uplug_getCurrentLevel();
666 if(newLevel
> currentLevel
) {
667 pluginToLoad
->pluginStatus
= U_PLUGIN_CHANGED_LEVEL_WARNING
;
668 currentLevel
= newLevel
;
671 pluginToLoad
->awaitingLoad
= FALSE
;
675 currentLevel
= uplug_getCurrentLevel();
677 for(i
=0;i
<pluginCount
;i
++) {
678 UErrorCode subStatus
= U_ZERO_ERROR
;
679 UPlugData
*pluginToLoad
= &pluginList
[i
];
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
;
687 uplug_loadPlug(pluginToLoad
, &subStatus
);
689 pluginToLoad
->awaitingLoad
= FALSE
;
694 DBG((stderr
, " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel()));
699 /* Name of the plugin config file */
700 static char plugin_file
[2048] = "";
703 U_INTERNAL
const char* U_EXPORT2
704 uplug_getPluginFile() {
713 U_CAPI
void U_EXPORT2
714 uplug_init(UErrorCode
*status
) {
716 (void)status
; /* unused */
718 const char *plugin_dir
;
720 if(U_FAILURE(*status
)) return;
721 plugin_dir
= getenv("ICU_PLUGINS");
723 #if defined(DEFAULT_ICU_PLUGINS)
724 if(plugin_dir
== NULL
|| !*plugin_dir
) {
725 plugin_dir
= DEFAULT_ICU_PLUGINS
;
730 DBG((stderr
, "ICU_PLUGINS=%s\n", plugin_dir
));
733 if(plugin_dir
!= NULL
&& *plugin_dir
) {
737 uprv_strncpy(plugin_file
, plugin_dir
, 2047);
738 uprv_strncat(plugin_file
, U_FILE_SEP_STRING
,2047);
739 uprv_strncat(plugin_file
, "icuplugins",2047);
740 uprv_strncat(plugin_file
, U_ICU_VERSION_SHORT
,2047);
741 uprv_strncat(plugin_file
, ".txt" ,2047);
744 DBG((stderr
, "pluginfile= %s\n", plugin_file
));
747 f
= fopen(plugin_file
, "r");
751 char *p
, *libName
=NULL
, *symName
=NULL
, *config
=NULL
;
755 while(fgets(linebuf
,1023,f
)) {
758 if(!*linebuf
|| *linebuf
=='#') {
762 while(*p
&&isspace(*p
))
764 if(!*p
|| *p
=='#') continue;
766 while(*p
&&!isspace(*p
)) {
769 if(!*p
|| *p
=='#') continue; /* no tab after libname */
770 *p
=0; /* end of libname */
772 while(*p
&&isspace(*p
)) {
775 if(!*p
||*p
=='#') continue; /* no symname after libname +tab */
777 while(*p
&&!isspace(*p
)) {
781 if(*p
) { /* has config */
784 while(*p
&&isspace(*p
)) {
792 /* chop whitespace at the end of the config */
793 if(config
!=NULL
&&*config
!=0) {
794 p
= config
+strlen(config
);
795 while(p
>config
&&isspace(*(--p
))) {
800 /* OK, we're good. */
802 UErrorCode subStatus
= U_ZERO_ERROR
;
803 UPlugData
*plug
= uplug_initPlugFromLibrary(libName
, symName
, config
, &subStatus
);
804 if(U_FAILURE(subStatus
) && U_SUCCESS(*status
)) {
808 DBG((stderr
, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName
, symName
, config
));
809 DBG((stderr
, " -> %p, %s\n", (void*)plug
, u_errorName(subStatus
)));
811 (void)plug
; /* unused */
818 DBG((stderr
, "Can't open plugin file %s\n", plugin_file
));
822 uplug_loadWaitingPlugs(status
);
823 #endif /* U_ENABLE_DYLOAD */
824 ucln_registerCleanup(UCLN_UPLUG
, uplug_cleanup
);