1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifdef FEATURE_ICU_LOCALE
17 #include <stdio.h>
18 #include <dlfcn.h>
19 #include <musl_log.h>
20 #include <string.h>
21 #include "locale_impl.h"
22 
23 #define ICU_UC_SO "libhmicuuc.z.so"
24 #define ICU_I18N_SO "libhmicui18n.z.so"
25 
26 static void *g_icuuc_handle = NULL;
27 static void *g_icui18n_handle = NULL;
28 hidden struct icu_opt_func g_icu_opt_func = { NULL };
29 static int dlopen_fail_flag = 0;
30 
get_icu_handle(icu_so_type type, const char *symbol_name)31 static void *get_icu_handle(icu_so_type type, const char *symbol_name)
32 {
33 	void *cur_handle;
34 	char *cur_so;
35 	if (type == ICU_UC) {
36 		cur_handle = g_icuuc_handle;
37 		cur_so = ICU_UC_SO;
38 	} else {
39 		cur_handle = g_icui18n_handle;
40 		cur_so = ICU_I18N_SO;
41 	}
42 
43 	if (!cur_handle && !dlopen_fail_flag) {
44 		cur_handle = dlopen(cur_so, RTLD_LOCAL);
45 	}
46 	if (!cur_handle) {
47 		dlopen_fail_flag = 1;
48 		MUSL_LOGE("dlopen icu so for musl locale fail %{public}s", dlerror());
49 		return NULL;
50 	}
51 	return dlsym(cur_handle, symbol_name);
52 }
53 
get_icu_version_numnull54 static char *get_icu_version_num()
55 {
56 	if (!(g_icu_opt_func.get_icu_version)) {
57 		g_icu_opt_func.get_icu_version = get_icu_handle(ICU_UC, ICU_GET_VERSION_NUM_SYMBOL);
58 	}
59 	if (g_icu_opt_func.get_icu_version) {
60 		return g_icu_opt_func.get_icu_version();
61 	} else {
62 		return "";
63 	}
64 }
65 
get_icu_symbol(icu_so_type type, void **icu_symbol_handle, const char *symbol_name)66 void get_icu_symbol(icu_so_type type, void **icu_symbol_handle, const char *symbol_name)
67 {
68 	if (!(*icu_symbol_handle)) {
69 		char *icu_version = get_icu_version_num();
70 		char *valid_icu_symbol = malloc(strlen(symbol_name) + strlen(icu_version) + 2);
71 		sprintf(valid_icu_symbol, "%s_%s", symbol_name, icu_version);
72 		*icu_symbol_handle = get_icu_handle(type, valid_icu_symbol);
73 		free(valid_icu_symbol);
74 	}
75 }
76 
set_icu_directorynull77 void set_icu_directory()
78 {
79 	if (!(g_icu_opt_func.set_data_directory)) {
80 		g_icu_opt_func.set_data_directory = get_icu_handle(ICU_UC, ICU_SET_DATA_DIRECTORY_SYMBOL);
81 		if (g_icu_opt_func.set_data_directory) {
82 			g_icu_opt_func.set_data_directory();
83 		}
84 	}
85 }
86 
87 /* ICU methods don't need charset for locale, process the given locale name */
get_valid_icu_locale_name(const char *name, const char *icu_name, int icu_name_len)88 void get_valid_icu_locale_name(const char *name, const char *icu_name, int icu_name_len)
89 {
90 	char *pos = memchr(name, '.', strlen(name));
91 	int valid_len;
92 	if (pos) {
93 		valid_len = pos - name;
94 	} else {
95 		valid_len = strlen(name);
96 	}
97 	if (icu_name_len > valid_len) {
98 		strncpy(icu_name, name, valid_len);
99 	}
100 }
101 #endif
102