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 fcitx-config.c
23 * @author CSSlayer wengxt@gmail.com
24 * @date 2010-04-30
25 *
26 * ini style config file
27 */
28 #include <stdlib.h>
29 #include <string.h>
30 #include <search.h>
31 #include <libintl.h>
32 #include <limits.h>
33 #include <locale.h>
34
35 #include "fcitx/fcitx.h"
36 #include "fcitx-config.h"
37 #include "fcitx-utils/log.h"
38 #include "hotkey.h"
39 #include "fcitx-utils/utils.h"
40
41
42 #define IsColorValid(c) ((c) >=0 && (c) <= 255)
43
44 #define RoundColor(c) ((c)>=0?((c)<=255?c:255):0)
45
46 /**
47 * Config Option parse function
48 **/
49 typedef FcitxConfigSyncResult(*FcitxConfigOptionFunc)(FcitxConfigOption *, FcitxConfigSync);
50
51 static FcitxConfigSyncResult FcitxConfigOptionInteger(FcitxConfigOption *option, FcitxConfigSync sync);
52 static FcitxConfigSyncResult FcitxConfigOptionBoolean(FcitxConfigOption *option, FcitxConfigSync sync);
53 static FcitxConfigSyncResult FcitxConfigOptionEnum(FcitxConfigOption *option, FcitxConfigSync sync);
54 static FcitxConfigSyncResult FcitxConfigOptionColor(FcitxConfigOption *option, FcitxConfigSync sync);
55 static FcitxConfigSyncResult FcitxConfigOptionString(FcitxConfigOption *option, FcitxConfigSync sync);
56 static FcitxConfigSyncResult FcitxConfigOptionHotkey(FcitxConfigOption *option, FcitxConfigSync sync);
57 static FcitxConfigSyncResult FcitxConfigOptionChar(FcitxConfigOption *option, FcitxConfigSync sync);
58 static FcitxConfigSyncResult FcitxConfigOptionI18NString(FcitxConfigOption *option, FcitxConfigSync sync);
59
60 /**
61 * File type is basically a string, but can be a hint for config tool
62 */
63 #define FcitxConfigOptionFile FcitxConfigOptionString
64
65 /**
66 * Font type is basically a string, but can be a hint for config tool
67 */
68 #define FcitxConfigOptionFont FcitxConfigOptionString
69
70 FCITX_EXPORT_API
FcitxConfigParseConfigFile(char * filename,FcitxConfigFileDesc * fileDesc)71 FcitxConfigFile *FcitxConfigParseConfigFile(char *filename, FcitxConfigFileDesc* fileDesc)
72 {
73 FILE* fp = fopen(filename, "r");
74
75 if (!fp)
76 return NULL;
77
78 FcitxConfigFile *cf = FcitxConfigParseConfigFileFp(fp, fileDesc);
79
80 fclose(fp);
81
82 return cf;
83 }
84
85 FCITX_EXPORT_API
FcitxConfigParseMultiConfigFile(char ** filename,int len,FcitxConfigFileDesc * fileDesc)86 FcitxConfigFile *FcitxConfigParseMultiConfigFile(char **filename, int len, FcitxConfigFileDesc*fileDesc)
87 {
88 FILE **fp = malloc(sizeof(FILE*) * len);
89 int i = 0;
90
91 for (i = 0 ; i < len ; i++) {
92 fp[i] = fopen(filename[i], "r");
93 }
94
95 FcitxConfigFile *cf = FcitxConfigParseMultiConfigFileFp(fp, len, fileDesc);
96
97 for (i = 0 ; i < len ; i++)
98 if (fp[i])
99 fclose(fp[i]);
100
101 free(fp);
102
103 return cf;
104 }
105
106 FCITX_EXPORT_API
FcitxConfigParseMultiConfigFileFp(FILE ** fp,int len,FcitxConfigFileDesc * fileDesc)107 FcitxConfigFile *FcitxConfigParseMultiConfigFileFp(FILE **fp, int len, FcitxConfigFileDesc* fileDesc)
108 {
109 FcitxConfigFile* cfile = NULL;
110 int i = 0;
111
112 for (i = 0 ; i < len ; i ++)
113 cfile = FcitxConfigParseIniFp(fp[i], cfile);
114
115 /* create a empty one, CheckConfig will do other thing for us */
116 if (cfile == NULL)
117 cfile = (FcitxConfigFile*) fcitx_utils_malloc0(sizeof(FcitxConfigFile));
118
119 if (FcitxConfigCheckConfigFile(cfile, fileDesc)) {
120 return cfile;
121 }
122
123 FcitxConfigFreeConfigFile(cfile);
124
125 return NULL;
126
127 }
128
129 FCITX_EXPORT_API
FcitxConfigParseConfigFileFp(FILE * fp,FcitxConfigFileDesc * fileDesc)130 FcitxConfigFile *FcitxConfigParseConfigFileFp(FILE *fp, FcitxConfigFileDesc* fileDesc)
131 {
132 FcitxConfigFile *cfile = FcitxConfigParseIniFp(fp, NULL);
133
134 /* create a empty one, CheckConfig will do other thing for us */
135
136 if (cfile == NULL)
137 cfile = (FcitxConfigFile*) fcitx_utils_malloc0(sizeof(FcitxConfigFile));
138
139 if (FcitxConfigCheckConfigFile(cfile, fileDesc)) {
140 return cfile;
141 }
142
143 FcitxConfigFreeConfigFile(cfile);
144
145 return NULL;
146 }
147
148 FCITX_EXPORT_API
FcitxConfigCheckConfigFile(FcitxConfigFile * cfile,FcitxConfigFileDesc * cfdesc)149 boolean FcitxConfigCheckConfigFile(FcitxConfigFile *cfile, FcitxConfigFileDesc* cfdesc)
150 {
151 if (!cfile)
152 return false;
153
154 HASH_FOREACH(cgdesc, cfdesc->groupsDesc, FcitxConfigGroupDesc) {
155 FcitxConfigGroup* group;
156 HASH_FIND_STR(cfile->groups, cgdesc->groupName, group);
157
158 if (!group) {
159 group = fcitx_utils_malloc0(sizeof(FcitxConfigGroup));
160 group->groupName = strdup(cgdesc->groupName);
161 group->groupDesc = cgdesc;
162 group->options = NULL;
163 HASH_ADD_KEYPTR(hh, cfile->groups, group->groupName, strlen(group->groupName), group);
164 }
165
166 HASH_FOREACH(codesc, cgdesc->optionsDesc, FcitxConfigOptionDesc) {
167 FcitxConfigOption *option;
168 HASH_FIND_STR(group->options, codesc->optionName, option);
169
170 if (!option) {
171 if (!codesc->rawDefaultValue) {
172 FcitxLog(WARNING, "missing value: %s", codesc->optionName);
173 return false;
174 }
175
176 option = fcitx_utils_malloc0(sizeof(FcitxConfigOption));
177
178 option->optionName = strdup(codesc->optionName);
179 option->rawValue = strdup(codesc->rawDefaultValue);
180 HASH_ADD_KEYPTR(hh, group->options, option->optionName, strlen(option->optionName), option);
181 }
182
183 option->optionDesc = codesc;
184 }
185 }
186
187 cfile->fileDesc = cfdesc;
188
189 return true;
190 }
191
192 /**
193 * @brief
194 *
195 * @param filename
196 *
197 * @return
198 */
199 FCITX_EXPORT_API
FcitxConfigParseConfigFileDesc(char * filename)200 FcitxConfigFileDesc *FcitxConfigParseConfigFileDesc(char* filename)
201 {
202 FILE* fp = fopen(filename, "r");
203
204 if (!fp)
205 return NULL;
206
207 FcitxConfigFileDesc *cfdesc = FcitxConfigParseConfigFileDescFp(fp);
208
209 fclose(fp);
210
211 return cfdesc;
212 }
213
214 /**
215 * @brief
216 *
217 * @param fp
218 *
219 * @return
220 */
221 FCITX_EXPORT_API
FcitxConfigParseConfigFileDescFp(FILE * fp)222 FcitxConfigFileDesc *FcitxConfigParseConfigFileDescFp(FILE *fp)
223 {
224 FcitxConfigFile *cfile = FcitxConfigParseIniFp(fp, NULL);
225
226 if (!cfile)
227 return NULL;
228
229 FcitxConfigFileDesc *cfdesc = fcitx_utils_malloc0(sizeof(FcitxConfigFileDesc));
230
231 FcitxConfigGroup* group;
232
233 for (group = cfile->groups;
234 group != NULL;
235 group = (FcitxConfigGroup*)group->hh.next) {
236 FcitxConfigGroupDesc *cgdesc = NULL;
237 FcitxConfigOption *options = group->options, *option = NULL;
238
239 if (strcmp(group->groupName, "DescriptionFile") == 0) {
240 HASH_FIND_STR(options, "LocaleDomain", option);
241 cfdesc->domain = strdup(option->rawValue);
242 continue;
243 }
244
245 char * p = strchr(group->groupName, '/');
246
247 if (p == NULL)
248 continue;
249
250 unsigned int groupNameLen = p - group->groupName;
251
252 unsigned int optionNameLen = strlen(p + 1);
253
254 if (groupNameLen == 0 || optionNameLen == 0)
255 continue;
256 HASH_FIND(hh, cfdesc->groupsDesc, group->groupName,
257 groupNameLen, cgdesc);
258 if (!cgdesc) {
259 cgdesc = fcitx_utils_new(FcitxConfigGroupDesc);
260 cgdesc->groupName = fcitx_utils_set_str_with_len(
261 NULL, group->groupName, groupNameLen);
262 cgdesc->optionsDesc = NULL;
263 HASH_ADD_KEYPTR(hh, cfdesc->groupsDesc, cgdesc->groupName,
264 groupNameLen, cgdesc);
265 }
266 char *optionName = strdup(p + 1);
267 FcitxConfigOptionDesc2 *codesc2 = fcitx_utils_new(FcitxConfigOptionDesc2);
268 FcitxConfigOptionDesc *codesc = (FcitxConfigOptionDesc*) codesc2;
269
270 codesc->optionName = optionName;
271
272 codesc->rawDefaultValue = NULL;
273
274 HASH_ADD_KEYPTR(hh, cgdesc->optionsDesc, codesc->optionName, optionNameLen, codesc);
275
276 HASH_FIND_STR(options, "Advance", option);
277 if (option && strcmp(option->rawValue, "True") == 0)
278 codesc2->advance = true;
279 else
280 codesc2->advance = false;
281
282 HASH_FIND_STR(options, "Description", option);
283 if (option)
284 codesc->desc = strdup(option->rawValue);
285 else
286 codesc->desc = strdup("");
287
288 HASH_FIND_STR(options, "LongDescription", option);
289 if (option)
290 codesc2->longDesc = strdup(option->rawValue);
291 else
292 codesc2->longDesc = strdup("");
293
294
295 /* Processing Type */
296 HASH_FIND_STR(options, "Type", option);
297
298 if (option) {
299 if (!strcmp(option->rawValue, "Integer")) {
300 codesc->type = T_Integer;
301 FcitxConfigOption* coption;
302 coption = NULL;
303 HASH_FIND_STR(options, "Min", coption);
304 if (coption) {
305 codesc2->constrain.integerConstrain.min = atoi(coption->rawValue);
306 }
307 else {
308 codesc2->constrain.integerConstrain.min = INT_MIN;
309 }
310 coption = NULL;
311 HASH_FIND_STR(options, "Max", coption);
312 if (coption) {
313 codesc2->constrain.integerConstrain.max = atoi(coption->rawValue);
314 }
315 else {
316 codesc2->constrain.integerConstrain.max = INT_MAX;
317 }
318 }
319 else if (!strcmp(option->rawValue, "Color"))
320 codesc->type = T_Color;
321 else if (!strcmp(option->rawValue, "Char"))
322 codesc->type = T_Char;
323 else if (!strcmp(option->rawValue, "String")) {
324 codesc->type = T_String;
325 FcitxConfigOption* coption;
326 coption = NULL;
327 HASH_FIND_STR(options, "MaxLength", coption);
328 if (coption) {
329 codesc2->constrain.stringConstrain.maxLength = atoi(coption->rawValue);
330 }
331 } else if (!strcmp(option->rawValue, "I18NString"))
332 codesc->type = T_I18NString;
333 else if (!strcmp(option->rawValue, "Boolean"))
334 codesc->type = T_Boolean;
335 else if (!strcmp(option->rawValue, "File"))
336 codesc->type = T_File;
337 else if (!strcmp(option->rawValue, "Font"))
338 codesc->type = T_Font;
339 else if (!strcmp(option->rawValue, "Hotkey")) {
340 codesc->type = T_Hotkey;
341 FcitxConfigOption* coption;
342 coption = NULL;
343 HASH_FIND_STR(options, "AllowModifierOnly", coption);
344 if (coption) {
345 codesc2->constrain.hotkeyConstrain.allowModifierOnly = strcmp(coption->rawValue, "True") == 0;
346 }
347 coption = NULL;
348 HASH_FIND_STR(options, "DisallowNoModifer", coption);
349 if (coption) {
350 codesc2->constrain.hotkeyConstrain.disallowNoModifer = strcmp(coption->rawValue, "True") == 0;
351 }
352 } else if (!strcmp(option->rawValue, "Enum")) {
353 FcitxConfigOption *eoption;
354 codesc->type = T_Enum;
355 HASH_FIND_STR(options, "EnumCount", eoption);
356 boolean enumError = false;
357 int i = 0;
358
359 if (eoption) {
360 int ecount = atoi(eoption->rawValue);
361
362 if (ecount > 0) {
363 char enumname[FCITX_INT_LEN + strlen("Enum") + 1];
364 memcpy(enumname, "Enum", strlen("Enum"));
365 codesc->configEnum.enumDesc = malloc(sizeof(char*) * ecount);
366 codesc->configEnum.enumCount = ecount;
367 size_t nel = 0;
368 for (i = 0; i < ecount; i++) {
369 sprintf(enumname + strlen("Enum"), "%d", i);
370 HASH_FIND_STR(options, enumname, eoption);
371
372 if (eoption) {
373 void* entry = lfind(eoption->rawValue, codesc->configEnum.enumDesc, &nel, sizeof(char*), (int (*)(const void *, const void *)) strcmp);
374
375 if (entry) {
376 FcitxLog(WARNING, _("Enum option duplicated."));
377 }
378
379 codesc->configEnum.enumDesc[i] = strdup(eoption->rawValue);
380 } else {
381 enumError = true;
382 goto config_enum_final;
383 }
384 }
385 } else {
386 FcitxLog(WARNING, _("Enum option number must larger than 0"));
387 enumError = true;
388 goto config_enum_final;
389 }
390 }
391
392 config_enum_final:
393
394 if (enumError) {
395 int j = 0;
396
397 for (; j < i; i++)
398 free(codesc->configEnum.enumDesc[j]);
399
400 FcitxLog(WARNING, _("Enum Option is invalid, take it as string"));
401
402 codesc->type = T_String;
403 }
404
405 } else {
406 FcitxLog(WARNING, _("Unknown type, take it as string: %s"), option->rawValue);
407 codesc->type = T_String;
408 }
409 } else {
410 FcitxLog(WARNING, _("Missing type, take it as string"));
411 codesc->type = T_String;
412 }
413
414 /* processing default value */
415 HASH_FIND_STR(options, "DefaultValue", option);
416
417 if (option)
418 codesc->rawDefaultValue = strdup(option->rawValue);
419 }
420
421 FcitxConfigFreeConfigFile(cfile);
422
423 return cfdesc;
424 }
425
FcitxConfigOptionInteger(FcitxConfigOption * option,FcitxConfigSync sync)426 FcitxConfigSyncResult FcitxConfigOptionInteger(FcitxConfigOption *option, FcitxConfigSync sync)
427 {
428 if (!option->value.integer)
429 return SyncNoBinding;
430
431 switch (sync) {
432
433 case Raw2Value: {
434 int value = atoi(option->rawValue);
435 if (value > option->optionDesc2->constrain.integerConstrain.max || value < option->optionDesc2->constrain.integerConstrain.min)
436 return SyncInvalid;
437 *option->value.integer = value;
438 return SyncSuccess;
439 }
440
441 case Value2Raw:
442 if (*option->value.integer > option->optionDesc2->constrain.integerConstrain.max || *option->value.integer < option->optionDesc2->constrain.integerConstrain.min)
443 return SyncInvalid;
444
445 if (option->rawValue)
446 free(option->rawValue);
447
448 asprintf(&option->rawValue, "%d", *option->value.integer);
449
450 return SyncSuccess;
451
452 case ValueFree:
453 return SyncSuccess;
454 }
455
456 return SyncInvalid;
457 }
458
FcitxConfigOptionBoolean(FcitxConfigOption * option,FcitxConfigSync sync)459 FcitxConfigSyncResult FcitxConfigOptionBoolean(FcitxConfigOption *option, FcitxConfigSync sync)
460 {
461 if (!option->value.boolvalue)
462 return SyncNoBinding;
463
464 switch (sync) {
465
466 case Raw2Value:
467
468 if (strcmp(option->rawValue, "True") == 0)
469 *option->value.boolvalue = true;
470 else
471 *option->value.boolvalue = false;
472
473 return SyncSuccess;
474
475 case Value2Raw:
476 if (*option->value.boolvalue)
477 fcitx_utils_string_swap(&option->rawValue, "True");
478 else
479 fcitx_utils_string_swap(&option->rawValue, "False");
480
481 return SyncSuccess;
482
483 case ValueFree:
484 return SyncSuccess;
485 }
486
487 return SyncInvalid;
488 }
489
FcitxConfigOptionEnum(FcitxConfigOption * option,FcitxConfigSync sync)490 FcitxConfigSyncResult FcitxConfigOptionEnum(FcitxConfigOption *option, FcitxConfigSync sync)
491 {
492 if (!option->value.enumerate || !option->optionDesc)
493 return SyncNoBinding;
494
495 FcitxConfigOptionDesc *codesc = option->optionDesc;
496
497 FcitxConfigEnum* cenum = &codesc->configEnum;
498
499 int i = 0;
500
501 switch (sync) {
502
503 case Raw2Value:
504
505 for (i = 0; i < cenum->enumCount; i++) {
506 if (strcmp(cenum->enumDesc[i], option->rawValue) == 0) {
507 *option->value.enumerate = i;
508 return SyncSuccess;
509 }
510 }
511
512 return SyncInvalid;
513
514 case Value2Raw:
515
516 if (*option->value.enumerate < 0 || *option->value.enumerate >= cenum->enumCount)
517 return SyncInvalid;
518
519 fcitx_utils_string_swap(&option->rawValue, cenum->enumDesc[*option->value.enumerate]);
520
521 return SyncSuccess;
522
523 case ValueFree:
524 return SyncSuccess;
525 }
526
527 return SyncInvalid;
528 }
529
FcitxConfigOptionColor(FcitxConfigOption * option,FcitxConfigSync sync)530 FcitxConfigSyncResult FcitxConfigOptionColor(FcitxConfigOption *option, FcitxConfigSync sync)
531 {
532 if (!option->value.color)
533 return SyncNoBinding;
534
535 FcitxConfigColor *color = option->value.color;
536
537 int r = 0, g = 0, b = 0;
538
539 switch (sync) {
540
541 case Raw2Value:
542
543 if (sscanf(option->rawValue, "%d %d %d", &r, &g, &b) != 3)
544 return SyncInvalid;
545
546 if (IsColorValid(r) && IsColorValid(g) && IsColorValid(b)) {
547 color->r = r / 255.0;
548 color->g = g / 255.0;
549 color->b = b / 255.0;
550 return SyncSuccess;
551 }
552
553 return SyncInvalid;
554
555 case Value2Raw:
556 r = (int)(color->r * 255);
557 g = (int)(color->g * 255);
558 b = (int)(color->b * 255);
559 r = RoundColor(r);
560 g = RoundColor(g);
561 b = RoundColor(b);
562
563 fcitx_utils_free(option->rawValue);
564 option->rawValue = NULL;
565
566 asprintf(&option->rawValue, "%d %d %d", r, g , b);
567
568 return SyncSuccess;
569
570 case ValueFree:
571 return SyncSuccess;
572 }
573
574 return SyncInvalid;
575
576 }
577
FcitxConfigOptionString(FcitxConfigOption * option,FcitxConfigSync sync)578 FcitxConfigSyncResult FcitxConfigOptionString(FcitxConfigOption *option, FcitxConfigSync sync)
579 {
580 if (!option->value.string)
581 return SyncNoBinding;
582
583 switch (sync) {
584
585 case Raw2Value:
586 if (option->optionDesc2->constrain.stringConstrain.maxLength
587 && strlen(option->rawValue) > option->optionDesc2->constrain.stringConstrain.maxLength)
588 return SyncInvalid;
589 fcitx_utils_string_swap(option->value.string, option->rawValue);
590
591 return SyncSuccess;
592
593 case Value2Raw:
594 if (option->optionDesc2->constrain.stringConstrain.maxLength
595 && strlen(*option->value.string) > option->optionDesc2->constrain.stringConstrain.maxLength)
596 return SyncInvalid;
597 fcitx_utils_string_swap(&option->rawValue, *option->value.string);
598
599 return SyncSuccess;
600
601 case ValueFree:
602 fcitx_utils_free(*option->value.string);
603 *option->value.string = NULL;
604 return SyncSuccess;
605 }
606
607 return SyncInvalid;
608 }
609
FcitxConfigOptionI18NString(FcitxConfigOption * option,FcitxConfigSync sync)610 FcitxConfigSyncResult FcitxConfigOptionI18NString(FcitxConfigOption *option, FcitxConfigSync sync)
611 {
612 if (!option->value.string)
613 return SyncNoBinding;
614
615 switch (sync) {
616
617 case Raw2Value:
618 fcitx_utils_string_swap(option->value.string, FcitxConfigOptionGetLocaleString(option));
619
620 return SyncSuccess;
621
622 case Value2Raw:
623 /* read only */
624 return SyncSuccess;
625
626 case ValueFree:
627 fcitx_utils_free(*option->value.string);
628 *option->value.string = NULL;
629 return SyncSuccess;
630 }
631
632 return SyncInvalid;
633 }
634
FcitxConfigOptionGetLocaleString(FcitxConfigOption * option)635 const char* FcitxConfigOptionGetLocaleString(FcitxConfigOption* option)
636 {
637 char* locale = setlocale(LC_MESSAGES, NULL);
638 char buf[40];
639 char *p;
640 size_t len;
641
642 if ((p = strchr(locale, '.')) != NULL)
643 len = p - locale;
644 else
645 len = strlen(locale);
646
647 if (len > sizeof(buf))
648 return option->rawValue;
649
650 strncpy(buf, locale, len);
651
652 buf[len] = '\0';
653
654 FcitxConfigOptionSubkey* subkey = NULL;
655
656 HASH_FIND_STR(option->subkey, buf, subkey);
657
658 if (subkey)
659 return subkey->rawValue;
660 else
661 return option->rawValue;
662 }
663
FcitxConfigOptionChar(FcitxConfigOption * option,FcitxConfigSync sync)664 FcitxConfigSyncResult FcitxConfigOptionChar(FcitxConfigOption *option, FcitxConfigSync sync)
665 {
666 if (!option->value.chr)
667 return SyncNoBinding;
668
669 switch (sync) {
670 case Raw2Value:
671 *option->value.chr = *option->rawValue;
672 return SyncSuccess;
673
674 case Value2Raw:
675 option->rawValue = realloc(option->rawValue, 2);
676 option->rawValue[0] = *option->value.chr;
677 option->rawValue[1] = '\0';
678 return SyncSuccess;
679
680 case ValueFree:
681 return SyncSuccess;
682 }
683
684 return SyncInvalid;
685 }
686
FcitxConfigOptionHotkey(FcitxConfigOption * option,FcitxConfigSync sync)687 FcitxConfigSyncResult FcitxConfigOptionHotkey(FcitxConfigOption *option, FcitxConfigSync sync)
688 {
689 /* we assume all hotkey can have 2 candidate key */
690 if (!option->value.hotkey)
691 return SyncNoBinding;
692
693 switch (sync) {
694
695 case Raw2Value:
696
697 if (option->value.hotkey[0].desc) {
698 free(option->value.hotkey[0].desc);
699 option->value.hotkey[0].desc = NULL;
700 }
701
702 if (option->value.hotkey[1].desc) {
703 free(option->value.hotkey[1].desc);
704 option->value.hotkey[1].desc = NULL;
705 }
706
707 FcitxHotkeySetKey(option->rawValue, option->value.hotkey);
708
709 return SyncSuccess;
710
711 case Value2Raw:
712
713 if (option->rawValue)
714 free(option->rawValue);
715
716 if (option->value.hotkey[1].desc) {
717 fcitx_utils_alloc_cat_str(option->rawValue,
718 option->value.hotkey[0].desc,
719 " ", option->value.hotkey[1].desc);
720 } else if (option->value.hotkey[0].desc) {
721 option->rawValue = strdup(option->value.hotkey[0].desc);
722 } else {
723 option->rawValue = strdup("");
724 }
725 return SyncSuccess;
726
727 case ValueFree:
728 FcitxHotkeyFree(option->value.hotkey);
729 return SyncSuccess;
730 }
731
732 return SyncInvalid;
733 }
734
735 FCITX_EXPORT_API
FcitxConfigFreeConfigFile(FcitxConfigFile * cfile)736 void FcitxConfigFreeConfigFile(FcitxConfigFile* cfile)
737 {
738 if (!cfile)
739 return;
740
741 FcitxConfigGroup *groups = cfile->groups, *curGroup;
742
743 while (groups) {
744 curGroup = groups;
745 HASH_DEL(groups, curGroup);
746 FcitxConfigFreeConfigGroup(curGroup);
747 }
748
749 free(cfile);
750 }
751
752 FCITX_EXPORT_API
FcitxConfigFreeConfigFileDesc(FcitxConfigFileDesc * cfdesc)753 void FcitxConfigFreeConfigFileDesc(FcitxConfigFileDesc* cfdesc)
754 {
755 if (!cfdesc)
756 return;
757
758 FcitxConfigGroupDesc *cgdesc = cfdesc->groupsDesc, *curGroup;
759
760 while (cgdesc) {
761 curGroup = cgdesc;
762 HASH_DEL(cgdesc, curGroup);
763 FcitxConfigFreeConfigGroupDesc(curGroup);
764 }
765
766 if (cfdesc->domain)
767 free(cfdesc->domain);
768 free(cfdesc);
769 }
770
771 FCITX_EXPORT_API
FcitxConfigFreeConfigGroup(FcitxConfigGroup * group)772 void FcitxConfigFreeConfigGroup(FcitxConfigGroup *group)
773 {
774 FcitxConfigOption *option = group->options, *curOption;
775
776 while (option) {
777 curOption = option;
778 HASH_DEL(option, curOption);
779 FcitxConfigFreeConfigOption(curOption);
780 }
781
782 free(group->groupName);
783
784 free(group);
785 }
786
787 FCITX_EXPORT_API
FcitxConfigFreeConfigGroupDesc(FcitxConfigGroupDesc * cgdesc)788 void FcitxConfigFreeConfigGroupDesc(FcitxConfigGroupDesc *cgdesc)
789 {
790 FcitxConfigOptionDesc *codesc = cgdesc->optionsDesc, *curOption;
791
792 while (codesc) {
793 curOption = codesc;
794 HASH_DEL(codesc, curOption);
795 FcitxConfigFreeConfigOptionDesc(curOption);
796 }
797
798 free(cgdesc->groupName);
799
800 free(cgdesc);
801 }
802
803 FCITX_EXPORT_API
FcitxConfigFreeConfigOption(FcitxConfigOption * option)804 void FcitxConfigFreeConfigOption(FcitxConfigOption *option)
805 {
806 free(option->optionName);
807
808 FcitxConfigOptionSubkey* item = option->subkey;
809 while (item) {
810 FcitxConfigOptionSubkey* curitem = item;
811 HASH_DEL(item, curitem);
812 free(curitem->rawValue);
813 free(curitem->subkeyName);
814 free(curitem);
815 }
816
817 if (option->rawValue)
818 free(option->rawValue);
819
820 free(option);
821 }
822
823 FCITX_EXPORT_API
FcitxConfigFreeConfigOptionDesc(FcitxConfigOptionDesc * codesc)824 void FcitxConfigFreeConfigOptionDesc(FcitxConfigOptionDesc *codesc)
825 {
826 FcitxConfigOptionDesc2* codesc2 = (FcitxConfigOptionDesc2*) codesc;
827 free(codesc->optionName);
828
829 if (codesc->configEnum.enumCount > 0) {
830 int i = 0;
831
832 for (i = 0 ; i < codesc->configEnum.enumCount; i ++) {
833 free(codesc->configEnum.enumDesc[i]);
834 }
835
836 free(codesc->configEnum.enumDesc);
837 }
838
839 if (codesc->rawDefaultValue)
840 free(codesc->rawDefaultValue);
841
842 free(codesc->desc);
843 free (codesc2->longDesc);
844
845 free(codesc);
846 }
847
848 FCITX_EXPORT_API
FcitxConfigParseIni(char * filename,FcitxConfigFile * reuse)849 FcitxConfigFile* FcitxConfigParseIni(char* filename, FcitxConfigFile* reuse)
850 {
851 FILE* fp = fopen(filename, "r");
852
853 if (!fp)
854 return NULL;
855
856 FcitxConfigFile *cf = FcitxConfigParseIniFp(fp, reuse);
857
858 fclose(fp);
859
860 return cf;
861 }
862
863 FCITX_EXPORT_API
FcitxConfigParseIniFp(FILE * fp,FcitxConfigFile * cfile)864 FcitxConfigFile* FcitxConfigParseIniFp(FILE *fp, FcitxConfigFile *cfile)
865 {
866 char *line = NULL, *buf = NULL;
867 size_t len = 0;
868 int lineLen = 0;
869
870 if (!fp)
871 return cfile;
872
873 if (!cfile)
874 cfile = fcitx_utils_new(FcitxConfigFile);
875
876 FcitxConfigGroup* curGroup = NULL;
877
878 int lineNo = 0;
879
880 while (getline(&buf, &len, fp) != -1) {
881 lineNo ++;
882
883 if (line)
884 free(line);
885 line = fcitx_utils_trim(buf);
886
887 lineLen = strlen(line);
888
889 if (lineLen == 0 || line[0] == '#')
890 continue;
891
892 if (line[0] == '[') {
893 if (!(line[lineLen - 1] == ']' && lineLen != 2)) {
894 FcitxLog(ERROR, _("Configure group name error: line %d"), lineNo);
895 return NULL;
896 }
897
898 size_t grp_len = lineLen - 2;
899 HASH_FIND(hh, cfile->groups, line + 1, grp_len, curGroup);
900 if (curGroup) {
901 FcitxLog(DEBUG, _("Duplicate group name, "
902 "merge with the previous: %s :line %d"),
903 curGroup->groupName, lineNo);
904 continue;
905 }
906
907 char *groupName;
908 groupName = fcitx_utils_set_str_with_len(NULL, line + 1, grp_len);
909 curGroup = fcitx_utils_malloc0(sizeof(FcitxConfigGroup));
910 curGroup->groupName = groupName;
911 curGroup->options = NULL;
912 curGroup->groupDesc = NULL;
913 HASH_ADD_KEYPTR(hh, cfile->groups, curGroup->groupName,
914 grp_len, curGroup);
915 } else {
916 if (curGroup == NULL)
917 continue;
918
919 char *value = strchr(line, '=');
920
921 if (!value) {
922 FcitxLog(WARNING, _("Invalid Entry: line %d missing '='"), lineNo);
923 goto next_line;
924 }
925
926 if (line == value)
927 goto next_line;
928
929 *value = '\0';
930
931 value ++;
932
933 char *name = line;
934
935 /* check subkey */
936 char *subkeyname = NULL;
937
938 if ((subkeyname = strchr(name, '[')) != NULL) {
939 size_t namelen = strlen(name);
940
941 if (name[namelen - 1] == ']') {
942 /* there is a subkey */
943 *subkeyname = '\0';
944 subkeyname++;
945 name[namelen - 1] = '\0';
946 }
947 }
948
949 FcitxConfigOption *option;
950
951 HASH_FIND_STR(curGroup->options, name, option);
952
953 if (option) {
954 if (subkeyname) {
955 FcitxConfigOptionSubkey* subkey = NULL;
956 HASH_FIND_STR(option->subkey, subkeyname, subkey);
957
958 if (subkey) {
959 free(subkey->rawValue);
960 subkey->rawValue = strdup(value);
961 } else {
962 subkey = fcitx_utils_malloc0(sizeof(FcitxConfigOptionSubkey));
963 subkey->subkeyName = strdup(subkeyname);
964 subkey->rawValue = strdup(value);
965 HASH_ADD_KEYPTR(hh, option->subkey, subkey->subkeyName, strlen(subkey->subkeyName), subkey);
966 }
967 } else {
968 FcitxLog(DEBUG, _("Duplicate option, overwrite: line %d"), lineNo);
969 free(option->rawValue);
970 option->rawValue = strdup(value);
971 }
972 } else {
973 option = fcitx_utils_malloc0(sizeof(FcitxConfigOption));
974 option->optionName = strdup(name);
975 option->rawValue = strdup(value);
976 HASH_ADD_KEYPTR(hh, curGroup->options, option->optionName, strlen(option->optionName), option);
977
978 /* if the subkey is new, and no default key exists, so we can assign it to default value */
979
980 if (subkeyname) {
981 FcitxConfigOptionSubkey* subkey = NULL;
982 subkey = fcitx_utils_malloc0(sizeof(FcitxConfigOptionSubkey));
983 subkey->subkeyName = strdup(subkeyname);
984 subkey->rawValue = strdup(value);
985 HASH_ADD_KEYPTR(hh, option->subkey, subkey->subkeyName, strlen(subkey->subkeyName), subkey);
986 }
987 }
988 }
989
990 next_line:
991
992 continue;
993 }
994
995 if (line)
996 free(line);
997
998 if (buf)
999 free(buf);
1000
1001 return cfile;
1002 }
1003
1004 FCITX_EXPORT_API
FcitxConfigSaveConfigFile(char * filename,FcitxGenericConfig * cfile,FcitxConfigFileDesc * cdesc)1005 boolean FcitxConfigSaveConfigFile(char *filename, FcitxGenericConfig *cfile, FcitxConfigFileDesc* cdesc)
1006 {
1007 FILE* fp = fopen(filename, "w");
1008
1009 if (!fp)
1010 return false;
1011
1012 boolean result = FcitxConfigSaveConfigFileFp(fp, cfile, cdesc);
1013
1014 fclose(fp);
1015
1016 return result;
1017 }
1018
1019 FCITX_EXPORT_API
FcitxConfigFree(FcitxGenericConfig * config)1020 void FcitxConfigFree(FcitxGenericConfig* config)
1021 {
1022 FcitxConfigFile *cfile = config->configFile;
1023 FcitxConfigFileDesc *cdesc = NULL;
1024
1025 if (!cfile)
1026 return;
1027
1028 cdesc = cfile->fileDesc;
1029
1030 HASH_FOREACH(groupdesc, cdesc->groupsDesc, FcitxConfigGroupDesc) {
1031 FcitxConfigGroup *group = NULL;
1032 HASH_FIND_STR(cfile->groups, groupdesc->groupName, group);
1033 HASH_FOREACH(optiondesc, groupdesc->optionsDesc, FcitxConfigOptionDesc) {
1034 FcitxConfigOption *option = NULL;
1035
1036 if (group)
1037 HASH_FIND_STR(group->options, optiondesc->optionName, option);
1038
1039 FcitxConfigSyncValue(config, group, option, ValueFree);
1040 }
1041 }
1042
1043 FcitxConfigFreeConfigFile(cfile);
1044 }
1045
1046 FCITX_EXPORT_API
FcitxConfigBindSync(FcitxGenericConfig * config)1047 void FcitxConfigBindSync(FcitxGenericConfig* config)
1048 {
1049 FcitxConfigFile *cfile = config->configFile;
1050 FcitxConfigFileDesc *cdesc = NULL;
1051
1052 if (!cfile)
1053 return;
1054
1055 cdesc = cfile->fileDesc;
1056
1057 HASH_FOREACH(groupdesc, cdesc->groupsDesc, FcitxConfigGroupDesc) {
1058 FcitxConfigGroup *group = NULL;
1059 HASH_FIND_STR(cfile->groups, groupdesc->groupName, group);
1060
1061 HASH_FOREACH(optiondesc, groupdesc->optionsDesc, FcitxConfigOptionDesc) {
1062 FcitxConfigOption *option = NULL;
1063
1064 if (group)
1065 HASH_FIND_STR(group->options, optiondesc->optionName, option);
1066
1067 FcitxConfigSyncValue(config, group, option, Raw2Value);
1068 }
1069 }
1070 }
1071
1072 FCITX_EXPORT_API
FcitxConfigSyncValue(FcitxGenericConfig * config,FcitxConfigGroup * group,FcitxConfigOption * option,FcitxConfigSync sync)1073 void FcitxConfigSyncValue(FcitxGenericConfig* config, FcitxConfigGroup* group, FcitxConfigOption *option, FcitxConfigSync sync)
1074 {
1075 FcitxConfigOptionDesc *codesc = option->optionDesc;
1076
1077 FcitxConfigOptionFunc f = NULL;
1078
1079 if (codesc == NULL)
1080 return;
1081
1082 if (sync == Value2Raw)
1083 if (option->filter)
1084 option->filter(config, group, option, option->value.untype, sync, option->filterArg);
1085
1086 switch (codesc->type) {
1087
1088 case T_Integer:
1089 f = FcitxConfigOptionInteger;
1090 break;
1091
1092 case T_Color:
1093 f = FcitxConfigOptionColor;
1094 break;
1095
1096 case T_Boolean:
1097 f = FcitxConfigOptionBoolean;
1098 break;
1099
1100 case T_Enum:
1101 f = FcitxConfigOptionEnum;
1102 break;
1103
1104 case T_String:
1105 f = FcitxConfigOptionString;
1106 break;
1107
1108 case T_I18NString:
1109 f = FcitxConfigOptionI18NString;
1110 break;
1111
1112 case T_Hotkey:
1113 f = FcitxConfigOptionHotkey;
1114 break;
1115
1116 case T_File:
1117 f = FcitxConfigOptionFile;
1118 break;
1119
1120 case T_Font:
1121 f = FcitxConfigOptionFont;
1122 break;
1123
1124 case T_Char:
1125 f = FcitxConfigOptionChar;
1126 break;
1127 }
1128
1129 FcitxConfigSyncResult r = SyncNoBinding;
1130
1131 if (f)
1132 r = f(option, sync);
1133
1134 if (r == SyncInvalid) {
1135 if (codesc->rawDefaultValue) {
1136 FcitxLog(WARNING, _("Option %s is Invalid, Use Default Value %s"),
1137 option->optionName, codesc->rawDefaultValue);
1138 fcitx_utils_free(option->rawValue);
1139 option->rawValue = strdup(codesc->rawDefaultValue);
1140
1141 if (sync == Raw2Value)
1142 f(option, sync);
1143 } else {
1144 FcitxLog(ERROR, _("Option %s is Invalid."), option->optionName);
1145 }
1146 }
1147
1148 if (sync == Raw2Value)
1149 if (option->filter)
1150 option->filter(config, group, option, option->value.untype, sync, option->filterArg);
1151 }
1152
1153 FCITX_EXPORT_API
FcitxConfigSaveConfigFileFp(FILE * fp,FcitxGenericConfig * config,FcitxConfigFileDesc * cdesc)1154 boolean FcitxConfigSaveConfigFileFp(FILE* fp, FcitxGenericConfig *config, FcitxConfigFileDesc* cdesc)
1155 {
1156 if (!fp)
1157 return false;
1158
1159 FcitxConfigFile* cfile = config->configFile;
1160
1161 HASH_FOREACH(groupdesc, cdesc->groupsDesc, FcitxConfigGroupDesc) {
1162 fprintf(fp, "[%s]\n", groupdesc->groupName);
1163
1164 FcitxConfigGroup *group = NULL;
1165
1166 if (cfile)
1167 HASH_FIND_STR(cfile->groups, groupdesc->groupName, group);
1168
1169 HASH_FOREACH(optiondesc, groupdesc->optionsDesc, FcitxConfigOptionDesc) {
1170 FcitxConfigOption *option = NULL;
1171
1172 if (group)
1173 HASH_FIND_STR(group->options, optiondesc->optionName, option);
1174
1175 if (optiondesc->desc && strlen(optiondesc->desc) != 0)
1176 fprintf(fp, "# %s\n", dgettext(cdesc->domain, optiondesc->desc));
1177
1178 switch (optiondesc->type) {
1179 case T_Enum: {
1180 fprintf(fp, "# %s\n", _("Available Value:"));
1181 int i;
1182 for (i = 0; i < optiondesc->configEnum.enumCount; i++)
1183 fprintf(fp, "# %s\n", optiondesc->configEnum.enumDesc[i]);
1184 }
1185 break;
1186 case T_Boolean: {
1187 fprintf(fp, "# %s\n", _("Available Value:"));
1188 fprintf(fp, "# True False\n");
1189 }
1190 break;
1191 default:
1192 break;
1193 }
1194
1195 if (!option) {
1196 if (optiondesc->rawDefaultValue)
1197 fprintf(fp, "#%s=%s\n", optiondesc->optionName,
1198 optiondesc->rawDefaultValue);
1199 else
1200 FcitxLog(FATAL, _("no default option for %s/%s"),
1201 groupdesc->groupName, optiondesc->optionName);
1202 } else {
1203 FcitxConfigSyncValue(config, group, option, Value2Raw);
1204 /* comment out the default value, for future automatical change */
1205 if (optiondesc->rawDefaultValue && strcmp(option->rawValue, optiondesc->rawDefaultValue) == 0)
1206 fprintf(fp, "#");
1207 fprintf(fp, "%s=%s\n", option->optionName, option->rawValue);
1208 HASH_FOREACH(subkey, option->subkey, FcitxConfigOptionSubkey) {
1209 fprintf(fp, "%s[%s]=%s\n", option->optionName, subkey->subkeyName, subkey->rawValue);
1210 }
1211 }
1212 }
1213
1214 fprintf(fp, "\n");
1215 }
1216
1217 return true;
1218 }
1219
1220 FCITX_EXPORT_API
FcitxConfigBindValue(FcitxConfigFile * cfile,const char * groupName,const char * optionName,void * var,FcitxSyncFilter filter,void * arg)1221 void FcitxConfigBindValue(FcitxConfigFile* cfile, const char *groupName, const char *optionName, void* var, FcitxSyncFilter filter, void *arg)
1222 {
1223 FcitxConfigGroup *group = NULL;
1224 HASH_FIND_STR(cfile->groups, groupName, group);
1225
1226 if (group) {
1227 FcitxConfigOption *option = NULL;
1228 HASH_FIND_STR(group->options, optionName, option);
1229
1230 if (option) {
1231 FcitxConfigOptionDesc* codesc = option->optionDesc;
1232 option->filter = filter;
1233 option->filterArg = arg;
1234
1235 if (!codesc) {
1236 FcitxLog(WARNING, "Unknown Option: %s/%s", groupName, optionName);
1237 return;
1238 }
1239
1240 switch (codesc->type) {
1241
1242 case T_Char:
1243 option->value.chr = (char*) var;
1244 break;
1245
1246 case T_Integer:
1247 option->value.integer = (int*) var;
1248 break;
1249
1250 case T_Color:
1251 option->value.color = (FcitxConfigColor*) var;
1252 break;
1253
1254 case T_Boolean:
1255 option->value.boolvalue = (boolean*) var;
1256 break;
1257
1258 case T_Hotkey:
1259 option->value.hotkey = (FcitxHotkey*) var;
1260 break;
1261
1262 case T_Enum:
1263 option->value.enumerate = (int*) var;
1264 break;
1265
1266 case T_I18NString:
1267
1268 case T_String:
1269
1270 case T_File:
1271
1272 case T_Font:
1273 option->value.string = (char**) var;
1274 break;
1275 }
1276 }
1277 }
1278 }
1279
1280 FCITX_EXPORT_API
FcitxConfigGetBindValue(FcitxGenericConfig * config,const char * groupName,const char * optionName)1281 FcitxConfigValueType FcitxConfigGetBindValue(FcitxGenericConfig *config, const char *groupName, const char* optionName)
1282 {
1283 FcitxConfigFile* cfile = config->configFile;
1284 FcitxConfigGroup *group = NULL;
1285 FcitxConfigValueType null;
1286 memset(&null, 0, sizeof(FcitxConfigValueType));
1287 HASH_FIND_STR(cfile->groups, groupName, group);
1288
1289 if (group) {
1290 FcitxConfigOption *option = NULL;
1291 HASH_FIND_STR(group->options, optionName, option);
1292
1293 if (option) {
1294 return option->value;
1295 }
1296 }
1297
1298 return null;
1299
1300 }
1301
1302 FCITX_EXPORT_API
FcitxConfigDescGetOptionDesc(FcitxConfigFileDesc * cfdesc,const char * groupName,const char * optionName)1303 const FcitxConfigOptionDesc* FcitxConfigDescGetOptionDesc(FcitxConfigFileDesc* cfdesc, const char* groupName, const char* optionName)
1304 {
1305 FcitxConfigGroupDesc* groupDesc;
1306 HASH_FIND_STR(cfdesc->groupsDesc, groupName, groupDesc);
1307
1308 if (groupDesc) {
1309 FcitxConfigOptionDesc *optionDesc = NULL;
1310 HASH_FIND_STR(groupDesc->optionsDesc, optionName, optionDesc);
1311
1312 if (optionDesc) {
1313 return optionDesc;
1314 }
1315 }
1316
1317 return NULL;
1318 }
1319
1320 FCITX_EXPORT_API
FcitxConfigFileGetOption(FcitxConfigFile * cfile,const char * groupName,const char * optionName)1321 FcitxConfigOption* FcitxConfigFileGetOption(FcitxConfigFile* cfile, const char* groupName, const char* optionName)
1322 {
1323 FcitxConfigGroup* group;
1324 HASH_FIND_STR(cfile->groups, groupName, group);
1325
1326 if (group) {
1327 FcitxConfigOption *option = NULL;
1328 HASH_FIND_STR(group->options, optionName, option);
1329
1330 if (option) {
1331 return option;
1332 }
1333 }
1334
1335 return NULL;
1336 }
1337
1338
1339 FCITX_EXPORT_API
FcitxConfigResetConfigToDefaultValue(FcitxGenericConfig * config)1340 void FcitxConfigResetConfigToDefaultValue(FcitxGenericConfig* config)
1341 {
1342 FcitxConfigFile* cfile = config->configFile;
1343
1344 if (!cfile)
1345 return;
1346
1347 FcitxConfigFileDesc* cfdesc = cfile->fileDesc;
1348
1349 if (!cfdesc)
1350 return;
1351
1352 HASH_FOREACH(cgdesc, cfdesc->groupsDesc, FcitxConfigGroupDesc) {
1353 FcitxConfigGroup* group;
1354 HASH_FIND_STR(cfile->groups, cgdesc->groupName, group);
1355
1356 if (!group) {
1357 /* should not happen */
1358 continue;
1359 }
1360
1361 HASH_FOREACH(codesc, cgdesc->optionsDesc, FcitxConfigOptionDesc) {
1362 FcitxConfigOption *option;
1363 HASH_FIND_STR(group->options, codesc->optionName, option);
1364
1365 if (!option) {
1366 /* should not happen */
1367 continue;
1368 }
1369
1370 if (!codesc->rawDefaultValue) {
1371 /* ignore it, actually the reset is meaningless
1372 * if it doesn't have a default value. */
1373 continue;
1374 }
1375
1376 if (option->rawValue)
1377 free(option->rawValue);
1378
1379 option->rawValue = strdup(codesc->rawDefaultValue);
1380 }
1381 }
1382 }
1383 // kate: indent-mode cstyle; space-indent on; indent-width 4;
1384