1 /***************************************************************************
2 * Copyright (C) 2010~2010 by CSSlayer *
3 * wengxt@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 /**
22 * @file addon.c
23 * Addon Support for fcitx
24 * @author CSSlayer wengxt@gmail.com
25 */
26
27 #include <sys/stat.h>
28 #include <libintl.h>
29 #include <dlfcn.h>
30
31 #include "fcitx/fcitx.h"
32 #include "addon.h"
33 #include "fcitx-config/xdg.h"
34 #include "fcitx-utils/log.h"
35 #include "fcitx-utils/utils.h"
36 #include "instance.h"
37 #include "instance-internal.h"
38 #include "addon-internal.h"
39
40 CONFIG_BINDING_BEGIN(FcitxAddon)
41 CONFIG_BINDING_REGISTER("Addon", "Name", name)
42 CONFIG_BINDING_REGISTER("Addon", "GeneralName", generalname)
43 CONFIG_BINDING_REGISTER("Addon", "Comment", comment)
44 CONFIG_BINDING_REGISTER("Addon", "Category", category)
45 CONFIG_BINDING_REGISTER("Addon", "Enabled", bEnabled)
46 CONFIG_BINDING_REGISTER("Addon", "Library", library)
47 CONFIG_BINDING_REGISTER("Addon", "Type", type)
48 CONFIG_BINDING_REGISTER("Addon", "Dependency", depend)
49 CONFIG_BINDING_REGISTER("Addon", "Priority", priority)
50 CONFIG_BINDING_REGISTER("Addon", "SubConfig", subconfig)
51 CONFIG_BINDING_REGISTER("Addon", "IMRegisterMethod", registerMethod)
52 CONFIG_BINDING_REGISTER("Addon", "IMRegisterArgument", registerArgument)
53 CONFIG_BINDING_REGISTER("Addon", "UIFallback", uifallback)
54 CONFIG_BINDING_REGISTER("Addon", "Advance", advance)
55 CONFIG_BINDING_REGISTER("Addon", "LoadLocal", loadLocal)
56 CONFIG_BINDING_END()
57
58 static const UT_icd addon_icd = {
59 sizeof(FcitxAddon), NULL , NULL, FcitxAddonFree
60 };
AddonPriorityCmp(const void * a,const void * b)61 static int AddonPriorityCmp(const void* a, const void* b)
62 {
63 FcitxAddon *aa = (FcitxAddon*)a, *ab = (FcitxAddon*)b;
64 return aa->priority - ab->priority;
65 }
66
67 FCITX_EXPORT_API
FcitxAddonsInit(UT_array * addons)68 void FcitxAddonsInit(UT_array* addons)
69 {
70 utarray_init(addons, &addon_icd);
71 /*
72 * FIXME: this is a workaround since everyone is using "FcitxAddon*" everywhere,
73 * so realloc will really do some evil things.
74 *
75 * We might better use UT_hash for fcitx addon in the future
76 */
77 utarray_reserve(addons, 512);
78 }
79
FcitxGetSymbol(void * handle,const char * addonName,const char * symbolName)80 void* FcitxGetSymbol(void* handle, const char* addonName, const char* symbolName)
81 {
82 char *p;
83 char *escapedAddonName;
84 fcitx_utils_alloc_cat_str(escapedAddonName, addonName, "_", symbolName);
85 for (p = escapedAddonName;*p;p++) {
86 if (*p == '-') {
87 *p = '_';
88 }
89 }
90 void *result = dlsym(handle, escapedAddonName);
91 free(escapedAddonName);
92 if (!result)
93 return dlsym(handle, symbolName);
94 return result;
95 }
96
97 /**
98 * Load Addon Info
99 */
100 FCITX_EXPORT_API
FcitxAddonsLoad(UT_array * addons)101 void FcitxAddonsLoad(UT_array* addons)
102 {
103 FcitxAddonsLoadInternal(addons, false);
104 }
105
FcitxAddonsLoadInternal(UT_array * addons,boolean reloadIM)106 FcitxAddon* FcitxAddonsLoadInternal(UT_array* addons, boolean reloadIM)
107 {
108 char **addonPath;
109 size_t len;
110 size_t start;
111 if (!reloadIM)
112 utarray_clear(addons);
113
114 start = utarray_len(addons);
115
116 FcitxStringHashSet* sset = FcitxXDGGetFiles("addon", NULL, ".conf");
117 addonPath = FcitxXDGGetPathWithPrefix(&len, "addon");
118 char *paths[len];
119 HASH_FOREACH(string, sset, FcitxStringHashSet) {
120 // FIXME: if it will cause realloc, then it's evil for fcitx 4.2 series
121 if (reloadIM && addons->i == addons->n) {
122 break;
123 }
124
125 int i;
126 for (i = len - 1; i >= 0; i--) {
127 fcitx_utils_alloc_cat_str(paths[i], addonPath[len - i - 1],
128 "/", string->name);
129 FcitxLog(DEBUG, "Load Addon Config File:%s", paths[i]);
130 }
131 FcitxConfigFile* cfile = FcitxConfigParseMultiConfigFile(paths, len, FcitxAddonGetConfigDesc());
132 if (cfile) {
133 utarray_extend_back(addons);
134 FcitxAddon *a = (FcitxAddon*) utarray_back(addons);
135 utarray_init(&a->functionList, fcitx_ptr_icd);
136 FcitxAddonConfigBind(a, cfile, FcitxAddonGetConfigDesc());
137 FcitxConfigBindSync((FcitxGenericConfig*)a);
138 FcitxLog(DEBUG, _("Addon Config %s is %s"), string->name, (a->bEnabled) ? "Enabled" : "Disabled");
139 boolean error = false;
140 if (reloadIM) {
141 if (a->category != AC_INPUTMETHOD)
142 error = true;
143 }
144 /* if loaded, don't touch the old one */
145 if (FcitxAddonsGetAddonByNameInternal(addons, a->name, true) != a)
146 error = true;
147
148 if (error)
149 utarray_pop_back(addons);
150 else
151 FcitxLog(INFO, _("Load Addon Config File:%s"), string->name);
152 }
153
154 for (i = len - 1;i >= 0;i--) {
155 free(paths[i]);
156 }
157 }
158 FcitxXDGFreePath(addonPath);
159
160 fcitx_utils_free_string_hash_set(sset);
161
162 size_t to = utarray_len(addons);
163 utarray_sort_range(addons, AddonPriorityCmp, start, to);
164
165 return (FcitxAddon*)utarray_eltptr(addons, start);
166 }
167
FcitxInstanceFillAddonOwner(FcitxInstance * instance,FcitxAddon * addonHead)168 void FcitxInstanceFillAddonOwner(FcitxInstance* instance, FcitxAddon* addonHead)
169 {
170 /* FIXME: a walkaround for not have instance in function FcitxModuleInvokeFunction */
171 FcitxAddon* addon;
172 if (addonHead)
173 addon = addonHead;
174 else
175 addon = (FcitxAddon *) utarray_front(&instance->addons);
176 for (; addon != NULL; addon = (FcitxAddon *) utarray_next(&instance->addons, addon)) {
177 addon->owner = instance;
178 }
179 }
180
181 FCITX_EXPORT_API
FcitxInstanceResolveAddonDependency(FcitxInstance * instance)182 void FcitxInstanceResolveAddonDependency(FcitxInstance* instance)
183 {
184 FcitxInstanceResolveAddonDependencyInternal(instance, NULL);
185 }
186
FcitxInstanceResolveAddonDependencyInternal(FcitxInstance * instance,FcitxAddon * startAddon)187 void FcitxInstanceResolveAddonDependencyInternal(FcitxInstance* instance, FcitxAddon* startAddon)
188 {
189 UT_array* addons = &instance->addons;
190 boolean remove = true;
191 FcitxAddon *addon;
192 FcitxAddon *uiaddon = NULL, *uifallbackaddon = NULL;
193 boolean reloadIM = true;
194
195 if (!startAddon) {
196 startAddon = (FcitxAddon*) utarray_front(addons);
197 reloadIM = false;
198 }
199
200 /* check "all" */
201 if (instance->disableList
202 && utarray_len(instance->disableList) == 1
203 && fcitx_utils_string_list_contains(instance->disableList, "all"))
204 {
205 for (addon = startAddon;
206 addon != NULL;
207 addon = (FcitxAddon *) utarray_next(addons, addon)) {
208 addon->bEnabled = false;
209 }
210 }
211
212 /* override the enable and disable option */
213 for (addon = startAddon;
214 addon != NULL;
215 addon = (FcitxAddon *) utarray_next(addons, addon)) {
216 if (instance->enableList && fcitx_utils_string_list_contains(instance->enableList, addon->name))
217 addon->bEnabled = true;
218 else if (instance->disableList && fcitx_utils_string_list_contains(instance->disableList, addon->name))
219 addon->bEnabled = false;
220 }
221
222 if (!reloadIM) {
223 /* choose ui */
224 for (addon = startAddon;
225 addon != NULL;
226 addon = (FcitxAddon *) utarray_next(addons, addon)) {
227 if (addon->category == AC_UI) {
228 if (instance->uiname == NULL) {
229 if (addon->bEnabled) {
230 uiaddon = addon;
231 break;
232 }
233 } else {
234 if (strcmp(instance->uiname, addon->name) == 0) {
235 addon->bEnabled = true;
236 uiaddon = addon;
237 break;
238 }
239 }
240 }
241 }
242
243 if (uiaddon && uiaddon->uifallback) {
244 for (addon = startAddon;
245 addon != NULL;
246 addon = (FcitxAddon *) utarray_next(addons, addon)) {
247 if (addon->category == AC_UI && addon->bEnabled && strcmp(uiaddon->uifallback, addon->name) == 0) {
248 FcitxAddon temp;
249 int uiidx = utarray_eltidx(addons, uiaddon);
250 int fallbackidx = utarray_eltidx(addons, addon);
251 if (fallbackidx < uiidx) {
252 temp = *uiaddon;
253 *uiaddon = *addon;
254 *addon = temp;
255
256 /* they swapped, addon is normal ui, and ui addon is fallback */
257 uifallbackaddon = uiaddon;
258 uiaddon = addon;
259 }
260 else {
261 uifallbackaddon = addon;
262 }
263 break;
264 }
265 }
266 }
267
268 for (addon = startAddon;
269 addon != NULL;
270 addon = (FcitxAddon *) utarray_next(addons, addon)) {
271 if (addon->category == AC_UI && addon != uiaddon && addon != uifallbackaddon) {
272 addon->bEnabled = false;
273 }
274 }
275 }
276
277 while (remove) {
278 remove = false;
279 for (addon = startAddon;
280 addon != NULL;
281 addon = (FcitxAddon *) utarray_next(addons, addon)) {
282 if (!addon->bEnabled)
283 continue;
284 UT_array* dependlist = fcitx_utils_split_string(addon->depend, ',');
285 boolean valid = true;
286 char **depend = NULL;
287 for (depend = (char **) utarray_front(dependlist);
288 depend != NULL;
289 depend = (char **) utarray_next(dependlist, depend)) {
290 if (!FcitxAddonsIsAddonAvailable(addons, *depend)) {
291 valid = false;
292 break;
293 }
294 }
295
296 utarray_free(dependlist);
297 if (!valid) {
298 FcitxLog(WARNING, _("Disable addon %s, dependency %s cannot be satisfied."), addon->name, addon->depend);
299 addon->bEnabled = false;
300 }
301 }
302 }
303 }
304
305 FCITX_EXPORT_API
FcitxAddonsIsAddonAvailable(UT_array * addons,const char * name)306 boolean FcitxAddonsIsAddonAvailable(UT_array* addons, const char* name)
307 {
308 return FcitxAddonsGetAddonByNameInternal(addons, name, false) != NULL;
309 }
310
FcitxAddonsGetAddonByNameInternal(UT_array * addons,const char * name,boolean checkDisabled)311 FcitxAddon* FcitxAddonsGetAddonByNameInternal(UT_array* addons, const char* name, boolean checkDisabled)
312 {
313 FcitxAddon *addon;
314 for (addon = (FcitxAddon *) utarray_front(addons);
315 addon != NULL;
316 addon = (FcitxAddon *) utarray_next(addons, addon)) {
317 if ((checkDisabled || addon->bEnabled) && strcmp(name, addon->name) == 0)
318 return addon;
319 }
320 return NULL;
321 }
322
323
324 FCITX_EXPORT_API
FcitxAddonsGetAddonByName(UT_array * addons,const char * name)325 FcitxAddon* FcitxAddonsGetAddonByName(UT_array* addons, const char* name)
326 {
327 return FcitxAddonsGetAddonByNameInternal(addons, name, false);
328 }
329
330 /**
331 * Load addon.desc file
332 *
333 * @return the description of addon configure.
334 */
335 FCITX_EXPORT_API
336 CONFIG_DESC_DEFINE(FcitxAddonGetConfigDesc, "addon.desc")
337
338 FCITX_EXPORT_API
FcitxAddonFree(void * v)339 void FcitxAddonFree(void* v)
340 {
341 FcitxAddon *addon = (FcitxAddon*) v;
342 if (!addon)
343 return ;
344 FcitxConfigFreeConfigFile(addon->config.configFile);
345 free(addon->name);
346 free(addon->library);
347 free(addon->comment);
348 free(addon->generalname);
349 free(addon->depend);
350 free(addon->subconfig);
351 }
352
FcitxCheckABIVersion(void * handle,const char * addonName)353 boolean FcitxCheckABIVersion(void* handle, const char* addonName)
354 {
355 int* version = (int*) FcitxGetSymbol(handle, addonName, "ABI_VERSION");
356 if (!version)
357 return false;
358 if (*version < FCITX_ABI_VERSION)
359 return false;
360 return true;
361 }
362
363 // kate: indent-mode cstyle; space-indent on; indent-width 0;
364