Add to check RW config file and reset config info
[platform/core/uifw/tts.git] / common / tts_config_parser.c
1 /*
2 *  Copyright (c) 2011-2016 Samsung Electronics Co., Ltd All Rights Reserved 
3 *  Licensed under the Apache License, Version 2.0 (the "License");
4 *  you may not use this file except in compliance with the License.
5 *  You may obtain a copy of the License at
6 *  http://www.apache.org/licenses/LICENSE-2.0
7 *  Unless required by applicable law or agreed to in writing, software
8 *  distributed under the License is distributed on an "AS IS" BASIS,
9 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 *  See the License for the specific language governing permissions and
11 *  limitations under the License.
12 */
13
14 #include <dlog.h>
15 #include <stdio.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <vconf.h>
19
20 #include "tts_config_parser.h"
21 #include "tts_defs.h"
22
23
24 #define TTS_TAG_ENGINE_BASE_TAG         "tts-engine"
25 #define TTS_TAG_ENGINE_NAME             "name"
26 #define TTS_TAG_ENGINE_ID               "id"
27 #define TTS_TAG_ENGINE_SETTING          "setting"
28 #define TTS_TAG_ENGINE_VOICE_SET        "voices"
29 #define TTS_TAG_ENGINE_VOICE            "voice"
30 #define TTS_TAG_ENGINE_VOICE_TYPE       "type"
31 #define TTS_TAG_ENGINE_PITCH_SUPPORT    "pitch-support"
32 #define TTS_TAG_ENGINE_TEXT_SIZE        "text-size"
33
34 #define TTS_TAG_CONFIG_BASE_TAG         "tts-config"
35 #define TTS_TAG_CONFIG_ENGINE_ID        "engine"
36 #define TTS_TAG_CONFIG_ENGINE_SETTING   "engine-setting"
37 #define TTS_TAG_CONFIG_AUTO_VOICE       "auto"
38 #define TTS_TAG_CONFIG_VOICE_TYPE       "voice-type"
39 #define TTS_TAG_CONFIG_LANGUAGE         "language"
40 #define TTS_TAG_CONFIG_SPEECH_RATE      "speech-rate"
41 #define TTS_TAG_CONFIG_PITCH            "pitch"
42 #define TTS_TAG_VOICE_TYPE_FEMALE       "female"
43 #define TTS_TAG_VOICE_TYPE_MALE         "male"
44 #define TTS_TAG_VOICE_TYPE_CHILD        "child"
45
46 #define TTS_MAX_TEXT_SIZE       2000
47
48 extern char* tts_tag();
49
50 static xmlDocPtr g_config_doc = NULL;
51 char g_engine_id[128] = {0,};
52 char g_setting[128] = {0,};
53 char g_language[128] = {0,};
54
55 int tts_parser_get_engine_info(const char* path, tts_engine_info_s** engine_info)
56 {
57         if (NULL == path || NULL == engine_info) {
58                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
59                 return -1;
60         }
61
62         bool isTextsize = false;
63         xmlDocPtr doc = NULL;
64         xmlNodePtr cur = NULL;
65         xmlChar *key = NULL;
66         xmlChar *attr = NULL;
67
68         doc = xmlParseFile(path);
69         if (doc == NULL) {
70                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse xml file");
71                 return -1;
72         }
73
74         cur = xmlDocGetRootElement(doc);
75         if (cur == NULL) {
76                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
77                 xmlFreeDoc(doc);
78                 doc = NULL;
79                 return -1;
80         }
81
82         if (xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_BASE_TAG)) {
83                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT 'tts-engine'");
84                 xmlFreeDoc(doc);
85                 doc = NULL;
86                 return -1;
87         }
88
89         cur = cur->xmlChildrenNode;
90         if (cur == NULL) {
91                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
92                 xmlFreeDoc(doc);
93                 doc = NULL;
94                 return -1;
95         }
96
97         /* alloc engine info */
98         tts_engine_info_s* temp;
99         temp = (tts_engine_info_s*)calloc(1, sizeof(tts_engine_info_s));
100         if (NULL == temp) {
101                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Out of memory");
102                 xmlFreeDoc(doc);
103                 doc = NULL;
104                 return -1;
105         }
106
107         temp->name = NULL;
108         temp->uuid = NULL;
109         temp->voices = NULL;
110         temp->setting = NULL;
111         temp->pitch_support = false;
112         temp->text_size = 0;
113
114         while (cur != NULL) {
115                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_NAME)) {
116                         key = xmlNodeGetContent(cur);
117                         if (NULL != key) {
118                                 if (NULL != temp->name) {
119                                         free(temp->name);
120                                         temp->name = NULL;
121                                 }
122                                 temp->name = strdup((char*)key);
123                                 xmlFree(key);
124                                 key = NULL;
125                         } else {
126                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_NAME);
127                         }
128                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_ID)) {
129                         key = xmlNodeGetContent(cur);
130                         if (NULL != key) {
131                                 if (NULL != temp->uuid) {
132                                         free(temp->uuid);
133                                         temp->uuid = NULL;
134                                 }
135                                 temp->uuid = strdup((char*)key);
136                                 xmlFree(key);
137                                 key = NULL;
138                         } else {
139                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_ID);
140                         }
141                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_SETTING)) {
142                         key = xmlNodeGetContent(cur);
143                         if (NULL != key) {
144                                 if (NULL != temp->setting) {
145                                         free(temp->setting);
146                                         temp->setting = NULL;
147                                 }
148                                 temp->setting = strdup((char*)key);
149                                 xmlFree(key);
150                                 key = NULL;
151                         } else {
152                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_SETTING);
153                         }
154                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_VOICE_SET)) {
155                         xmlNodePtr voice_node = NULL;
156                         voice_node = cur->xmlChildrenNode;
157
158                         while (NULL != voice_node) {
159                                 if (0 == xmlStrcmp(voice_node->name, (const xmlChar *)TTS_TAG_ENGINE_VOICE)) {
160                                         tts_config_voice_s* temp_voice = (tts_config_voice_s*)calloc(1, sizeof(tts_config_voice_s));
161                                         if (NULL == temp_voice) {
162                                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Out of memory");
163                                                 break;
164                                         }
165
166                                         attr = xmlGetProp(voice_node, (const xmlChar*)TTS_TAG_ENGINE_VOICE_TYPE);
167                                         if (NULL != attr) {
168                                                 if (0 == xmlStrcmp(attr, (const xmlChar *)TTS_TAG_VOICE_TYPE_FEMALE)) {
169                                                         temp_voice->type = (int)TTS_CONFIG_VOICE_TYPE_FEMALE;
170                                                 } else if (0 == xmlStrcmp(attr, (const xmlChar *)TTS_TAG_VOICE_TYPE_MALE)) {
171                                                         temp_voice->type = (int)TTS_CONFIG_VOICE_TYPE_MALE;
172                                                 } else if (0 == xmlStrcmp(attr, (const xmlChar *)TTS_TAG_VOICE_TYPE_CHILD)) {
173                                                         temp_voice->type = (int)TTS_CONFIG_VOICE_TYPE_CHILD;
174                                                 } else {
175                                                         temp_voice->type = (int)TTS_CONFIG_VOICE_TYPE_USER_DEFINED;
176                                                 }
177                                                 xmlFree(attr);
178                                                 attr = NULL;
179                                         } else {
180                                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_VOICE_TYPE);
181                                                 free(temp_voice);
182                                                 temp_voice = NULL;
183                                                 continue;
184                                         }
185
186                                         key = xmlNodeGetContent(voice_node);
187                                         if (NULL != key) {
188                                                 if (NULL != temp_voice->language) {
189                                                         free(temp_voice->language);
190                                                         temp_voice->language = NULL;
191                                                 }
192                                                 temp_voice->language = strdup((char*)key);
193                                                 xmlFree(key);
194                                                 key = NULL;
195                                                 temp->voices = g_slist_append(temp->voices, temp_voice);
196                                         } else {
197                                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_VOICE);
198                                                 if (NULL != temp_voice) {
199                                                         free(temp_voice);
200                                                         temp_voice = NULL;
201                                                 }
202                                         }
203                                 }
204                                 voice_node = voice_node->next;
205                         }
206                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_PITCH_SUPPORT)) {
207                         key = xmlNodeGetContent(cur);
208                         if (NULL != key) {
209                                 if (0 == xmlStrcmp(key, (const xmlChar *)"true")) {
210                                         temp->pitch_support = true;
211                                 }
212                                 xmlFree(key);
213                                 key = NULL;
214                         } else {
215                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] <%s> has no content", TTS_TAG_ENGINE_PITCH_SUPPORT);
216                         }
217                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_ENGINE_TEXT_SIZE)) {
218                         isTextsize = true;
219                         key = xmlNodeGetContent(cur);
220                         if (NULL != key) {
221                                 temp->text_size = atoi((char*)key);
222                                 xmlFree(key);
223                         } else {
224                                 SLOG(LOG_INFO, tts_tag(), "[INFO] text size is unlimited.");
225                                 temp->text_size = 0;
226                         }
227                 }
228                 cur = cur->next;
229         }
230
231         if (false == isTextsize) {
232                 temp->text_size = TTS_MAX_TEXT_SIZE;
233         }
234
235         xmlFreeDoc(doc);
236         doc = NULL;
237
238         if (NULL == temp->uuid) {
239                 /* Invalid engine */
240                 SECURE_SLOG(LOG_ERROR, tts_tag(), "[ERROR] Invalid engine : %s", path);
241                 tts_parser_free_engine_info(temp);
242                 return -1;
243         }
244
245         *engine_info = temp;
246
247         return 0;
248 }
249
250 int tts_parser_free_engine_info(tts_engine_info_s* engine_info)
251 {
252         if (NULL == engine_info) {
253                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
254                 return -1;
255         }
256
257         if (NULL != engine_info->name) {
258                 free(engine_info->name);
259                 engine_info->name = NULL;
260         }
261         if (NULL != engine_info->uuid) {
262                 free(engine_info->uuid);
263                 engine_info->uuid = NULL;
264         }
265         if (NULL != engine_info->setting) {
266                 free(engine_info->setting);
267                 engine_info->setting = NULL;
268         }
269
270         tts_config_voice_s *temp_voice;
271         temp_voice = g_slist_nth_data(engine_info->voices, 0);
272
273         while (NULL != temp_voice) {
274                 if (NULL != temp_voice) {
275                         if (NULL != temp_voice->language) {
276                                 free(temp_voice->language);
277                                 temp_voice->language = NULL;
278                         }
279                         engine_info->voices = g_slist_remove(engine_info->voices, temp_voice);
280                         free(temp_voice);
281                         temp_voice = NULL;
282                 }
283
284                 temp_voice = g_slist_nth_data(engine_info->voices, 0);
285         }
286
287         if (NULL != engine_info) {
288                 free(engine_info);
289                 engine_info = NULL;
290         }
291
292         return 0;
293 }
294
295 int tts_parser_print_engine_info(tts_engine_info_s* engine_info)
296 {
297         if (NULL == engine_info) {
298                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
299                 return -1;
300         }
301
302         SLOG(LOG_DEBUG, tts_tag(), "== get engine info ==");
303         SLOG(LOG_DEBUG, tts_tag(), " name : %s", engine_info->name);
304         SLOG(LOG_DEBUG, tts_tag(), " id   : %s", engine_info->uuid);
305         if (NULL != engine_info->setting)
306                 SLOG(LOG_DEBUG, tts_tag(), " setting : %s", engine_info->setting);
307
308         SLOG(LOG_DEBUG, tts_tag(), " voices");
309         GSList *iter = NULL;
310         tts_config_voice_s *temp_voice;
311
312         if (g_slist_length(engine_info->voices) > 0) {
313                 /* Get a first item */
314                 iter = g_slist_nth(engine_info->voices, 0);
315
316                 int i = 1;
317                 while (NULL != iter) {
318                         /*Get handle data from list*/
319                         temp_voice = iter->data;
320
321                         SLOG(LOG_DEBUG, tts_tag(), "  [%dth] type(%d) lang(%s)", 
322                                 i, temp_voice->type, temp_voice->language);
323
324                         /*Get next item*/
325                         iter = g_slist_next(iter);
326                         i++;
327                 }
328         } else {
329                 SLOG(LOG_ERROR, tts_tag(), "  Voice is NONE");
330         }
331
332         SLOG(LOG_DEBUG, tts_tag(), "@@@");
333
334         return 0;
335 }
336
337 int tts_parser_load_config(tts_config_s** config_info)
338 {
339         if (NULL == config_info) {
340                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
341                 return -1;
342         }
343
344         xmlDocPtr doc = NULL;
345         xmlNodePtr cur = NULL;
346         xmlChar *key;
347         bool is_default_open = false;
348
349         /* For Thread safety */
350         xmlInitParser();
351
352         if (0 != access(TTS_CONFIG, F_OK)) {
353                 doc = xmlParseFile(TTS_DEFAULT_CONFIG);
354                 if (doc == NULL) {
355                         SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse file error : %s", TTS_DEFAULT_CONFIG);
356                         xmlCleanupParser();
357                         return -1;
358                 }
359                 is_default_open = true;
360         } else {
361                 int retry_count = 0;
362
363                 while (NULL == doc) {
364                         doc = xmlParseFile(TTS_CONFIG);
365                         if (NULL != doc) {
366                                 break;
367                         }
368                         retry_count++;
369                         usleep(10000);
370
371                         if (TTS_RETRY_COUNT == retry_count) {
372                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse file error : %s", TTS_CONFIG);
373                                 doc = xmlParseFile(TTS_DEFAULT_CONFIG);
374                                 if (NULL == doc) {
375                                         SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse file error : %s", TTS_DEFAULT_CONFIG);
376                                         xmlCleanupParser();
377                                         return -1;
378                                 }
379                                 is_default_open = true;
380                                 break;
381                         }
382                 }
383         }
384
385         cur = xmlDocGetRootElement(doc);
386         if (cur == NULL) {
387                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
388                 xmlFreeDoc(doc);
389                 doc = NULL;
390                 xmlCleanupParser();
391                 return -1;
392         }
393
394         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
395                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
396                 xmlFreeDoc(doc);
397                 doc = NULL;
398                 xmlCleanupParser();
399                 return -1;
400         }
401
402         cur = cur->xmlChildrenNode;
403         if (cur == NULL) {
404                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
405                 xmlFreeDoc(doc);
406                 doc = NULL;
407                 xmlCleanupParser();
408                 return -1;
409         }
410
411         /* alloc engine info */
412         tts_config_s* temp;
413         temp = (tts_config_s*)calloc(1, sizeof(tts_config_s));
414         if (NULL == temp) {
415                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Out of memory");
416                 xmlFreeDoc(doc);
417                 doc = NULL;
418                 xmlCleanupParser();
419                 return -1;
420         }
421
422         memset(g_engine_id, '\0', sizeof(g_engine_id));
423         memset(g_setting, '\0', sizeof(g_setting));
424         memset(g_language, '\0', sizeof(g_language));
425
426         temp->engine_id = g_engine_id;
427         temp->setting = g_setting;
428         temp->language = g_language;
429
430         while (cur != NULL) {
431                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_ENGINE_ID)) {
432                         key = xmlNodeGetContent(cur);
433                         if (NULL != key) {
434                                 strncpy(temp->engine_id, (char*)key, sizeof(g_engine_id) - 1);
435                                 xmlFree(key);
436                                 key = NULL;
437                         } else {
438                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] engine id is NULL");
439                         }
440                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_ENGINE_SETTING)) {
441                         key = xmlNodeGetContent(cur);
442                         if (NULL != key) {
443                                 strncpy(temp->setting, (char*)key, sizeof(g_setting) - 1);
444                                 xmlFree(key);
445                                 key = NULL;
446                         } else {
447                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] setting path is NULL");
448                         }
449                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_AUTO_VOICE)) {
450                         key = xmlNodeGetContent(cur);
451                         if (NULL != key) {
452                                 if (0 == xmlStrcmp(key, (const xmlChar *)"on")) {
453                                         temp->auto_voice = true;
454                                 } else if (0 == xmlStrcmp(key, (const xmlChar *)"off")) {
455                                         temp->auto_voice = false;
456                                 } else {
457                                         SLOG(LOG_ERROR, tts_tag(), "Auto voice is wrong");
458                                         temp->auto_voice = true;
459                                 }
460
461                                 xmlFree(key);
462                                 key = NULL;
463                         } else {
464                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] voice type is NULL");
465                         }
466                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_VOICE_TYPE)) {
467                         key = xmlNodeGetContent(cur);
468                         if (NULL != key) {
469                                 if (0 == xmlStrcmp(key, (const xmlChar *)TTS_TAG_VOICE_TYPE_MALE)) {
470                                         temp->type = (int)TTS_CONFIG_VOICE_TYPE_MALE;
471                                 } else if (0 == xmlStrcmp(key, (const xmlChar *)TTS_TAG_VOICE_TYPE_FEMALE)) {
472                                         temp->type = (int)TTS_CONFIG_VOICE_TYPE_FEMALE;
473                                 } else if (0 == xmlStrcmp(key, (const xmlChar *)TTS_TAG_VOICE_TYPE_CHILD)) {
474                                         temp->type = (int)TTS_CONFIG_VOICE_TYPE_CHILD;
475                                 } else {
476                                         SLOG(LOG_WARN, tts_tag(), "Voice type is user defined");
477                                         temp->type = (int)TTS_CONFIG_VOICE_TYPE_USER_DEFINED;
478                                 }
479
480                                 xmlFree(key);
481                                 key = NULL;
482                         } else {
483                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] voice type is NULL");
484                         }
485                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_LANGUAGE)) {
486                         key = xmlNodeGetContent(cur);
487                         if (NULL != key) {
488                                 strncpy(temp->language, (char*)key, sizeof(g_language) - 1);
489                                 xmlFree(key);
490                                 key = NULL;
491                         } else {
492                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] engine uuid is NULL");
493                         }
494
495                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_SPEECH_RATE)) {
496                         key = xmlNodeGetContent(cur);
497                         if (NULL != key) {
498                                 temp->speech_rate = atoi((char*)key);
499                                 xmlFree(key);
500                         } else {
501                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] speech rate is NULL");
502                         }
503                 } else if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_PITCH)) {
504                         key = xmlNodeGetContent(cur);
505                         if (NULL != key) {
506                                 temp->pitch = atoi((char*)key);
507                                 xmlFree(key);
508                                 key = NULL;
509                         } else {
510                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Pitch is NULL");
511                         }
512                 } else {
513
514                 }
515
516                 cur = cur->next;
517         }
518
519         *config_info = temp;
520         g_config_doc = doc;
521
522         if (true == is_default_open) {
523                 int retry_count = 0;
524                 int ret = -1;
525                 do {
526                         ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
527                         if (0 < ret)
528                                 break;
529                         retry_count++;
530                         usleep(10000);
531
532                         if (TTS_RETRY_COUNT == retry_count) {
533                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
534                                 return -1;
535                         }
536                 } while (0 != ret);
537
538                 /* Set mode */
539                 if (0 > chmod(TTS_CONFIG, 0600)) {
540                         SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to change file mode : %d", ret);
541                 }
542
543                 /* Set owner */
544                 if (0 > chown(TTS_CONFIG, 5000, 5000)) {
545                         SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to change file owner : %d", ret);
546                 }
547                 SLOG(LOG_DEBUG, tts_tag(), "Default config is changed : pid(%d)", getpid());
548         }
549
550         return 0;
551 }
552
553 int tts_parser_unload_config(tts_config_s* config_info)
554 {
555         if (NULL != g_config_doc) {
556                 xmlFreeDoc(g_config_doc);
557                 g_config_doc = NULL;
558         }
559         if (NULL != config_info) {
560                 free(config_info);
561                 config_info = NULL;
562         }
563
564         xmlCleanupParser();
565
566         return 0;
567 }
568
569
570 int tts_parser_copy_xml(const char* original, const char* destination)
571 {
572         if (NULL == original || NULL == destination) {
573                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
574                 return -1;
575         }
576
577         xmlDocPtr doc = NULL;
578         doc = xmlParseFile(original);
579         if (doc == NULL) {
580                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse file error : %s", original);
581                 return -1;
582         }
583
584         int ret = xmlSaveFile(destination, doc);
585         if (0 > ret) {
586                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
587         } else {
588                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", destination);
589         }
590
591         /* Set mode */
592         if (0 > chmod(destination, 0600)) {
593                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to change file mode : %d", ret);
594         }
595
596         xmlFreeDoc(doc);
597         doc = NULL;
598         SLOG(LOG_DEBUG, tts_tag(), "[SUCCESS] Copying xml");
599
600         return 0;
601 }
602
603 int tts_parser_set_engine(const char* engine_id, const char* setting, const char* language, int type)
604 {
605         if (NULL == engine_id) {
606                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
607                 return -1;
608         }
609
610         xmlNodePtr cur = NULL;
611         cur = xmlDocGetRootElement(g_config_doc);
612         if (cur == NULL) {
613                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
614                 return -1;
615         }
616
617         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
618                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
619                 return -1;
620         }
621
622         cur = cur->xmlChildrenNode;
623         if (cur == NULL) {
624                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
625                 return -1;
626         }
627
628         while (cur != NULL) {
629                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_ENGINE_ID)) {
630                         xmlNodeSetContent(cur, (const xmlChar *)engine_id);
631                 }
632
633                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_LANGUAGE)) {
634                         xmlNodeSetContent(cur, (const xmlChar *)language);
635                 }
636
637                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_ENGINE_SETTING)) {
638                         xmlNodeSetContent(cur, (const xmlChar *)setting);
639                 }
640
641                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_VOICE_TYPE)) {
642                         switch (type) {
643                         case TTS_CONFIG_VOICE_TYPE_MALE:        xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_MALE);        break;
644                         case TTS_CONFIG_VOICE_TYPE_FEMALE:      xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_FEMALE);      break;
645                         case TTS_CONFIG_VOICE_TYPE_CHILD:       xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_CHILD);       break;
646                         default:                                xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_FEMALE);      break;
647                         }
648                 }
649
650                 cur = cur->next;
651         }
652
653         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
654         if (0 > ret) {
655                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
656         } else {
657                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", TTS_CONFIG);
658         }
659
660         return 0;
661 }
662
663 int tts_parser_set_voice(const char* language, int type)
664 {
665         if (NULL == language) {
666                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
667                 return -1;
668         }
669
670         xmlNodePtr cur = NULL;
671         cur = xmlDocGetRootElement(g_config_doc);
672         if (cur == NULL) {
673                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
674                 return -1;
675         }
676
677         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
678                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
679                 return -1;
680         }
681
682         cur = cur->xmlChildrenNode;
683         if (cur == NULL) {
684                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
685                 return -1;
686         }
687
688         while (cur != NULL) {
689                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_LANGUAGE)) {
690                         xmlNodeSetContent(cur, (const xmlChar *)language);
691                 }
692
693                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_VOICE_TYPE)) {
694                         switch (type) {
695                         case TTS_CONFIG_VOICE_TYPE_MALE:        xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_MALE);        break;
696                         case TTS_CONFIG_VOICE_TYPE_FEMALE:      xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_FEMALE);      break;
697                         case TTS_CONFIG_VOICE_TYPE_CHILD:       xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_CHILD);       break;
698                         default:
699                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Invalid type : %d", type);
700                                 xmlNodeSetContent(cur, (const xmlChar*)TTS_TAG_VOICE_TYPE_FEMALE);
701                                 break;
702                         }
703                 }
704
705                 cur = cur->next;
706         }
707
708         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
709         if (0 > ret) {
710                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
711         } else {
712                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", TTS_CONFIG);
713         }
714
715         return 0;
716 }
717
718 int tts_parser_set_auto_voice(bool value)
719 {
720         xmlNodePtr cur = NULL;
721         cur = xmlDocGetRootElement(g_config_doc);
722         if (cur == NULL) {
723                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
724                 return -1;
725         }
726
727         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
728                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
729                 return -1;
730         }
731
732         cur = cur->xmlChildrenNode;
733         if (cur == NULL) {
734                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
735                 return -1;
736         }
737
738         while (cur != NULL) {
739                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_AUTO_VOICE)) {
740                         if (true == value) {
741                                 xmlNodeSetContent(cur, (const xmlChar *)"on");
742                         } else if (false == value) {
743                                 xmlNodeSetContent(cur, (const xmlChar *)"off");
744                         } else {
745                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong value of auto voice");
746                                 return -1;
747                         }
748                         break;
749                 }
750                 cur = cur->next;
751         }
752
753         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
754         if (0 > ret) {
755                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
756         } else {
757                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", TTS_CONFIG);
758         }
759
760         return 0;
761 }
762
763 int tts_parser_set_speech_rate(int value)
764 {
765         xmlNodePtr cur = NULL;
766         cur = xmlDocGetRootElement(g_config_doc);
767         if (cur == NULL) {
768                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
769                 return -1;
770         }
771
772         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
773                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
774                 return -1;
775         }
776
777         cur = cur->xmlChildrenNode;
778         if (cur == NULL) {
779                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
780                 return -1;
781         }
782
783         while (cur != NULL) {
784                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_SPEECH_RATE)) {
785                         char temp[10];
786                         memset(temp, '\0', 10);
787                         snprintf(temp, 10, "%d", value);
788
789                         xmlNodeSetContent(cur, (const xmlChar *)temp);
790
791                         SLOG(LOG_DEBUG, tts_tag(), "Set speech rate : %s", temp);
792                         break;
793                 }
794
795                 cur = cur->next;
796         }
797
798         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
799         if (0 > ret) {
800                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
801         } else {
802                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", TTS_CONFIG);
803         }
804
805         return 0;
806 }
807
808 int tts_parser_set_pitch(int value)
809 {
810         xmlNodePtr cur = NULL;
811         cur = xmlDocGetRootElement(g_config_doc);
812         if (cur == NULL) {
813                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
814                 return -1;
815         }
816
817         if (xmlStrcmp(cur->name, (const xmlChar *) TTS_TAG_CONFIG_BASE_TAG)) {
818                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
819                 return -1;
820         }
821
822         cur = cur->xmlChildrenNode;
823         if (cur == NULL) {
824                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
825                 return -1;
826         }
827
828         while (cur != NULL) {
829                 if (0 == xmlStrcmp(cur->name, (const xmlChar *)TTS_TAG_CONFIG_PITCH)) {
830                         char temp[10];
831                         memset(temp, '\0', 10);
832                         snprintf(temp, 10, "%d", value);
833                         xmlNodeSetContent(cur, (const xmlChar *)temp);
834                         break;
835                 }
836
837                 cur = cur->next;
838         }
839
840         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
841         if (0 > ret) {
842                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Save result : %d", ret);
843         } else {
844                 SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Success to save %s", TTS_CONFIG);
845         }
846
847         return 0;
848 }
849
850 int tts_parser_find_config_changed(char** engine, char**setting, bool* auto_voice, char** language, int* voice_type, 
851                                    int* speech_rate, int* pitch)
852 {
853         if (NULL == engine || NULL == setting || NULL == language || NULL == voice_type || NULL == speech_rate) {
854                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Input parameter is NULL");
855                 return -1;
856         }
857
858         xmlDocPtr doc = NULL;
859         xmlNodePtr cur_new = NULL;
860         xmlNodePtr cur_old = NULL;
861
862         xmlChar *key_new;
863         xmlChar *key_old;
864
865         int retry_count = 0;
866         while (NULL == doc) {
867                 doc = xmlParseFile(TTS_CONFIG);
868                 if (NULL != doc) {
869                         break;
870                 }
871                 retry_count++;
872                 usleep(10000);
873
874                 if (TTS_RETRY_COUNT == retry_count) {
875                         SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse file error : %s", TTS_CONFIG);
876                         return -1;
877                 }
878         }
879
880         cur_new = xmlDocGetRootElement(doc);
881         cur_old = xmlDocGetRootElement(g_config_doc);
882         if (cur_new == NULL || cur_old == NULL) {
883                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
884                 xmlFreeDoc(doc);
885                 doc = NULL;
886                 return -1;
887         }
888
889         if (xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_BASE_TAG) || 
890         xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_BASE_TAG)) {
891                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] The wrong type, root node is NOT %s", TTS_TAG_CONFIG_BASE_TAG);
892                 xmlFreeDoc(doc);
893                 doc = NULL;
894                 return -1;
895         }
896
897         cur_new = cur_new->xmlChildrenNode;
898         cur_old = cur_old->xmlChildrenNode;
899         if (cur_new == NULL || cur_old == NULL) {
900                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Empty document");
901                 xmlFreeDoc(doc);
902                 doc = NULL;
903                 return -1;
904         }
905
906         while (cur_new != NULL && cur_old != NULL) {
907                 if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_ENGINE_ID)) {
908                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_ENGINE_ID)) {
909                                 key_old = xmlNodeGetContent(cur_old);
910                                 if (NULL != key_old) {
911                                         key_new = xmlNodeGetContent(cur_new);
912                                         if (NULL != key_new) {
913                                                 if (0 != xmlStrcmp(key_old, key_new)) {
914                                                         SLOG(LOG_DEBUG, tts_tag(), "Old engine id(%s), New engine(%s)", 
915                                                                 (char*)key_old, (char*)key_new);
916                                                         if (NULL != *engine) {
917                                                                 free(*engine);
918                                                                 *engine = NULL;
919                                                         }
920                                                         *engine = strdup((char*)key_new);
921                                                 }
922                                                 xmlFree(key_new);
923                                                 key_new = NULL;
924                                         }
925                                         xmlFree(key_old);
926                                         key_old = NULL;
927                                 }
928                         } else {
929                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
930                         }
931                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_ENGINE_SETTING)) {
932                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_ENGINE_SETTING)) {
933                                 key_old = xmlNodeGetContent(cur_old);
934                                 if (NULL != key_old) {
935                                         key_new = xmlNodeGetContent(cur_new);
936                                         if (NULL != key_new) {
937                                                 if (0 != xmlStrcmp(key_old, key_new)) {
938                                                         SLOG(LOG_DEBUG, tts_tag(), "Old engine setting(%s), New engine setting(%s)", 
939                                                                 (char*)key_old, (char*)key_new);
940                                                         if (NULL != *setting) {
941                                                                 free(*setting);
942                                                                 *setting = NULL;
943                                                         }
944                                                         *setting = strdup((char*)key_new);
945                                                 }
946                                                 xmlFree(key_new);
947                                                 key_new = NULL;
948                                         }
949                                         xmlFree(key_old);
950                                         key_old = NULL;
951                                 }
952                         } else {
953                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
954                         }
955                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_AUTO_VOICE)) {
956                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_AUTO_VOICE)) {
957                                 key_old = xmlNodeGetContent(cur_old);
958                                 if (NULL != key_old) {
959                                         key_new = xmlNodeGetContent(cur_new);
960                                         if (NULL != key_new) {
961                                                 if (0 != xmlStrcmp(key_old, key_new)) {
962                                                         SLOG(LOG_DEBUG, tts_tag(), "Old auto voice (%s), New auto voice(%s)", 
963                                                                 (char*)key_old, (char*)key_new);
964                                                         if (0 == xmlStrcmp((const xmlChar*)"on", key_new)) {
965                                                                 *auto_voice = true;
966                                                         } else {
967                                                                 *auto_voice = false;
968                                                         }
969                                                 }
970                                                 xmlFree(key_new);
971                                                 key_new = NULL;
972                                         }
973                                         xmlFree(key_old);
974                                         key_old = NULL;
975                                 }
976                         } else {
977                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
978                         }
979                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_LANGUAGE)) {
980                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_LANGUAGE)) {
981                                 key_old = xmlNodeGetContent(cur_old);
982                                 if (NULL != key_old) {
983                                         key_new = xmlNodeGetContent(cur_new);
984                                         if (NULL != key_new) {
985                                                 if (0 != xmlStrcmp(key_old, key_new)) {
986                                                         SLOG(LOG_DEBUG, tts_tag(), "Old language(%s), New language(%s)", 
987                                                                 (char*)key_old, (char*)key_new);
988                                                         if (NULL != *language) {
989                                                                 free(*language);
990                                                                 *language = NULL;
991                                                         }
992                                                         *language = strdup((char*)key_new);
993                                                 }
994                                                 xmlFree(key_new);
995                                                 key_new = NULL;
996                                         }
997                                         xmlFree(key_old);
998                                         key_old = NULL;
999                                 }
1000                         } else {
1001                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
1002                         }
1003                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_VOICE_TYPE)) {
1004                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_VOICE_TYPE)) {
1005                                 key_old = xmlNodeGetContent(cur_old);
1006                                 if (NULL != key_old) {
1007                                         key_new = xmlNodeGetContent(cur_new);
1008                                         if (NULL != key_new) {
1009                                                 if (0 != xmlStrcmp(key_old, key_new)) {
1010                                                         SLOG(LOG_DEBUG, tts_tag(), "Old voice type(%s), New voice type(%s)", 
1011                                                                 (char*)key_old, (char*)key_new);
1012                                                         if (0 == xmlStrcmp(key_new, (const xmlChar *)TTS_TAG_VOICE_TYPE_FEMALE)) {
1013                                                                 *voice_type = (int)TTS_CONFIG_VOICE_TYPE_FEMALE;
1014                                                         } else if (0 == xmlStrcmp(key_new, (const xmlChar *)TTS_TAG_VOICE_TYPE_MALE)) {
1015                                                                 *voice_type = (int)TTS_CONFIG_VOICE_TYPE_MALE;
1016                                                         } else if (0 == xmlStrcmp(key_new, (const xmlChar *)TTS_TAG_VOICE_TYPE_CHILD)) {
1017                                                                 *voice_type = (int)TTS_CONFIG_VOICE_TYPE_CHILD;
1018                                                         } else {
1019                                                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] New voice type is not valid");
1020                                                         }
1021                                                 }
1022                                                 xmlFree(key_new);
1023                                                 key_new = NULL;
1024                                         }
1025                                         xmlFree(key_old);
1026                                         key_old = NULL;
1027                                 }
1028                         } else {
1029                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
1030                         }
1031                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_SPEECH_RATE)) {
1032                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_SPEECH_RATE)) {
1033                                 key_old = xmlNodeGetContent(cur_old);
1034                                 if (NULL != key_old) {
1035                                         key_new = xmlNodeGetContent(cur_new);
1036                                         if (NULL != key_new) {
1037                                                 if (0 != xmlStrcmp(key_old, key_new)) {
1038                                                         SLOG(LOG_DEBUG, tts_tag(), "Old speech rate(%s), New speech rate(%s)", 
1039                                                                 (char*)key_old, (char*)key_new);
1040                                                         *speech_rate = atoi((char*)key_new);
1041                                                 }
1042                                                 xmlFree(key_new);
1043                                                 key_new = NULL;
1044                                         }
1045                                         xmlFree(key_old);
1046                                         key_old = NULL;
1047                                 }
1048                         } else {
1049                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
1050                         }
1051                 } else if (0 == xmlStrcmp(cur_new->name, (const xmlChar*)TTS_TAG_CONFIG_PITCH)) {
1052                         if (0 == xmlStrcmp(cur_old->name, (const xmlChar*)TTS_TAG_CONFIG_PITCH)) {
1053                                 key_old = xmlNodeGetContent(cur_old);
1054                                 if (NULL != key_old) {
1055                                         key_new = xmlNodeGetContent(cur_new);
1056                                         if (NULL != key_new) {
1057                                                 if (0 != xmlStrcmp(key_old, key_new)) {
1058                                                         SLOG(LOG_DEBUG, tts_tag(), "Old pitch(%s), New pitch(%s)", 
1059                                                                 (char*)key_old, (char*)key_new);
1060                                                         *pitch = atoi((char*)key_new);
1061                                                 }
1062                                                 xmlFree(key_new);
1063                                                 key_new = NULL;
1064                                         }
1065                                         xmlFree(key_old);
1066                                         key_old = NULL;
1067                                 }
1068                         } else {
1069                                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] old config and new config are different");
1070                         }
1071                 } else {
1072
1073                 }
1074
1075                 cur_new = cur_new->next;
1076                 cur_old = cur_old->next;
1077         }
1078
1079         if (NULL != g_config_doc) {
1080                 xmlFreeDoc(g_config_doc);
1081                 g_config_doc = NULL;
1082         }
1083         g_config_doc = doc;
1084
1085         return 0;
1086 }
1087
1088 int tts_parser_reset()
1089 {
1090         SLOG(LOG_DEBUG, tts_tag(), "[DEBUG] Reset g_config_doc as %s", TTS_DEFAULT_CONFIG);
1091
1092         if (NULL != g_config_doc) {
1093                 xmlFreeDoc(g_config_doc);
1094                 g_config_doc = NULL;
1095         }
1096
1097         g_config_doc = xmlParseFile(TTS_DEFAULT_CONFIG);
1098         if (NULL == g_config_doc) {
1099                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to parse %s", TTS_DEFAULT_CONFIG);
1100                 return -1;
1101         }
1102
1103         int ret = xmlSaveFile(TTS_CONFIG, g_config_doc);
1104         if (0 > ret) {
1105                 SLOG(LOG_ERROR, tts_tag(), "[ERROR] Fail to save %s", TTS_CONFIG);
1106         }
1107
1108         return 0;
1109 }