Merge "Fix NULL deference" into tizen
[platform/core/uifw/tts.git] / server / ttsd_engine_agent.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
15 #include <dlfcn.h>
16 #include <dirent.h>
17
18 #include "tts_defs.h"
19 #include "ttsd_main.h"
20 #include "ttsd_engine_agent.h"
21 #include "ttsd_config.h"
22
23 #define ENGINE_PATH_SIZE        256
24
25 /*
26 * Internal data structure
27 */
28 typedef struct {
29         /* base info */
30         char*   engine_uuid;
31         char*   engine_name;
32         char*   engine_path;
33
34         /* info for using engine load */
35         bool    is_set;
36         bool    is_loaded;
37         bool    need_network;
38         void    *handle;
39
40         /* engine base setting */
41         char*   default_lang;
42         int     default_vctype;
43         int     default_speed;
44         int     default_pitch;
45
46         ttspe_funcs_s*  pefuncs;
47         ttspd_funcs_s*  pdfuncs;
48
49         int (*ttsp_load_engine)(const ttspd_funcs_s* pdfuncs, ttspe_funcs_s* pefuncs);
50         int (*ttsp_unload_engine)();
51 } ttsengine_s;
52
53 typedef struct {
54         char*   engine_uuid;
55         char*   engine_path;
56         char*   engine_name;
57         char*   setting_ug_path;
58         bool    use_network;
59 } ttsengine_info_s;
60
61 typedef struct {
62         bool    is_default;
63         bool    is_loaded;
64         int     client_ref_count;
65
66         char*   lang;
67         int     type;
68 } ttsvoice_s;
69
70
71 /** Init flag */
72 static bool g_agent_init;
73
74 /** TTS engine list */
75 static GList *g_engine_list;
76
77 /** Current engine information */
78 static ttsengine_s g_cur_engine;
79
80 /** Current voice information */
81 static GSList* g_cur_voices;
82
83 /** Result callback function */
84 static synth_result_callback g_result_cb;
85
86
87 /** Set current engine */
88 static int __internal_set_current_engine(const char* engine_uuid);
89
90 /** Check engine id */
91 static int __internal_check_engine_id(const char* engine_uuid);
92
93 /** Update engine list */
94 static int __internal_update_engine_list();
95
96 /** Get engine info */
97 static int __internal_get_engine_info(const char* filepath, ttsengine_info_s** info);
98
99 /** Callback function for result */
100 bool __result_cb(ttsp_result_event_e event, const void* data, unsigned int data_size, 
101                  ttsp_audio_type_e audio_type, int rate, void *user_data);
102
103 /** Callback function for voice list */
104 static bool __supported_voice_cb(const char* language, int type, void* user_data);
105
106 /** Free voice list */
107 static void __free_voice_list(GList* voice_list);
108
109 /** Callback function for engine info */
110 static void __engine_info_cb(const char* engine_uuid, const char* engine_name, const char* setting_ug_name,
111                       bool use_network, void* user_data);
112
113
114 /** Print list */
115 static int ttsd_print_enginelist();
116
117 static int ttsd_print_voicelist();
118
119 static const char* __ttsd_get_engine_error_code(ttsp_error_e err)
120 {
121         switch (err) {
122         case TTSP_ERROR_NONE:                   return "TTSP_ERROR_NONE";
123         case TTSP_ERROR_OUT_OF_MEMORY:          return "TTSP_ERROR_OUT_OF_MEMORY";
124         case TTSP_ERROR_IO_ERROR:               return "TTSP_ERROR_IO_ERROR";
125         case TTSP_ERROR_INVALID_PARAMETER:      return "TTSP_ERROR_INVALID_PARAMETER";
126         case TTSP_ERROR_OUT_OF_NETWORK:         return "TTSP_ERROR_OUT_OF_NETWORK";
127         case TTSP_ERROR_INVALID_STATE:          return "TTSP_ERROR_INVALID_STATE";
128         case TTSP_ERROR_INVALID_VOICE:          return "TTSP_ERROR_INVALID_VOICE";
129         case TTSP_ERROR_OPERATION_FAILED:       return "TTSP_ERROR_OPERATION_FAILED";
130         default:
131                 return "Invalid error code";
132         }
133 }
134
135 int ttsd_engine_agent_init(synth_result_callback result_cb)
136 {
137         /* initialize static data */
138         if (result_cb == NULL) {
139                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] invalid parameter");
140                 return TTSD_ERROR_INVALID_PARAMETER;
141         }
142
143         g_result_cb = result_cb;
144
145         g_cur_engine.engine_uuid = NULL;
146         g_cur_engine.engine_name = NULL;
147         g_cur_engine.engine_path = NULL;
148
149         g_cur_engine.is_set = false;
150         g_cur_engine.handle = NULL;
151         g_cur_engine.pefuncs = (ttspe_funcs_s*)calloc(1, sizeof(ttspe_funcs_s));
152         if (NULL == g_cur_engine.pefuncs) {
153                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to allocate memory");
154                 return TTSP_ERROR_OUT_OF_MEMORY;
155         }
156
157         g_cur_engine.pdfuncs = (ttspd_funcs_s*)calloc(1, sizeof(ttspd_funcs_s));
158         if (NULL == g_cur_engine.pdfuncs) {
159                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to allocate memory");
160                 free(g_cur_engine.pefuncs);
161                 g_cur_engine.pefuncs = NULL;
162                 return TTSP_ERROR_OUT_OF_MEMORY;
163         }
164
165         g_agent_init = true;
166
167         if (0 != ttsd_config_get_default_voice(&(g_cur_engine.default_lang), &(g_cur_engine.default_vctype))) {
168                 SLOG(LOG_WARN, get_tag(), "[Server WARNING] There is No default voice in config");
169                 /* Set default voice */
170                 g_cur_engine.default_lang = strdup(TTS_BASE_LANGUAGE);
171                 g_cur_engine.default_vctype = TTSP_VOICE_TYPE_FEMALE;
172         }
173
174         if (0 != ttsd_config_get_default_speed(&(g_cur_engine.default_speed))) {
175                 SLOG(LOG_WARN, get_tag(), "[Server WARNING] There is No default speed in config");
176                 g_cur_engine.default_speed = TTS_SPEED_NORMAL;
177         }
178
179         if (0 != ttsd_config_get_default_pitch(&(g_cur_engine.default_pitch))) {
180                 SLOG(LOG_WARN, get_tag(), "[Server WARNING] There is No default pitch in config");
181                 g_cur_engine.default_pitch = TTS_PITCH_NORMAL;
182         }
183
184         SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Initialize Engine Agent");
185
186         return 0;
187 }
188
189 int ttsd_engine_agent_release()
190 {
191         if (false == g_agent_init) {
192                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
193                 return TTSD_ERROR_OPERATION_FAILED;
194         }
195
196         /* unload current engine */
197         ttsd_engine_agent_unload_current_engine();
198
199         /* release engine list */
200         GList *iter = NULL;
201         ttsengine_info_s *data = NULL;
202
203         if (g_list_length(g_engine_list) > 0) {
204                 /* Get a first item */
205                 iter = g_list_first(g_engine_list);
206                 while (NULL != iter) {
207                         /* Get data from item */
208                         data = iter->data;
209                         iter = g_list_remove(iter, data);
210
211                         if (NULL != data) {
212                                 if (NULL != data->engine_uuid)          free(data->engine_uuid);
213                                 if (NULL != data->engine_name)          free(data->engine_name);
214                                 if (NULL != data->setting_ug_path)      free(data->setting_ug_path);
215                                 if (NULL != data->engine_path)          free(data->engine_path);
216                                 free(data);
217                         }
218                 }
219         }
220         g_list_free(iter);
221
222         /* release current engine data */
223         if (g_cur_engine.engine_uuid != NULL)   free(g_cur_engine.engine_uuid);
224         if (g_cur_engine.engine_name != NULL)   free(g_cur_engine.engine_name);
225         if (g_cur_engine.engine_path != NULL)   free(g_cur_engine.engine_path);
226
227         if (g_cur_engine.pefuncs != NULL)       free(g_cur_engine.pefuncs);
228         if (g_cur_engine.pdfuncs != NULL)       free(g_cur_engine.pdfuncs);
229         if (g_cur_engine.default_lang != NULL)  free(g_cur_engine.default_lang);
230         g_result_cb = NULL;
231         g_agent_init = false;
232
233         SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Release Engine Agent");
234
235         return 0;
236 }
237
238 int ttsd_engine_agent_initialize_current_engine()
239 {
240         if (false == g_agent_init) {
241                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
242                 return TTSD_ERROR_OPERATION_FAILED;
243         }
244
245         /* update engine list */
246         if (0 != __internal_update_engine_list()) {
247                 SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] No engine error");
248                 return TTSD_ERROR_OPERATION_FAILED;
249         }
250
251         /* 2. get current engine from config */
252         char* cur_engine_uuid = NULL;
253         bool is_get_engineid_from_config = false;
254
255         if (0 != ttsd_config_get_default_engine(&cur_engine_uuid)) {
256                 /*not set current engine */
257                 /*set system default engine*/
258                 GList *iter = NULL;
259                 ttsengine_info_s *data = NULL;
260
261                 if (g_list_length(g_engine_list) > 0) {
262                         iter = g_list_first(g_engine_list);
263                         if (NULL == iter) {
264                                 SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] Fail to get a engine of engine list");
265                                 return TTSD_ERROR_OPERATION_FAILED;
266                         }
267                         data = iter->data;
268
269                         if (NULL != data) {
270                                 if (NULL != data->engine_uuid) {
271                                         cur_engine_uuid = strdup(data->engine_uuid);
272                                         ttsd_config_set_default_engine(cur_engine_uuid);
273                                 } else {
274                                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Data of current engine is corrupt");
275                                         return TTSD_ERROR_OPERATION_FAILED;
276                                 }
277                         }
278                 } else {
279                         SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] Fail to get a engine of engine list");
280                         return TTSD_ERROR_OPERATION_FAILED;
281                 }
282
283                 is_get_engineid_from_config = false;
284         } else {
285                 is_get_engineid_from_config = true;
286         }
287
288         if (NULL == cur_engine_uuid) {
289                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Current engine id is NULL");
290                 return TTSD_ERROR_OPERATION_FAILED;
291         }
292
293         /* check whether cur engine uuid is valid or not. */
294         if (0 != __internal_check_engine_id(cur_engine_uuid)) {
295                 SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] It is not valid engine id from config");
296
297                 GList *iter = NULL;
298
299                 if (g_list_length(g_engine_list) > 0)
300                         iter = g_list_first(g_engine_list);
301                 else {
302                         SLOG(LOG_WARN, get_tag(), "[Engine Agent ERROR] NO TTS Engine !!");
303                         if (NULL != cur_engine_uuid) {
304                                 free(cur_engine_uuid);
305                                 cur_engine_uuid = NULL;
306                         }
307                         return TTSD_ERROR_OPERATION_FAILED;
308                 }
309
310                 if (NULL == iter) {
311                         SLOG(LOG_WARN, get_tag(), "[Engine Agent ERROR] No valid TTS Engine");
312                         if (NULL != cur_engine_uuid) {
313                                 free(cur_engine_uuid);
314                                 cur_engine_uuid = NULL;
315                         }
316                         return TTSD_ERROR_OPERATION_FAILED;
317                 }
318
319                 if (cur_engine_uuid != NULL) {
320                         free(cur_engine_uuid);
321                         cur_engine_uuid = NULL;
322                 }
323                 ttsengine_info_s *data = NULL;
324                 data = iter->data;
325
326                 if (NULL == data || NULL == data->engine_uuid) {
327                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No valid TTS Engine");
328                         return TTSD_ERROR_OPERATION_FAILED;
329                 }
330
331                 cur_engine_uuid = strdup(data->engine_uuid);
332
333                 is_get_engineid_from_config = false;
334         }
335
336         if (NULL != cur_engine_uuid)
337                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Current Engine Id : %s", cur_engine_uuid);
338         else
339                 return TTSD_ERROR_OPERATION_FAILED;
340
341         /* set current engine */
342         if (0 != __internal_set_current_engine(cur_engine_uuid)) {
343                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to set current engine ");
344                 if (NULL != cur_engine_uuid)    free(cur_engine_uuid);
345                 return TTSD_ERROR_OPERATION_FAILED;
346         }
347
348         if (false == is_get_engineid_from_config) {
349                 if (0 != ttsd_config_set_default_engine(cur_engine_uuid)) {
350                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to set id to config");
351                 }
352         }
353
354         if (NULL != cur_engine_uuid)    free(cur_engine_uuid);
355
356         SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Set current engine");
357
358         return 0;
359 }
360
361 static int __internal_check_engine_id(const char* engine_uuid)
362 {
363         GList *iter = NULL;
364         ttsengine_s *data = NULL;
365
366         if (g_list_length(g_engine_list) > 0) {
367                 iter = g_list_first(g_engine_list);
368
369                 while (NULL != iter) {
370                         data = iter->data;
371
372                         if (0 == strncmp(engine_uuid, data->engine_uuid, strlen(data->engine_uuid)))
373                                 return 0;
374
375                         iter = g_list_next(iter);
376                 }
377         }
378
379         return -1;
380 }
381
382 void __engine_info_cb(const char* engine_uuid, const char* engine_name, const char* setting_ug_name, 
383                              bool use_network, void* user_data)
384 {
385         ttsengine_info_s* temp = (ttsengine_info_s*)user_data;
386
387         if (NULL != engine_uuid)
388                 temp->engine_uuid = strdup(engine_uuid);
389
390         if (NULL != engine_name)
391                 temp->engine_name = strdup(engine_name);
392
393         if (NULL != setting_ug_name)
394                 temp->setting_ug_path = strdup(setting_ug_name);
395
396         temp->use_network = use_network;
397         return;
398 }
399
400 static int __internal_get_engine_info(const char* filepath, ttsengine_info_s** info)
401 {
402         char *error;
403         void* handle;
404
405         handle = dlopen(filepath, RTLD_LAZY);
406
407         if (!handle) {
408                 SECURE_SLOG(LOG_WARN, get_tag(), "[Engine Agent] Invalid engine : %s", filepath);
409                 return TTSD_ERROR_OPERATION_FAILED;
410         }
411
412         /* link engine to daemon */
413         dlsym(handle, "ttsp_load_engine");
414         if ((error = dlerror()) != NULL) {
415                 SECURE_SLOG(LOG_WARN, get_tag(), "[Engine Agent] Fail to open ttsp_load_engine : path(%s) message(%s)", filepath, error);
416                 dlclose(handle);
417                 return TTSD_ERROR_OPERATION_FAILED;
418         }
419
420         dlsym(handle, "ttsp_unload_engine");
421         if ((error = dlerror()) != NULL) {
422                 SECURE_SLOG(LOG_WARN, get_tag(), "[Engine Agent] Fail to open ttsp_unload_engine : path(%s) message(%s)", filepath, error);
423                 dlclose(handle);
424                 return TTSD_ERROR_OPERATION_FAILED;
425         }
426
427         int (*get_engine_info)(ttsp_engine_info_cb callback, void* user_data);
428
429         get_engine_info = (int (*)(ttsp_engine_info_cb, void*))dlsym(handle, "ttsp_get_engine_info");
430         if (NULL != (error = dlerror()) || NULL == get_engine_info) {
431                 SLOG(LOG_WARN, get_tag(), "[Engine Agent] Fail to open ttsp_get_engine_info() :path(%s) message(%s)", filepath, error);
432                 dlclose(handle);
433                 return TTSD_ERROR_OPERATION_FAILED;
434         }
435
436         ttsengine_info_s* temp;
437         temp = (ttsengine_info_s*)calloc(1, sizeof(ttsengine_info_s));
438         if (NULL == temp) {
439                 SLOG(LOG_WARN, get_tag(), "[Engine Agent] Fail to alloc memory");
440                 dlclose(handle);
441                 return TTSD_ERROR_OUT_OF_MEMORY;
442         }
443
444         /* get engine info */
445         if (0 != get_engine_info(&__engine_info_cb, (void*)temp)) {
446                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to get engine info");
447                 dlclose(handle);
448                 free(temp);
449                 return TTSD_ERROR_OPERATION_FAILED;
450         }
451
452         /* close engine */
453         dlclose(handle);
454
455         if (TTSD_MODE_SCREEN_READER == ttsd_get_mode() && true == temp->use_network) {
456                 SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent WARNING] %s is invalid because of network based", temp->engine_name);
457                 if (NULL != temp->engine_uuid)          free(temp->engine_uuid);
458                 if (NULL != temp->engine_name)          free(temp->engine_name);
459                 if (NULL != temp->setting_ug_path)      free(temp->setting_ug_path);
460                 free(temp);
461                 return TTSD_ERROR_OPERATION_FAILED;
462         }
463
464         temp->engine_path = strdup(filepath);
465
466         SLOG(LOG_DEBUG, get_tag(), "----- Valid engine");
467         SECURE_SLOG(LOG_DEBUG, get_tag(), "Engine uuid : %s", temp->engine_uuid);
468         SECURE_SLOG(LOG_DEBUG, get_tag(), "Engine name : %s", temp->engine_name);
469         SECURE_SLOG(LOG_DEBUG, get_tag(), "Setting path : %s", temp->setting_ug_path);
470         SECURE_SLOG(LOG_DEBUG, get_tag(), "Engine path : %s", temp->engine_path);
471         SECURE_SLOG(LOG_DEBUG, get_tag(), "Use network : %s", temp->use_network ? "true" : "false");
472         SLOG(LOG_DEBUG, get_tag(), "-----");
473         SLOG(LOG_DEBUG, get_tag(), "  ");
474
475         *info = temp;
476
477         return 0;
478 }
479
480 static int __internal_update_engine_list()
481 {
482         /* relsease engine list */
483         GList *iter = NULL;
484         ttsengine_info_s *data = NULL;
485
486         if (g_list_length(g_engine_list) > 0) {
487                 iter = g_list_first(g_engine_list);
488
489                 while (NULL != iter) {
490                         data = iter->data;
491
492                         if (data != NULL)       free(data);
493                         g_engine_list = g_list_remove_link(g_engine_list, iter);
494                         g_list_free(iter);
495                         iter = g_list_first(g_engine_list);
496                 }
497         }
498
499         /* get file name from engine directory and get engine information from each filename */
500         DIR *dp = NULL;
501         int ret = -1;
502         struct dirent entry;
503         struct dirent *dirp = NULL;
504         dp = opendir(TTS_DEFAULT_ENGINE);
505
506         if (dp != NULL) {
507                 do {
508                         ret = readdir_r(dp, &entry, &dirp);
509                         if (0 != ret) {
510                                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to read directory");
511                                 break;
512                         }
513
514                         if (NULL != dirp) {
515                                 ttsengine_info_s* info;
516                                 char* filepath = NULL;
517                                 int file_size;
518
519                                 file_size = strlen(TTS_DEFAULT_ENGINE) + strlen(dirp->d_name) + 5;
520                                 filepath = (char*)calloc(file_size, sizeof(char));
521
522                                 if (NULL != filepath) {
523                                         snprintf(filepath, file_size, "%s/%s", TTS_DEFAULT_ENGINE, dirp->d_name);
524                                 } else {
525                                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not enough memory!!");
526                                         continue;
527                                 }
528
529                                 /* get its info and update engine list */
530                                 if (0 == __internal_get_engine_info(filepath, &info)) {
531                                         /* add engine info to g_engine_list */
532                                         g_engine_list = g_list_append(g_engine_list, info);
533                                 }
534
535                                 if (NULL != filepath)   free(filepath);
536                         }
537                 } while (NULL != dirp);
538
539                 closedir(dp);
540         }
541
542         if (g_list_length(g_engine_list) <= 0) {
543                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No Engine");
544                 return TTSD_ERROR_OPERATION_FAILED;
545         }
546
547 #ifdef ENGINE_AGENT_DEBUG
548         ttsd_print_enginelist();
549 #endif
550         return 0;
551 }
552
553 static int __internal_set_current_engine(const char* engine_uuid)
554 {
555         /* check whether engine id is valid or not. */
556         GList *iter = NULL;
557         ttsengine_info_s *data = NULL;
558
559         bool flag = false;
560         if (g_list_length(g_engine_list) > 0) {
561                 iter = g_list_first(g_engine_list);
562
563                 while (NULL != iter) {
564                         data = iter->data;
565
566                         if (0 == strncmp(data->engine_uuid, engine_uuid, strlen(engine_uuid))) {
567                                 flag = true;
568                                 break;
569                         }
570
571                         /*Get next item*/
572                         iter = g_list_next(iter);
573                 }
574         }
575
576         /* If current engine does not exist, return error */
577         if (false == flag) {
578                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Cannot find engine id");
579                 return TTSD_ERROR_OPERATION_FAILED;
580         } else {
581                 if (g_cur_engine.engine_uuid != NULL) {
582                         /*compare current engine uuid */
583                         if (0 == strncmp(g_cur_engine.engine_uuid, data->engine_uuid, strlen(engine_uuid))) {
584                                 SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] tts engine has already been set");
585                                 return 0;
586                         }
587                 }
588         }
589
590         /* set data from g_engine_list */
591         if (g_cur_engine.engine_uuid != NULL)   free(g_cur_engine.engine_uuid);
592         if (g_cur_engine.engine_name != NULL)   free(g_cur_engine.engine_name);
593         if (g_cur_engine.engine_path != NULL)   free(g_cur_engine.engine_path);
594
595         if (NULL == data->engine_uuid || NULL == data->engine_name || NULL == data->engine_path) {
596                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Engine data is NULL");
597                 return TTSD_ERROR_OPERATION_FAILED;
598         }
599
600         g_cur_engine.engine_uuid = strdup(data->engine_uuid);
601         g_cur_engine.engine_name = strdup(data->engine_name);
602         g_cur_engine.engine_path = strdup(data->engine_path);
603
604         g_cur_engine.handle = NULL;
605         g_cur_engine.is_loaded = false;
606         g_cur_engine.is_set = true;
607         g_cur_engine.need_network = data->use_network;
608
609         SLOG(LOG_DEBUG, get_tag(), "-----");
610         SECURE_SLOG(LOG_DEBUG, get_tag(), "Current engine uuid : %s", g_cur_engine.engine_uuid);
611         SECURE_SLOG(LOG_DEBUG, get_tag(), "Current engine name : %s", g_cur_engine.engine_name);
612         SECURE_SLOG(LOG_DEBUG, get_tag(), "Current engine path : %s", g_cur_engine.engine_path);
613         SLOG(LOG_DEBUG, get_tag(), "-----");
614
615         return 0;
616 }
617
618 int __ttsd_get_mode(ttsp_mode_e* mode)
619 {
620         if (NULL == mode) {
621                 SLOG(LOG_ERROR, get_tag(), "[ERROR] Input parameter is null");
622                 return TTSP_ERROR_INVALID_PARAMETER;
623         }
624
625         switch (ttsd_get_mode()) {
626         case TTSD_MODE_DEFAULT:         *mode = TTSP_MODE_DEFAULT;      break;
627         case TTSD_MODE_NOTIFICATION:    *mode = TTSP_MODE_NOTIFICATION; break;
628         case TTSD_MODE_SCREEN_READER:   *mode = TTSP_MODE_SCREEN_READER;        break;
629         default:
630                 SLOG(LOG_ERROR, get_tag(), "[ERROR] tts mode is NOT valid");
631         }
632
633         return 0;
634 }
635
636 int __ttsd_engine_agent_get_speed_range(int* min, int* normal, int* max)
637 {
638         if (NULL == min || NULL == normal || NULL == max) {
639                 SLOG(LOG_ERROR, get_tag(), "[ERROR] Input parameter is null");
640                 return TTSP_ERROR_INVALID_PARAMETER;
641         }
642
643         *min = TTS_SPEED_MIN;
644         *normal = TTS_SPEED_NORMAL;
645         *max = TTS_SPEED_MAX;
646
647         return 0;
648 }
649
650 int __ttsd_engine_agent_get_pitch_range(int* min, int* normal, int* max)
651 {
652         if (NULL == min || NULL == normal || NULL == max) {
653                 SLOG(LOG_ERROR, get_tag(), "[ERROR] Input parameter is null");
654                 return TTSP_ERROR_INVALID_PARAMETER;
655         }
656
657         *min = TTS_PITCH_MIN;
658         *normal = TTS_PITCH_NORMAL;
659         *max = TTS_PITCH_MAX;
660
661         return 0;
662 }
663
664 static bool __set_voice_info_cb(const char* language, int type, void* user_data)
665 {
666         if (NULL == language) {
667                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Input parameter is NULL in voice list callback!!!!");
668                 return false;
669         }
670
671         ttsvoice_s* voice = calloc(1, sizeof(ttsvoice_s));
672         if (NULL == voice) {
673                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to allocate memory");
674                 return false;
675         }
676         voice->lang = strdup(language);
677         voice->type = type;
678
679         voice->client_ref_count = 0;
680         voice->is_loaded = false;
681
682         if (0 == strcmp(g_cur_engine.default_lang, language) && g_cur_engine.default_vctype == type) {
683                 voice->is_default = true;
684                 voice->is_loaded = true;
685         } else {
686                 voice->is_default = false;
687         }
688
689         g_cur_voices = g_slist_append(g_cur_voices, voice);
690
691         return true;
692 }
693
694 static int __update_voice_list()
695 {
696         /* Get voice list */
697         g_cur_voices = NULL;
698         int ret = 0;
699
700         ret = g_cur_engine.pefuncs->foreach_voices(__set_voice_info_cb, NULL);
701         if (0 != ret || 0 >= g_slist_length(g_cur_voices)) {
702                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to get voice list : result(%d)", ret);
703                 return -1;
704         }
705
706 #ifdef ENGINE_AGENT_DEBUG
707         ttsd_print_voicelist();
708 #endif
709         return 0;
710 }
711
712 int ttsd_engine_agent_load_current_engine()
713 {
714         if (false == g_agent_init) {
715                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
716                 return TTSD_ERROR_OPERATION_FAILED;
717         }
718
719         if (false == g_cur_engine.is_set) {
720                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No Current Engine ");
721                 return TTSD_ERROR_OPERATION_FAILED;
722         }
723
724         /* check whether current engine is loaded or not */
725         if (true == g_cur_engine.is_loaded) {
726                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent] Engine has already been loaded ");
727                 return 0;
728         }
729
730         /* open engine */
731         char *error = NULL;
732         g_cur_engine.handle = dlopen(g_cur_engine.engine_path, RTLD_LAZY); /* RTLD_LAZY RTLD_NOW*/
733
734         if (NULL != (error = dlerror()) || NULL == g_cur_engine.handle) {
735                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to get current engine handle : %s", error);
736                 return -2;
737         }
738
739         g_cur_engine.ttsp_unload_engine = (int (*)())dlsym(g_cur_engine.handle, "ttsp_unload_engine");
740         if (NULL != (error = dlerror()) || NULL == g_cur_engine.ttsp_unload_engine) {
741                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to link daemon to ttsp_unload_engine() : %s", error);
742                 return -3;
743         }
744
745         g_cur_engine.ttsp_load_engine = (int (*)(const ttspd_funcs_s* , ttspe_funcs_s*))dlsym(g_cur_engine.handle, "ttsp_load_engine");
746         if (NULL != (error = dlerror()) || NULL == g_cur_engine.ttsp_load_engine) {
747                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to link daemon to ttsp_load_engine() : %s", error);
748                 return -3;
749         }
750
751         /* load engine */
752         g_cur_engine.pdfuncs->version = 1;
753         g_cur_engine.pdfuncs->size = sizeof(ttspd_funcs_s);
754         g_cur_engine.pdfuncs->get_mode = __ttsd_get_mode;
755         g_cur_engine.pdfuncs->get_speed_range = __ttsd_engine_agent_get_speed_range;
756         g_cur_engine.pdfuncs->get_pitch_range = __ttsd_engine_agent_get_pitch_range;
757
758         int ret = 0;
759         ret = g_cur_engine.ttsp_load_engine(g_cur_engine.pdfuncs, g_cur_engine.pefuncs);
760         if (0 != ret) {
761                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to load engine - %s : result(%s)",
762                          g_cur_engine.engine_path, __ttsd_get_engine_error_code(ret));
763                 return TTSD_ERROR_OPERATION_FAILED;
764         }
765
766         /* engine error check */
767         if (g_cur_engine.pefuncs->size != sizeof(ttspe_funcs_s)) {
768                 SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] The size of engine function is not valid");
769         }
770
771         if (NULL == g_cur_engine.pefuncs->initialize ||
772                 NULL == g_cur_engine.pefuncs->deinitialize ||
773                 NULL == g_cur_engine.pefuncs->foreach_voices ||
774                 NULL == g_cur_engine.pefuncs->is_valid_voice ||
775                 NULL == g_cur_engine.pefuncs->start_synth ||
776                 NULL == g_cur_engine.pefuncs->cancel_synth) {
777                 SLOG(LOG_ERROR, get_tag(), "[Engine ERROR] The engine functions are NOT valid");
778                 return TTSD_ERROR_OPERATION_FAILED;
779         }
780
781         ret = g_cur_engine.pefuncs->initialize(__result_cb);
782         if (0 != ret) {
783                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to initialize current engine : %s", __ttsd_get_engine_error_code(ret));
784                 return TTSD_ERROR_OPERATION_FAILED;
785         }
786
787         /* Get voice info of current engine */
788         ret = __update_voice_list();
789         if (0 != ret) {
790                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to set voice info : result(%d)", ret);
791                 return TTSD_ERROR_OPERATION_FAILED;
792         }
793
794         /* Select default voice */
795         if (NULL != g_cur_engine.default_lang) {
796                 if (true == g_cur_engine.pefuncs->is_valid_voice(g_cur_engine.default_lang, g_cur_engine.default_vctype)) {
797                         SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Set origin default voice to current engine : lang(%s), type(%d)",
798                                  g_cur_engine.default_lang,  g_cur_engine.default_vctype);
799                 } else {
800                         SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] Fail set origin default voice : lang(%s), type(%d)",
801                                  g_cur_engine.default_lang, g_cur_engine.default_vctype);
802
803                         return TTSD_ERROR_OPERATION_FAILED;
804                 }
805         }
806
807         /* load default voice */
808         if (NULL != g_cur_engine.pefuncs->load_voice) {
809                 ret = g_cur_engine.pefuncs->load_voice(g_cur_engine.default_lang, g_cur_engine.default_vctype);
810                 if (0 == ret) {
811                         SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Load default voice : lang(%s), type(%d)",
812                                  g_cur_engine.default_lang,  g_cur_engine.default_vctype);
813                 } else {
814                         SLOG(LOG_WARN, get_tag(), "[Engine Agent ERROR] Fail to load default voice : lang(%s), type(%d) result(%s)",
815                                 g_cur_engine.default_lang, g_cur_engine.default_vctype, __ttsd_get_engine_error_code(ret));
816
817                         return TTSD_ERROR_OPERATION_FAILED;
818                 }
819         }
820
821         /* set default pitch */
822         if (NULL != g_cur_engine.pefuncs->set_pitch) {
823                 ret = g_cur_engine.pefuncs->set_pitch(g_cur_engine.default_pitch);
824                 if (0 != ret) {
825                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to set pitch : pitch(%d), result(%s)",
826                                 g_cur_engine.default_pitch, __ttsd_get_engine_error_code(ret));
827                         return TTSD_ERROR_OPERATION_FAILED;
828                 }
829                 SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Set default pitch : pitch(%d)", g_cur_engine.default_pitch);
830         }
831
832         g_cur_engine.is_loaded = true;
833
834         return 0;
835 }
836
837 int ttsd_engine_agent_unload_current_engine()
838 {
839         if (false == g_agent_init) {
840                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
841                 return TTSD_ERROR_OPERATION_FAILED;
842         }
843
844         if (false == g_cur_engine.is_set) {
845                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No Current Engine ");
846                 return TTSD_ERROR_OPERATION_FAILED;
847         }
848
849         if (false == g_cur_engine.is_loaded) {
850                 SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Engine has already been unloaded ");
851                 return 0;
852         }
853
854         /* shutdown engine */
855         int ret = 0;
856         ret = g_cur_engine.pefuncs->deinitialize();
857         if (0 != ret) {
858                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent] Fail deinitialize() : %s", __ttsd_get_engine_error_code(ret));
859         }
860
861         /* unload engine */
862         g_cur_engine.ttsp_unload_engine();
863
864         dlclose(g_cur_engine.handle);
865
866         /* reset current engine data */
867         g_cur_engine.handle = NULL;
868         g_cur_engine.is_loaded = false;
869
870         GSList *iter = NULL;
871         ttsvoice_s* data = NULL;
872
873         iter = g_slist_nth(g_cur_voices, 0);
874         while (NULL != iter) {
875                 data = iter->data;
876
877                 if (NULL != data) {
878                         if (NULL != data->lang)         free(data->lang);
879                         g_cur_voices = g_slist_remove(g_cur_voices, data);
880                         free(data);
881                         data = NULL;
882                 }
883
884                 iter = g_slist_nth(g_cur_voices, 0);
885         }
886
887         return 0;
888 }
889
890 bool ttsd_engine_agent_need_network()
891 {
892         if (false == g_agent_init) {
893                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
894                 return TTSD_ERROR_OPERATION_FAILED;
895         }
896
897         if (false == g_cur_engine.is_loaded) {
898                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
899                 return TTSD_ERROR_OPERATION_FAILED;
900         }
901
902         return g_cur_engine.need_network;
903 }
904
905 bool ttsd_engine_select_valid_voice(const char* lang, int type, char** out_lang, int* out_type)
906 {
907         if (NULL == lang || NULL == out_lang || NULL == out_type) {
908                 return false;
909         }
910
911         if (false == g_cur_engine.is_loaded) {
912                 SLOG(LOG_WARN, get_tag(), "[Engine Agent WARNING] Not loaded engine");
913                 return false;
914         }
915
916         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Select voice : input lang(%s), input type(%d), default lang(%s), default type(%d)", 
917                 lang, type, g_cur_engine.default_lang, g_cur_engine.default_vctype);
918
919         /* case 1 : Both are default */
920         if (0 == strncmp(lang, "default", strlen("default")) && 0 == type) {
921                 *out_lang = strdup(g_cur_engine.default_lang);
922                 *out_type = g_cur_engine.default_vctype;
923                 return true;
924         }
925
926         /* Get voice list */
927         GList* voice_list = NULL;
928         int ret = 0;
929
930         ret = g_cur_engine.pefuncs->foreach_voices(__supported_voice_cb, &voice_list);
931         if (0 != ret || 0 >= g_list_length(voice_list)) {
932                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail to get voice list : result(%d)", ret);
933                 return false;
934         }
935
936         bool result;
937         result = false;
938
939         GList *iter = NULL;
940         voice_s* voice;
941
942         /* lang and type are not default type */
943         if (0 != strncmp(lang, "default", strlen("default")) && 0 != type) {
944                 iter = g_list_first(voice_list);
945
946                 while (NULL != iter) {
947                         /* Get handle data from list */
948                         voice = iter->data;
949
950                         if (0 == strncmp(voice->language, lang, strlen(lang)) &&  voice->type == type) {
951                                 *out_lang = strdup(voice->language);
952                                 *out_type = voice->type;
953                                 result = true;
954                                 break;
955                         }
956
957                         iter = g_list_next(iter);
958                 }
959
960         } else if (0 != strncmp(lang, "default", strlen("default")) && 0 == type) {
961                 /* Only type is default */
962                 if (0 == strncmp(lang, g_cur_engine.default_lang, strlen(g_cur_engine.default_lang))) {
963                         *out_lang = strdup(g_cur_engine.default_lang);
964                         *out_type = g_cur_engine.default_vctype;
965                         result = true;
966                 } else {
967                         voice_s* voice_selected = NULL;
968                         iter = g_list_first(voice_list);
969                         while (NULL != iter) {
970                                 /* Get handle data from list */
971                                 voice = iter->data;
972
973                                 if (0 == strncmp(voice->language, lang, strlen(lang))) {
974                                         voice_selected = voice;
975                                         if (voice->type == g_cur_engine.default_vctype) {
976                                                 voice_selected = voice;
977                                                 break;
978                                         }
979                                 }
980                                 iter = g_list_next(iter);
981                         }
982
983                         if (NULL != voice_selected) {
984                                 *out_lang = strdup(voice_selected->language);
985                                 *out_type = voice_selected->type;
986                                 result = true;
987                         }
988                 }
989         } else if (0 == strncmp(lang, "default", strlen("default")) && 0 != type) {
990                 /* Only lang is default */
991                 if (type == g_cur_engine.default_vctype) {
992                         *out_lang = strdup(g_cur_engine.default_lang);
993                         *out_type = g_cur_engine.default_vctype;
994                         result = true;
995                 } else {
996                         voice_s* voice_selected = NULL;
997                         iter = g_list_first(voice_list);
998                         while (NULL != iter) {
999                                 /* Get handle data from list */
1000                                 voice = iter->data;
1001
1002                                 if (0 == strncmp(voice->language, g_cur_engine.default_lang, strlen(g_cur_engine.default_lang))) {
1003                                         voice_selected = voice;
1004                                         if (voice->type == type) {
1005                                                 voice_selected = voice;
1006                                                 break;
1007                                         }
1008                                 }
1009                                 iter = g_list_next(iter);
1010                         }
1011
1012                         if (NULL != voice_selected) {
1013                                 *out_lang = strdup(voice->language);
1014                                 *out_type = voice_selected->type;
1015                                 result = true;
1016                         }
1017                 }
1018         }
1019
1020         if (true == result) {
1021                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Selected voice : lang(%s), type(%d)", *out_lang, *out_type);
1022         }
1023
1024         __free_voice_list(voice_list);
1025
1026         return result;
1027 }
1028
1029 bool ttsd_engine_agent_is_same_engine(const char* engine_id)
1030 {
1031         if (false == g_agent_init) {
1032                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1033                 return false;
1034         }
1035
1036         if (false == g_cur_engine.is_loaded) {
1037                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1038                 return false;
1039         }
1040
1041         /* compare current engine and engine id.*/
1042         if (0 == strncmp(g_cur_engine.engine_uuid, engine_id, strlen(engine_id))) {
1043                 return true;
1044         }
1045
1046         return false;
1047 }
1048
1049 int ttsd_engine_agent_set_default_engine(const char* engine_id)
1050 {
1051         if (false == g_agent_init) {
1052                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1053                 return TTSD_ERROR_OPERATION_FAILED;
1054         }
1055
1056         /* compare current engine and new engine.*/
1057         if (0 == strncmp(g_cur_engine.engine_uuid, engine_id, strlen(engine_id))) {
1058                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] new engine(%s) is the same as current engine", engine_id);
1059                 return 0;
1060         }
1061
1062         bool is_engine_loaded = false;
1063         char* tmp_uuid = NULL;
1064         tmp_uuid = strdup(g_cur_engine.engine_uuid);
1065         if (NULL == tmp_uuid) {
1066                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Current engine id is NULL");
1067                         return TTSD_ERROR_OPERATION_FAILED;
1068         }
1069
1070         is_engine_loaded = g_cur_engine.is_loaded;
1071
1072         if (true == is_engine_loaded) {
1073                 /* unload engine */
1074                 if (0 != ttsd_engine_agent_unload_current_engine()) {
1075                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent Error] fail to unload current engine");
1076                 }
1077         }
1078
1079         /* change current engine */
1080         if (0 != __internal_set_current_engine(engine_id)) {
1081                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to set current engine. Recovery origin engine");
1082
1083                 /* roll back to old current engine. */
1084                 __internal_set_current_engine(tmp_uuid);
1085                 if (true == is_engine_loaded) {
1086                         ttsd_engine_agent_load_current_engine();
1087                 }
1088
1089                 if (tmp_uuid != NULL)   free(tmp_uuid);
1090                 return TTSD_ERROR_OPERATION_FAILED;
1091         }
1092
1093         if (true == is_engine_loaded) {
1094                 /* load engine */
1095                 if (0 != ttsd_engine_agent_load_current_engine()) {
1096                         SLOG(LOG_ERROR, get_tag(), "[Engine Agent Error] Fail to load new engine. Recovery origin engine");
1097
1098                         /* roll back to old current engine. */
1099                         __internal_set_current_engine(tmp_uuid);
1100                         if (true == is_engine_loaded) {
1101                                 ttsd_engine_agent_load_current_engine();
1102                         }
1103
1104                         if (tmp_uuid != NULL)   free(tmp_uuid);
1105                         return TTSD_ERROR_OPERATION_FAILED;
1106                 } else {
1107                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] load new engine : %s", engine_id);
1108                 }
1109         }
1110
1111         if (tmp_uuid != NULL)   free(tmp_uuid);
1112         return 0;
1113 }
1114
1115 int ttsd_engine_agent_set_default_voice(const char* language, int vctype)
1116 {
1117         if (false == g_agent_init) {
1118                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1119                 return TTSD_ERROR_OPERATION_FAILED;
1120         }
1121
1122         if (false == g_cur_engine.is_loaded) {
1123                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1124                 return TTSD_ERROR_OPERATION_FAILED;
1125         }
1126
1127         if (false == g_cur_engine.pefuncs->is_valid_voice(language, vctype)) {
1128                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Voice is NOT valid !!");
1129                 return TTSD_ERROR_INVALID_VOICE;
1130         }
1131
1132         int ret = -1;
1133         GSList *iter = NULL;
1134         ttsvoice_s* data = NULL;
1135
1136         /* Update new default voice info */
1137         iter = g_slist_nth(g_cur_voices, 0);
1138         while (NULL != iter) {
1139                 /* Get handle data from list */
1140                 data = iter->data;
1141
1142                 if (NULL == data) {
1143                         SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Voice data is NULL");
1144                         return TTSD_ERROR_OPERATION_FAILED;
1145                 }
1146
1147                 if (0 == strcmp(data->lang, language) && data->type == vctype) {
1148                         data->is_default = true;
1149                         if (0 == data->client_ref_count) {
1150                                 /* load voice */
1151                                 if (NULL != g_cur_engine.pefuncs->load_voice) {
1152                                         ret = g_cur_engine.pefuncs->load_voice(data->lang, data->type);
1153                                         if (0 == ret) {
1154                                                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] load voice : lang(%s), type(%d)", 
1155                                                         data->lang, data->type);
1156                                                 data->is_loaded = true;
1157                                         } else {
1158                                                 SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to load voice : lang(%s), type(%d), result(%s)",
1159                                                         data->lang, data->type, __ttsd_get_engine_error_code(ret));
1160                                         }
1161                                 } else {
1162                                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent ERROR] Load voice of engine function is NULL");
1163                                 }
1164                         }
1165                         break;
1166                 }
1167
1168                 /*Get next item*/
1169                 iter = g_slist_next(iter);
1170         }
1171
1172 #ifdef ENGINE_AGENT_DEBUG
1173         ttsd_print_voicelist();
1174 #endif
1175
1176         /* Update old default voice info */
1177         iter = g_slist_nth(g_cur_voices, 0);
1178         while (NULL != iter) {
1179                 /*Get handle data from list*/
1180                 data = iter->data;
1181
1182                 if (NULL == data) {
1183                         SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Voice data is NULL");
1184                         return TTSD_ERROR_OPERATION_FAILED;
1185                 }
1186
1187                 if (0 == strcmp(data->lang, g_cur_engine.default_lang) && data->type == g_cur_engine.default_vctype) {
1188                         data->is_default = false;
1189                         if (0 == data->client_ref_count) {
1190                                 /* Unload voice */
1191                                 if (NULL != g_cur_engine.pefuncs->unload_voice) {
1192                                         ret = g_cur_engine.pefuncs->unload_voice(data->lang, data->type);
1193                                         if (0 == ret) {
1194                                                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Unload voice : lang(%s), type(%d)", 
1195                                                         data->lang, data->type);
1196                                                 data->is_loaded = false;
1197                                         } else {
1198                                                 SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to unload voice : lang(%s), type(%d), result(%s)",
1199                                                         data->lang, data->type, __ttsd_get_engine_error_code(ret));
1200                                         }
1201                                 } else {
1202                                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent ERROR] Unload voice of engine function is NULL");
1203                                 }
1204                         }
1205                         break;
1206                 }
1207
1208                 /*Get next item*/
1209                 iter = g_slist_next(iter);
1210         }
1211
1212         if (NULL != g_cur_engine.default_lang)  free(g_cur_engine.default_lang);
1213
1214         g_cur_engine.default_lang = strdup(language);
1215         g_cur_engine.default_vctype = vctype;
1216
1217 #ifdef ENGINE_AGENT_DEBUG
1218         ttsd_print_voicelist();
1219 #endif
1220
1221         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Set default voice : lang(%s), type(%d)",
1222                 g_cur_engine.default_lang, g_cur_engine.default_vctype);
1223
1224         return 0;
1225 }
1226
1227 int ttsd_engine_agent_set_default_speed(int speed)
1228 {
1229         if (false == g_agent_init) {
1230                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1231                 return TTSD_ERROR_OPERATION_FAILED;
1232         }
1233
1234         g_cur_engine.default_speed = speed;
1235
1236         return 0;
1237 }
1238
1239 int ttsd_engine_agent_set_default_pitch(int pitch)
1240 {
1241         if (false == g_agent_init) {
1242                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1243                 return TTSD_ERROR_OPERATION_FAILED;
1244         }
1245
1246         if (false == g_cur_engine.is_loaded) {
1247                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1248                 return TTSD_ERROR_OPERATION_FAILED;
1249         }
1250
1251         if (NULL == g_cur_engine.pefuncs->set_pitch) {
1252                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not support pitch");
1253                 return TTSD_ERROR_OPERATION_FAILED;
1254         }
1255
1256         int ret = g_cur_engine.pefuncs->set_pitch(pitch);
1257         if (0 != ret) {
1258                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to set pitch : pitch(%d), result(%s)",
1259                          pitch, __ttsd_get_engine_error_code(ret));
1260                 return TTSD_ERROR_OPERATION_FAILED;
1261         }
1262
1263         g_cur_engine.default_pitch = pitch;
1264
1265         return 0;
1266 }
1267
1268 int ttsd_engine_agent_is_credential_needed(int uid, bool* credential_needed)
1269 {
1270         if (NULL == credential_needed) {
1271                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Invalid Parameter");
1272                 return TTSP_ERROR_INVALID_PARAMETER;
1273         }
1274
1275         if (false == g_agent_init) {
1276                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1277                 return TTSD_ERROR_OPERATION_FAILED;
1278         }
1279
1280         if (false == g_cur_engine.is_loaded) {
1281                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1282                 return TTSD_ERROR_OPERATION_FAILED;
1283         }
1284
1285         if (NULL == g_cur_engine.pefuncs->need_app_credential) {
1286                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not support to check app credential");
1287                 return TTSD_ERROR_NOT_SUPPORTED_FEATURE;
1288         }
1289
1290         bool result = false;
1291         result = g_cur_engine.pefuncs->need_app_credential();
1292         *credential_needed = result;
1293
1294         return TTSP_ERROR_NONE;
1295 }
1296
1297 /******************************************************************************************
1298 * TTS Engine Interfaces for client
1299 *******************************************************************************************/
1300
1301 int ttsd_engine_load_voice(const char* lang, const int vctype)
1302 {
1303         /* 1. Find voice info */
1304         int ret = -1;
1305         GSList *iter = NULL;
1306         ttsvoice_s* data = NULL;
1307
1308         iter = g_slist_nth(g_cur_voices, 0);
1309         while (NULL != iter) {
1310                 /*Get handle data from list*/
1311                 data = iter->data;
1312
1313                 if (NULL != data) {
1314                         if (0 == strcmp(data->lang, lang) && data->type == vctype) {
1315                                 SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Find voice : default(%d) loaded(%d) ref(%d) lang(%s) type(%d)",
1316                                          data->is_default, data->is_loaded,  data->client_ref_count, data->lang, data->type);
1317                                 break;
1318                         }
1319                 }
1320
1321                 /*Get next item*/
1322                 iter = g_slist_next(iter);
1323                 data = NULL;
1324         }
1325
1326         if (NULL == data) {
1327                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] This voice is not supported voice : lang(%s) type(%d)", lang, vctype);
1328                 return TTSD_ERROR_OPERATION_FAILED;
1329         }
1330
1331         /* 2. increse ref count */
1332         data->client_ref_count++;
1333
1334         /* 3. if ref count change 0 to 1 and not default, load voice */
1335         if (1 == data->client_ref_count && false == data->is_default) {
1336                 /* load voice */
1337                 if (NULL != g_cur_engine.pefuncs->load_voice) {
1338                         ret = g_cur_engine.pefuncs->load_voice(data->lang, data->type);
1339                         if (0 == ret) {
1340                                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Load voice : lang(%s), type(%d)", 
1341                                         data->lang, data->type);
1342                                 data->is_loaded = true;
1343                         } else {
1344                                 SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to load voice : lang(%s), type(%d), result(%s)",
1345                                         data->lang, data->type, __ttsd_get_engine_error_code(ret));
1346
1347                                 return TTSD_ERROR_OPERATION_FAILED;
1348                         }
1349                 } else {
1350                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent ERROR] Load voice of engine function is NULL");
1351                 }
1352         } else {
1353                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Not load voice : default voice(%d) or ref count(%d)",
1354                         data->is_default, data->client_ref_count);
1355         }
1356
1357 #ifdef ENGINE_AGENT_DEBUG
1358         ttsd_print_voicelist();
1359 #endif
1360
1361         return 0;
1362 }
1363
1364 int ttsd_engine_unload_voice(const char* lang, const int vctype)
1365 {
1366         /* 1. Find voice info */
1367         int ret = -1;
1368         GSList *iter = NULL;
1369         ttsvoice_s* data = NULL;
1370
1371         iter = g_slist_nth(g_cur_voices, 0);
1372         while (NULL != iter) {
1373                 /*Get handle data from list*/
1374                 data = iter->data;
1375
1376                 if (NULL != data) {
1377                         if (0 == strcmp(data->lang, lang) && data->type == vctype) {
1378                                 SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Find voice : default(%d) loaded(%d) ref(%d) lang(%s) type(%d)",
1379                                          data->is_default, data->is_loaded,  data->client_ref_count, data->lang, data->type);
1380                                 break;
1381                         }
1382                 }
1383
1384                 /*Get next item*/
1385                 iter = g_slist_next(iter);
1386                 data = NULL;
1387         }
1388
1389         if (NULL == data) {
1390                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] This voice is not supported voice : lang(%s) type(%d)", lang, vctype);
1391                 return TTSD_ERROR_OPERATION_FAILED;
1392         }
1393
1394         /* 2. Decrese ref count */
1395         data->client_ref_count--;
1396
1397         /* 3. if ref count change 0 and not default, load voice */
1398         if (0 == data->client_ref_count && false == data->is_default) {
1399                 /* load voice */
1400                 if (NULL != g_cur_engine.pefuncs->unload_voice) {
1401                         ret = g_cur_engine.pefuncs->unload_voice(data->lang, data->type);
1402                         if (0 == ret) {
1403                                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent SUCCESS] Unload voice : lang(%s), type(%d)", 
1404                                         data->lang, data->type);
1405                                 data->is_loaded = false;
1406                         } else {
1407                                 SECURE_SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to unload voice : lang(%s), type(%d), result(%s)",
1408                                         data->lang, data->type, __ttsd_get_engine_error_code(ret));
1409
1410                                 return TTSD_ERROR_OPERATION_FAILED;
1411                         }
1412                 } else {
1413                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent ERROR] Unload voice of engine function is NULL");
1414                 }
1415         } else {
1416                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Not unload voice : default voice(%d) or ref count(%d)",
1417                         data->is_default, data->client_ref_count);
1418         }
1419
1420 #ifdef ENGINE_AGENT_DEBUG
1421         ttsd_print_voicelist();
1422 #endif
1423         return 0;
1424 }
1425
1426 int ttsd_engine_start_synthesis(const char* lang, int vctype, const char* text, int speed, const char* credential, void* user_param)
1427 {
1428         if (false == g_agent_init) {
1429                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1430                 return TTSD_ERROR_OPERATION_FAILED;
1431         }
1432
1433         if (false == g_cur_engine.is_loaded) {
1434                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1435                 return TTSD_ERROR_OPERATION_FAILED;
1436         }
1437
1438         /* select voice for default */
1439         char* temp_lang = NULL;
1440         int temp_type;
1441         if (true != ttsd_engine_select_valid_voice(lang, vctype, &temp_lang, &temp_type)) {
1442                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to select default voice");
1443                 if (NULL != temp_lang)  free(temp_lang);
1444                 return TTSD_ERROR_INVALID_VOICE;
1445         } else {
1446                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine Agent] Start synthesis : language(%s), type(%d), speed(%d), text(%s), credential(%s)", 
1447                         temp_lang, temp_type, speed, text, credential);
1448         }
1449
1450         if (NULL == g_cur_engine.pefuncs->start_synth) {
1451                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] start_synth() of engine is NULL!!");
1452                 if (NULL != temp_lang)  free(temp_lang);
1453                 return TTSD_ERROR_OPERATION_FAILED;
1454         }
1455
1456         int temp_speed;
1457
1458         if (0 == speed) {
1459                 temp_speed = g_cur_engine.default_speed;
1460         } else {
1461                 temp_speed = speed;
1462         }
1463
1464         /* synthesize text */
1465         int ret = 0;
1466         ret = g_cur_engine.pefuncs->start_synth(temp_lang, temp_type, text, temp_speed, credential, user_param);
1467         if (0 != ret) {
1468                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] ***************************************");
1469                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] * synthesize error : %s *", __ttsd_get_engine_error_code(ret));
1470                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] ***************************************");
1471                 if (NULL != temp_lang)  free(temp_lang);
1472                 return ret;
1473         }
1474
1475         if (NULL != temp_lang)  free(temp_lang);
1476         return 0;
1477 }
1478
1479 int ttsd_engine_cancel_synthesis()
1480 {
1481         if (false == g_agent_init) {
1482                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1483                 return TTSD_ERROR_OPERATION_FAILED;
1484         }
1485
1486         if (false == g_cur_engine.is_loaded) {
1487                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1488                 return TTSD_ERROR_OPERATION_FAILED;
1489         }
1490
1491         if (NULL == g_cur_engine.pefuncs->cancel_synth) {
1492                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] cancel_synth() of engine is NULL!!");
1493                 return TTSD_ERROR_OPERATION_FAILED;
1494         }
1495
1496         /* stop synthesis */
1497         int ret = 0;
1498         ret = g_cur_engine.pefuncs->cancel_synth();
1499         if (0 != ret) {
1500                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] fail cancel synthesis : %s", __ttsd_get_engine_error_code(ret));
1501                 return TTSD_ERROR_OPERATION_FAILED;
1502         }
1503
1504         return 0;
1505 }
1506
1507 bool __supported_voice_cb(const char* language, int type, void* user_data)
1508 {
1509         GList** voice_list = (GList**)user_data;
1510
1511         if (NULL == language || NULL == voice_list) {
1512                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Input parameter is NULL in voice list callback!!!!");
1513                 return false;
1514         }
1515
1516         voice_s* voice = calloc(1, sizeof(voice_s));
1517         if (NULL == voice) {
1518                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to allocate memory");
1519                 return false;
1520         }
1521         voice->language = strdup(language);
1522         voice->type = type;
1523
1524         *voice_list = g_list_append(*voice_list, voice);
1525
1526         return true;
1527 }
1528
1529 int ttsd_engine_get_voice_list(GList** voice_list)
1530 {
1531         if (false == g_agent_init) {
1532                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1533                 return TTSD_ERROR_OPERATION_FAILED;
1534         }
1535
1536         if (false == g_cur_engine.is_loaded) {
1537                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1538                 return TTSD_ERROR_OPERATION_FAILED;
1539         }
1540
1541         int ret = 0;
1542         ret = g_cur_engine.pefuncs->foreach_voices(__supported_voice_cb, (void*)voice_list);
1543         if (0 != ret) {
1544                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to get voice list : %s", __ttsd_get_engine_error_code(ret));
1545                 return TTSD_ERROR_OPERATION_FAILED;
1546         }
1547
1548         return 0;
1549 }
1550
1551 int ttsd_engine_get_default_voice(char** lang, int* vctype)
1552 {
1553         if (false == g_agent_init) {
1554                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1555                 return TTSD_ERROR_OPERATION_FAILED;
1556         }
1557
1558         if (false == g_cur_engine.is_loaded) {
1559                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not loaded engine");
1560                 return TTSD_ERROR_OPERATION_FAILED;
1561         }
1562
1563         if (NULL == lang || NULL == vctype) {
1564                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] BAD Parameter");
1565                 return TTSD_ERROR_INVALID_PARAMETER;
1566         }
1567
1568         if (NULL != g_cur_engine.default_lang) {
1569                 *lang = strdup(g_cur_engine.default_lang);
1570                 *vctype = g_cur_engine.default_vctype;
1571
1572                 SECURE_SLOG(LOG_DEBUG, get_tag(), "[Engine] Get default voice : language(%s), type(%d)", *lang, *vctype);
1573         } else {
1574                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Default voice is NULL");
1575                 return TTSD_ERROR_OPERATION_FAILED;
1576         }
1577
1578         return 0;
1579 }
1580
1581 int ttsd_engine_set_private_data(const char* key, const char* data)
1582 {
1583         if (false == g_agent_init) {
1584                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1585                 return TTSD_ERROR_OPERATION_FAILED;
1586         }
1587
1588         if (false == g_cur_engine.is_loaded) {
1589                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No loaded engine");
1590                 return TTSD_ERROR_ENGINE_NOT_FOUND;
1591         }
1592
1593         if (NULL == key) {
1594                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Invalid parameter");
1595                 return TTSD_ERROR_INVALID_PARAMETER;
1596         }
1597
1598         if (NULL == g_cur_engine.pefuncs->set_private_data) {
1599                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not supported feature");
1600                 return TTSD_ERROR_NOT_SUPPORTED_FEATURE;
1601         }
1602
1603         int ret = 0;
1604         ret = g_cur_engine.pefuncs->set_private_data(key, data);
1605         if (0 != ret) {
1606                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to set private data(%d)", ret);
1607         }
1608
1609         return ret;
1610 }
1611
1612 int ttsd_engine_get_private_data(const char* key, char** data)
1613 {
1614         if (false == g_agent_init) {
1615                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not Initialized");
1616                 return TTSD_ERROR_OPERATION_FAILED;
1617         }
1618
1619         if (false == g_cur_engine.is_loaded) {
1620                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] No loaded engine");
1621                 return TTSD_ERROR_ENGINE_NOT_FOUND;
1622         }
1623
1624         if (NULL == key || NULL == data) {
1625                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Invalid parameter");
1626                 return TTSD_ERROR_INVALID_PARAMETER;
1627         }
1628
1629         if (NULL == g_cur_engine.pefuncs->get_private_data) {
1630                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Not supported feature");
1631                 return TTSD_ERROR_NOT_SUPPORTED_FEATURE;
1632         }
1633
1634         char* temp = NULL;
1635         int ret = 0;
1636         ret = g_cur_engine.pefuncs->get_private_data(key, &temp);
1637         if (0 != ret) {
1638                 SLOG(LOG_ERROR, get_tag(), "[Engine Agent ERROR] Fail to get private data(%d)", ret);
1639         }
1640
1641         *data = strdup(temp);
1642
1643         return ret;
1644 }
1645
1646
1647 void __free_voice_list(GList* voice_list)
1648 {
1649         GList *iter = NULL;
1650         voice_s* data = NULL;
1651
1652         /* if list have item */
1653         if (g_list_length(voice_list) > 0) {
1654                 /* Get a first item */
1655                 iter = g_list_first(voice_list);
1656
1657                 while (NULL != iter) {
1658                         data = iter->data;
1659
1660                         if (NULL != data) {
1661                                 if (NULL != data->language)     free(data->language);
1662                                 free(data);
1663                         }
1664
1665                         voice_list = g_list_remove_link(voice_list, iter);
1666                         g_list_free(iter);
1667                         iter = g_list_first(voice_list);
1668                 }
1669         }
1670 }
1671
1672 /*
1673 * TTS Engine Callback Functions                                                                                 `                                 *
1674 */
1675 bool __result_cb(ttsp_result_event_e event, const void* data, unsigned int data_size, ttsp_audio_type_e audio_type, int rate, void *user_data)
1676 {
1677         g_result_cb(event, data, data_size, audio_type, rate, user_data);
1678         return true;
1679 }
1680
1681 /* function for debugging */
1682 int ttsd_print_enginelist()
1683 {
1684         GList *iter = NULL;
1685         ttsengine_info_s *data = NULL;
1686
1687         if (g_list_length(g_engine_list) > 0) {
1688                 iter = g_list_first(g_engine_list);
1689
1690                 SLOG(LOG_DEBUG, get_tag(), "----- engine list -----");
1691
1692                 int i = 1;
1693                 while (NULL != iter) {
1694                         data = iter->data;
1695
1696                         SECURE_SLOG(LOG_DEBUG, get_tag(), "[%dth]", i);
1697                         SECURE_SLOG(LOG_DEBUG, get_tag(), "engine uuid : %s", data->engine_uuid);
1698                         SECURE_SLOG(LOG_DEBUG, get_tag(), "engine name : %s", data->engine_name);
1699                         SECURE_SLOG(LOG_DEBUG, get_tag(), "engine path : %s", data->engine_path);
1700                         SECURE_SLOG(LOG_DEBUG, get_tag(), "setting ug path : %s", data->setting_ug_path);
1701
1702                         iter = g_list_next(iter);
1703                         i++;
1704                 }
1705                 SLOG(LOG_DEBUG, get_tag(), "-----------------------");
1706                 SLOG(LOG_DEBUG, get_tag(), "  ");
1707         } else {
1708                 SLOG(LOG_DEBUG, get_tag(), "----- engine list -----");
1709                 SLOG(LOG_DEBUG, get_tag(), "No Engine in directory");
1710                 SLOG(LOG_DEBUG, get_tag(), "-----------------------");
1711         }
1712
1713         return 0;
1714 }
1715
1716 int ttsd_print_voicelist()
1717 {
1718         /* Test log */
1719         GSList *iter = NULL;
1720         ttsvoice_s* data = NULL;
1721
1722         SLOG(LOG_DEBUG, get_tag(), "=== Voice list ===");
1723
1724         if (g_slist_length(g_cur_voices) > 0) {
1725                 /* Get a first item */
1726                 iter = g_slist_nth(g_cur_voices, 0);
1727
1728                 int i = 1;
1729                 while (NULL != iter) {
1730                         /*Get handle data from list*/
1731                         data = iter->data;
1732
1733                         if (NULL == data || NULL == data->lang) {
1734                                 SLOG(LOG_ERROR, get_tag(), "[ERROR] Data is invalid");
1735                                 return 0;
1736                         }
1737
1738                         SLOG(LOG_DEBUG, get_tag(), "[%dth] default(%d) loaded(%d) ref(%d) lang(%s) type(%d)",
1739                                  i, data->is_default, data->is_loaded,  data->client_ref_count, data->lang, data->type);
1740
1741                         /*Get next item*/
1742                         iter = g_slist_next(iter);
1743                         i++;
1744                 }
1745         }
1746
1747         SLOG(LOG_DEBUG, get_tag(), "==================");
1748
1749         return 0;
1750 }