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