1 /* This file is part of GNU Dico.
2    Copyright (C) 2008-2020 Sergey Poznyakoff
3 
4    GNU Dico is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Dico is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Dico.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <config.h>
18 #include <dico.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <setjmp.h>
24 #include <libguile.h>
25 #include <appi18n.h>
26 #include <wordsplit.h>
27 
28 #ifndef HAVE_SCM_T_OFF
29 typedef off_t scm_t_off;
30 #endif
31 
32 
33 /* General-purpose eval handlers */
34 
35 struct apply_data {
36 	SCM proc;
37 	SCM arg;
38 };
39 
40 SCM
apply_catch_body(void * data)41 apply_catch_body(void *data)
42 {
43     struct apply_data *xp = data;
44     return scm_apply_0(xp->proc, xp->arg);
45 }
46 
47 static SCM
eval_catch_handler(void * data,SCM tag,SCM throw_args)48 eval_catch_handler(void *data, SCM tag, SCM throw_args)
49 {
50     scm_handle_by_message_noexit("dico", tag, throw_args);
51     longjmp(*(jmp_buf*)data, 1);
52 }
53 
54 struct scheme_exec_data {
55     SCM (*handler)(void *data);
56     void *data;
57     SCM result;
58 };
59 
60 static SCM
scheme_safe_exec_body(void * data)61 scheme_safe_exec_body(void *data)
62 {
63     struct scheme_exec_data *ed = data;
64     ed->result = ed->handler(ed->data);
65     return SCM_BOOL_F;
66 }
67 
68 static int
guile_safe_exec(SCM (* handler)(void * data),void * data,SCM * result)69 guile_safe_exec(SCM (*handler)(void *data), void *data, SCM *result)
70 {
71     jmp_buf jmp_env;
72     struct scheme_exec_data ed;
73 
74     if (setjmp(jmp_env))
75 	return 1;
76     ed.handler = handler;
77     ed.data = data;
78     scm_c_with_throw_handler(SCM_BOOL_T,
79 			     scheme_safe_exec_body, (void*)&ed,
80 			     eval_catch_handler, &jmp_env, 0);
81     if (result)
82 	*result = ed.result;
83     return 0;
84 }
85 
86 struct load_closure {
87     char *filename;
88     int argc;
89     char **argv;
90 };
91 
92 static SCM
load_path_handler(void * data)93 load_path_handler(void *data)
94 {
95     struct load_closure *lp = data;
96 
97     scm_set_program_arguments(lp->argc, lp->argv, lp->filename);
98     scm_primitive_load_path(scm_from_locale_string(lp->filename));
99     return SCM_UNDEFINED;
100 }
101 
102 static int
guile_load(char * filename,char * args)103 guile_load(char *filename, char *args)
104 {
105     struct load_closure lc;
106     if (args) {
107 	struct wordsplit ws;
108 
109 	if (wordsplit(args, &ws, WRDSF_DEFFLAGS)) {
110 	    dico_log(L_ERR, 0, "wordsplit: %s", wordsplit_strerror(&ws));
111 	    return 1;
112 	}
113 	lc.argc = ws.ws_wordc;
114 	lc.argv = ws.ws_wordv;
115 	wordsplit_free(&ws);
116     } else {
117 	lc.argc = 0;
118 	lc.argv = NULL;
119     }
120     lc.filename = filename;
121     return guile_safe_exec(load_path_handler, &lc, NULL);
122 }
123 
124 static void
_add_load_path(char * path)125 _add_load_path(char *path)
126 {
127     SCM scm, path_scm;
128     SCM *pscm;
129 
130     path_scm = SCM_VARIABLE_REF(scm_c_lookup("%load-path"));
131     for (scm = path_scm; scm != SCM_EOL; scm = SCM_CDR(scm)) {
132 	SCM val = SCM_CAR(scm);
133 	if (scm_is_string(val)) {
134 	    char *s = scm_to_locale_string(val);
135 	    int res = strcmp(s, path);
136 	    free(s);
137 	    if (res == 0)
138 		return;
139 	}
140     }
141 
142     pscm = SCM_VARIABLE_LOC(scm_c_lookup("%load-path"));
143     *pscm = scm_append(scm_list_3(path_scm,
144 				  scm_list_1(scm_from_locale_string(path)),
145 				  SCM_EOL));
146 }
147 
148 static void
memerr(const char * fname)149 memerr(const char *fname)
150 {
151     dico_log(L_ERR, 0, _("%s: not enough memory"), fname);
152 }
153 
154 static char *
proc_name(SCM proc)155 proc_name(SCM proc)
156 {
157     return scm_to_locale_string(
158 		  scm_symbol_to_string(scm_procedure_name(proc)));
159 }
160 
161 static void
str_rettype_error(const char * name)162 str_rettype_error(const char *name)
163 {
164     dico_log(L_ERR, 0, _("%s: invalid return type"), name);
165 }
166 
167 static void
rettype_error(SCM proc)168 rettype_error(SCM proc)
169 {
170     char *name = proc_name(proc);
171     str_rettype_error(name);
172     free(name);
173 }
174 
175 static int
guile_call_proc(SCM * result,SCM proc,SCM arglist)176 guile_call_proc(SCM *result, SCM proc, SCM arglist)
177 {
178     jmp_buf jmp_env;
179     struct apply_data adata;
180 
181     if (setjmp(jmp_env)) {
182 	char *name = proc_name(proc);
183 	dico_log(L_NOTICE, 0,
184 		 _("procedure `%s' failed: see error output for details"),
185 		 name);
186 	free(name);
187 	return 1;
188     }
189     adata.proc = proc;
190     adata.arg = arglist;
191     *result = scm_c_with_throw_handler(SCM_BOOL_T,
192 				       apply_catch_body, &adata,
193 				       eval_catch_handler, &jmp_env, 0);
194     return 0;
195 }
196 
197 
198 scm_t_bits _guile_dico_key_tag;
199 
200 static SCM
dico_new_scm_key(struct dico_key ** pkey)201 dico_new_scm_key(struct dico_key **pkey)
202 {
203     struct dico_key *kptr;
204 
205     kptr = scm_gc_malloc (sizeof (*kptr), "Dico key");
206     *pkey = kptr;
207     SCM_RETURN_NEWSMOB(_guile_dico_key_tag, kptr);
208 }
209 
210 static size_t
_guile_dico_key_free(SCM message_smob)211 _guile_dico_key_free(SCM message_smob)
212 {
213     struct dico_key *kp = (struct dico_key *) SCM_CDR (message_smob);
214     dico_key_deinit(kp);
215     return 0;
216 }
217 
218 static int
_guile_dico_key_print(SCM message_smob,SCM port,scm_print_state * pstate)219 _guile_dico_key_print(SCM message_smob, SCM port, scm_print_state *pstate)
220 {
221     struct dico_key *kp = (struct dico_key *) SCM_CDR (message_smob);
222     scm_puts("#<key ", port);
223     scm_puts(kp->strat->name, port);
224     scm_puts(" (", port);
225     scm_puts(kp->word, port);
226     scm_puts(")>", port);
227     return 1;
228 }
229 
230 static void
_guile_init_dico_key(void)231 _guile_init_dico_key(void)
232 {
233     _guile_dico_key_tag =
234 	scm_make_smob_type("Dico key", sizeof (struct dico_key));
235     scm_set_smob_free(_guile_dico_key_tag, _guile_dico_key_free);
236     scm_set_smob_print(_guile_dico_key_tag, _guile_dico_key_print);
237 }
238 
239 #define CELL_IS_KEY(s) \
240     (!SCM_IMP(s) && SCM_CELL_TYPE(s) == _guile_dico_key_tag)
241 
242 SCM_DEFINE_PUBLIC(scm_dico_key_p, "dico-key?",
243 		  1, 0, 0,
244 		  (SCM obj),
245 		  "Return @samp{#t} if @var{obj} is a selection key.")
246 #define FUNC_NAME s_scm_dico_key_p
247 {
248     return CELL_IS_KEY(obj) ? SCM_BOOL_T : SCM_BOOL_F;
249 }
250 #undef FUNC_NAME
251 
252 SCM_DEFINE_PUBLIC(scm_dico_key__word, "dico-key->word",
253 		  1, 0, 0,
254 		  (SCM key),
255 		  "Return search word from the @var{key}.")
256 #define FUNC_NAME s_scm_dico_key__word
257 {
258     struct dico_key *kp;
259     SCM_ASSERT(CELL_IS_KEY(key), key, SCM_ARG1, FUNC_NAME);
260     kp = (struct dico_key *) SCM_CDR(key);
261     return scm_from_locale_string(kp->word);
262 }
263 #undef FUNC_NAME
264 
265 
266 scm_t_bits _guile_strategy_tag;
267 
268 struct _guile_strategy
269 {
270     dico_strategy_t strat;
271 };
272 
273 static SCM
_make_strategy(const dico_strategy_t strat)274 _make_strategy(const dico_strategy_t strat)
275 {
276     struct _guile_strategy *sp;
277 
278     sp = scm_gc_malloc (sizeof (struct _guile_strategy), "strategy");
279     sp->strat = strat;
280     SCM_RETURN_NEWSMOB(_guile_strategy_tag, sp);
281 }
282 
283 static size_t
_guile_strategy_free(SCM message_smob)284 _guile_strategy_free(SCM message_smob)
285 {
286     /* Nothing to free in struct _guile_strategy: the strat member is
287        constant */
288     return 0;
289 }
290 
291 static int
_guile_strategy_print(SCM message_smob,SCM port,scm_print_state * pstate)292 _guile_strategy_print(SCM message_smob, SCM port, scm_print_state * pstate)
293 {
294     struct _guile_strategy *sp =
295 	(struct _guile_strategy *) SCM_CDR(message_smob);
296     scm_puts("#<strategy ", port);
297     scm_puts(sp->strat->name, port);
298     scm_puts(" [", port);
299     scm_puts(sp->strat->descr, port);
300     scm_puts("]>", port);
301     return 1;
302 }
303 
304 static void
_guile_init_strategy(void)305 _guile_init_strategy(void)
306 {
307     _guile_strategy_tag = scm_make_smob_type("strategy",
308 					     sizeof (struct _guile_strategy));
309     scm_set_smob_free(_guile_strategy_tag, _guile_strategy_free);
310     scm_set_smob_print(_guile_strategy_tag, _guile_strategy_print);
311 }
312 
313 #define CELL_IS_STRAT(s) \
314     (!SCM_IMP(s) && SCM_CELL_TYPE(s) == _guile_strategy_tag)
315 
316 SCM_DEFINE_PUBLIC(scm_dico_strat_selector_p, "dico-strat-selector?", 1, 0, 0,
317 		  (SCM strat),
318 		  "Return true if @var{strat} has a selector.")
319 #define FUNC_NAME s_scm_dico_strat_selector_p
320 {
321     struct _guile_strategy *sp;
322 
323     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
324     sp = (struct _guile_strategy *) SCM_CDR(strat);
325     return sp->strat->sel ? SCM_BOOL_T : SCM_BOOL_F;
326 }
327 #undef FUNC_NAME
328 
329 SCM_DEFINE_PUBLIC(scm_dico_strat_select_p, "dico-strat-select?", 3, 0, 0,
330 		  (SCM strat, SCM word, SCM key),
331 		  "Return true if @var{key} matches @var{word} as per strategy selector @var{strat}.")
332 #define FUNC_NAME s_scm_dico_strat_select_p
333 {
334     struct _guile_strategy *sp;
335     struct dico_strategy *stratp;
336     char *wordstr;
337     int rc;
338 
339     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
340     SCM_ASSERT(scm_is_string(word), word, SCM_ARG2, FUNC_NAME);
341 
342     sp = (struct _guile_strategy *) SCM_CDR(strat);
343     stratp = sp->strat;
344 
345     wordstr = scm_to_locale_string(word);
346     if (scm_is_string(key)) {
347 	char *keystr = scm_to_locale_string(key);
348 	struct dico_key skey;
349 
350 	rc = dico_key_init(&skey, stratp, keystr);
351 	free(keystr);
352 	if (rc) {
353 	    free(wordstr);
354 	    scm_misc_error(FUNC_NAME,
355 			   "key initialization failed: ~S",
356 			   scm_list_1(key));
357 	}
358 	rc = dico_key_match(&skey, wordstr);
359 	dico_key_deinit(&skey);
360     } else {
361 	struct dico_key *kptr;
362 
363 	SCM_ASSERT(CELL_IS_KEY(key), key, SCM_ARG3, FUNC_NAME);
364 
365 	kptr = (struct dico_key *) SCM_CDR(key);
366 	rc = dico_key_match(kptr, wordstr);
367     }
368     free(wordstr);
369     return rc ? SCM_BOOL_T : SCM_BOOL_F;
370 }
371 #undef FUNC_NAME
372 
373 SCM_DEFINE_PUBLIC(scm_dico_strat_name, "dico-strat-name", 1, 0, 0,
374 		  (SCM strat),
375 		  "Return the name of the strategy @var{strat}.")
376 #define FUNC_NAME s_scm_dico_strat_name
377 {
378     struct _guile_strategy *sp;
379 
380     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
381     sp = (struct _guile_strategy *) SCM_CDR(strat);
382     return scm_from_locale_string(sp->strat->name);
383 }
384 #undef FUNC_NAME
385 
386 SCM_DEFINE_PUBLIC(scm_dico_strat_description, "dico-strat-description",
387 		  1, 0, 0,
388 		  (SCM strat),
389 		  "Return a textual description of the strategy @var{strat}.")
390 #define FUNC_NAME s_scm_dico_strat_description
391 {
392     struct _guile_strategy *sp;
393 
394     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
395     sp = (struct _guile_strategy *) SCM_CDR(strat);
396     return scm_from_locale_string(sp->strat->descr);
397 }
398 #undef FUNC_NAME
399 
400 SCM_DEFINE_PUBLIC(scm_dico_strat_default_p, "dico-strat-default?", 1, 0, 0,
401 		  (SCM strat),
402 		  "Return true if @var{strat} is a default strategy.")
403 #define FUNC_NAME s_scm_dico_strat_default_p
404 {
405     struct _guile_strategy *sp;
406 
407     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
408     sp = (struct _guile_strategy *) SCM_CDR(strat);
409     return dico_strategy_is_default_p(sp->strat) ? SCM_BOOL_T : SCM_BOOL_F;
410 }
411 #undef FUNC_NAME
412 
413 
414 SCM_DEFINE_PUBLIC(scm_dico_make_key, "dico-make-key",
415 		  2, 0, 0,
416 		  (SCM strat, SCM word),
417 		  "Make a key for given @var{word} and strategy @var{strat}.")
418 #define FUNC_NAME s_scm_dico_make_key
419 {
420     SCM ret;
421     struct dico_key *key;
422     struct _guile_strategy *sp;
423     char *wordstr;
424     int rc;
425 
426     SCM_ASSERT(CELL_IS_STRAT(strat), strat, SCM_ARG1, FUNC_NAME);
427     SCM_ASSERT(scm_is_string(word), word, SCM_ARG2, FUNC_NAME);
428     sp = (struct _guile_strategy *) SCM_CDR(strat);
429     wordstr = scm_to_locale_string(word);
430     ret = dico_new_scm_key(&key);
431     rc = dico_key_init(key, sp->strat, wordstr);
432     free(wordstr);
433     if (rc)
434 	scm_misc_error(FUNC_NAME,
435 		       "key initialization failed: ~S",
436 		       scm_list_1(ret));
437     return ret;
438 }
439 #undef FUNC_NAME
440 
441 
442 static int
_guile_selector(int cmd,struct dico_key * key,const char * dict_word)443 _guile_selector(int cmd, struct dico_key *key, const char *dict_word)
444 {
445     SCM result;
446     SCM list = scm_list_4((SCM)key->strat->closure,
447 			  scm_from_int (cmd),
448 			  scm_from_locale_string(key->word),
449 			  scm_from_locale_string(dict_word));
450     if (guile_safe_exec(apply_catch_body, list, &result))
451 	return 0;
452     return result != SCM_BOOL_F;
453 }
454 
455 SCM_DEFINE_PUBLIC(scm_dico_register_strat, "dico-register-strat", 2, 1, 0,
456 		  (SCM strat, SCM descr, SCM fun),
457 		  "Register a new strategy.")
458 #define FUNC_NAME s_scm_dico_register_strat
459 {
460     struct dico_strategy strategy;
461 
462     SCM_ASSERT(scm_is_string(strat), strat, SCM_ARG1, FUNC_NAME);
463     SCM_ASSERT(scm_is_string(descr), descr, SCM_ARG2, FUNC_NAME);
464 
465     if (!SCM_UNBNDP(fun))
466 	SCM_ASSERT(scm_procedure_p(fun), fun, SCM_ARG3, FUNC_NAME);
467 
468     strategy.name = scm_to_locale_string(strat);
469     strategy.descr = scm_to_locale_string(descr);
470     if (SCM_UNBNDP(fun)) {
471 	strategy.sel = NULL;
472 	strategy.closure = NULL;
473     } else {
474 	strategy.sel = _guile_selector;
475 	strategy.closure = fun;
476     }
477     dico_strategy_add(&strategy);
478     free(strategy.name);
479     free(strategy.descr);
480     return SCM_UNSPECIFIED;
481 }
482 #undef FUNC_NAME
483 
484 
485 SCM_DEFINE_PUBLIC(scm_dico_register_markup, "dico-register-markup", 1, 0, 0,
486 		  (SCM type),
487 		  "Register new markup type.")
488 #define FUNC_NAME s_scm_dico_register_markup
489 {
490     int rc;
491     char *str;
492     SCM_ASSERT(scm_is_string(type), type, SCM_ARG1, FUNC_NAME);
493     str = scm_to_locale_string(type);
494     rc = dico_markup_register(str);
495     free(str);
496     switch (rc) {
497     case 0:
498 	break;
499 
500     case ENOMEM:
501 	scm_report_out_of_memory ();
502 	break;
503 
504     case EINVAL:
505 	scm_misc_error(FUNC_NAME,
506 		       "Invalid markup name: ~S",
507 		       scm_list_1(type));
508 
509     default:
510 	scm_misc_error(FUNC_NAME,
511 		       "Unexpected error: ~S",
512 		       scm_list_1(scm_from_int(rc)));
513     }
514     return SCM_UNSPECIFIED;
515 }
516 #undef FUNC_NAME
517 
518 SCM_DEFINE_PUBLIC(scm_dico_current_markup, "dico-current-markup", 0, 0, 0,
519 		  (),
520 		  "Return current dico markup type.")
521 #define FUNC_NAME s_scm_dico_current_markup
522 {
523     return scm_from_locale_string(dico_markup_type);
524 }
525 #undef FUNC_NAME
526 
527 
528 static scm_t_port_type *scm_dico_port_type;
529 struct _guile_dico_port {
530     dico_stream_t str;
531 };
532 
533 static SCM
_make_dico_port(dico_stream_t str)534 _make_dico_port(dico_stream_t str)
535 {
536     struct _guile_dico_port *dp;
537 
538     dp = scm_gc_typed_calloc (struct _guile_dico_port);
539     dp->str = str;
540     return scm_c_make_port (scm_dico_port_type,
541 			    SCM_BUF0 | SCM_WRTNG, (scm_t_bits) dp);
542 }
543 
544 #define DICO_PORT(x) ((struct _guile_dico_port *) SCM_STREAM (x))
545 
546 static void
_dico_port_close(SCM port)547 _dico_port_close(SCM port)
548 {
549     struct _guile_dico_port *dp = DICO_PORT(port);
550 
551     if (dp && dp->str)
552 	dico_stream_flush(dp->str);
553 }
554 
555 static size_t
_dico_port_write(SCM port,SCM src,size_t start,size_t count)556 _dico_port_write(SCM port, SCM src, size_t start, size_t count)
557 {
558     struct _guile_dico_port *dp = DICO_PORT(port);
559     dico_stream_write(dp->str, SCM_BYTEVECTOR_CONTENTS (src) + start,
560 		      count);
561     return count;
562 }
563 
564 static scm_t_off
_dico_port_seek(SCM port,scm_t_off offset,int whence)565 _dico_port_seek (SCM port, scm_t_off offset, int whence)
566 {
567     struct _guile_dico_port *dp = DICO_PORT(port);
568     return (scm_t_off) dico_stream_seek(dp->str, (off_t) offset, whence);
569 }
570 
571 static int
_dico_port_print(SCM exp,SCM port,scm_print_state * pstate)572 _dico_port_print(SCM exp, SCM port, scm_print_state *pstate)
573 {
574     scm_puts ("#<Dico port>", port);
575     return 1;
576 }
577 
578 static void
_guile_init_dico_port(void)579 _guile_init_dico_port(void)
580 {
581     scm_dico_port_type = scm_make_port_type("dico-port",
582 					    NULL,
583 					    _dico_port_write);
584     scm_set_port_print (scm_dico_port_type, _dico_port_print);
585     scm_set_port_needs_close_on_gc (scm_dico_port_type, 1);
586     scm_set_port_close (scm_dico_port_type, _dico_port_close);
587     scm_set_port_seek (scm_dico_port_type, _dico_port_seek);
588 }
589 
590 static scm_t_port_type *scm_dico_log_port_type;
591 
592 static SCM
_make_dico_log_port(int level)593 _make_dico_log_port(int level)
594 {
595     dico_stream_t str = dico_log_stream_create(level);
596     return str ? _make_dico_port(str) : SCM_BOOL_F;
597 }
598 
599 static int
_dico_log_port_print(SCM exp,SCM port,scm_print_state * pstate)600 _dico_log_port_print(SCM exp, SCM port, scm_print_state *pstate)
601 {
602     scm_puts ("#<Dico log port>", port);
603     return 1;
604 }
605 
606 static void
_guile_init_dico_log_port(void)607 _guile_init_dico_log_port(void)
608 {
609     scm_dico_log_port_type = scm_make_port_type("dico-log-port",
610 						NULL,
611 						_dico_port_write);
612     scm_set_port_print (scm_dico_log_port_type, _dico_log_port_print);
613     scm_set_port_close (scm_dico_log_port_type, _dico_port_close);
614     scm_set_port_needs_close_on_gc (scm_dico_log_port_type, 1);
615     scm_set_port_seek (scm_dico_log_port_type, _dico_port_seek);
616 }
617 
618 
619 static void
_guile_init_funcs(void)620 _guile_init_funcs (void)
621 {
622 #include <guile.x>
623 }
624 
625 
626 static int guile_debug;
627 
628 static char *guile_init_script;
629 static char *guile_init_args;
630 static char *guile_init_fun;
631 
632 enum guile_proc_ind {
633     open_proc,
634     close_proc,
635     info_proc,
636     descr_proc,
637     lang_proc,
638     match_proc,
639     define_proc,
640     output_proc,
641     result_count_proc,
642     compare_count_proc,
643     free_result_proc,
644     result_headers_proc,
645     db_mime_header_proc,
646 
647     MAX_PROC
648 };
649 
650 static char *guile_proc_name[] = {
651     "open",
652     "close",
653     "info",
654     "descr",
655     "lang",
656     "match",
657     "define",
658     "output",
659     "result-count",
660     "compare-count",
661     "free-result",
662     "result-headers",
663     "db-mime-header"
664 };
665 
666 typedef SCM guile_vtab[MAX_PROC];
667 
668 static guile_vtab global_vtab;
669 
670 struct _guile_database {
671     const char *dbname;
672     guile_vtab vtab;
673     int argc;
674     char **argv;
675     SCM handle;
676 };
677 
678 static int
proc_name_to_index(const char * name)679 proc_name_to_index(const char *name)
680 {
681     int i;
682     for (i = 0; i < MAX_PROC; i++)
683 	if (strcmp(guile_proc_name[i], name) == 0)
684 	    break;
685     return i;
686 }
687 
688 struct init_struct {
689     const char *init_fun;
690     const char *db_name;
691 };
692 
693 static SCM
call_init_handler(void * data)694 call_init_handler(void *data)
695 {
696     struct init_struct *p = (struct init_struct *)data;
697     SCM procsym = SCM_VARIABLE_REF(scm_c_lookup(p->init_fun));
698     SCM arg;
699     if (p->db_name)
700 	arg = scm_from_locale_string(p->db_name);
701     else
702 	arg = SCM_BOOL_F;
703     return scm_apply_0(procsym, scm_list_1(arg));
704 }
705 
706 static int
init_vtab(const char * init_fun,const char * dbname,guile_vtab vtab)707 init_vtab(const char *init_fun, const char *dbname, guile_vtab vtab)
708 {
709     SCM res;
710     struct init_struct istr;
711 
712     istr.init_fun = init_fun;
713     istr.db_name = dbname;
714     if (guile_safe_exec(call_init_handler, &istr, &res))
715 	return 1;
716 
717     if (!scm_list_p(res) && res != SCM_EOL) {
718 	str_rettype_error(init_fun);
719 	return 1;
720     }
721     for (; res != SCM_EOL; res = SCM_CDR(res)) {
722 	int idx;
723 	char *ident;
724 	SCM name, proc;
725 	SCM car = SCM_CAR(res);
726 	if (!scm_list_p(res)
727 	    || !scm_is_string(name = SCM_CAR(car))
728 	    || !scm_procedure_p(proc = SCM_CDR(car)))  {
729 	    str_rettype_error(init_fun);
730 	    return 1;
731 	}
732 	ident = scm_to_locale_string(name);
733 	idx = proc_name_to_index(ident);
734 	if (idx == MAX_PROC) {
735 	    dico_log(L_ERR, 0, _("%s: %s: unknown virtual function"),
736 		     init_fun, ident);
737 	    free(ident);
738 	    return 1;
739 	}
740 	free(ident);
741 	vtab[idx] = proc;
742     }
743     return 0;
744 }
745 
746 static int
set_load_path(struct dico_option * opt,const char * val)747 set_load_path(struct dico_option *opt, const char *val)
748 {
749     char *p;
750     char *tmp = strdup(val);
751     if (!tmp)
752 	return 1;
753     for (p = strtok(tmp, ":"); p; p = strtok(NULL, ":"))
754 	_add_load_path(p);
755     free(tmp);
756     return 0;
757 }
758 
759 static struct dico_option init_option[] = {
760     { DICO_OPTSTR(debug), dico_opt_bool, &guile_debug },
761     { DICO_OPTSTR(init-script), dico_opt_string, &guile_init_script },
762     { DICO_OPTSTR(init-args), dico_opt_string, &guile_init_args },
763     { DICO_OPTSTR(load-path), dico_opt_null, NULL, { 0 }, set_load_path },
764     { DICO_OPTSTR(init-fun), dico_opt_string, &guile_init_fun },
765     { NULL }
766 };
767 
768 static int
mod_init(int argc,char ** argv)769 mod_init(int argc, char **argv)
770 {
771     SCM port;
772 
773     scm_init_guile();
774     scm_load_goops();
775 
776     if (dico_parseopt(init_option, argc, argv, 0, NULL))
777 	return 1;
778 
779     _guile_init_strategy();
780     _guile_init_dico_key();
781     _guile_init_dico_port();
782     _guile_init_dico_log_port();
783     _guile_init_funcs();
784 #ifdef GUILE_DEBUG_MACROS
785     if (guile_debug) {
786 	SCM_DEVAL_P = 1;
787 	SCM_BACKTRACE_P = 1;
788 	SCM_RECORD_POSITIONS_P = 1;
789 	SCM_RESET_DEBUG_MODE;
790     }
791 #endif
792     port = _make_dico_log_port(L_ERR);
793     if (port == SCM_BOOL_F) {
794 	dico_log(L_ERR, 0, _("mod_init: cannot initialize error port"));
795 	return 1;
796     }
797     scm_set_current_output_port(port);
798     scm_set_current_error_port(port);
799 
800     if (guile_init_script
801 	&& guile_load(guile_init_script, guile_init_args)) {
802 	dico_log(L_ERR, 0, _("mod_init: cannot load init script %s"),
803 		 guile_init_script);
804 	return 1;
805     }
806 
807     if (guile_init_fun && init_vtab(guile_init_fun, NULL, global_vtab))
808 	return 1;
809 
810     return 0;
811 }
812 
813 static dico_handle_t
mod_init_db(const char * dbname,int argc,char ** argv)814 mod_init_db(const char *dbname, int argc, char **argv)
815 {
816     struct _guile_database *db;
817     int i;
818     int err = 0;
819     char *init_script = NULL;
820     char *init_args = NULL;
821     char *init_fun = guile_init_fun;
822 
823     struct dico_option db_option[] = {
824 	{ DICO_OPTSTR(init-script), dico_opt_string, &init_script },
825 	{ DICO_OPTSTR(init-args), dico_opt_string, &init_args },
826 	{ DICO_OPTSTR(init-fun), dico_opt_string, &init_fun },
827 	{ NULL }
828     };
829 
830     if (dico_parseopt(db_option, argc, argv, DICO_PARSEOPT_PERMUTE, &i))
831 	return NULL;
832     argc -= i;
833     argv += i;
834 
835     if (init_script && guile_load(init_script, init_args)) {
836 	dico_log(L_ERR, 0, _("mod_init: cannot load init script %s"),
837 		 init_script);
838 	return NULL;
839     }
840 
841     db = malloc(sizeof(*db));
842     if (!db) {
843 	memerr("mod_init_db");
844 	return NULL;
845     }
846     db->dbname = dbname;
847     memcpy(db->vtab, global_vtab, sizeof(db->vtab));
848     if (init_fun && init_vtab(init_fun, dbname, db->vtab)) {
849 	free(db);
850 	return NULL;
851     }
852 
853     for (i = 0; i < MAX_PROC; i++) {
854 	if (!db->vtab[i]) {
855 	    switch (i) {
856 	    case open_proc:
857 	    case match_proc:
858 	    case define_proc:
859 	    case output_proc:
860 	    case result_count_proc:
861 		dico_log(L_ERR, 0,
862 			 _("%s: %s: void virtual function"),
863 			 argv[0], guile_proc_name[i]);
864 		err++;
865 	    default:
866 		break;
867 	    }
868 	}
869     }
870 
871     if (err) {
872 	free(db);
873 	return NULL;
874     }
875 
876     db->argc = argc;
877     db->argv = argv;
878     return (dico_handle_t)db;
879 }
880 
881 static int
mod_free_db(dico_handle_t hp)882 mod_free_db(dico_handle_t hp)
883 {
884     struct _guile_database *db = (struct _guile_database *)hp;
885     free(db);
886     return 0;
887 }
888 
889 static int
mod_close(dico_handle_t hp)890 mod_close(dico_handle_t hp)
891 {
892     struct _guile_database *db = (struct _guile_database *)hp;
893     SCM res;
894 
895     if (db->vtab[close_proc])
896 	if (guile_call_proc(&res, db->vtab[close_proc],
897 			    scm_list_1(db->handle)))
898 	    return 1;
899     scm_gc_unprotect_object(db->handle);
900 
901     return 0;
902 }
903 
904 static SCM
argv_to_scm(int argc,char ** argv)905 argv_to_scm(int argc, char **argv)
906 {
907     SCM scm_first = SCM_EOL, scm_last;
908 
909     for (; argc; argc--, argv++) {
910 	SCM new = scm_cons(scm_from_locale_string(*argv), SCM_EOL);
911 	if (scm_first == SCM_EOL)
912 	    scm_last = scm_first = new;
913 	else {
914 	    SCM_SETCDR(scm_last, new);
915 	    scm_last = new;
916 	}
917     }
918     return scm_first;
919 }
920 
921 static SCM
assoc_to_scm(dico_assoc_list_t assoc)922 assoc_to_scm(dico_assoc_list_t assoc)
923 {
924     SCM scm_first = SCM_EOL, scm_last;
925     dico_iterator_t itr;
926     struct dico_assoc *p;
927 
928     itr = dico_assoc_iterator(assoc);
929     for (p = dico_iterator_first(itr); p; p = dico_iterator_next(itr)) {
930 	SCM new = scm_cons(scm_cons(scm_from_locale_string(p->key),
931 				    scm_from_locale_string(p->value)),
932 			   SCM_EOL);
933 	if (scm_first == SCM_EOL)
934 	    scm_last = scm_first = new;
935 	else {
936 	    SCM_SETCDR(scm_last, new);
937 	    scm_last = new;
938 	}
939     }
940     dico_iterator_destroy(&itr);
941     return scm_first;
942 }
943 
944 static void
scm_to_assoc(dico_assoc_list_t assoc,SCM scm)945 scm_to_assoc(dico_assoc_list_t assoc, SCM scm)
946 {
947     dico_assoc_clear(assoc);
948 
949     for (; scm != SCM_EOL && scm_list_p(scm); scm = SCM_CDR(scm)) {
950 	SCM elt = SCM_CAR(scm);
951 
952 	if (!scm_is_pair(elt)) {
953 	    scm_misc_error(NULL, "Wrong element type: ~S", scm_list_1(elt));
954 	}
955 	dico_assoc_append(assoc, scm_to_locale_string(SCM_CAR(elt)),
956 			  scm_to_locale_string(SCM_CDR(elt)));
957     }
958 }
959 
960 
961 static int
mod_open(dico_handle_t dp)962 mod_open(dico_handle_t dp)
963 {
964     struct _guile_database *db = (struct _guile_database *)dp;
965     if (guile_call_proc(&db->handle, db->vtab[open_proc],
966 			scm_cons(scm_from_locale_string(db->dbname),
967 				 argv_to_scm(db->argc, db->argv))))
968 	return 1;
969     if (db->handle == SCM_EOL || db->handle == SCM_BOOL_F)
970 	return 1;
971     scm_gc_protect_object(db->handle);
972     return 0;
973 }
974 
975 static char *
mod_get_text(struct _guile_database * db,int n)976 mod_get_text(struct _guile_database *db, int n)
977 {
978     if (db->vtab[n]) {
979 	SCM res;
980 
981 	if (guile_call_proc(&res, db->vtab[n], scm_list_1(db->handle)))
982 	    return NULL;
983 	if (scm_is_string(res))
984 	    return scm_to_locale_string(res);
985 	else {
986 	    rettype_error(db->vtab[n]);
987 	    return NULL;
988 	}
989     }
990     return NULL;
991 }
992 
993 static char *
mod_info(dico_handle_t hp)994 mod_info(dico_handle_t hp)
995 {
996     struct _guile_database *db = (struct _guile_database *)hp;
997     return mod_get_text(db, info_proc);
998 }
999 
1000 static char *
mod_descr(dico_handle_t hp)1001 mod_descr(dico_handle_t hp)
1002 {
1003     struct _guile_database *db = (struct _guile_database *)hp;
1004     return mod_get_text(db, descr_proc);
1005 }
1006 
1007 static dico_list_t
scm_to_langlist(SCM scm,SCM procsym)1008 scm_to_langlist(SCM scm, SCM procsym)
1009 {
1010     dico_list_t list = NULL;
1011 
1012     if (scm == SCM_EOL)
1013 	return NULL;
1014     else if (scm_is_string(scm)) {
1015 	list = dico_list_create();
1016 	dico_list_append(list, scm_to_locale_string(scm));
1017     } else if (scm_list_p(scm)) {
1018 	list = dico_list_create();
1019 	for (; scm != SCM_EOL && scm_list_p(scm); scm = SCM_CDR(scm))
1020 	    dico_list_append(list, scm_to_locale_string(SCM_CAR(scm)));
1021     } else
1022 	rettype_error(procsym);
1023     return list;
1024 }
1025 
1026 static int
mod_lang(dico_handle_t hp,dico_list_t list[2])1027 mod_lang(dico_handle_t hp, dico_list_t list[2])
1028 {
1029     struct _guile_database *db = (struct _guile_database *)hp;
1030     SCM proc = db->vtab[lang_proc];
1031     list[0] = list[1] = NULL;
1032     if (proc) {
1033 	SCM res;
1034 
1035 	if (guile_call_proc(&res, proc, scm_list_1(db->handle)))
1036 	    return 1;
1037 	if (res == SCM_EOL)
1038 	    /* ok, nothing */;
1039 	else if (scm_is_string(res)) {
1040 	    list[0] = dico_list_create();
1041 	    dico_list_append(list[0], scm_to_locale_string(res));
1042 	} else if (scm_is_pair(res)) {
1043 	    list[0] = scm_to_langlist(SCM_CAR(res), proc);
1044 	    list[1] = scm_to_langlist(SCM_CDR(res), proc);
1045 	} else {
1046 	    rettype_error(proc);
1047 	    return 1;
1048 	}
1049     }
1050 
1051     return 0;
1052 }
1053 
1054 
1055 
1056 struct guile_result {
1057     struct _guile_database *db;
1058     SCM result;
1059 };
1060 
1061 static dico_result_t
make_guile_result(struct _guile_database * db,SCM res)1062 make_guile_result(struct _guile_database *db, SCM res)
1063 {
1064     struct guile_result *rp = malloc(sizeof(*rp));
1065     if (rp) {
1066 	rp->db = db;
1067 	rp->result = res;
1068     }
1069     return (dico_result_t) rp;
1070 }
1071 
1072 static dico_result_t
mod_match(dico_handle_t hp,const dico_strategy_t strat,const char * word)1073 mod_match(dico_handle_t hp, const dico_strategy_t strat, const char *word)
1074 {
1075     struct _guile_database *db = (struct _guile_database *)hp;
1076     SCM scm_strat = _make_strategy(strat);
1077     SCM res;
1078     struct dico_key *key;
1079     SCM scm_key;
1080 
1081     scm_key = dico_new_scm_key(&key);
1082 
1083     if (dico_key_init(key, strat, word)) {
1084 	dico_log(L_ERR, 0, _("mod_match: key initialization failed"));
1085 	return NULL;
1086     }
1087 
1088     if (guile_call_proc(&res, db->vtab[match_proc],
1089 			scm_list_3(db->handle, scm_strat, scm_key)))
1090 	return NULL;
1091 
1092     dico_key_deinit(key);
1093 
1094     if (res == SCM_BOOL_F || res == SCM_EOL)
1095 	return NULL;
1096     scm_gc_protect_object(res);
1097     return make_guile_result(db, res);
1098 }
1099 
1100 static dico_result_t
mod_define(dico_handle_t hp,const char * word)1101 mod_define(dico_handle_t hp, const char *word)
1102 {
1103     struct _guile_database *db = (struct _guile_database *)hp;
1104     SCM res;
1105 
1106     if (guile_call_proc(&res, db->vtab[define_proc],
1107 			scm_list_2(db->handle,
1108 				   scm_from_locale_string(word))))
1109 	return NULL;
1110 
1111     if (res == SCM_BOOL_F || res == SCM_EOL)
1112 	return NULL;
1113     scm_gc_protect_object(res);
1114     return make_guile_result(db, res);
1115 }
1116 
1117 static int
mod_output_result(dico_result_t rp,size_t n,dico_stream_t str)1118 mod_output_result (dico_result_t rp, size_t n, dico_stream_t str)
1119 {
1120     int rc;
1121     struct guile_result *gres = (struct guile_result *)rp;
1122     SCM res;
1123     SCM oport = scm_current_output_port();
1124     SCM port = _make_dico_port(str);
1125 
1126     scm_set_current_output_port(port);
1127 
1128     rc = guile_call_proc(&res, gres->db->vtab[output_proc],
1129 			 scm_list_2(gres->result, scm_from_int(n)));
1130     scm_set_current_output_port(oport);
1131     _dico_port_close(port);
1132     if (rc)
1133 	return 1;
1134     return 0;
1135 }
1136 
1137 static size_t
mod_result_count(dico_result_t rp)1138 mod_result_count (dico_result_t rp)
1139 {
1140     struct guile_result *gres = (struct guile_result *)rp;
1141     SCM res;
1142 
1143     if (guile_call_proc(&res, gres->db->vtab[result_count_proc],
1144 			scm_list_1(gres->result)))
1145 	return 0;
1146     if (scm_is_integer(res))
1147 	return scm_to_int32(res);
1148     else
1149 	rettype_error(gres->db->vtab[result_count_proc]);
1150     return 0;
1151 }
1152 
1153 static size_t
mod_compare_count(dico_result_t rp)1154 mod_compare_count (dico_result_t rp)
1155 {
1156     struct guile_result *gres = (struct guile_result *)rp;
1157 
1158     if (gres->db->vtab[compare_count_proc]) {
1159 	SCM res;
1160 
1161 	if (guile_call_proc(&res, gres->db->vtab[compare_count_proc],
1162 			    scm_list_1(gres->result)))
1163 	    return 0;
1164 	if (scm_is_integer(res))
1165 	    return scm_to_int32(res);
1166 	else
1167 	    rettype_error(gres->db->vtab[compare_count_proc]);
1168     }
1169     return 0;
1170 }
1171 
1172 static void
mod_free_result(dico_result_t rp)1173 mod_free_result(dico_result_t rp)
1174 {
1175     struct guile_result *gres = (struct guile_result *)rp;
1176 
1177     if (gres->db->vtab[free_result_proc]) {
1178 	SCM res;
1179 
1180 	guile_call_proc(&res, gres->db->vtab[free_result_proc],
1181 			scm_list_1(gres->result));
1182     }
1183     scm_gc_unprotect_object(gres->result);
1184     free(gres);
1185 }
1186 
1187 static int
mod_result_headers(dico_result_t rp,dico_assoc_list_t hdr)1188 mod_result_headers (dico_result_t rp, dico_assoc_list_t hdr)
1189 {
1190     struct guile_result *gres = (struct guile_result *)rp;
1191     SCM proc = gres->db->vtab[result_headers_proc];
1192 
1193     if (proc) {
1194 	SCM res;
1195 
1196 	if (guile_call_proc(&res, proc,
1197 			    scm_list_2(gres->result, assoc_to_scm(hdr))))
1198 	    return 1;
1199 	if (!scm_list_p(res)) {
1200 	    rettype_error(proc);
1201 	    return 1;
1202 	}
1203 	scm_to_assoc(hdr, res);
1204     }
1205     return 0;
1206 }
1207 
1208 static char *
mod_db_mime_header(dico_handle_t hp)1209 mod_db_mime_header(dico_handle_t hp)
1210 {
1211     struct _guile_database *db = (struct _guile_database *)hp;
1212     return mod_get_text(db, db_mime_header_proc);
1213 }
1214 
1215 struct dico_database_module DICO_EXPORT(guile, module) = {
1216     .dico_version = DICO_MODULE_VERSION,
1217     .dico_capabilities = DICO_CAPA_NONE,
1218     .dico_init = mod_init,
1219     .dico_init_db = mod_init_db,
1220     .dico_free_db = mod_free_db,
1221     .dico_open = mod_open,
1222     .dico_close = mod_close,
1223     .dico_db_info = mod_info,
1224     .dico_db_descr = mod_descr,
1225     .dico_db_lang = mod_lang,
1226     .dico_match = mod_match,
1227     .dico_define = mod_define,
1228     .dico_output_result = mod_output_result,
1229     .dico_result_count = mod_result_count,
1230     .dico_compare_count = mod_compare_count,
1231     .dico_free_result = mod_free_result,
1232     .dico_result_headers = mod_result_headers,
1233     .dico_db_mime_header = mod_db_mime_header
1234 };
1235