1 /***************************************************************************
2  *   Copyright (C) 2002~2005 by Yuking                                     *
3  *   yuking_net@sohu.com                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
19  ***************************************************************************/
20 /**
21  * @file   utils.c
22  * @author Yuking yuking_net@sohu.com
23  * @date   2008-1-16
24  *
25  *  misc util function
26  *
27  */
28 
29 #define FCITX_USE_INTERNAL_PATH
30 
31 #include "config.h"
32 
33 #include <pthread.h>
34 #include <stdio.h>
35 #include <signal.h>
36 #include <unistd.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 #include <limits.h>
40 #include <libgen.h>
41 #include <ctype.h>
42 #include <stdarg.h>
43 #include <errno.h>
44 
45 #if defined(ENABLE_BACKTRACE)
46 #include <execinfo.h>
47 #endif
48 
49 #include "fcitx/fcitx.h"
50 #include "utils.h"
51 #include "utf8.h"
52 #include "log.h"
53 
54 #if defined(LIBKVM_FOUND)
55 #include <kvm.h>
56 #include <fcntl.h>
57 #include <sys/param.h>
58 #include <sys/sysctl.h>
59 #include <sys/user.h>
60 #endif
61 
62 #if defined(__linux__)
63 #include <sys/prctl.h>
64 #endif
65 
66 #if defined(__linux__) || defined(__GLIBC__)
67 #include <endian.h>
68 #else
69 #include <sys/endian.h>
70 #endif
71 
72 #define DEFINE_SIMPLE_UT_ICD(type, name)                      \
73     static const UT_icd __fcitx_##name##_icd = {              \
74         sizeof(type), NULL, NULL, NULL                        \
75     };                                                        \
76     FCITX_EXPORT_API const UT_icd *const fcitx_##name##_icd = \
77         &__fcitx_##name##_icd;
78 
79 DEFINE_SIMPLE_UT_ICD(void*, ptr)
80 DEFINE_SIMPLE_UT_ICD(int8_t, int8)
81 DEFINE_SIMPLE_UT_ICD(int16_t, int16)
82 DEFINE_SIMPLE_UT_ICD(int32_t, int32)
83 DEFINE_SIMPLE_UT_ICD(int64_t, int64)
84 
85 FCITX_EXPORT_API const UT_icd *const fcitx_str_icd = &ut_str_icd;
86 FCITX_EXPORT_API const UT_icd *const fcitx_int_icd = &ut_int_icd;
87 
88 FCITX_EXPORT_API UT_array*
fcitx_utils_string_list_append_no_copy(UT_array * list,char * str)89 fcitx_utils_string_list_append_no_copy(UT_array *list, char *str)
90 {
91     utarray_extend_back(list);
92     *(char**)utarray_back(list) = str;
93     return list;
94 }
95 
96 FCITX_EXPORT_API UT_array*
fcitx_utils_string_list_append_len(UT_array * list,const char * str,size_t len)97 fcitx_utils_string_list_append_len(UT_array *list, const char *str, size_t len)
98 {
99     char *buff = fcitx_utils_set_str_with_len(NULL, str, len);
100     fcitx_utils_string_list_append_no_copy(list, buff);
101     return list;
102 }
103 
104 FCITX_EXPORT_API
fcitx_utils_calculate_record_number(FILE * fpDict)105 int fcitx_utils_calculate_record_number(FILE* fpDict)
106 {
107     char           *strBuf = NULL;
108     size_t          bufLen = 0;
109     int             nNumber = 0;
110 
111     while (getline(&strBuf, &bufLen, fpDict) != -1) {
112         nNumber++;
113     }
114     rewind(fpDict);
115 
116     fcitx_utils_free(strBuf);
117 
118     return nNumber;
119 }
120 
121 FCITX_EXPORT_API
fcitx_utils_custom_bsearch(const void * key,const void * base,size_t nmemb,size_t size,int accurate,int (* compar)(const void *,const void *))122 void *fcitx_utils_custom_bsearch(const void *key, const void *base,
123                                  size_t nmemb, size_t size, int accurate,
124                                  int (*compar)(const void *, const void *))
125 {
126     if (accurate)
127         return bsearch(key, base, nmemb, size, compar);
128     else {
129         size_t l, u, idx;
130         const void *p;
131         int comparison;
132 
133         l = 0;
134         u = nmemb;
135         while (l < u) {
136             idx = (l + u) / 2;
137             p = (void *)(((const char *) base) + (idx * size));
138             comparison = (*compar)(key, p);
139             if (comparison <= 0)
140                 u = idx;
141             else if (comparison > 0)
142                 l = idx + 1;
143         }
144 
145         if (u >= nmemb)
146             return NULL;
147         else
148             return (void *)(((const char *) base) + (l * size));
149     }
150 }
151 
152 typedef void (*_fcitx_sighandler_t) (int);
153 
154 FCITX_EXPORT_API
fcitx_utils_init_as_daemon()155 void fcitx_utils_init_as_daemon()
156 {
157     pid_t pid;
158     if ((pid = fork()) > 0) {
159         waitpid(pid, NULL, 0);
160         exit(0);
161     }
162     setsid();
163     _fcitx_sighandler_t oldint = signal(SIGINT, SIG_IGN);
164     _fcitx_sighandler_t oldhup  =signal(SIGHUP, SIG_IGN);
165     _fcitx_sighandler_t oldquit = signal(SIGQUIT, SIG_IGN);
166     _fcitx_sighandler_t oldpipe = signal(SIGPIPE, SIG_IGN);
167     _fcitx_sighandler_t oldttou = signal(SIGTTOU, SIG_IGN);
168     _fcitx_sighandler_t oldttin = signal(SIGTTIN, SIG_IGN);
169     _fcitx_sighandler_t oldchld = signal(SIGCHLD, SIG_IGN);
170     if (fork() > 0)
171         exit(0);
172     chdir("/");
173 
174     signal(SIGINT, oldint);
175     signal(SIGHUP, oldhup);
176     signal(SIGQUIT, oldquit);
177     signal(SIGPIPE, oldpipe);
178     signal(SIGTTOU, oldttou);
179     signal(SIGTTIN, oldttin);
180     signal(SIGCHLD, oldchld);
181 }
182 
183 FCITX_EXPORT_API UT_array*
fcitx_utils_new_string_list()184 fcitx_utils_new_string_list()
185 {
186     UT_array *array;
187     utarray_new(array, fcitx_str_icd);
188     return array;
189 }
190 
191 FCITX_EXPORT_API UT_array*
fcitx_utils_append_split_string(UT_array * list,const char * str,const char * delm)192 fcitx_utils_append_split_string(UT_array *list,
193                                 const char* str, const char *delm)
194 {
195     const char *src = str;
196     const char *pos;
197     size_t len;
198     while ((len = strcspn(src, delm)), *(pos = src + len)) {
199         fcitx_utils_string_list_append_len(list, src, len);
200         src = pos + 1;
201     }
202     if (len)
203         fcitx_utils_string_list_append_len(list, src, len);
204     return list;
205 }
206 
207 FCITX_EXPORT_API
fcitx_utils_split_string(const char * str,char delm)208 UT_array* fcitx_utils_split_string(const char* str, char delm)
209 {
210     UT_array* array;
211     char delm_s[2] = {delm, '\0'};
212     utarray_new(array, fcitx_str_icd);
213     return fcitx_utils_append_split_string(array, str, delm_s);
214 }
215 
216 FCITX_EXPORT_API
fcitx_utils_string_list_printf_append(UT_array * list,const char * fmt,...)217 void fcitx_utils_string_list_printf_append(UT_array* list, const char* fmt,...)
218 {
219     char* buffer;
220     va_list ap;
221     va_start(ap, fmt);
222     vasprintf(&buffer, fmt, ap);
223     va_end(ap);
224     fcitx_utils_string_list_append_no_copy(list, buffer);
225 }
226 
227 FCITX_EXPORT_API
fcitx_utils_join_string_list(UT_array * list,char delm)228 char* fcitx_utils_join_string_list(UT_array* list, char delm)
229 {
230     if (!list)
231         return NULL;
232 
233     if (utarray_len(list) == 0)
234         return strdup("");
235 
236     size_t len = 0;
237     char** str;
238     for (str = (char**) utarray_front(list);
239          str != NULL;
240          str = (char**) utarray_next(list, str))
241     {
242         len += strlen(*str) + 1;
243     }
244 
245     char* result = (char*)malloc(sizeof(char) * len);
246     char* p = result;
247     for (str = (char**) utarray_front(list);
248          str != NULL;
249          str = (char**) utarray_next(list, str))
250     {
251         size_t strl = strlen(*str);
252         memcpy(p, *str, strl);
253         p += strl;
254         *p = delm;
255         p++;
256     }
257     result[len - 1] = '\0';
258 
259     return result;
260 }
261 
262 FCITX_EXPORT_API
fcitx_utils_string_list_contains(UT_array * list,const char * scmp)263 int fcitx_utils_string_list_contains(UT_array* list, const char* scmp)
264 {
265     char** str;
266     for (str = (char**) utarray_front(list);
267          str != NULL;
268          str = (char**) utarray_next(list, str))
269     {
270         if (strcmp(scmp, *str) == 0)
271             return 1;
272     }
273     return 0;
274 }
275 
276 FCITX_EXPORT_API
fcitx_utils_free_string_list(UT_array * list)277 void fcitx_utils_free_string_list(UT_array* list)
278 {
279     utarray_free(list);
280 }
281 
282 FCITX_EXPORT_API
fcitx_utils_string_hash_set_join(FcitxStringHashSet * sset,char delim)283 char* fcitx_utils_string_hash_set_join(FcitxStringHashSet* sset, char delim)
284 {
285     if (!sset)
286         return NULL;
287 
288     if (HASH_COUNT(sset) == 0)
289         return strdup("");
290 
291     size_t len = 0;
292     HASH_FOREACH(string, sset, FcitxStringHashSet) {
293         len += strlen(string->name) + 1;
294     }
295 
296     char* result = (char*)malloc(sizeof(char) * len);
297     char* p = result;
298     HASH_FOREACH(string2, sset, FcitxStringHashSet) {
299         size_t strl = strlen(string2->name);
300         memcpy(p, string2->name, strl);
301         p += strl;
302         *p = delim;
303         p++;
304     }
305     result[len - 1] = '\0';
306 
307     return result;
308 }
309 
310 FCITX_EXPORT_API
fcitx_utils_string_hash_set_parse(const char * str,char delim)311 FcitxStringHashSet* fcitx_utils_string_hash_set_parse(const char* str, char delim)
312 {
313     FcitxStringHashSet* sset = NULL;
314     const char *src = str;
315     const char *pos;
316     size_t len;
317 
318     char delim_s[2] = {delim, '\0'};
319     while ((len = strcspn(src, delim_s)), *(pos = src + len)) {
320         sset = fcitx_utils_string_hash_set_insert_len(sset, src, len);
321         src = pos + 1;
322     }
323     if (len)
324         sset = fcitx_utils_string_hash_set_insert_len(sset, src, len);
325     return sset;
326 }
327 
328 FCITX_EXPORT_API
fcitx_utils_string_hash_set_insert(FcitxStringHashSet * sset,const char * str)329 FcitxStringHashSet* fcitx_utils_string_hash_set_insert(FcitxStringHashSet* sset, const char* str)
330 {
331     FcitxStringHashSet* string = fcitx_utils_new(FcitxStringHashSet);
332     string->name = strdup(str);
333     HASH_ADD_KEYPTR(hh, sset, string->name, strlen(string->name), string);
334     return sset;
335 }
336 
337 FCITX_EXPORT_API
fcitx_utils_string_hash_set_insert_len(FcitxStringHashSet * sset,const char * str,size_t len)338 FcitxStringHashSet* fcitx_utils_string_hash_set_insert_len(FcitxStringHashSet* sset, const char* str, size_t len)
339 {
340     FcitxStringHashSet* string = fcitx_utils_new(FcitxStringHashSet);
341     string->name = strndup(str, len);
342     HASH_ADD_KEYPTR(hh, sset, string->name, strlen(string->name), string);
343     return sset;
344 }
345 
346 FCITX_EXPORT_API
fcitx_utils_string_hash_set_contains(FcitxStringHashSet * sset,const char * str)347 boolean fcitx_utils_string_hash_set_contains(FcitxStringHashSet* sset, const char* str)
348 {
349     FcitxStringHashSet* string = NULL;
350     HASH_FIND_STR(sset, str, string);
351     return (string != NULL);
352 }
353 
354 FCITX_EXPORT_API
fcitx_util_string_hash_set_remove(FcitxStringHashSet * sset,const char * str)355 FcitxStringHashSet* fcitx_util_string_hash_set_remove(FcitxStringHashSet* sset, const char* str)
356 {
357     FcitxStringHashSet* string = NULL;
358     HASH_FIND_STR(sset, str, string);
359     if (string) {
360         HASH_DEL(sset, string);
361         free(string->name);
362         free(string);
363     }
364     return sset;
365 }
366 
367 FCITX_EXPORT_API
fcitx_utils_free_string_hash_set(FcitxStringHashSet * sset)368 void fcitx_utils_free_string_hash_set(FcitxStringHashSet* sset)
369 {
370     FcitxStringHashSet *curStr;
371     while (sset) {
372         curStr = sset;
373         HASH_DEL(sset, curStr);
374         free(curStr->name);
375         free(curStr);
376     }
377 }
378 
379 FCITX_EXPORT_API
fcitx_utils_string_hash_set_compare(FcitxStringHashSet * sseta,FcitxStringHashSet * ssetb)380 int fcitx_utils_string_hash_set_compare(FcitxStringHashSet* sseta, FcitxStringHashSet* ssetb)
381 {
382     return strcmp(sseta->name, ssetb->name);
383 }
384 
385 FCITX_EXPORT_API
fcitx_utils_malloc0(size_t bytes)386 void* fcitx_utils_malloc0(size_t bytes)
387 {
388     void *p = malloc(bytes);
389     if (!p)
390         return NULL;
391 
392     memset(p, 0, bytes);
393     return p;
394 }
395 
396 FCITX_EXPORT_API
fcitx_utils_trim(const char * s)397 char* fcitx_utils_trim(const char* s)
398 {
399     register const char *end;
400 
401     s += strspn(s, "\f\n\r\t\v ");
402     end = s + (strlen(s) - 1);
403     while (end >= s && isspace(*end))               /* skip trailing space */
404         --end;
405 
406     end++;
407 
408     size_t len = end - s;
409 
410     char* result = malloc(len + 1);
411     memcpy(result, s, len);
412     result[len] = '\0';
413     return result;
414 }
415 
416 FCITX_EXPORT_API int
fcitx_utils_get_display_number()417 fcitx_utils_get_display_number()
418 {
419     const char *display = getenv("DISPLAY");
420     if (!display)
421         return 0;
422     size_t len;
423     const char *p = display + strcspn(display, ":");
424     if (*p != ':')
425         return 0;
426     p++;
427     len = strcspn(p, ".");
428     char *str_disp_num = fcitx_utils_set_str_with_len(NULL, p, len);
429     int displayNumber = atoi(str_disp_num);
430     free(str_disp_num);
431     return displayNumber;
432 }
433 
434 FCITX_EXPORT_API
fcitx_utils_current_locale_is_utf8()435 int fcitx_utils_current_locale_is_utf8()
436 {
437     const char* p;
438     p = getenv("LC_CTYPE");
439     if (!p) {
440         p = getenv("LC_ALL");
441         if (!p)
442             p = getenv("LANG");
443     }
444     if (p) {
445         if (strcasestr(p, "utf8") || strcasestr(p, "utf-8"))
446             return 1;
447     }
448     return 0;
449 }
450 
451 FCITX_EXPORT_API
fcitx_utils_get_current_langcode()452 char* fcitx_utils_get_current_langcode()
453 {
454     /* language[_territory][.codeset][@modifier]" or "C" */
455     const char* p;
456     p = getenv("LC_CTYPE");
457     if (!p) {
458         p = getenv("LC_ALL");
459         if (!p)
460             p = getenv("LANG");
461     }
462     if (p)
463         return fcitx_utils_set_str_with_len(NULL, p, strcspn(p, ".@"));
464     return strdup("C");
465 }
466 
467 FCITX_EXPORT_API
fcitx_utils_pid_exists(pid_t pid)468 int fcitx_utils_pid_exists(pid_t pid)
469 {
470     if (pid <= 0)
471         return 0;
472     return !(kill(pid, 0) && (errno == ESRCH));
473 }
474 
475 FCITX_EXPORT_API
fcitx_utils_get_process_name()476 char* fcitx_utils_get_process_name()
477 {
478 #if defined(__linux__)
479     #define _PR_GET_NAME_MAX 16
480     char name[_PR_GET_NAME_MAX + 1];
481     if (prctl(PR_GET_NAME, (unsigned long)name, 0, 0, 0))
482         return strdup("");
483     name[_PR_GET_NAME_MAX] = '\0';
484     return strdup(name);
485     /**
486      * Keep the old code here in case we want to get the name of
487      * another process with known pid sometime.
488      **/
489     /* do { */
490     /*     FILE* fp = fopen("/proc/self/stat", "r"); */
491     /*     if (!fp) */
492     /*         break; */
493 
494     /*     const size_t bufsize = 1024; */
495     /*     char buf[bufsize]; */
496     /*     fgets(buf, bufsize, fp); */
497     /*     fclose(fp); */
498 
499     /*     char* S = strchr(buf, '('); */
500     /*     if (!S) */
501     /*         break; */
502     /*     char* E = strchr(S, ')'); */
503     /*     if (!E) */
504     /*         break; */
505 
506     /*     return strndup(S+1, E-S-1); */
507     /* } while(0); */
508     /* return strdup(""); */
509 #elif defined(LIBKVM_FOUND)
510 #if defined(__NetBSD__) || defined(__OpenBSD__)
511     kvm_t *vm = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
512 #else
513     kvm_t *vm = kvm_open(0, "/dev/null", 0, O_RDONLY, NULL);
514 #endif
515     if (vm == 0)
516         return strdup("");
517 
518     char* result = NULL;
519     do {
520         int cnt;
521         int mypid = getpid();
522 #ifdef __NetBSD__
523         struct kinfo_proc2 * kp = kvm_getproc2(vm, KERN_PROC_PID, mypid, sizeof(struct kinfo_proc2), &cnt);
524 #else
525         struct kinfo_proc * kp = kvm_getprocs(vm, KERN_PROC_PID, mypid, &cnt);
526 #endif
527         if ((cnt != 1) || (kp == 0)) {
528             break;
529         }
530         int i;
531         for (i = 0; i < cnt; i++)
532 #if defined(__NetBSD__) || defined(__OpenBSD__)
533             if (kp->p_pid == mypid)
534 #else
535             if (kp->ki_pid == mypid)
536 #endif
537                 break;
538         if (i != cnt)
539 #if defined(__NetBSD__) || defined(__OpenBSD__)
540             result = strdup(kp->p_comm);
541 #else
542             result = strdup(kp->ki_comm);
543 #endif
544     } while (0);
545     kvm_close(vm);
546     if (result == NULL)
547         result = strdup("");
548     return result;
549 #else
550     return strdup("");
551 #endif
552 }
553 
554 FCITX_EXPORT_API
fcitx_utils_get_fcitx_path(const char * type)555 char* fcitx_utils_get_fcitx_path(const char* type)
556 {
557     char* fcitxdir = getenv("FCITXDIR");
558     char* result = NULL;
559     if (strcmp(type, "datadir") == 0) {
560         if (fcitxdir) {
561             fcitx_utils_alloc_cat_str(result, fcitxdir, "/share");
562         } else {
563             result = strdup(DATADIR);
564         }
565     }
566     else if (strcmp(type, "pkgdatadir") == 0) {
567         if (fcitxdir) {
568             fcitx_utils_alloc_cat_str(result, fcitxdir, "/share/"PACKAGE);
569         } else {
570             result = strdup(PKGDATADIR);
571         }
572     }
573     else if (strcmp(type, "bindir") == 0) {
574         if (fcitxdir) {
575             fcitx_utils_alloc_cat_str(result, fcitxdir, "/bin");
576         }
577         else
578             result = strdup(BINDIR);
579     }
580     else if (strcmp(type, "libdir") == 0) {
581         if (fcitxdir) {
582             fcitx_utils_alloc_cat_str(result, fcitxdir, "/lib");
583         }
584         else
585             result = strdup(LIBDIR);
586     }
587     else if (strcmp(type, "localedir") == 0) {
588         if (fcitxdir) {
589             fcitx_utils_alloc_cat_str(result, fcitxdir, "/share/locale");
590         }
591         else
592             result = strdup(LOCALEDIR);
593     }
594     return result;
595 }
596 
597 FCITX_EXPORT_API
fcitx_utils_get_fcitx_path_with_filename(const char * type,const char * filename)598 char* fcitx_utils_get_fcitx_path_with_filename(const char* type, const char* filename)
599 {
600     char* path = fcitx_utils_get_fcitx_path(type);
601     if (path == NULL)
602         return NULL;
603     char *result;
604     fcitx_utils_alloc_cat_str(result, path, "/", filename);
605     free(path);
606     return result;
607 }
608 
609 FCITX_EXPORT_API
fcitx_utils_string_swap(char ** obj,const char * str)610 void fcitx_utils_string_swap(char** obj, const char* str)
611 {
612     if (str) {
613         *obj = fcitx_utils_set_str(*obj, str);
614     } else if (*obj) {
615         free(*obj);
616         *obj = NULL;
617     }
618 }
619 
620 FCITX_EXPORT_API
fcitx_utils_string_swap_with_len(char ** obj,const char * str,size_t len)621 void fcitx_utils_string_swap_with_len(char** obj, const char* str, size_t len)
622 {
623     if (str) {
624         *obj = fcitx_utils_set_str_with_len(*obj, str, len);
625     } else if (*obj) {
626         free(*obj);
627         *obj = NULL;
628     }
629 }
630 
631 FCITX_EXPORT_API void
fcitx_utils_launch_tool(const char * name,const char * arg)632 fcitx_utils_launch_tool(const char* name, const char* arg)
633 {
634     char* command = fcitx_utils_get_fcitx_path_with_filename("bindir", name);
635     char* args[] = {
636         command,
637         (char*)(intptr_t)arg, /* parent process haven't even touched this... */
638         NULL
639     };
640     fcitx_utils_start_process(args);
641     free(command);
642 }
643 
644 FCITX_EXPORT_API
fcitx_utils_launch_configure_tool()645 void fcitx_utils_launch_configure_tool()
646 {
647     fcitx_utils_launch_tool("fcitx-configtool", NULL);
648 }
649 
650 FCITX_EXPORT_API
fcitx_utils_launch_configure_tool_for_addon(const char * imaddon)651 void fcitx_utils_launch_configure_tool_for_addon(const char* imaddon)
652 {
653     fcitx_utils_launch_tool("fcitx-configtool", imaddon);
654 }
655 
656 FCITX_EXPORT_API
fcitx_utils_launch_restart()657 void fcitx_utils_launch_restart()
658 {
659     fcitx_utils_launch_tool("fcitx", "-r");
660 }
661 
662 FCITX_EXPORT_API
fcitx_utils_restart_in_place(void)663 void fcitx_utils_restart_in_place(void)
664 {
665     char* command = fcitx_utils_get_fcitx_path_with_filename("bindir", "fcitx");
666     char* const argv[] = {
667         command,
668         "-D", /* Don't start as daemon */
669         NULL
670     };
671     execv(argv[0], argv);
672     perror("Restart failed: execvp:");
673     _exit(1);
674 }
675 
676 FCITX_EXPORT_API
fcitx_utils_start_process(char ** args)677 void fcitx_utils_start_process(char** args)
678 {
679     /* exec command */
680     pid_t child_pid;
681 
682     child_pid = fork();
683     if (child_pid < 0) {
684         perror("fork");
685     } else if (child_pid == 0) {         /* child process  */
686         setsid();
687         pid_t grandchild_pid;
688 
689         grandchild_pid = fork();
690         if (grandchild_pid < 0) {
691             perror("fork");
692             _exit(1);
693         } else if (grandchild_pid == 0) { /* grandchild process  */
694             execvp(args[0], args);
695             perror("execvp");
696             _exit(1);
697         } else {
698             _exit(0);
699         }
700     } else {                              /* parent process */
701         int status;
702         waitpid(child_pid, &status, 0);
703     }
704 }
705 
706 FCITX_EXPORT_API
707 void
fcitx_utils_backtrace()708 fcitx_utils_backtrace()
709 {
710 #if defined(ENABLE_BACKTRACE)
711     void *array[32];
712 
713     size_t size;
714 
715     size = backtrace(array, 32);
716     backtrace_symbols_fd(array, size, STDERR_FILENO);
717 #endif
718 }
719 
720 FCITX_EXPORT_API
721 int
fcitx_utils_get_boolean_env(const char * name,int defval)722 fcitx_utils_get_boolean_env(const char *name,
723                             int defval)
724 {
725     const char *value = getenv(name);
726 
727     if (value == NULL)
728         return defval;
729 
730     if ((!*value) ||
731         strcmp(value, "0") == 0 ||
732         strcasecmp(value, "false") == 0)
733         return 0;
734 
735     return 1;
736 }
737 
738 FCITX_EXPORT_API
739 size_t
fcitx_utils_read_uint32(FILE * fp,uint32_t * p)740 fcitx_utils_read_uint32(FILE *fp, uint32_t *p)
741 {
742     uint32_t res = 0;
743     size_t size;
744     size = fread(&res, sizeof(uint32_t), 1, fp);
745     *p = le32toh(res);
746     return size;
747 }
748 
749 FCITX_EXPORT_API
750 size_t
fcitx_utils_write_uint32(FILE * fp,uint32_t i)751 fcitx_utils_write_uint32(FILE *fp, uint32_t i)
752 {
753     i = htole32(i);
754     return fwrite(&i, sizeof(uint32_t), 1, fp);
755 }
756 
757 FCITX_EXPORT_API
758 size_t
fcitx_utils_read_uint16(FILE * fp,uint16_t * p)759 fcitx_utils_read_uint16(FILE *fp, uint16_t *p)
760 {
761     uint16_t res = 0;
762     size_t size;
763     size = fread(&res, sizeof(uint16_t), 1, fp);
764     *p = le16toh(res);
765     return size;
766 }
767 
768 FCITX_EXPORT_API
769 size_t
fcitx_utils_write_uint16(FILE * fp,uint16_t i)770 fcitx_utils_write_uint16(FILE *fp, uint16_t i)
771 {
772     i = htole16(i);
773     return fwrite(&i, sizeof(uint16_t), 1, fp);
774 }
775 
776 FCITX_EXPORT_API
777 size_t
fcitx_utils_read_uint64(FILE * fp,uint64_t * p)778 fcitx_utils_read_uint64(FILE *fp, uint64_t *p)
779 {
780     uint64_t res = 0;
781     size_t size;
782     size = fread(&res, sizeof(uint64_t), 1, fp);
783     *p = le64toh(res);
784     return size;
785 }
786 
787 FCITX_EXPORT_API
788 size_t
fcitx_utils_write_uint64(FILE * fp,uint64_t i)789 fcitx_utils_write_uint64(FILE *fp, uint64_t i)
790 {
791     i = htole64(i);
792     return fwrite(&i, sizeof(uint64_t), 1, fp);
793 }
794 
795 FCITX_EXPORT_API size_t
fcitx_utils_str_lens(size_t n,const char ** str_list,size_t * size_list)796 fcitx_utils_str_lens(size_t n, const char **str_list, size_t *size_list)
797 {
798     size_t i;
799     size_t total = 0;
800     for (i = 0;i < n;i++) {
801         total += (size_list[i] = str_list[i] ? strlen(str_list[i]) : 0);
802     }
803     return total + 1;
804 }
805 
806 FCITX_EXPORT_API void
fcitx_utils_cat_str(char * out,size_t n,const char ** str_list,const size_t * size_list)807 fcitx_utils_cat_str(char *out, size_t n, const char **str_list,
808                         const size_t *size_list)
809 {
810     size_t i = 0;
811     for (i = 0;i < n;i++) {
812         if (!size_list[i])
813             continue;
814         memcpy(out, str_list[i], size_list[i]);
815         out += size_list[i];
816     }
817     *out = '\0';
818 }
819 
820 FCITX_EXPORT_API void
fcitx_utils_cat_str_with_len(char * out,size_t len,size_t n,const char ** str_list,const size_t * size_list)821 fcitx_utils_cat_str_with_len(char *out, size_t len, size_t n,
822                              const char **str_list, const size_t *size_list)
823 {
824     char *limit = out + len - 1;
825     char *tmp = out;
826     size_t i = 0;
827     for (i = 0;i < n;i++) {
828         if (!size_list[i])
829             continue;
830         tmp += size_list[i];
831         if (tmp > limit) {
832             memcpy(out, str_list[i], limit - out);
833             out = limit;
834             break;
835         }
836         memcpy(out, str_list[i], size_list[i]);
837         out = tmp;
838     }
839     *out = '\0';
840 }
841 
842 FCITX_EXPORT_API int
fcitx_utils_strcmp0(const char * a,const char * b)843 fcitx_utils_strcmp0(const char *a, const char *b)
844 {
845     if (!a) {
846         if (!b)
847             return 0;
848         return -1;
849     } else if (!b) {
850         return 1;
851     }
852     return strcmp(a, b);
853 
854 }
855 
856 FCITX_EXPORT_API
857 int
fcitx_utils_strcmp_empty(const char * a,const char * b)858 fcitx_utils_strcmp_empty(const char* a, const char* b)
859 {
860     int isemptya = (a == NULL || (*a) == 0);
861     int isemptyb = (b == NULL || (*b) == 0);
862     if (isemptya && isemptyb)
863         return 0;
864     if (isemptya && !isemptyb)
865         return -1;
866     if (!isemptya && isemptyb)
867         return 1;
868     return strcmp(a, b);
869 }
870 
871 FCITX_EXPORT_API char*
fcitx_utils_set_str_with_len(char * res,const char * str,size_t len)872 fcitx_utils_set_str_with_len(char *res, const char *str, size_t len)
873 {
874     if (res) {
875         res = realloc(res, len + 1);
876     } else {
877         res = malloc(len + 1);
878     }
879     memcpy(res, str, len);
880     res[len] = '\0';
881     return res;
882 }
883 
884 FCITX_EXPORT_API char
fcitx_utils_unescape_char(char c)885 fcitx_utils_unescape_char(char c)
886 {
887     switch (c) {
888 #define CASE_UNESCAPE(from, to) case from: return to
889         CASE_UNESCAPE('a', '\a');
890         CASE_UNESCAPE('b', '\b');
891         CASE_UNESCAPE('f', '\f');
892         CASE_UNESCAPE('n', '\n');
893         CASE_UNESCAPE('r', '\r');
894         CASE_UNESCAPE('t', '\t');
895         CASE_UNESCAPE('e', '\e');
896         CASE_UNESCAPE('v', '\v');
897 #undef CASE_UNESCAPE
898     }
899     return c;
900 }
901 
902 FCITX_EXPORT_API char*
fcitx_utils_unescape_str_inplace(char * str)903 fcitx_utils_unescape_str_inplace(char *str)
904 {
905     char *dest = str;
906     char *src = str;
907     char *pos;
908     size_t len;
909     while ((len = strcspn(src, "\\")), *(pos = src + len)) {
910         if (dest != src && len)
911             memmove(dest, src, len);
912         dest += len;
913         src = pos + 1;
914         *dest = fcitx_utils_unescape_char(*src);
915         dest++;
916         src++;
917     }
918     if (dest != src && len)
919         memmove(dest, src, len);
920     dest[len] = '\0';
921     return str;
922 }
923 
924 FCITX_EXPORT_API char*
fcitx_utils_set_unescape_str(char * res,const char * str)925 fcitx_utils_set_unescape_str(char *res, const char *str)
926 {
927     size_t len = strlen(str) + 1;
928     if (res) {
929         res = realloc(res, len);
930     } else {
931         res = malloc(len);
932     }
933     char *dest = res;
934     const char *src = str;
935     const char *pos;
936     while ((len = strcspn(src, "\\")), *(pos = src + len)) {
937         memcpy(dest, src, len);
938         dest += len;
939         src = pos + 1;
940         *dest = fcitx_utils_unescape_char(*src);
941         dest++;
942         src++;
943     }
944     if (len)
945         memcpy(dest, src, len);
946     dest[len] = '\0';
947     return res;
948 }
949 
950 FCITX_EXPORT_API char
fcitx_utils_escape_char(char c)951 fcitx_utils_escape_char(char c)
952 {
953     switch (c) {
954 #define CASE_ESCAPE(to, from) case from: return to
955         CASE_ESCAPE('a', '\a');
956         CASE_ESCAPE('b', '\b');
957         CASE_ESCAPE('f', '\f');
958         CASE_ESCAPE('n', '\n');
959         CASE_ESCAPE('r', '\r');
960         CASE_ESCAPE('t', '\t');
961         CASE_ESCAPE('e', '\e');
962         CASE_ESCAPE('v', '\v');
963 #undef CASE_ESCAPE
964     }
965     return c;
966 }
967 
968 FCITX_EXPORT_API char*
fcitx_utils_set_escape_str_with_set(char * res,const char * str,const char * set)969 fcitx_utils_set_escape_str_with_set(char *res, const char *str, const char *set)
970 {
971     if (!set)
972         set = FCITX_CHAR_NEED_ESCAPE;
973     size_t len = strlen(str) * 2 + 1;
974     if (res) {
975         res = realloc(res, len);
976     } else {
977         res = malloc(len);
978     }
979     char *dest = res;
980     const char *src = str;
981     const char *pos;
982     while ((len = strcspn(src, set)), *(pos = src + len)) {
983         memcpy(dest, src, len);
984         dest += len;
985         *dest = '\\';
986         dest++;
987         *dest = fcitx_utils_escape_char(*pos);
988         dest++;
989         src = pos + 1;
990     }
991     if (len)
992         memcpy(dest, src, len);
993     dest += len;
994     *dest = '\0';
995     res = realloc(res, dest - res + 1);
996     return res;
997 }
998 
999 #ifdef __FCITX_ATOMIC_USE_SYNC_FETCH
1000 /**
1001  * Also define lib function when there is builtin function for
1002  * atomic operation in case the function address is needed or the builtin
1003  * is not available when compiling other modules.
1004  **/
1005 #define FCITX_UTIL_DEFINE_ATOMIC(name, op, type)                        \
1006     FCITX_EXPORT_API type                                               \
1007     (fcitx_utils_atomic_##name)(volatile type *atomic, type val)        \
1008     {                                                                   \
1009         return __sync_fetch_and_##name(atomic, val);                    \
1010     }
1011 #else
1012 static pthread_mutex_t __fcitx_utils_atomic_lock = PTHREAD_MUTEX_INITIALIZER;
1013 #define FCITX_UTIL_DEFINE_ATOMIC(name, op, type)                        \
1014     FCITX_EXPORT_API type                                               \
1015     (fcitx_utils_atomic_##name)(volatile type *atomic, type val)        \
1016     {                                                                   \
1017         type oldval;                                                    \
1018         pthread_mutex_lock(&__fcitx_utils_atomic_lock);                 \
1019         oldval = *atomic;                                               \
1020         *atomic = oldval op val;                                        \
1021         pthread_mutex_unlock(&__fcitx_utils_atomic_lock);               \
1022         return oldval;                                                  \
1023     }
1024 #endif
1025 
1026 FCITX_UTIL_DEFINE_ATOMIC(add, +, int32_t)
1027 FCITX_UTIL_DEFINE_ATOMIC(and, &, uint32_t)
1028 FCITX_UTIL_DEFINE_ATOMIC(or, |, uint32_t)
1029 FCITX_UTIL_DEFINE_ATOMIC(xor, ^, uint32_t)
1030 
1031 #undef FCITX_UTIL_DEFINE_ATOMIC
1032 
1033 // kate: indent-mode cstyle; space-indent on; indent-width 0;
1034