1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 /*
35   static functions that have uim_custom_ prefix could be exported as API
36   function if needed.  -- YamaKen 2004-12-30
37 */
38 
39 /*
40   Don't insert NULL checks for free(3). free(3) accepts NULL as proper
41   argument that causes no action.  -- YamaKen 2004-12-17
42 */
43 
44 #include <config.h>
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <stdarg.h>
53 #include "gettext.h"
54 
55 #include "uim-scm.h"
56 #include "uim-custom.h"
57 #include "uim-internal.h"
58 #include "uim-helper.h"
59 
60 #if 0
61 /*
62  * The UIM_CUSTOM_EXPERIMENTAL_MTIME_SENSING is disabled since:
63  *
64  * - file_content_is_same() has a bug which may return invalid result
65  *   when the file size is greater than 4095 bytes
66  *
67  * - The codes aim to save custom-groups that some changes are
68  *   applied, but it should not be achieved by such violent method
69  *   (comparing entire content of saved files). Observing updated
70  *   group in uim-custom client program is recommended way
71  *
72  * - It breaks original behavior. See the comment of
73  *   custom-reload-user-configs in custom-rt.scm
74  *
75  *  -- YamaKen 2005-09-12
76  */
77 #define UIM_CUSTOM_EXPERIMENTAL_MTIME_SENSING
78 #endif
79 
80 #define MAX_LENGTH_OF_INT_AS_STR (((sizeof(int) == 4) ? sizeof("-2147483648") : sizeof("-9223372036854775808")) - sizeof((char)'\0'))
81 
82 #define UGETTEXT(str) (dgettext(GETTEXT_PACKAGE, (str)))
83 
84 /* we cannot use the variadic macro (i.e. __VA_ARGS__) because we
85    should also support C89 compilers
86 */
87 #define UIM_EVAL_STRING_INTERNAL(uc, sexp_str) \
88   (uim_scm_last_val = uim_scm_eval_c_string(sexp_str))
89 
90 #define UIM_EVAL_STRING(uc, sexp_str) \
91   { \
92     UIM_EVAL_STRING_INTERNAL(uc, sexp_str); \
93   }
94 
95 #define UIM_EVAL_FSTRING1(uc, sexp_tmpl, arg1) \
96   { \
97     int form_size; \
98     char *buf; \
99     form_size = uim_sizeof_sexp_str(sexp_tmpl, arg1); \
100     if (form_size != -1) { \
101       uim_asprintf(&buf, sexp_tmpl, arg1); \
102       UIM_EVAL_STRING_INTERNAL(uc, buf); \
103       free(buf); \
104     } \
105   }
106 
107 #define UIM_EVAL_FSTRING2(uc, sexp_tmpl, arg1, arg2) \
108   { \
109     int form_size; \
110     char *buf; \
111     form_size = uim_sizeof_sexp_str(sexp_tmpl, arg1, arg2); \
112     if (form_size != -1) { \
113       uim_asprintf(&buf, sexp_tmpl, arg1, arg2); \
114       UIM_EVAL_STRING_INTERNAL(uc, buf); \
115       free(buf); \
116     } \
117   }
118 
119 #define UIM_EVAL_FSTRING3(uc, sexp_tmpl, arg1, arg2, arg3) \
120   { \
121     int form_size; \
122     char *buf; \
123     form_size = uim_sizeof_sexp_str(sexp_tmpl, arg1, arg2, arg3); \
124     if (form_size != -1) { \
125       uim_asprintf(&buf, sexp_tmpl, arg1, arg2, arg3); \
126       UIM_EVAL_STRING_INTERNAL(uc, buf); \
127       free(buf); \
128     } \
129   }
130 
131 #define UIM_EVAL_FSTRING4(uc, sexp_tmpl, arg1, arg2, arg3, arg4) \
132   { \
133     int form_size; \
134     char *buf; \
135     form_size = uim_sizeof_sexp_str(sexp_tmpl, arg1, arg2, arg3, arg4); \
136     if (form_size != -1) { \
137       uim_asprintf(&buf, sexp_tmpl, arg1, arg2, arg3, arg4); \
138       UIM_EVAL_STRING_INTERNAL(uc, buf); \
139       free(buf); \
140     } \
141   }
142 
143 #define UIM_EVAL_FSTRING5(uc, sexp_tmpl, arg1, arg2, arg3, arg4, arg5) \
144   { \
145     int form_size; \
146     char *buf; \
147     form_size = uim_sizeof_sexp_str(sexp_tmpl, arg1, arg2, arg3, arg4, arg5); \
148     if (form_size != -1) { \
149       uim_asprintf(&buf, sexp_tmpl, arg1, arg2, arg3, arg4, arg5); \
150       UIM_EVAL_STRING_INTERNAL(uc, buf); \
151       free(buf); \
152     } \
153   }
154 
155 typedef void (*uim_custom_cb_update_cb_t)(void *ptr, const char *custom_sym);
156 typedef void (*uim_custom_global_cb_update_cb_t)(void *ptr);
157 
158 typedef void *(*uim_scm_c_list_conv_func)(uim_lisp elem);
159 typedef void (*uim_scm_c_list_free_func)(void *elem);
160 
161 /* exported for internal use */
162 uim_bool uim_custom_init(void);
163 uim_bool uim_custom_quit(void);
164 
165 /* uim_scm_return_value() can only be used to retrieve result of
166  * UIM_EVAL_FSTRINGn() or UIM_EVAL_STRING(). */
167 static uim_lisp uim_scm_return_value(void);
168 static int uim_sizeof_sexp_str(const char *tmpl, ...);
169 
170 static void **uim_scm_c_list(const char *list_repl, const char *mapper_proc,
171                              uim_scm_c_list_conv_func conv_func);
172 static char *uim_scm_c_str_failsafe(uim_lisp str);
173 static char **uim_scm_c_str_list(const char *list_repl,
174                                  const char *mapper_proc);
175 static void uim_scm_c_list_free(void **list,
176                                 uim_scm_c_list_free_func free_func);
177 
178 static char *literalize_string(const char *str);
179 static char *literalize_string_internal(const char *str);
180 
181 static char *c_list_to_str(const void *const *list, char *(*mapper)(const void *elem), const char *sep);
182 
183 static int uim_custom_type_eq(const char *custom_sym, const char *custom_type);
184 static int uim_custom_type(const char *custom_sym);
185 static int uim_custom_is_active(const char *custom_sym);
186 static const char *uim_custom_get_str(const char *custom_sym,
187                                       const char *proc);
188 static char *uim_custom_label(const char *custom_sym);
189 static char *uim_custom_desc(const char *custom_sym);
190 
191 static struct uim_custom_pathname *uim_custom_pathname_get(const char *custom_sym, const char *getter_proc);
192 static struct uim_custom_pathname *uim_custom_pathname_new(char *str, int type);
193 static void uim_custom_pathname_free(struct uim_custom_pathname *custom_pathname);
194 static struct uim_custom_choice *uim_custom_choice_get(const char *custom_sym, const char *choice_sym);
195 static char *extract_choice_symbol(const struct uim_custom_choice *custom_choice);
196 static char *choice_list_to_str(const struct uim_custom_choice *const *list, const char *sep);
197 static void uim_custom_choice_free(struct uim_custom_choice *custom_choice);
198 static struct uim_custom_choice **extract_choice_list(const char *list_repl, const char *custom_sym);
199 static struct uim_custom_choice **uim_custom_choice_item_list(const char *custom_sym);
200 
201 static struct uim_custom_choice **uim_custom_olist_get(const char *custom_sym, const char *getter_proc);
202 static struct uim_custom_choice **uim_custom_olist_item_list(const char *custom_sym);
203 
204 static struct uim_custom_key **uim_custom_key_get(const char *custom_sym, const char *getter_proc);
205 static void uim_custom_key_free(struct uim_custom_key *custom_key);
206 static char *extract_key_literal(const struct uim_custom_key *custom_key);
207 static char *key_list_to_str(const struct uim_custom_key *const *list, const char *sep);
208 
209 static char ***uim_custom_table_get(const char *custom_sym, const char *getter_proc);
210 static char *literalized_strdup(const char *str);
211 static char *row_list_to_str(const char *const *list);
212 static char *table_to_str(const char** const *list, const char *sep);
213 static struct uim_custom_choice **uim_custom_table_header_item_list(const char *custom_sym);
214 
215 
216 static union uim_custom_value *uim_custom_value_internal(const char *custom_sym, const char *getter_proc);
217 static union uim_custom_value *uim_custom_value(const char *custom_sym);
218 static union uim_custom_value *uim_custom_default_value(const char *custom_sym);
219 static void uim_custom_value_free(int custom_type, union uim_custom_value *custom_value);
220 static uim_lisp uim_custom_range_elem(const char *custom_sym, const char *accessor_proc);
221 static union uim_custom_range *uim_custom_range_get(const char *custom_sym);
222 static void uim_custom_range_free(int custom_type, union uim_custom_range *custom_range);
223 static uim_lisp uim_custom_cb_update_cb_gate(uim_lisp cb, uim_lisp ptr, uim_lisp custom_sym);
224 static uim_lisp uim_custom_global_cb_update_cb_gate(uim_lisp cb, uim_lisp ptr);
225 static uim_bool custom_cb_remove(const char *key_sym, const char *hook);
226 static uim_bool custom_cb_add(const char *hook, const char *validator,
227 			      const char *custom_sym, void *ptr,
228 			      const char *gate_func, void (*cb)(void));
229 struct custom_cb_add_args {
230   const char *hook;
231   const char *validator;
232   const char *custom_sym;
233   void *ptr;
234   const char *gate_func;
235   void (*cb)(void);
236 };
237 static void *custom_cb_add_internal(struct custom_cb_add_args *args);
238 
239 static void helper_disconnect_cb(void);
240 static char *uim_conf_path(const char *subpath);
241 static char *custom_file_path(const char *group, pid_t pid);
242 static uim_bool prepare_dir(const char *dir);
243 static uim_bool uim_conf_prepare_dir(const char *subdir);
244 static uim_bool for_each_primary_groups(uim_bool (*func)(const char *));
245 static uim_bool uim_custom_load_group(const char *group);
246 static uim_bool uim_custom_save_group(const char *group);
247 static const char *uim_custom_get_primary_group_by_custom(const char *custom_sym);
248 
249 static const char str_list_arg[] = "uim-custom-c-str-list-arg";
250 static const char custom_subdir[] = "customs";
251 static const char custom_msg_tmpl[] = "prop_update_custom\n%s\n%s\n";
252 static int helper_fd = -1;
253 static uim_lisp return_val;
254 static uim_lisp uim_scm_last_val;
255 
256 
257 static uim_lisp
uim_scm_return_value(void)258 uim_scm_return_value(void)
259 {
260   return uim_scm_last_val;
261 }
262 
263 /** Calculate actual sexp string size from printf-style args.
264  * This function calculates actual sexp string size from printf-style
265  * args. Format string \a sexp_tmpl only accepts %d and %s.
266  */
267 int
uim_sizeof_sexp_str(const char * sexp_tmpl,...)268 uim_sizeof_sexp_str(const char *sexp_tmpl, ...)
269 {
270   va_list ap;
271   int len, size;
272   int tmp;
273   const char *sexp_tmpl_end, *escp = sexp_tmpl, *strarg;
274   char fmtchr;
275 
276   va_start(ap, sexp_tmpl);
277   len = strlen(sexp_tmpl);
278   sexp_tmpl_end = sexp_tmpl + len - 1;
279   while ((escp = strchr(escp, '%'))) {
280     if (escp < sexp_tmpl_end) {
281       escp += sizeof((char)'%');
282       fmtchr = *escp++;
283       switch (fmtchr) {
284       case 'd':
285 	tmp = va_arg(ap, int);
286 	len += MAX_LENGTH_OF_INT_AS_STR;
287 	break;
288       case 's':
289 	strarg = va_arg(ap, const char *);
290 	len += strlen(strarg);
291 	break;
292       default:
293 	/* unexpected format string */
294 	size = -1;
295 	goto end;
296       }
297     } else {
298       /* invalid format string */
299       size = -1;
300       goto end;
301     }
302   }
303   size = len + sizeof((char)'\0');
304 
305  end:
306   va_end(ap);
307 
308   return size;
309 }
310 
311 /*
312   - list_repl must always returns same list for each evaluation
313   - returns NULL terminated array. NULL will not appeared except terminator
314   - non-string element such as #f is converted to ""
315  */
316 static void **
uim_scm_c_list(const char * list_repl,const char * mapper_proc,uim_scm_c_list_conv_func conv_func)317 uim_scm_c_list(const char *list_repl, const char *mapper_proc,
318 	       uim_scm_c_list_conv_func conv_func)
319 {
320   int list_len, i;
321   void **result;
322 
323   UIM_EVAL_FSTRING1(NULL, "(length %s)", list_repl);
324   list_len = uim_scm_c_int(uim_scm_return_value());
325 
326   result = (void **)malloc(sizeof(void *) * (list_len + 1));
327   if (!result)
328     return NULL;
329 
330   result[list_len] = NULL;
331   for (i = 0; i < list_len; i++) {
332     UIM_EVAL_FSTRING3(NULL, "(%s (nth %d %s))", mapper_proc, i, list_repl);
333     result[i] = (*conv_func)(uim_scm_return_value());
334   }
335 
336   return result;
337 }
338 
339 static char *
uim_scm_c_str_failsafe(uim_lisp str)340 uim_scm_c_str_failsafe(uim_lisp str)
341 {
342   return (uim_scm_truep(str)) ? uim_scm_c_str(str) : strdup("");
343 }
344 
345 static char **
uim_scm_c_str_list(const char * list_repl,const char * mapper_proc)346 uim_scm_c_str_list(const char *list_repl, const char *mapper_proc)
347 {
348   void **list;
349 
350   list = uim_scm_c_list(list_repl, mapper_proc,
351 			(uim_scm_c_list_conv_func)uim_scm_c_str_failsafe);
352 
353   return (char **)list;
354 }
355 
356 static void
uim_scm_c_list_free(void ** list,uim_scm_c_list_free_func free_func)357 uim_scm_c_list_free(void **list, uim_scm_c_list_free_func free_func)
358 {
359   void *elem;
360   void **p;
361 
362   if (!list)
363     return;
364 
365   for (p = list; *p; p++) {
366     elem = *p;
367     free_func(elem);
368   }
369   free(list);
370 }
371 
372 static char *
literalize_string(const char * str)373 literalize_string(const char *str)
374 {
375   return uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)literalize_string_internal, (void *)str);
376 }
377 
378 static char *
literalize_string_internal(const char * str)379 literalize_string_internal(const char *str)
380 {
381   uim_lisp form;
382   char *escaped;
383 
384   form = uim_scm_list2(uim_scm_make_symbol("string-escape"),
385 		       uim_scm_make_str(str));
386   escaped = uim_scm_c_str(uim_scm_eval(form));
387 
388   return escaped;
389 }
390 
391 static char *
c_list_to_str(const void * const * list,char * (* mapper)(const void * elem),const char * sep)392 c_list_to_str(const void *const *list, char *(*mapper)(const void *elem), const char *sep)
393 {
394   size_t buf_size;
395   char *buf, *bufp, *str;
396   const void *const *elem;
397 
398   buf_size = sizeof(char);
399   for (elem = list; *elem; elem++) {
400     if (elem != list)
401       buf_size += strlen(sep);
402     str = (*mapper)(*elem);
403     buf_size += strlen(str);
404     free(str);
405   }
406   buf = (char *)malloc(buf_size);
407   buf[0] = '\0';
408 
409   for (bufp = buf, elem = list; *elem; elem++) {
410     if (elem != list) {
411       strlcat(buf, sep, buf_size);
412       bufp += strlen(sep);
413     }
414     str = (*mapper)(*elem);
415     strlcat(buf, str, buf_size);
416     bufp += strlen(str);
417     free(str);
418   }
419 
420   return buf;
421 }
422 
423 static int
uim_custom_type_eq(const char * custom_sym,const char * custom_type)424 uim_custom_type_eq(const char *custom_sym, const char *custom_type)
425 {
426   UIM_EVAL_FSTRING2(NULL, "(eq? (custom-type '%s) '%s)",
427 		    custom_sym, custom_type);
428 
429   return uim_scm_c_bool(uim_scm_return_value());
430 }
431 
432 static int
uim_custom_type(const char * custom_sym)433 uim_custom_type(const char *custom_sym)
434 {
435   if (uim_custom_type_eq(custom_sym, "boolean")) {
436     return UCustom_Bool;
437   } else if (uim_custom_type_eq(custom_sym, "integer")) {
438     return UCustom_Int;
439   } else if (uim_custom_type_eq(custom_sym, "string")) {
440     return UCustom_Str;
441   } else if (uim_custom_type_eq(custom_sym, "pathname")) {
442     return UCustom_Pathname;
443   } else if (uim_custom_type_eq(custom_sym, "choice")) {
444     return UCustom_Choice;
445   } else if (uim_custom_type_eq(custom_sym, "ordered-list")) {
446     return UCustom_OrderedList;
447   } else if (uim_custom_type_eq(custom_sym, "key")) {
448     return UCustom_Key;
449   } else if (uim_custom_type_eq(custom_sym, "table")) {
450     return UCustom_Table;
451   } else {
452     return UCustom_Bool;
453   }
454 }
455 
456 static int
uim_custom_is_active(const char * custom_sym)457 uim_custom_is_active(const char *custom_sym)
458 {
459   UIM_EVAL_FSTRING1(NULL, "(custom-active? '%s)", custom_sym);
460   return_val = uim_scm_return_value();
461 
462   return uim_scm_c_bool(return_val);
463 }
464 
465 static const char *
uim_custom_get_str(const char * custom_sym,const char * proc)466 uim_custom_get_str(const char *custom_sym, const char *proc)
467 {
468   UIM_EVAL_FSTRING2(NULL, "(%s '%s)", proc, custom_sym);
469   return_val = uim_scm_return_value();
470 
471   return uim_scm_refer_c_str(return_val);
472 }
473 
474 static char *
uim_custom_label(const char * custom_sym)475 uim_custom_label(const char *custom_sym)
476 {
477   const char *str;
478 
479   str = uim_custom_get_str(custom_sym, "custom-label");
480   return strdup(UGETTEXT(str));
481 }
482 
483 static char *
uim_custom_desc(const char * custom_sym)484 uim_custom_desc(const char *custom_sym)
485 {
486   const char *str;
487 
488   str = uim_custom_get_str(custom_sym, "custom-desc");
489   return strdup(UGETTEXT(str));
490 }
491 
492 /* pathname */
493 static struct uim_custom_pathname *
uim_custom_pathname_get(const char * custom_sym,const char * getter_proc)494 uim_custom_pathname_get(const char *custom_sym, const char *getter_proc)
495 {
496   struct uim_custom_pathname *custom_pathname;
497   char *str, *type_sym;
498   int type;
499 
500   UIM_EVAL_FSTRING2(NULL, "(%s '%s)", getter_proc, custom_sym);
501   return_val = uim_scm_return_value();
502   str = uim_scm_c_str(return_val);
503 
504   UIM_EVAL_FSTRING1(NULL, "(custom-pathname-type '%s)", custom_sym);
505   return_val = uim_scm_return_value();
506   type_sym = uim_scm_c_symbol(return_val);
507   if (strcmp(type_sym, "directory") == 0)
508     type = UCustomPathnameType_Directory;
509   else
510     type = UCustomPathnameType_RegularFile;
511   free(type_sym);
512 
513   custom_pathname = uim_custom_pathname_new(str, type);
514   if (!custom_pathname)
515     return NULL;
516 
517   return custom_pathname;
518 }
519 
520 static struct uim_custom_pathname *
uim_custom_pathname_new(char * str,int type)521 uim_custom_pathname_new(char *str, int type)
522 {
523   struct uim_custom_pathname *custom_pathname;
524 
525   custom_pathname = malloc(sizeof(struct uim_custom_pathname));
526   if (!custom_pathname)
527     return NULL;
528 
529   custom_pathname->str = str;
530   custom_pathname->type = type;
531 
532   return custom_pathname;
533 }
534 
535 static void
uim_custom_pathname_free(struct uim_custom_pathname * custom_pathname)536 uim_custom_pathname_free(struct uim_custom_pathname *custom_pathname)
537 {
538   if (!custom_pathname)
539     return;
540 
541   free(custom_pathname->str);
542 }
543 
544 /* choice */
545 static struct uim_custom_choice *
uim_custom_choice_get(const char * custom_sym,const char * choice_sym)546 uim_custom_choice_get(const char *custom_sym, const char *choice_sym)
547 {
548   struct uim_custom_choice *c_choice;
549 
550   c_choice = uim_custom_choice_new(NULL, NULL, NULL);
551   if (!c_choice)
552     return NULL;
553 
554   c_choice->symbol = strdup(choice_sym);
555 
556   UIM_EVAL_FSTRING2(NULL, "(custom-choice-label '%s '%s)",
557 		    custom_sym, choice_sym);
558   return_val = uim_scm_return_value();
559   c_choice->label = strdup(UGETTEXT(uim_scm_refer_c_str(return_val)));
560 
561   UIM_EVAL_FSTRING2(NULL, "(custom-choice-desc '%s '%s)",
562 		    custom_sym, choice_sym);
563   return_val = uim_scm_return_value();
564   c_choice->desc = strdup(UGETTEXT(uim_scm_refer_c_str(return_val)));
565 
566   return c_choice;
567 }
568 
569 /**
570  * TODO
571  */
572 struct uim_custom_choice *
uim_custom_choice_new(char * symbol,char * label,char * desc)573 uim_custom_choice_new(char *symbol, char *label, char *desc)
574 {
575   struct uim_custom_choice *custom_choice;
576 
577   custom_choice = (struct uim_custom_choice *)malloc(sizeof(struct uim_custom_choice));
578   if (!custom_choice)
579     return NULL;
580 
581   custom_choice->symbol = symbol;
582   custom_choice->label = label;
583   custom_choice->desc = desc;
584 
585   return custom_choice;
586 }
587 
588 static void
uim_custom_choice_free(struct uim_custom_choice * custom_choice)589 uim_custom_choice_free(struct uim_custom_choice *custom_choice)
590 {
591   if (!custom_choice)
592     return;
593 
594   free(custom_choice->symbol);
595   free(custom_choice->label);
596   free(custom_choice->desc);
597   free(custom_choice);
598 }
599 
600 static struct uim_custom_choice **
extract_choice_list(const char * list_repl,const char * custom_sym)601 extract_choice_list(const char *list_repl, const char *custom_sym)
602 {
603   char *choice_sym, **choice_sym_list, **p;
604   struct uim_custom_choice *custom_choice, **custom_choice_list;
605 
606   choice_sym_list =
607     (char **)uim_scm_c_list(list_repl, "symbol->string",
608 			    (uim_scm_c_list_conv_func)uim_scm_c_str);
609   if (!choice_sym_list)
610     return NULL;
611 
612   for (p = choice_sym_list; *p; p++) {
613     choice_sym = *p;
614     custom_choice = uim_custom_choice_get(custom_sym, choice_sym);
615     free(choice_sym); /* free the old contents */
616     *p = (char *)custom_choice;  /* intentionally overwrite */
617   }
618 
619   /* reuse the list structure */
620   custom_choice_list = (struct uim_custom_choice **)choice_sym_list;
621 
622   return custom_choice_list;
623 }
624 
625 static struct uim_custom_choice **
uim_custom_choice_item_list(const char * custom_sym)626 uim_custom_choice_item_list(const char *custom_sym)
627 {
628   UIM_EVAL_FSTRING2(NULL, "(define %s (custom-range '%s))",
629 		    str_list_arg, custom_sym);
630   return extract_choice_list(str_list_arg, custom_sym);
631 }
632 
633 static char *
extract_choice_symbol(const struct uim_custom_choice * custom_choice)634 extract_choice_symbol(const struct uim_custom_choice *custom_choice)
635 {
636   return strdup(custom_choice->symbol);
637 }
638 
639 static char *
choice_list_to_str(const struct uim_custom_choice * const * list,const char * sep)640 choice_list_to_str(const struct uim_custom_choice *const *list, const char *sep)
641 {
642   return c_list_to_str((const void *const *)list,
643 		       (char *(*)(const void *))extract_choice_symbol, sep);
644 }
645 
646 /**
647  * TODO
648  */
649 void
uim_custom_choice_list_free(struct uim_custom_choice ** list)650 uim_custom_choice_list_free(struct uim_custom_choice **list)
651 {
652   uim_scm_c_list_free((void **)list,
653 		      (uim_scm_c_list_free_func)uim_custom_choice_free);
654 }
655 
656 /* ordered list */
657 static struct uim_custom_choice **
uim_custom_olist_get(const char * custom_sym,const char * getter_proc)658 uim_custom_olist_get(const char *custom_sym, const char *getter_proc)
659 {
660   UIM_EVAL_FSTRING3(NULL, "(define %s (%s '%s))",
661 		    str_list_arg, getter_proc, custom_sym);
662   return extract_choice_list(str_list_arg, custom_sym);
663 }
664 
665 static struct uim_custom_choice **
uim_custom_olist_item_list(const char * custom_sym)666 uim_custom_olist_item_list(const char *custom_sym)
667 {
668   return uim_custom_choice_item_list(custom_sym);
669 }
670 
671 /* key */
672 static struct uim_custom_key **
uim_custom_key_get(const char * custom_sym,const char * getter_proc)673 uim_custom_key_get(const char *custom_sym, const char *getter_proc)
674 {
675   char **key_literal_list, **key_label_list, **key_desc_list;
676   int *key_type_list, editor_type, list_len, i;
677   struct uim_custom_key *custom_key, **custom_key_list;
678 
679   UIM_EVAL_FSTRING3(NULL, "(define %s ((if uim-custom-expand-key? custom-expand-key-references (lambda (l) l)) (%s '%s)))",
680 		    str_list_arg, getter_proc, custom_sym);
681   key_literal_list =
682     (char **)uim_scm_c_list(str_list_arg,
683 			    "(lambda (key) (if (symbol? key) (symbol->string key) key))",
684 			    (uim_scm_c_list_conv_func)uim_scm_c_str);
685   key_type_list =
686     (int *)uim_scm_c_list(str_list_arg,
687 			  "(lambda (key) (if (symbol? key) 1 0))",
688 			  (uim_scm_c_list_conv_func)uim_scm_c_int);
689   key_label_list =
690     (char **)uim_scm_c_list(str_list_arg,
691 			    "(lambda (key) (if (symbol? key) (custom-label key) #f))",
692 			    (uim_scm_c_list_conv_func)uim_scm_c_str_failsafe);
693   key_desc_list =
694     (char **)uim_scm_c_list(str_list_arg,
695 			    "(lambda (key) (if (symbol? key) (custom-desc key) #f))",
696 			    (uim_scm_c_list_conv_func)uim_scm_c_str_failsafe);
697   if (!key_type_list || !key_literal_list || !key_label_list || !key_desc_list)
698   {
699     free(key_type_list);
700     uim_custom_symbol_list_free(key_literal_list);
701     uim_custom_symbol_list_free(key_label_list);
702     uim_custom_symbol_list_free(key_desc_list);
703     return NULL;
704   }
705 
706   UIM_EVAL_FSTRING1(NULL, "(custom-key-advanced-editor? '%s)", custom_sym);
707   return_val = uim_scm_return_value();
708   editor_type = uim_scm_c_bool(return_val) ? UCustomKeyEditor_Advanced : UCustomKeyEditor_Basic;
709 
710   UIM_EVAL_FSTRING1(NULL, "(length %s)", str_list_arg);
711   return_val = uim_scm_return_value();
712   list_len = uim_scm_c_int(return_val);
713 
714   for (i = 0; i < list_len; i++) {
715     char *literal, *label, *desc;
716     int type;
717     type = (key_type_list[i] == 1) ? UCustomKey_Reference : UCustomKey_Regular;
718     literal = key_literal_list[i];
719     label = key_label_list[i];
720     desc = key_desc_list[i];
721     custom_key = uim_custom_key_new(type, editor_type, literal, label, desc);
722     key_literal_list[i] = (char *)custom_key;  /* intentionally overwrite */
723   }
724   /* reuse the list structure */
725   custom_key_list = (struct uim_custom_key **)key_literal_list;
726 
727   /* ownership of elements had been transferred to custom_key_list */
728   free(key_type_list);
729   free(key_label_list);
730   free(key_desc_list);
731 
732   return custom_key_list;
733 }
734 
735 /**
736  * TODO
737  */
738 struct uim_custom_key *
uim_custom_key_new(int type,int editor_type,char * literal,char * label,char * desc)739 uim_custom_key_new(int type, int editor_type,
740 		   char *literal, char *label, char *desc)
741 {
742   struct uim_custom_key *custom_key;
743 
744   custom_key = (struct uim_custom_key *)malloc(sizeof(struct uim_custom_key));
745   if (!custom_key)
746     return NULL;
747 
748   custom_key->type = type;
749   custom_key->editor_type = editor_type;
750   custom_key->literal = literal;
751   custom_key->label = label;
752   custom_key->desc = desc;
753 
754   return custom_key;
755 }
756 
757 static void
uim_custom_key_free(struct uim_custom_key * custom_key)758 uim_custom_key_free(struct uim_custom_key *custom_key)
759 {
760   if (!custom_key)
761     return;
762 
763   free(custom_key->literal);
764   free(custom_key->label);
765   free(custom_key->desc);
766   free(custom_key);
767 }
768 
769 static char *
extract_key_literal(const struct uim_custom_key * custom_key)770 extract_key_literal(const struct uim_custom_key *custom_key)
771 {
772   char *literal;
773 
774   switch (custom_key->type) {
775   case UCustomKey_Regular:
776     literal = literalize_string(custom_key->literal);
777     break;
778   case UCustomKey_Reference:
779     literal = strdup(custom_key->literal);
780     break;
781   default:
782     literal = strdup("\"\"");
783   }
784 
785   return literal;
786 }
787 
788 static char *
key_list_to_str(const struct uim_custom_key * const * list,const char * sep)789 key_list_to_str(const struct uim_custom_key *const *list, const char *sep)
790 {
791   return c_list_to_str((const void *const *)list,
792 		       (char *(*)(const void *))extract_key_literal, sep);
793 }
794 
795 /**
796  * TODO
797  */
798 void
uim_custom_key_list_free(struct uim_custom_key ** list)799 uim_custom_key_list_free(struct uim_custom_key **list)
800 {
801   uim_scm_c_list_free((void **)list,
802 		      (uim_scm_c_list_free_func)uim_custom_key_free);
803 }
804 
805 /* table */
806 static char ***
uim_custom_table_get(const char * custom_sym,const char * getter_proc)807 uim_custom_table_get(const char *custom_sym, const char *getter_proc)
808 {
809   char ***custom_table;
810   int row_count;
811   int row;
812 
813   UIM_EVAL_FSTRING1(NULL, "(length %s)", custom_sym);
814   row_count = uim_scm_c_int(uim_scm_return_value());
815 
816   custom_table = (char ***)malloc(sizeof(char **) * (row_count + 1));
817   if (!custom_table)
818     return NULL;
819 
820   custom_table[row_count] = NULL;
821   for (row = 0; row < row_count; row++) {
822     int column_count;
823     int column;
824     UIM_EVAL_FSTRING2(NULL, "(length (nth %d %s))", row, custom_sym);
825     column_count = uim_scm_c_int(uim_scm_return_value());
826 
827     custom_table[row] = (char **)malloc(sizeof(char *) * (column_count + 1));
828     if (!custom_table[row])
829       return NULL;
830     custom_table[row][column_count] = NULL;
831     for (column = 0; column < column_count; column++) {
832       char *str;
833       UIM_EVAL_FSTRING3(NULL, "(nth %d (nth %d %s))", column, row, custom_sym);
834       str = uim_scm_c_str(uim_scm_return_value());
835       if (!str)
836         return NULL;
837       custom_table[row][column] = malloc(sizeof(char) * (strlen(str) + 1));
838       if (!custom_table[row][column])
839         return NULL;
840       custom_table[row][column] = str;
841     }
842   }
843 
844   return custom_table;
845 }
846 
847 static char *
literalized_strdup(const char * str)848 literalized_strdup(const char *str)
849 {
850   return strdup(literalize_string(str));
851 }
852 
853 static char *
row_list_to_str(const char * const * list)854 row_list_to_str(const char *const *list)
855 {
856   return c_list_to_str((const void *const *)list,
857                        (char *(*)(const void *))literalized_strdup, " ");
858 }
859 
860 static char *
table_to_str(const char ** const * list,const char * sep)861 table_to_str(const char** const *list, const char *sep)
862 {
863   return c_list_to_str((const void *const *)list,
864 		       (char *(*)(const void *))row_list_to_str, sep);
865 }
866 
867 static struct uim_custom_choice **
uim_custom_table_header_item_list(const char * custom_sym)868 uim_custom_table_header_item_list(const char *custom_sym)
869 {
870   return uim_custom_choice_item_list(custom_sym);
871 }
872 
873 static union uim_custom_value *
uim_custom_value_internal(const char * custom_sym,const char * getter_proc)874 uim_custom_value_internal(const char *custom_sym, const char *getter_proc)
875 {
876   int type;
877   union uim_custom_value *value;
878   char *custom_value_symbol;
879 
880   if (!custom_sym || !getter_proc)
881     return NULL;
882 
883   value = (union uim_custom_value *)malloc(sizeof(union uim_custom_value));
884   if (!value)
885     return NULL;
886 
887   type = uim_custom_type(custom_sym);
888   UIM_EVAL_FSTRING2(NULL, "(%s '%s)", getter_proc, custom_sym);
889   return_val = uim_scm_return_value();
890   switch (type) {
891   case UCustom_Bool:
892     value->as_bool = uim_scm_c_bool(return_val);
893     break;
894   case UCustom_Int:
895     value->as_int = uim_scm_c_int(return_val);
896     break;
897   case UCustom_Str:
898     value->as_str = uim_scm_c_str(return_val);
899     break;
900   case UCustom_Pathname:
901     value->as_pathname = uim_custom_pathname_get(custom_sym, getter_proc);
902     break;
903   case UCustom_Choice:
904     custom_value_symbol = uim_scm_c_symbol(return_val);
905     value->as_choice = uim_custom_choice_get(custom_sym, custom_value_symbol);
906     free(custom_value_symbol);
907     break;
908   case UCustom_OrderedList:
909     value->as_olist = uim_custom_olist_get(custom_sym, getter_proc);
910     break;
911   case UCustom_Key:
912     value->as_key = uim_custom_key_get(custom_sym, getter_proc);
913     break;
914   case UCustom_Table:
915     value->as_table = uim_custom_table_get(custom_sym, getter_proc);
916     break;
917   default:
918     free(value);
919     value = NULL;
920   }
921 
922   return value;
923 }
924 
925 static union uim_custom_value *
uim_custom_value(const char * custom_sym)926 uim_custom_value(const char *custom_sym)
927 {
928   return uim_custom_value_internal(custom_sym, "custom-value");
929 }
930 
931 static union uim_custom_value *
uim_custom_default_value(const char * custom_sym)932 uim_custom_default_value(const char *custom_sym)
933 {
934   return uim_custom_value_internal(custom_sym, "custom-default-value");
935 }
936 
937 static void
uim_custom_value_free(int custom_type,union uim_custom_value * custom_value)938 uim_custom_value_free(int custom_type, union uim_custom_value *custom_value)
939 {
940   if (!custom_value)
941     return;
942 
943   switch (custom_type) {
944   case UCustom_Str:
945     free(custom_value->as_str);
946     break;
947   case UCustom_Pathname:
948     uim_custom_pathname_free(custom_value->as_pathname);
949     break;
950   case UCustom_Choice:
951     uim_custom_choice_free(custom_value->as_choice);
952     break;
953   case UCustom_OrderedList:
954     uim_custom_choice_list_free(custom_value->as_olist);
955     break;
956   case UCustom_Key:
957     uim_custom_key_list_free(custom_value->as_key);
958     break;
959   }
960   free(custom_value);
961 }
962 
963 /*
964   The arg must be assigned to return_val by caller to be proteced
965   from GC while subsequent evaluation
966 */
967 static uim_lisp
uim_custom_range_elem(const char * custom_sym,const char * accessor_proc)968 uim_custom_range_elem(const char *custom_sym, const char *accessor_proc)
969 {
970   UIM_EVAL_FSTRING2(NULL, "(%s (custom-range '%s))",
971 		    accessor_proc, custom_sym);
972 
973   return uim_scm_return_value();
974 }
975 
976 static union uim_custom_range *
uim_custom_range_get(const char * custom_sym)977 uim_custom_range_get(const char *custom_sym)
978 {
979   int type;
980   union uim_custom_range *range;
981 
982   range = (union uim_custom_range *)malloc(sizeof(union uim_custom_range));
983   if (!range)
984     return NULL;
985 
986   type = uim_custom_type(custom_sym);
987   switch (type) {
988   case UCustom_Int:
989     return_val = uim_custom_range_elem(custom_sym, "car");
990     range->as_int.min = uim_scm_c_int(return_val);
991     return_val = uim_custom_range_elem(custom_sym, "cadr");
992     range->as_int.max = uim_scm_c_int(return_val);
993     break;
994   case UCustom_Str:
995     return_val = uim_custom_range_elem(custom_sym, "car");
996     range->as_str.regex = uim_scm_c_str(return_val);
997     break;
998   case UCustom_Choice:
999     range->as_choice.valid_items = uim_custom_choice_item_list(custom_sym);
1000     break;
1001   case UCustom_OrderedList:
1002     range->as_olist.valid_items = uim_custom_olist_item_list(custom_sym);
1003     break;
1004   case UCustom_Table:
1005     range->as_table_header.valid_items
1006         = uim_custom_table_header_item_list(custom_sym);
1007     break;
1008   }
1009 
1010   return range;
1011 }
1012 
1013 static void
uim_custom_range_free(int custom_type,union uim_custom_range * custom_range)1014 uim_custom_range_free(int custom_type, union uim_custom_range *custom_range)
1015 {
1016   if (!custom_range)
1017     return;
1018 
1019   switch (custom_type) {
1020   case UCustom_Str:
1021     free(custom_range->as_str.regex);
1022     break;
1023   case UCustom_Choice:
1024     uim_custom_choice_list_free(custom_range->as_choice.valid_items);
1025     break;
1026   case UCustom_OrderedList:
1027     uim_custom_choice_list_free(custom_range->as_olist.valid_items);
1028     break;
1029   }
1030   free(custom_range);
1031 }
1032 
1033 static void
helper_disconnect_cb(void)1034 helper_disconnect_cb(void)
1035 {
1036   helper_fd = -1;
1037 }
1038 
1039 /**
1040  * Enables use of custom API. This function must be called before
1041  * uim_custom_*() functions are called. uim_init() must be called before this
1042  * function.
1043  *
1044  * @see uim_init()
1045  * @retval UIM_TRUE succeeded
1046  * @retval UIM_FALSE failed
1047  */
1048 uim_bool
uim_custom_enable(void)1049 uim_custom_enable(void)
1050 {
1051   UIM_EVAL_STRING(NULL, "(require-dynlib \"custom-enabler\")");
1052   return uim_scm_c_bool(uim_scm_return_value());
1053 }
1054 
1055 /*
1056  * This function is exported as internal use. Intentionally disdocumented.
1057  *
1058  * Initializes custom API. This function must be called before uim_custom_*()
1059  * functions are called. uim_init() must be called before this function.
1060  *
1061  * @see uim_init()
1062  * @retval UIM_TRUE succeeded
1063  * @retval UIM_FALSE failed
1064  */
1065 uim_bool
uim_custom_init(void)1066 uim_custom_init(void)
1067 {
1068   const char *client_codeset;
1069 
1070   return_val = uim_scm_f();
1071   uim_scm_last_val = uim_scm_f();
1072   uim_scm_gc_protect(&return_val);
1073   uim_scm_gc_protect(&uim_scm_last_val);
1074 
1075   uim_scm_init_proc3("custom-update-cb-gate", uim_custom_cb_update_cb_gate);
1076   uim_scm_init_proc2("custom-global-update-cb-gate",
1077 		     uim_custom_global_cb_update_cb_gate);
1078 
1079   uim_scm_require_file("custom.scm");
1080 
1081   /* temporary solution to control key definition expantion */
1082   UIM_EVAL_STRING(NULL, "(define uim-custom-expand-key? #t)");
1083 
1084   /* Assumes that bind_textdomain_codeset() is already called in client
1085    * program. */
1086   client_codeset = bind_textdomain_codeset(textdomain(NULL), NULL);
1087   bind_textdomain_codeset(GETTEXT_PACKAGE, client_codeset);
1088 
1089   return UIM_TRUE;
1090 }
1091 
1092 /*
1093  * This function is exported as internal use. Intentionally disdocumented.
1094  *
1095  * Finalizes custom API. This function must be called before uim_quit().
1096  *
1097  * @see uim_quit()
1098  * @retval UIM_TRUE succeeded
1099  * @retval UIM_FALSE failed
1100  */
1101 uim_bool
uim_custom_quit(void)1102 uim_custom_quit(void)
1103 {
1104   uim_custom_cb_remove(NULL);
1105   uim_custom_group_cb_remove(NULL);
1106   uim_custom_global_cb_remove();
1107 
1108   return UIM_TRUE;
1109 }
1110 
1111 static char *
uim_conf_path(const char * subpath)1112 uim_conf_path(const char *subpath)
1113 {
1114   char *dir;
1115 
1116   UIM_EVAL_STRING(NULL, "(string-append (or (home-directory (user-name)) \"\") \"/.uim.d\")");
1117   dir = uim_scm_c_str(uim_scm_return_value());
1118   if (subpath) {
1119     UIM_EVAL_FSTRING2(NULL, "\"%s/%s\"", dir, subpath);
1120     free(dir);
1121     dir = uim_scm_c_str(uim_scm_return_value());
1122   }
1123 
1124   return dir;
1125 }
1126 
1127 static char *
custom_file_path(const char * group,pid_t pid)1128 custom_file_path(const char *group, pid_t pid)
1129 {
1130   char *custom_dir, *file_path;
1131 
1132   custom_dir = uim_conf_path(custom_subdir);
1133   if (pid) {
1134     UIM_EVAL_FSTRING3(NULL, "\"%s/.custom-%s.scm.%d\"", custom_dir, group, (int)pid);
1135   } else {
1136     UIM_EVAL_FSTRING2(NULL, "\"%s/custom-%s.scm\"", custom_dir, group);
1137   }
1138   file_path = uim_scm_c_str(uim_scm_return_value());
1139   free(custom_dir);
1140 
1141   return file_path;
1142 }
1143 
1144 static uim_bool
prepare_dir(const char * dir)1145 prepare_dir(const char *dir)
1146 {
1147   struct stat st;
1148 
1149   if (stat(dir, &st) < 0) {
1150     return (mkdir(dir, 0700) < 0) ? UIM_FALSE : UIM_TRUE;
1151   } else {
1152     mode_t mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
1153 
1154     return ((st.st_mode & mode) == mode) ? UIM_TRUE : UIM_FALSE;
1155   }
1156 }
1157 
1158 static uim_bool
uim_conf_prepare_dir(const char * subdir)1159 uim_conf_prepare_dir(const char *subdir)
1160 {
1161   uim_bool succeeded;
1162   char *dir;
1163 
1164   dir = uim_conf_path(NULL);
1165   succeeded = prepare_dir(dir);
1166   free(dir);
1167   if (!succeeded)
1168     return UIM_FALSE;
1169 
1170   if (subdir) {
1171     dir = uim_conf_path(subdir);
1172     succeeded = prepare_dir(dir);
1173     free(dir);
1174     if (!succeeded)
1175       return UIM_FALSE;
1176   }
1177 
1178   return UIM_TRUE;
1179 }
1180 
1181 static uim_bool
for_each_primary_groups(uim_bool (* func)(const char *))1182 for_each_primary_groups(uim_bool (*func)(const char *))
1183 {
1184   uim_bool succeeded = UIM_TRUE;
1185   char **primary_groups, **grp;
1186 
1187   primary_groups = uim_custom_primary_groups();
1188   for (grp = primary_groups; *grp; grp++) {
1189     succeeded = (*func)(*grp) && succeeded;
1190   }
1191   uim_custom_symbol_list_free(primary_groups);
1192 
1193   return succeeded;
1194 }
1195 
1196 static uim_bool
uim_custom_load_group(const char * group)1197 uim_custom_load_group(const char *group)
1198 {
1199   char *file_path;
1200   uim_bool succeeded;
1201 
1202   file_path = custom_file_path(group, 0);
1203   succeeded = uim_scm_load_file(file_path);
1204   free(file_path);
1205 
1206   return succeeded;
1207 }
1208 
1209 /**
1210  * Loads per-user custom variable configurations. This function loads per-user
1211  * custom variable values from ~/.uim.d/customs/custom-*.scm previously saved
1212  * by uim_custom_save().
1213  *
1214  * @see uim_custom_save()
1215  * @retval UIM_TRUE succeeded
1216  * @retval UIM_FALSE failed
1217  */
1218 uim_bool
uim_custom_load(void)1219 uim_custom_load(void)
1220 {
1221   if(uim_helper_is_setugid() ==UIM_FALSE) {
1222     return for_each_primary_groups(uim_custom_load_group);
1223   } else {
1224     return UIM_FALSE;
1225   }
1226 }
1227 
1228 #ifdef UIM_CUSTOM_EXPERIMENTAL_MTIME_SENSING
1229 static uim_bool
file_content_is_same(const char * a_path,const char * b_path)1230 file_content_is_same(const char *a_path, const char *b_path)
1231 {
1232   uim_bool ret;
1233   FILE *a, *b;
1234   char a_buf[4096], b_buf[4096];
1235 
1236   a = fopen(a_path, "r");
1237   b = fopen(b_path, "r");
1238 
1239   while(1) {
1240     char *a_eof, *b_eof;
1241 
1242     if (!a || !b) {
1243       ret = UIM_FALSE;
1244       break;
1245     }
1246 
1247     a_eof = fgets(a_buf, sizeof(a_buf), a);
1248     b_eof = fgets(b_buf, sizeof(b_buf), b);
1249 
1250     if (!a_eof && !b_eof) {
1251       ret = UIM_TRUE;
1252       break;
1253     }
1254 
1255     if ((!a_eof && b_eof) || (a_eof && !b_eof)) {
1256       ret = UIM_FALSE;
1257       break;
1258     }
1259 
1260     if (strcmp(a_buf, b_buf) != 0) {
1261       ret = UIM_FALSE;
1262       break;
1263     }
1264   }
1265 
1266   if (a)
1267     fclose(a);
1268   if (b)
1269     fclose(b);
1270   return ret;
1271 }
1272 #endif
1273 
1274 static uim_bool
uim_custom_save_group(const char * group)1275 uim_custom_save_group(const char *group)
1276 {
1277   uim_bool succeeded = UIM_FALSE;
1278   char **custom_syms, **sym;
1279   char *def_literal;
1280   pid_t pid;
1281   char *tmp_file_path, *file_path;
1282   FILE *file;
1283 
1284   if (!uim_conf_prepare_dir(custom_subdir))
1285     return UIM_FALSE;
1286 
1287   /*
1288     to avoid write conflict and broken by accident, we write customs
1289     to temporary file first
1290   */
1291   pid = getpid();
1292   tmp_file_path = custom_file_path(group, pid);
1293   file = fopen(tmp_file_path, "w");
1294   if (!file)
1295     goto error;
1296 
1297   custom_syms = uim_custom_collect_by_group(group);
1298   if (!custom_syms) {
1299     fclose(file);
1300     goto error;
1301   }
1302 
1303   for (sym = custom_syms; *sym; sym++) {
1304     def_literal = uim_custom_definition_as_literal(*sym);
1305     if (def_literal) {
1306       fputs(def_literal, file);
1307       fprintf(file, "\n");
1308       free(def_literal);
1309     }
1310   }
1311   uim_custom_symbol_list_free(custom_syms);
1312 
1313   if (fclose(file) < 0)
1314     goto error;
1315 
1316   /* rename prepared temporary file to proper name */
1317   file_path = custom_file_path(group, 0);
1318 #ifdef UIM_CUSTOM_EXPERIMENTAL_MTIME_SENSING
1319   /*
1320    * Avoiding a file saving at here by such method is a layer
1321    * violation. Observing updated group is recommended way.
1322    *   -- YamaKen 2005-08-09
1323    */
1324   if (file_content_is_same(tmp_file_path, file_path)) {
1325     succeeded = UIM_TRUE;
1326     remove(tmp_file_path);
1327   } else {
1328     succeeded = (rename(tmp_file_path, file_path) == 0);
1329   }
1330 #else
1331   succeeded = (rename(tmp_file_path, file_path) == 0);
1332 #endif
1333   free(file_path);
1334 
1335  error:
1336   free(tmp_file_path);
1337 
1338   return succeeded;
1339 }
1340 
1341 /**
1342  * Saves per-user custom variable configurations. This function saves current
1343  * custom variable values into ~/.uim.d/customs/custom-*.scm. The directory
1344  * will be made if not exist. The saved values will be implicitly loaded at
1345  * uim_init() or can explicitly be loaded by uim_custom_load().
1346  *
1347  * @see uim_init()
1348  * @see uim_custom_load()
1349  * @retval UIM_TRUE succeeded
1350  * @retval UIM_FALSE failed
1351  */
1352 uim_bool
uim_custom_save(void)1353 uim_custom_save(void)
1354 {
1355   if(uim_helper_is_setugid() == UIM_FALSE) {
1356     return for_each_primary_groups(uim_custom_save_group);
1357   } else {
1358     return UIM_FALSE;
1359   }
1360 }
1361 
1362 /**
1363  * Saves a per-user custom variable configuration. This function saves a
1364  * primary custom group values which contains the specified custom variable as
1365  * ~/.uim.d/customs/custom-primary-group-file-containing-the-variable.scm. The
1366  * directory will be made if not exist. The saved values will be implicitly
1367  * loaded at uim_init() or can explicitly be loaded by uim_custom_load().
1368  *
1369  * @see uim_init()
1370  * @see uim_custom_load()
1371  * @retval UIM_TRUE succeeded
1372  * @retval UIM_FALSE failed
1373  */
1374 uim_bool
uim_custom_save_custom(const char * custom_sym)1375 uim_custom_save_custom(const char *custom_sym)
1376 {
1377   if(uim_helper_is_setugid() == UIM_FALSE) {
1378     const char *group_sym = uim_custom_get_primary_group_by_custom(custom_sym);
1379     return uim_custom_save_group(group_sym);
1380   } else {
1381     return UIM_FALSE;
1382   }
1383 }
1384 
1385 /**
1386  * Broadcasts custom variable configurations to other uim-enabled application
1387  * processes via uim-helper-server. This function broadcasts current custom
1388  * variable values to other uim-enabled application processes via
1389  * uim-helper-server. The received processes updates custom variables
1390  * dynamically. This enables dynamic re-configuration of input methods.
1391  *
1392  * @retval UIM_TRUE succeeded
1393  * @retval UIM_FALSE failed
1394  */
1395 uim_bool
uim_custom_broadcast(void)1396 uim_custom_broadcast(void)
1397 {
1398   char **custom_syms, **sym;
1399   char *value, *msg;
1400 
1401   if (helper_fd < 0) {
1402     helper_fd = uim_helper_init_client_fd(helper_disconnect_cb);
1403   }
1404 
1405   custom_syms = uim_custom_collect_by_group(NULL);
1406   for (sym = custom_syms; *sym; sym++) {
1407     value = uim_custom_value_as_literal(*sym);
1408     if (value) {
1409 #if 1
1410       uim_asprintf(&msg, custom_msg_tmpl, *sym, value);
1411 #else
1412       /* old behavior: useless since other memory exhaustions disable uim */
1413       if (asprintf(&msg, custom_msg_tmpl, *sym, value) < 0 || !msg) {
1414 	free(msg);
1415 	free(value);
1416 	uim_custom_symbol_list_free(custom_syms);
1417 	return UIM_FALSE;
1418       }
1419 #endif
1420       uim_helper_send_message(helper_fd, msg);
1421       free(msg);
1422       free(value);
1423     }
1424   }
1425   uim_custom_symbol_list_free(custom_syms);
1426 
1427   if (helper_fd != -1) {
1428     uim_helper_close_client_fd(helper_fd);
1429   }
1430 
1431   return UIM_TRUE;
1432 }
1433 
1434 /**
1435  * Broadcasts a request to reload custom files to other uim-enabled
1436  * application processes via uim-helper-server.
1437  *
1438  * @retval UIM_TRUE succeeded
1439  * @retval UIM_FALSE failed
1440  */
1441 uim_bool
uim_custom_broadcast_reload_request(void)1442 uim_custom_broadcast_reload_request(void)
1443 {
1444   if (helper_fd < 0) {
1445     helper_fd = uim_helper_init_client_fd(helper_disconnect_cb);
1446   }
1447 
1448   uim_helper_send_message(helper_fd, "custom_reload_notify\n");
1449 
1450   if (helper_fd != -1) {
1451     uim_helper_close_client_fd(helper_fd);
1452   }
1453 
1454   return UIM_TRUE;
1455 }
1456 
1457 /**
1458  * Returns attributes and current value of a custom variable. Returned value
1459  * must be freed by uim_custom_free().
1460  *
1461  * @see uim_custom_free()
1462  * @return custom variable attributes and current value
1463  * @param custom_sym custom variable name
1464  */
1465 struct uim_custom *
uim_custom_get(const char * custom_sym)1466 uim_custom_get(const char *custom_sym)
1467 {
1468   struct uim_custom *custom;
1469 
1470   if (!custom_sym)
1471     return UIM_FALSE;
1472 
1473   custom = (struct uim_custom *)malloc(sizeof(struct uim_custom));
1474   if (!custom)
1475     return UIM_FALSE;
1476 
1477   custom->type = uim_custom_type(custom_sym);
1478   custom->is_active = uim_custom_is_active(custom_sym);
1479   custom->symbol = strdup(custom_sym);
1480   custom->label = uim_custom_label(custom_sym);
1481   custom->desc = uim_custom_desc(custom_sym);
1482   custom->value = uim_custom_value(custom_sym);
1483   custom->default_value = uim_custom_default_value(custom_sym);
1484   custom->range = uim_custom_range_get(custom_sym);
1485 
1486   return custom;
1487 }
1488 
1489 /**
1490  * Updates value of a custom variable. This function tries that an update of
1491  * the custom variable specified by symbol and value of contained in @a
1492  * custom. Update failes when passed value is invalid for the custom
1493  * variable. Previous value is kept in real custom variable when the
1494  * failure. @a custom should be created by uim_custom_get() and then user of
1495  * uim-custom API can modify value of the @custom before passing to this
1496  * function.
1497  *
1498  * If you want to set null list as value for ordered-list or key,
1499  * allocate an array contains 1 NULL element and set it into
1500  * custom->value->as_foo.
1501  *
1502  * @see uim_custom_get()
1503  * @param custom custom variable symbol and value
1504  * @retval UIM_TRUE succeeded
1505  * @retval UIM_FALSE failed
1506  */
1507 uim_bool
uim_custom_set(const struct uim_custom * custom)1508 uim_custom_set(const struct uim_custom *custom)
1509 {
1510   char *literal;
1511 
1512   if (!custom)
1513     return UIM_FALSE;
1514 
1515   switch (custom->type) {
1516   case UCustom_Bool:
1517     UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s #%s)",
1518 		      custom->symbol, (custom->value->as_bool) ? "t" : "f");
1519     break;
1520   case UCustom_Int:
1521     UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s %d)",
1522 		      custom->symbol, custom->value->as_int);
1523     break;
1524   case UCustom_Str:
1525     literal = literalize_string(custom->value->as_str);
1526     UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s %s)",
1527 		      custom->symbol, literal);
1528     free(literal);
1529     break;
1530   case UCustom_Pathname:
1531     literal = literalize_string(custom->value->as_pathname->str);
1532     UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s %s)",
1533 		      custom->symbol, literal);
1534     free(literal);
1535     break;
1536   case UCustom_Choice:
1537     UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s '%s)",
1538 		      custom->symbol, custom->value->as_choice->symbol);
1539     break;
1540   case UCustom_OrderedList:
1541     {
1542       char *val;
1543       val = choice_list_to_str((const struct uim_custom_choice *const *)custom->value->as_olist, " ");
1544       UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s '(%s))", custom->symbol, val);
1545       free(val);
1546     }
1547     break;
1548   case UCustom_Key:
1549     {
1550       char *val;
1551       val = key_list_to_str((const struct uim_custom_key *const *)custom->value->as_key, " ");
1552       UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s (map gui-key-str->key-str '(%s)))", custom->symbol, val);
1553       free(val);
1554     }
1555     break;
1556   case UCustom_Table:
1557     {
1558       char *val
1559         = table_to_str((const char ** const *)custom->value->as_table, ") (");
1560       UIM_EVAL_FSTRING2(NULL, "(custom-set-value! '%s '((%s)))",
1561         custom->symbol, val);
1562       free(val);
1563     }
1564     break;
1565   default:
1566     return UIM_FALSE;
1567   }
1568   return uim_scm_c_bool(uim_scm_return_value());
1569 }
1570 
1571 /**
1572  * Frees pre-allocated C representation of a custom variable. All C
1573  * representation of a custom variable allocated by uim_custom_get() must be
1574  * freed by this function.
1575  *
1576  * @see uim_custom_get()
1577  * @param custom C representation of a custom variable
1578  */
1579 void
uim_custom_free(struct uim_custom * custom)1580 uim_custom_free(struct uim_custom *custom)
1581 {
1582   if (!custom)
1583     return;
1584 
1585   free(custom->symbol);
1586   free(custom->label);
1587   free(custom->desc);
1588   uim_custom_value_free(custom->type, custom->value);
1589   uim_custom_value_free(custom->type, custom->default_value);
1590   uim_custom_range_free(custom->type, custom->range);
1591   free(custom);
1592 }
1593 
1594 /**
1595  * Returns Scheme literal of a custom variable value. Returned string must be
1596  * free() by caller.
1597  *
1598  * @return the literal
1599  * @param custom_sym custom variable name
1600  */
1601 char *
uim_custom_value_as_literal(const char * custom_sym)1602 uim_custom_value_as_literal(const char *custom_sym)
1603 {
1604   return strdup(uim_custom_get_str(custom_sym, "custom-value-as-literal"));
1605 }
1606 
1607 /**
1608  * Returns Scheme literal of a custom variable definition. Returned string
1609  * must be free() by caller.
1610  *
1611  * @return the literal
1612  * @param custom_sym custom variable name
1613  */
1614 char *
uim_custom_definition_as_literal(const char * custom_sym)1615 uim_custom_definition_as_literal(const char *custom_sym)
1616 {
1617   return strdup(uim_custom_get_str(custom_sym, "custom-definition-as-literal"));
1618 }
1619 
1620 /**
1621  * Returns attributes of a custom group.and current value of a custom
1622  * variable. Returned value must be freed by uim_custom_group_free().
1623  *
1624  * @see uim_custom_group_free()
1625  * @return attributes of custom group
1626  * @param group_sym custom group name
1627  */
1628 struct uim_custom_group *
uim_custom_group_get(const char * group_sym)1629 uim_custom_group_get(const char *group_sym)
1630 {
1631   struct uim_custom_group *custom_group;
1632   const char *label, *desc;
1633 
1634   custom_group = (struct uim_custom_group *)malloc(sizeof(struct uim_custom_group));
1635   if (!custom_group)
1636     return NULL;
1637 
1638   label = uim_custom_get_str(group_sym, "custom-group-label");
1639   desc = uim_custom_get_str(group_sym, "custom-group-desc");
1640 
1641   custom_group->symbol = strdup(group_sym);
1642   custom_group->label = strdup(UGETTEXT(label));
1643   custom_group->desc = strdup(UGETTEXT(desc));
1644 
1645   return custom_group;
1646 }
1647 
1648 /**
1649  * Frees C representation of a custom group.
1650  *
1651  * @see uim_custom_group_get()
1652  * @param custom_group C representation of custom group
1653  */
1654 void
uim_custom_group_free(struct uim_custom_group * custom_group)1655 uim_custom_group_free(struct uim_custom_group *custom_group)
1656 {
1657   if (!custom_group)
1658     return;
1659 
1660   free(custom_group->symbol);
1661   free(custom_group->label);
1662   free(custom_group->desc);
1663   free(custom_group);
1664 }
1665 
1666 /**
1667  * Returns custom variable symbols that belongs to @a group_sym. The symbols
1668  * consist of NULL-terminated array of C string and must be freed by
1669  * uim_custom_symbol_list_free().
1670  *
1671  * @see uim_custom_symbol_list_free()
1672  * @return custom variable symbols
1673  * @param group_sym custom group name. NULL means 'any group'
1674  */
1675 char **
uim_custom_collect_by_group(const char * group_sym)1676 uim_custom_collect_by_group(const char *group_sym)
1677 {
1678   char **custom_list;
1679 
1680   UIM_EVAL_FSTRING2(NULL, "(define %s (custom-collect-by-group '%s))",
1681 		    str_list_arg, (group_sym) ? group_sym : "#f");
1682   custom_list = uim_scm_c_str_list(str_list_arg, "symbol->string");
1683 
1684   return custom_list;
1685 }
1686 
1687 /**
1688  * Returns all existing custom group symbols. The symbols consist of
1689  * NULL-terminated array of C string and must be freed by
1690  * uim_custom_symbol_list_free().
1691  *
1692  * @see uim_custom_symbol_list_free()
1693  * @return custom variable symbols
1694  */
1695 char **
uim_custom_groups(void)1696 uim_custom_groups(void)
1697 {
1698   char **group_list;
1699 
1700   UIM_EVAL_FSTRING1(NULL, "(define %s (custom-list-groups))", str_list_arg);
1701   group_list = uim_scm_c_str_list(str_list_arg, "symbol->string");
1702 
1703   return group_list;
1704 }
1705 
1706 /**
1707  * Returns all existing primary custom group symbols. Subgroups are not
1708  * returned. The symbols consist of NULL-terminated array of C string and must
1709  * be freed by uim_custom_symbol_list_free().
1710  *
1711  * @see uim_custom_symbol_list_free()
1712  * @return custom variable symbols
1713  */
1714 char **
uim_custom_primary_groups(void)1715 uim_custom_primary_groups(void)
1716 {
1717   char **group_list;
1718 
1719   UIM_EVAL_FSTRING1(NULL, "(define %s (custom-list-primary-groups))",
1720 		    str_list_arg);
1721   group_list = uim_scm_c_str_list(str_list_arg, "symbol->string");
1722 
1723   return group_list;
1724 }
1725 
1726 /**
1727  * Returns subgroup symbols of @a group_sym. The symbols consist of
1728  * NULL-terminated array of C string and must be freed by
1729  * uim_custom_symbol_list_free().
1730  *
1731  * @see uim_custom_symbol_list_free()
1732  * @return custom subgroup symbols
1733  * @param group_sym custom group name
1734  */
1735 char **
uim_custom_group_subgroups(const char * group_sym)1736 uim_custom_group_subgroups(const char *group_sym)
1737 {
1738   char **group_list;
1739 
1740   UIM_EVAL_FSTRING2(NULL, "(define %s (custom-group-subgroups '%s))",
1741 		    str_list_arg, group_sym);
1742   group_list = uim_scm_c_str_list(str_list_arg, "symbol->string");
1743 
1744   return group_list;
1745 }
1746 
1747 /**
1748  * Frees a symbol list allocated by uim_custom_*() functions. The term 'list'
1749  * does not mean linked list. Actually NULL-terminated array.
1750  *
1751  * @see uim_custom_collect_by_group()
1752  * @see uim_custom_groups()
1753  * @see uim_custom_primary_groups()
1754  * @see uim_custom_group_subgroups()
1755  * @param symbol_list pre-allocated symbol list
1756  */
1757 void
uim_custom_symbol_list_free(char ** symbol_list)1758 uim_custom_symbol_list_free(char **symbol_list)
1759 {
1760   uim_scm_c_list_free((void **)symbol_list, (uim_scm_c_list_free_func)free);
1761 }
1762 
1763 static const char *
uim_custom_get_primary_group_by_custom(const char * custom_sym)1764 uim_custom_get_primary_group_by_custom(const char *custom_sym)
1765 {
1766   uim_lisp groups;
1767   groups = uim_scm_callf("custom-groups", "y", custom_sym);
1768 
1769   return uim_scm_refer_c_str(uim_scm_car(groups));
1770 }
1771 
1772 static uim_lisp
uim_custom_cb_update_cb_gate(uim_lisp cb,uim_lisp ptr,uim_lisp custom_sym)1773 uim_custom_cb_update_cb_gate(uim_lisp cb, uim_lisp ptr, uim_lisp custom_sym)
1774 {
1775   uim_custom_cb_update_cb_t update_cb;
1776   void *c_ptr;
1777   char *c_custom_sym;
1778 
1779   update_cb = (uim_custom_cb_update_cb_t)uim_scm_c_func_ptr(cb);
1780   c_ptr = uim_scm_c_ptr(ptr);
1781   c_custom_sym = uim_scm_c_symbol(custom_sym);
1782   (*update_cb)(c_ptr, c_custom_sym);
1783   free(c_custom_sym);
1784 
1785   return uim_scm_f();
1786 }
1787 
1788 static uim_lisp
uim_custom_global_cb_update_cb_gate(uim_lisp cb,uim_lisp ptr)1789 uim_custom_global_cb_update_cb_gate(uim_lisp cb, uim_lisp ptr)
1790 {
1791   uim_custom_global_cb_update_cb_t update_cb;
1792   void *c_ptr;
1793 
1794   update_cb = (uim_custom_global_cb_update_cb_t)uim_scm_c_func_ptr(cb);
1795   c_ptr = uim_scm_c_ptr(ptr);
1796   (*update_cb)(c_ptr);
1797 
1798   return uim_scm_f();
1799 }
1800 
1801 static uim_bool
custom_cb_add(const char * hook,const char * validator,const char * custom_sym,void * ptr,const char * gate_func,void (* cb)(void))1802 custom_cb_add(const char *hook, const char *validator,
1803 	      const char *custom_sym, void *ptr,
1804 	      const char *gate_func, void (*cb)(void))
1805 {
1806   struct custom_cb_add_args args;
1807 
1808   args.hook = hook;
1809   args.validator = validator;
1810   args.custom_sym = custom_sym;
1811   args.ptr = ptr;
1812   args.gate_func = gate_func;
1813   args.cb = cb;
1814   return (uim_bool)(uintptr_t)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)custom_cb_add_internal, &args);
1815 }
1816 
1817 static void *
custom_cb_add_internal(struct custom_cb_add_args * args)1818 custom_cb_add_internal(struct custom_cb_add_args *args)
1819 {
1820   uim_bool succeeded;
1821   const char *hook;
1822   const char *validator;
1823   const char *custom_sym;
1824   void *ptr;
1825   const char *gate_func;
1826   void (*cb)(void);
1827   uim_lisp form;
1828 
1829   hook = args->hook;
1830   validator = args->validator;
1831   custom_sym = args->custom_sym;
1832   ptr = args->ptr;
1833   gate_func = args->gate_func;
1834   cb = args->cb;
1835 
1836   form = uim_scm_list5(uim_scm_make_symbol(validator),
1837 		       uim_scm_quote(uim_scm_make_symbol(custom_sym)),
1838 		       uim_scm_make_ptr(ptr),
1839 		       uim_scm_make_symbol(gate_func),
1840 		       uim_scm_make_func_ptr(cb));
1841   form = uim_scm_cons(uim_scm_quote(uim_scm_make_symbol(hook)), form);
1842   form = uim_scm_cons(uim_scm_make_symbol("custom-register-cb"), form);
1843   succeeded = uim_scm_c_bool(uim_scm_eval(form));
1844 
1845   return (void *)(uintptr_t)succeeded;
1846 }
1847 
1848 static uim_bool
custom_cb_remove(const char * key_sym,const char * hook)1849 custom_cb_remove(const char *key_sym, const char *hook)
1850 {
1851   uim_bool removed;
1852 
1853   UIM_EVAL_FSTRING2(NULL, "(custom-remove-hook '%s '%s)",
1854 		    (key_sym) ? key_sym : "#f", hook);
1855   removed = uim_scm_c_bool(uim_scm_return_value());
1856 
1857   return removed;
1858 }
1859 
1860 /**
1861  * Set a callback function in a custom variable. The @a update_cb is called
1862  * back when the custom variable specified by @a custom_sym is
1863  * updated. Multiple callbacks for one custom variable is allowed.
1864  *
1865  * @retval UIM_TRUE succeeded
1866  * @retval UIM_FALSE failed
1867  * @param custom_sym custom variable name
1868  * @param ptr an opaque value passed back to client at callback
1869  * @param update_cb function pointer called back when the custom variable is
1870  *        updated
1871  */
1872 uim_bool
uim_custom_cb_add(const char * custom_sym,void * ptr,void (* update_cb)(void * ptr,const char * custom_sym))1873 uim_custom_cb_add(const char *custom_sym, void *ptr,
1874 		  void (*update_cb)(void *ptr, const char *custom_sym))
1875 {
1876   return custom_cb_add("custom-update-hooks", "custom-rec",
1877 		       custom_sym, ptr,
1878 		       "custom-update-cb-gate", (void (*)(void))update_cb);
1879 }
1880 
1881 /**
1882  * Remove the callback functions in a custom variable. All functions set for @a
1883  * custom_sym will be removed.
1884  *
1885  * @retval UIM_TRUE some functions are removed
1886  * @retval UIM_FALSE no functions are removed
1887  * @param custom_sym custom variable name. NULL instructs 'all callbacks'
1888  */
1889 uim_bool
uim_custom_cb_remove(const char * custom_sym)1890 uim_custom_cb_remove(const char *custom_sym)
1891 {
1892   return custom_cb_remove(custom_sym, "custom-update-hooks");
1893 }
1894 
1895 /**
1896  * Set a callback function in a custom group. The @a update_cb is
1897  * called back when the custom group specified by @a group_sym is
1898  * updated (i.e. new custom variable has been defined in the group or
1899  * a custom variable is removed from the group). Multiple callbacks
1900  * for one custom variable is allowed.
1901  *
1902  * @retval UIM_TRUE succeeded
1903  * @retval UIM_FALSE failed
1904  * @param group_sym custom group name
1905  * @param ptr an opaque value passed back to client at callback
1906  * @param update_cb function pointer called back when the custom group is
1907  *        updated
1908  */
1909 uim_bool
uim_custom_group_cb_add(const char * group_sym,void * ptr,void (* update_cb)(void * ptr,const char * group_sym))1910 uim_custom_group_cb_add(const char *group_sym, void *ptr,
1911 			void (*update_cb)(void *ptr, const char *group_sym))
1912 {
1913   return custom_cb_add("custom-group-update-hooks", "custom-group-rec",
1914 		       group_sym, ptr,
1915 		       "custom-update-cb-gate", (void (*)(void))update_cb);
1916 }
1917 
1918 /**
1919  * Remove the callback functions in a custom group. All functions set for @a
1920  * group_sym will be removed.
1921  *
1922  * @retval UIM_TRUE some functions are removed
1923  * @retval UIM_FALSE no functions are removed
1924  * @param group_sym custom group name. NULL instructs 'all callbacks'
1925  */
1926 uim_bool
uim_custom_group_cb_remove(const char * group_sym)1927 uim_custom_group_cb_remove(const char *group_sym)
1928 {
1929   return custom_cb_remove(group_sym, "custom-group-update-hooks");
1930 }
1931 
1932 /**
1933  * Set a callback function for global events. The @a
1934  * group_list_update_cb is called back when group list is updated
1935  * (i.e. new custom group has been defined or a custom group is
1936  * undefined). Multiple callbacks is allowed.
1937  *
1938  * @retval UIM_TRUE succeeded
1939  * @retval UIM_FALSE failed
1940  * @param ptr an opaque value passed back to client at callback
1941  * @param group_list_update_cb function pointer called back when
1942  *        custom group list is updated
1943  */
1944 uim_bool
uim_custom_global_cb_add(void * ptr,void (* group_list_update_cb)(void * ptr))1945 uim_custom_global_cb_add(void *ptr, void (*group_list_update_cb)(void *ptr))
1946 {
1947   return custom_cb_add("custom-group-update-hooks", "(lambda (dummy) #t)",
1948 		       "global", ptr,
1949 		       "custom-global-update-cb-gate",
1950 		       (void (*)(void))group_list_update_cb);
1951 }
1952 
1953 /**
1954  * Remove all callback functions for global events.
1955  *
1956  * @retval UIM_TRUE some functions are removed
1957  * @retval UIM_FALSE no functions are removed
1958  */
uim_custom_global_cb_remove(void)1959 uim_bool uim_custom_global_cb_remove(void)
1960 {
1961   return custom_cb_remove("global", "custom-group-list-update-hooks");
1962 }
1963