1 /* index.c --
2  * Created: Sat Mar 15 16:47:42 2003 by Aleksey Cheusov <vle@gmx.net>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 1, or (at your option) any
7  * later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #include "dictP.h"
20 
21 #include "dictd.h"
22 #include "plugin.h"
23 #include "strategy.h"
24 #include "data.h"
25 #include "index.h"
26 
27 #ifndef HAVE_DLFCN_H
28 #include <ltdl.h>
29 #else
30 #include <dlfcn.h>
31 #endif
32 
33 #include <maa.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 
dict_search_plugin(lst_List l,const char * const word,const dictDatabase * database,int strategy,int * extra_result,const dictPluginData ** extra_data,int * extra_data_size)37 int dict_search_plugin (
38    lst_List l,
39    const char *const word,
40    const dictDatabase *database,
41    int strategy,
42    int *extra_result,
43    const dictPluginData **extra_data,
44    int *extra_data_size)
45 {
46    int ret;
47    int                  failed = 0;
48    const char * const * defs;
49    const int          * defs_sizes;
50    int                  defs_count;
51    const char         * err_msg;
52    int                  i;
53    dictWord           * def;
54    int                  len;
55 
56    int match         = strategy & DICT_MATCH_MASK;
57    int strategy_real = strategy & ~DICT_MATCH_MASK;
58 
59    assert (database);
60    assert (database -> plugin);
61 
62    if (strategy_real == DICT_STRAT_DOT){
63       strategy = (match | database -> default_strategy);
64       PRINTF (DBG_SEARCH, (":S:     def strategy for database '%s': %d\n",
65 			   database -> databaseName, strategy));
66    }
67 
68    PRINTF (DBG_SEARCH, (":S:     searching for '%s' in '%s' using strat '%d'\n",
69 			word, database -> databaseName, strategy));
70 
71    failed = database -> plugin -> dictdb_search (
72       database -> plugin -> data,
73       word, -1,
74       strategy,
75       &ret,
76       extra_data, extra_data_size,
77       &defs, &defs_sizes, &defs_count);
78 
79    database -> plugin -> dictdb_free_called = 1;
80 
81    if (extra_result)
82       *extra_result = ret;
83 
84    if (failed){
85       err_msg = database -> plugin -> dictdb_error (
86 	 database -> plugin -> data);
87 
88       fprintf (stderr, ":E: Plugin failed: %s\n", (err_msg ? err_msg : ""));
89       PRINTF (DBG_SEARCH, (":E: Plugin failed: %s\n", err_msg ? err_msg : ""));
90    }else{
91       switch (ret){
92       case DICT_PLUGIN_RESULT_FOUND:
93 	 PRINTF (DBG_SEARCH, (":S:     found %i definitions\n", defs_count));
94 	 break;
95       case DICT_PLUGIN_RESULT_NOTFOUND:
96 	 PRINTF (DBG_SEARCH, (":S:     not found\n"));
97 	 return 0;
98       case DICT_PLUGIN_RESULT_EXIT:
99 	 PRINTF (DBG_SEARCH, (":S:     exiting\n"));
100 	 return 0;
101       case DICT_PLUGIN_RESULT_PREPROCESS:
102 	 PRINTF (DBG_SEARCH, (":S:     preprocessing\n"));
103 	 break;
104       default:
105 	 err_fatal (__func__, "invalid pligin's exit status\n");
106       }
107 
108       for (i = 0; i < defs_count; ++i){
109 	 def = xmalloc (sizeof (dictWord));
110 	 memset (def, 0, sizeof (*def));
111 
112 	 def -> database = database;
113 	 def -> start    = def -> end = 0;
114 
115 	 len = defs_sizes [i];
116 	 if (-1 == len)
117 	    len = strlen (defs [i]);
118 
119 	 if (
120 	    strategy & DICT_MATCH_MASK &&
121 	    ret != DICT_PLUGIN_RESULT_PREPROCESS)
122 	 {
123 	    def -> word     = xstrdup (defs [i]);
124 	    def -> def      = def -> word;
125 	    def -> def_size = -1;
126 	 }else{
127 	    def -> word     = xstrdup (word);
128 	    def -> def      = defs [i];
129 	    def -> def_size = len;
130 	 }
131 
132 	 if (ret == DICT_PLUGIN_RESULT_PREPROCESS){
133 	    lst_push (l, def);
134 	 }else{
135 	    lst_append (l, def);
136 	 }
137       }
138 
139       return defs_count;
140    }
141 
142    return 0;
143 }
144 
145 /* reads data without headword 00-... */
dict_plugin_data(const dictDatabase * db,const dictWord * dw)146 static char *dict_plugin_data (const dictDatabase *db, const dictWord *dw)
147 {
148    char *buf = dict_data_obtain (db, dw);
149    char *p = buf;
150    int len;
151 
152    assert (db);
153    assert (db -> index);
154 
155    if (!strncmp (p, DICT_ENTRY_PLUGIN_DATA, strlen (DICT_ENTRY_PLUGIN_DATA))){
156       while (*p != '\n')
157 	 ++p;
158    }
159 
160    while (*p == '\n')
161       ++p;
162 
163    len = strlen (p);
164 
165    while (len > 0 && p [len - 1] == '\n')
166       --len;
167 
168    p [len] = 0;
169 
170    p = xstrdup (p);
171    xfree (buf);
172 
173    return p;
174 }
175 
176 /* set data fields from 00-database-plugin-data entry */
177 /* return a number of inserted items */
plugin_initdata_set_data_file(dictPluginData * data,int data_size,const dictDatabase * db)178 static int plugin_initdata_set_data_file (
179    dictPluginData *data, int data_size,
180    const dictDatabase *db)
181 {
182    char *plugin_data;
183    int ret = 0;
184    lst_List list;
185    dictWord *dw;
186 
187    if (data_size <= 0)
188       err_fatal (__func__, "invalid initial array size");
189 
190    list = lst_create ();
191 
192    ret = dict_search_database_ (
193       list, DICT_ENTRY_PLUGIN_DATA, db, DICT_STRAT_EXACT);
194 
195    if (0 == ret){
196       dict_destroy_list (list);
197       return 0;
198    }
199 
200    dw = (dictWord *) lst_pop (list);
201    plugin_data = dict_plugin_data (db, dw);
202 
203    dict_destroy_datum (dw);
204 
205    data -> id   = DICT_PLUGIN_INITDATA_DICT;
206    data -> data = plugin_data;
207    data -> size = -1;
208 
209    dict_destroy_list (list);
210 
211    return 1;
212 }
213 
214 /* set data fields from db -> plugin_data */
215 /* return a number of inserted items */
plugin_initdata_set_data_array(dictPluginData * data,int data_size,const dictDatabase * db)216 static int plugin_initdata_set_data_array (
217    dictPluginData *data, int data_size,
218    const dictDatabase *db)
219 {
220    if (data_size <= 0)
221       err_fatal (__func__, "invalid initial array size");
222 
223    if (db -> plugin_data){
224       data [0].id   = DICT_PLUGIN_INITDATA_DICT;
225       data [0].data = xstrdup (db -> plugin_data);
226       data [0].size = -1;
227 
228       return 1;
229    }else{
230       return 0;
231    }
232 }
233 
234 /* return a number of inserted items */
plugin_initdata_set_data(dictPluginData * data,int data_size,const dictDatabase * db)235 static int plugin_initdata_set_data (
236    dictPluginData *data, int data_size,
237    const dictDatabase *db)
238 {
239    if (db -> plugin_data)
240       return plugin_initdata_set_data_array (data, data_size, db);
241    else if (db -> index)
242       return plugin_initdata_set_data_file (data, data_size, db);
243    else
244       return 0;
245 }
246 
plugin_initdata_set_dbnames(dictPluginData * data,int data_size)247 static int plugin_initdata_set_dbnames (dictPluginData *data, int data_size)
248 {
249    const dictDatabase *db;
250    int count;
251    int i;
252 
253    if (data_size <= 0)
254       err_fatal (__func__, "too small initial array");
255 
256    count = lst_length (DictConfig -> dbl);
257    if (count == 0)
258       return 0;
259 
260    if (count > data_size)
261       err_fatal (__func__, "too small initial array");
262 
263    for (i = 1; i <= count; ++i){
264       db = (const dictDatabase *)(lst_nth_get (DictConfig -> dbl, i));
265 
266       data -> id   = DICT_PLUGIN_INITDATA_DBNAME;
267       if (db -> databaseName){
268 	 data -> size = strlen (db -> databaseName);
269 	 data -> data = xstrdup (db -> databaseName);
270       }else{
271 	 data -> size = 0;
272 	 data -> data = NULL;
273       }
274 
275       ++data;
276    }
277 
278    return count;
279 }
280 
plugin_initdata_set_stratnames(dictPluginData * data,int data_size,const dictDatabase * db)281 static int plugin_initdata_set_stratnames (
282    dictPluginData *data,
283    int data_size,
284    const dictDatabase *db)
285 {
286    dictStrategy const * const *strats;
287    int count;
288    int ret = 0;
289    int i;
290    dictPluginData_strategy datum;
291 
292    if (data_size <= 0)
293       err_fatal (__func__, "too small initial array");
294 
295    count = get_strategy_count ();
296    assert (count > 0);
297 
298    strats = get_strategies ();
299 
300    for (i = 0; i < count; ++i){
301       if (
302 	 !db -> strategy_disabled ||
303 	 !db -> strategy_disabled [strats [i] -> number])
304       {
305 	 data -> id   = DICT_PLUGIN_INITDATA_STRATEGY;
306 
307 	 if (
308 	    strlen (strats [i] -> name) + 1 >
309 	    sizeof (datum.name))
310 	 {
311 	    err_fatal (__func__, "too small initial array");
312 	 }
313 
314 	 datum.number = strats [i] -> number;
315 	 strcpy (datum.name, strats [i] -> name);
316 
317 	 data -> size = sizeof (datum);
318 	 data -> data = xmalloc (sizeof (datum));
319 
320 	 memcpy ((void *) data -> data, &datum, sizeof (datum));
321 
322 	 ++data;
323 	 ++ret;
324       }
325    }
326 
327    return ret;
328 }
329 
plugin_initdata_set_defdbdir(dictPluginData * data,int data_size)330 static int plugin_initdata_set_defdbdir (dictPluginData *data, int data_size)
331 {
332    if (data_size <= 0)
333       err_fatal (__func__, "too small initial array");
334 
335    data -> size = -1;
336    data -> data = xstrdup (DICT_DICTIONARY_PATH);
337    data -> id   = DICT_PLUGIN_INITDATA_DEFDBDIR;
338 
339    return 1;
340 }
341 
plugin_initdata_set_alphabet_8bit(dictPluginData * data,int data_size)342 static int plugin_initdata_set_alphabet_8bit (
343    dictPluginData *data, int data_size)
344 {
345    if (data_size <= 0)
346       err_fatal (__func__, "too small initial array");
347 
348    data -> size = -1;
349    data -> data = xstrdup (global_alphabet_8bit);
350    data -> id   = DICT_PLUGIN_INITDATA_ALPHABET_8BIT;
351 
352    return 1;
353 }
354 
plugin_initdata_set_alphabet_ascii(dictPluginData * data,int data_size)355 static int plugin_initdata_set_alphabet_ascii (
356    dictPluginData *data, int data_size)
357 {
358    if (data_size <= 0)
359       err_fatal (__func__, "too small initial array");
360 
361    data -> size = -1;
362    data -> data = xstrdup (global_alphabet_ascii);
363    data -> id   = DICT_PLUGIN_INITDATA_ALPHABET_ASCII;
364 
365    return 1;
366 }
367 
368 /* all dict [i]->data are xmalloc'ed */
plugin_initdata_set(dictPluginData * data,int data_size,const dictDatabase * db)369 static int plugin_initdata_set (
370    dictPluginData *data, int data_size,
371    const dictDatabase *db)
372 {
373    int count = 0;
374    dictPluginData *p = data;
375 
376    count = plugin_initdata_set_defdbdir (data, data_size);
377    data      += count;
378    data_size -= count;
379 
380    count = plugin_initdata_set_dbnames (data, data_size);
381    data      += count;
382    data_size -= count;
383 
384    count = plugin_initdata_set_stratnames (data, data_size, db);
385    data      += count;
386    data_size -= count;
387 
388    count = plugin_initdata_set_data (data, data_size, db);
389    data      += count;
390    data_size -= count;
391 
392    count = plugin_initdata_set_alphabet_8bit (data, data_size);
393    data      += count;
394    data_size -= count;
395 
396    count = plugin_initdata_set_alphabet_ascii (data, data_size);
397    data      += count;
398    data_size -= count;
399 
400    return data - p;
401 }
402 
plugin_init_data_free(dictPluginData * data,int data_size)403 static void plugin_init_data_free (
404    dictPluginData *data, int data_size)
405 {
406    int i=0;
407 
408    for (i = 0; i < data_size; ++i){
409       if (data -> data)
410 	 xfree ((void *) data -> data);
411 
412       ++data;
413    }
414 }
415 
416 /* Reads plugin's file name from .dict file */
417 /* do not free() returned value*/
dict_plugin_filename(const dictDatabase * db,const dictWord * dw)418 static char *dict_plugin_filename (
419    const dictDatabase *db,
420    const dictWord *dw)
421 {
422    static char filename [FILENAME_MAX];
423 
424    char *buf = dict_data_obtain (db, dw);
425    char *p = buf;
426    int len;
427 
428    if (!strncmp (p, DICT_ENTRY_PLUGIN, strlen (DICT_ENTRY_PLUGIN))){
429       while (*p != '\n')
430 	 ++p;
431    }
432 
433    while (*p == '\n' || isspace ((unsigned char) *p))
434       ++p;
435 
436    len = strlen (p);
437 
438    while (
439       len > 0 &&
440       (p [len - 1] == '\n' || isspace ((unsigned char) p [len - 1])))
441    {
442       --len;
443    }
444 
445    p [len] = 0;
446 
447    if (p [0] != '.' && p [0] != '/'){
448       if (sizeof (filename) < strlen (DICT_PLUGIN_PATH) + strlen (p) + 1)
449 	 err_fatal (__func__, "too small initial array\n");
450 
451       strcpy (filename, DICT_PLUGIN_PATH);
452       strcat (filename, p);
453    }else{
454       strlcpy (filename, p, sizeof (filename));
455    }
456 
457    xfree (buf);
458 
459    return filename;
460 }
461 
462 
dict_plugin_test(dictPlugin * plugin,int version,int ret)463 static void dict_plugin_test (dictPlugin *plugin, int version, int ret)
464 {
465    const char *err_msg = NULL;
466 
467    if (ret){
468       err_msg = plugin -> dictdb_error (
469 	 plugin -> data);
470 
471       if (err_msg){
472 	 err_fatal (
473 	    __func__,
474 	    "%s\n",
475 	    plugin -> dictdb_error (plugin -> data));
476       }else{
477 	 err_fatal (
478 	    __func__,
479 	    "Error code %i\n", ret);
480       }
481    }
482 
483    switch (version){
484    case 0:
485       break;
486 /*
487    case 1:
488       if (!i -> plugin -> dictdb_set)
489 	 err_fatal (__func__, "'%s' function is not found\n", DICT_PLUGINFUN_SET);
490       break;
491 */
492    default:
493       err_fatal (__func__, "Invalid version returned by plugin\n");
494    }
495 }
496 
dict_plugin_dlsym(dictPlugin * plugin)497 static void dict_plugin_dlsym (dictPlugin *plugin)
498 {
499    PRINTF(DBG_INIT, (":I:     getting functions addresses\n"));
500 
501    plugin -> dictdb_open   = (dictdb_open_type)
502       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_OPEN);
503    plugin -> dictdb_free   = (dictdb_free_type)
504       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_FREE);
505    plugin -> dictdb_search = (dictdb_search_type)
506       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_SEARCH);
507    plugin -> dictdb_close  = (dictdb_close_type)
508       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_CLOSE);
509    plugin -> dictdb_error  = (dictdb_error_type)
510       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_ERROR);
511    plugin -> dictdb_set   = (dictdb_set_type)
512       lt_dlsym (plugin -> handle, DICT_PLUGINFUN_SET);
513 
514    if (!plugin -> dictdb_open ||
515        !plugin -> dictdb_search ||
516        !plugin -> dictdb_free ||
517        !plugin -> dictdb_error ||
518        !plugin -> dictdb_close)
519    {
520       PRINTF(DBG_INIT, (":I:     faild\n"));
521       exit (1);
522    }
523 }
524 
create_plugin(const char * datababsename,const char * plugin_filename,const dictPluginData * plugin_init_data,int plugin_init_data_size)525 static dictPlugin *create_plugin (
526    const char *datababsename,
527    const char *plugin_filename,
528    const dictPluginData *plugin_init_data,
529    int plugin_init_data_size)
530 {
531    dictPlugin *plugin;
532    int ret;
533    int version;
534 
535    PRINTF(
536       DBG_INIT, (
537 	 ":I:   Initializing db/plugin '%s'/'%s'\n",
538 	 datababsename ? datababsename : "(null)", plugin_filename));
539 
540    plugin = xmalloc (sizeof (dictPlugin));
541    memset (plugin, 0, sizeof (dictPlugin));
542 
543    PRINTF(DBG_INIT, (":I:     opening plugin\n"));
544    plugin -> handle = lt_dlopen (plugin_filename);
545    if (!plugin -> handle){
546       PRINTF(DBG_INIT, (":I:     faild: %s\n", dlerror ()));
547       exit (1);
548    }
549 
550    dict_plugin_dlsym (plugin);
551 
552    PRINTF(DBG_INIT, (":I:     initializing plugin\n"));
553    ret = plugin -> dictdb_open (
554       plugin_init_data, plugin_init_data_size, &version, &plugin -> data);
555 
556    dict_plugin_test (plugin, version, ret);
557 
558    return plugin;
559 }
560 
dict_plugin_init(dictDatabase * db)561 int dict_plugin_init (dictDatabase *db)
562 {
563    int ret = 0;
564    lst_List list;
565    const char *plugin_filename = NULL;
566    dictWord *dw;
567 
568    dictPluginData init_data [3000];
569    int init_data_size = 0;
570 
571    if (db -> pluginFilename){
572       plugin_filename = db -> pluginFilename;
573    }else if (db -> index){
574 
575       list = lst_create ();
576 
577       ret = dict_search_database_ (list, DICT_ENTRY_PLUGIN, db, DICT_STRAT_EXACT);
578       switch (ret){
579       case 1: case 2:
580 	 dw = (dictWord *) lst_pop (list);
581 
582 	 plugin_filename = dict_plugin_filename (db, dw);
583 
584 	 dict_destroy_datum (dw);
585 	 break;
586       case 0:
587 	 break;
588       default:
589 	 err_internal( __func__, "Corrupted .index file'\n" );
590       }
591 
592       dict_destroy_list (list);
593    }
594 
595    if (plugin_filename){
596       init_data_size = plugin_initdata_set (
597 	 init_data, sizeof (init_data)/sizeof (init_data [0]),
598 	 db);
599 
600       db -> plugin = create_plugin (
601 	 db -> databaseName,
602 	 plugin_filename, init_data, init_data_size);
603 
604       plugin_init_data_free (init_data, init_data_size);
605    }
606 
607    return 0;
608 }
609 
dict_plugin_destroy(dictDatabase * db)610 void dict_plugin_destroy ( dictDatabase *db )
611 {
612    int ret;
613 
614    if (!db)
615       return;
616 
617    if (!db -> plugin)
618       return;
619 
620    if (db -> plugin -> dictdb_close){
621       ret = db -> plugin -> dictdb_close (db -> plugin -> data);
622       if (ret){
623 	 PRINTF(DBG_INIT, ("exiting plugin failed"));
624 	 exit (1);
625       }
626    }
627 
628    ret = lt_dlclose (db -> plugin -> handle);
629    if (ret)
630       PRINTF(DBG_INIT, ("%s", lt_dlerror ()));
631 
632    xfree (db -> plugin);
633    db -> plugin = NULL;
634 }
635 
call_dictdb_free(lst_List db_list)636 void call_dictdb_free (lst_List db_list)
637 {
638    const dictDatabase *db = NULL;
639    lst_Position pos;
640 
641    LST_ITERATE (db_list, pos, db){
642       if (db -> plugin){
643 	 if (db -> plugin -> dictdb_free_called){
644 	    db -> plugin -> dictdb_free (db -> plugin -> data);
645 
646 	    db -> plugin -> dictdb_free_called = 0;
647 	 }
648       }
649    }
650 }
651