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