Include "config.h" if `HAVE_CONFIG_H' is defined.
[chise/concord.git] / concord.c
1 /* Copyright (C) 2003,2004,2005,2006,2011 MORIOKA Tomohiko
2    This file is part of the CONCORD Library.
3
4    The CONCORD Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The CONCORD Library 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 GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the CONCORD Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <dirent.h>
24 #include "sysdep.h"
25 #include "concord.h"
26 #include "cos-i.h"
27 #include "concord-name.h"
28 #include "concord-bdb.h"
29
30
31 int
32 CONCORD_String_size (const CONCORD_String s)
33 {
34   return s->size;
35 }
36
37 unsigned char*
38 CONCORD_String_data (const CONCORD_String s)
39 {
40   return s->data;
41 }
42
43 CONCORD_Genre concord_ds_open_genre (CONCORD_DS ds, const char* name);
44
45
46 CONCORD_Feature
47 concord_genre_open_feature (CONCORD_Genre genre, const char* name);
48
49
50 CONCORD_INDEX
51 concord_genre_open_index (CONCORD_Genre genre, const char* index);
52
53 CONCORD_Feature
54 concord_genre_get_feature_0 (CONCORD_Genre genre, const char* name);
55
56
57 CONCORD_Object
58 concord_default_read_object (const unsigned char* string, size_t length);
59
60
61 struct COS_DS_ent
62 {
63   COS_Object_Header header;
64   CONCORD_Backend_Type type;
65   char *location;
66   CONCORD_NAME_TABLE* genre_names;
67   DBTYPE subtype;
68   int modemask;
69
70   CONCORD_Object object_nil;
71   CONCORD_Object (*read_object) (const unsigned char* str, size_t length);
72   CONCORD_NAME_TABLE* symbol_names;
73 };
74
75 CONCORD_Object
76 concord_default_read_object (const unsigned char* str, size_t length)
77 {
78   unsigned char* buf = malloc (length + 1);
79
80   if (buf == NULL)
81     return NULL;
82   strncpy ((char*)buf, (char*)str, length);
83   buf[length] = '\0';
84   return (CONCORD_Object)buf;
85 }
86
87 CONCORD_DS
88 concord_open_ds (CONCORD_Backend_Type type, const char* location,
89                  int subtype, int modemask)
90 {
91 #if 0
92   CONCORD_DS ds = (CONCORD_DS)malloc (sizeof (CONCORD_DS_ent));
93   size_t len = strlen (location);
94
95   if (ds == NULL)
96     return NULL;
97
98   ds->header.prefix = 0xff;
99   ds->header.type = CONCORD_Object_Type_DS;
100 #else
101   CONCORD_DS ds = COS_ALLOCATE_OBJECT (DS);
102   size_t len = strlen (location);
103
104   if (ds == NULL)
105     return NULL;
106 #endif
107
108   ds->type = type;
109   ds->subtype = ( (subtype != 0) ? subtype : DB_HASH );
110   ds->modemask = modemask;
111   ds->location = (char*)malloc (len + 1);
112   if (ds->location == NULL)
113     goto location_failure;
114
115   strcpy (ds->location, location);
116
117   ds->genre_names = concord_make_name_table ();
118   if (ds->genre_names == NULL)
119     {
120       free (ds->location);
121     location_failure:
122       free (ds);
123       return NULL;
124     }
125
126   ds->symbol_names = concord_make_name_table ();
127   if (ds->symbol_names == NULL)
128     {
129       concord_destroy_name_table (ds->genre_names);
130       free (ds->location);
131       free (ds);
132       return NULL;
133     }
134
135   ds->object_nil = NULL;
136   ds->read_object = &concord_default_read_object;
137
138   return ds;
139 }
140
141 int
142 concord_close_ds (CONCORD_DS ds)
143 {
144   if (ds->location != NULL)
145     free (ds->location);
146   if (ds->genre_names != NULL)
147     concord_destroy_name_table (ds->genre_names);
148   if (ds->symbol_names != NULL)
149     concord_destroy_name_table (ds->symbol_names);
150   free (ds);
151   return 0;
152 }
153
154 char*
155 concord_ds_location (CONCORD_DS ds)
156 {
157   return ds->location;
158 }
159
160 int
161 concord_ds_set_object_failure (CONCORD_DS ds, CONCORD_Object object_nil)
162 {
163   ds->object_nil = object_nil;
164   return 0;
165 }
166
167 int
168 concord_ds_set_read_object_function (CONCORD_DS ds,
169                                      CONCORD_Object (*read_object)
170                                      (const unsigned char* str,
171                                       size_t length))
172 {
173   ds->read_object = read_object;
174   return 0;
175 }
176
177 CONCORD_Genre
178 concord_ds_get_genre (CONCORD_DS ds, const char* name)
179 {
180   CONCORD_Genre genre;
181
182   genre = concord_name_table_get (ds->genre_names, name);
183   if (genre != NULL)
184     return genre;
185
186   genre = concord_ds_open_genre (ds, name);
187   if (genre == NULL)
188     return NULL;
189
190   if (concord_name_table_put (ds->genre_names, name, genre))
191     {
192       concord_close_genre (genre);
193       return NULL;
194     }
195   return genre;
196 }
197
198 int
199 concord_ds_foreach_genre_name (CONCORD_DS ds,
200                                int (*func) (CONCORD_DS ds, char* name))
201 {
202   char* dname = ds->location;
203   DIR *dir;
204   struct dirent *de;
205
206   if ( (dir = opendir (dname)) == NULL)
207     return -1;
208
209   while ( (de = readdir (dir)) != NULL )
210     {
211       if ( (strcmp (de->d_name, ".") != 0) &&
212            (strcmp (de->d_name, "..") != 0) )
213         {
214           int i, need_to_decode = 0;
215           unsigned char *cp;
216           char *name;
217           unsigned char *np;
218
219           for (cp = (unsigned char*)de->d_name, i = 0; *cp != '\0'; i++)
220             {
221               if (*cp++ == '%')
222                 need_to_decode = 1;
223             }
224           if (need_to_decode)
225             {
226               int index = -1;
227               int ch, c[2];
228               int hex[2];
229
230               name = (char*) alloca (i);
231               cp = (unsigned char*)de->d_name;
232               np = (unsigned char*)name;
233
234               while ( (ch = *cp++) != '\0')
235                 {
236                   if (ch == '%')
237                     {
238                       if (index >= 0)
239                         {
240                           *np++ = '%';
241                           if (index == 1)
242                             *np++ = c[0];
243                         }
244                       index = 0;
245                     }
246                   else if (index >= 0)
247                     {
248                       c[index] = ch;
249
250                       if ( ('0' <= ch) && (ch <= '9') )
251                         hex[index++] = ch - '0';
252                       else if ( ('A' <= ch) && (ch <= 'F') )
253                         hex[index++] = ch - 'A' + 10;
254                       else if ( ('a' <= ch) && (ch <= 'f') )
255                         hex[index++] = ch - 'a' + 10;
256                       else
257                         {
258                           *np++ = '%';
259                           if (index == 1)
260                             *np++ = c[0];
261                           *np++ = ch;
262                           index = -1;
263                           continue;
264                         }
265                       if (index == 2)
266                         {
267                           *np++ = (hex[0] << 4) | hex[1];
268                           index = -1;
269                           continue;
270                         }
271                     }
272                   else
273                     *np++ = ch;
274                 }
275               *np = '\0';
276             }  
277           else
278             name = de->d_name;
279
280           if (func (ds, name))
281             return closedir (dir);
282         }
283     }
284   return closedir (dir);
285 }
286
287
288 struct COS_Genre_ent
289 {
290   COS_Object_Header header;
291   CONCORD_DS ds;
292   char *name;
293   CONCORD_NAME_TABLE* feature_names;
294   CONCORD_NAME_TABLE* index_names;
295 };
296
297 CONCORD_Genre
298 concord_ds_open_genre (CONCORD_DS ds, const char* name)
299 {
300   CONCORD_Genre genre;
301   size_t len = strlen (name);
302
303   if (ds == NULL)
304     return NULL;
305
306 #if 0
307   genre = (CONCORD_Genre)malloc (sizeof (CONCORD_Genre_ent));
308   if (genre == NULL)
309     return NULL;
310
311   genre->header.prefix = 0xff;
312   genre->header.type = CONCORD_Object_Type_Genre;
313 #else
314   genre = COS_ALLOCATE_OBJECT (Genre);
315   if (genre == NULL)
316     return NULL;
317 #endif
318
319   genre->ds = ds;
320   genre->name = (char*)malloc (len + 1);
321   if (genre->name == NULL)
322     {
323       free (genre);
324       return NULL;
325     }
326   strcpy (genre->name, name);
327
328   genre->feature_names = concord_make_name_table ();
329   if (genre->feature_names == NULL)
330     {
331       free (genre->name);
332       free (genre);
333       return NULL;
334     }
335
336   genre->index_names = concord_make_name_table ();
337   if (genre->index_names == NULL)
338     {
339       free (genre->feature_names);
340       free (genre->name);
341       free (genre);
342       return NULL;
343     }
344   return genre;
345 }
346
347 int
348 concord_close_genre (CONCORD_Genre genre)
349 {
350   int status;
351
352   if (genre == NULL)
353     return -1;
354
355   if (genre->name == NULL)
356     status = -1;
357   else
358     {
359       free (genre->name);
360       status = 0;
361     }
362
363   if (genre->feature_names != NULL)
364     concord_destroy_name_table (genre->feature_names);
365
366   if (genre->index_names != NULL)
367     concord_destroy_name_table (genre->index_names);
368
369   free (genre);
370   return status;
371 }
372
373 char*
374 concord_genre_get_name (CONCORD_Genre genre)
375 {
376   return genre->name;
377 }
378
379 CONCORD_DS
380 concord_genre_get_data_source (CONCORD_Genre genre)
381 {
382   return genre->ds;
383 }
384
385 int
386 concord_genre_foreach_feature_name (CONCORD_Genre genre,
387                                     int (*func) (CONCORD_Genre genre,
388                                                  char* name))
389 {
390   char *dname
391     = alloca (strlen (genre->ds->location)
392               + 1 + strlen (genre->name) + sizeof ("/feature") + 1);
393   DIR *dir;
394   struct dirent *de;
395
396   strcpy (dname, genre->ds->location);
397   strcat (dname, "/");
398   strcat (dname, genre->name);
399   strcat (dname, "/feature");
400
401   if ( (dir = opendir (dname)) == NULL)
402     return -1;
403
404   while ( (de = readdir (dir)) != NULL )
405     {
406       if ( (strcmp (de->d_name, ".") != 0) &&
407            (strcmp (de->d_name, "..") != 0) )
408         {
409           int i, need_to_decode = 0;
410           unsigned char *cp;
411           char *name;
412           unsigned char *np;
413
414           for (cp = (unsigned char*)de->d_name, i = 0; *cp != '\0'; i++)
415             {
416               if (*cp++ == '%')
417                 need_to_decode = 1;
418             }
419           if (need_to_decode)
420             {
421               int index = -1;
422               int ch, c[2];
423               int hex[2];
424
425               name = (char*) alloca (i);
426               cp = (unsigned char*)de->d_name;
427               np = (unsigned char*)name;
428
429               while ( (ch = *cp++) != '\0')
430                 {
431                   if (ch == '%')
432                     {
433                       if (index >= 0)
434                         {
435                           *np++ = '%';
436                           if (index == 1)
437                             *np++ = c[0];
438                         }
439                       index = 0;
440                     }
441                   else if (index >= 0)
442                     {
443                       c[index] = ch;
444
445                       if ( ('0' <= ch) && (ch <= '9') )
446                         hex[index++] = ch - '0';
447                       else if ( ('A' <= ch) && (ch <= 'F') )
448                         hex[index++] = ch - 'A' + 10;
449                       else if ( ('a' <= ch) && (ch <= 'f') )
450                         hex[index++] = ch - 'a' + 10;
451                       else
452                         {
453                           *np++ = '%';
454                           if (index == 1)
455                             *np++ = c[0];
456                           *np++ = ch;
457                           index = -1;
458                           continue;
459                         }
460                       if (index == 2)
461                         {
462                           *np++ = (hex[0] << 4) | hex[1];
463                           index = -1;
464                           continue;
465                         }
466                     }
467                   else
468                     *np++ = ch;
469                 }
470               *np = '\0';
471             }  
472           else
473             name = de->d_name;
474
475           if (func (genre, name))
476             return closedir (dir);
477         }
478     }
479   return closedir (dir);
480 }
481
482 CONCORD_Feature
483 concord_genre_get_feature_0 (CONCORD_Genre genre, const char* name)
484 {
485   CONCORD_Feature feature;
486
487   feature = concord_name_table_get (genre->feature_names, name);
488   if (feature != NULL)
489     return feature;
490
491   feature = concord_genre_open_feature (genre, name);
492   if (feature == NULL)
493     return NULL;
494
495   if (concord_name_table_put (genre->feature_names, name, feature))
496     {
497       concord_close_feature (feature);
498       return NULL;
499     }
500   return feature;
501 }
502
503 CONCORD_Feature
504 concord_genre_get_feature (CONCORD_Genre genre, const char* name)
505 {
506   CONCORD_Genre g_feature
507     = concord_ds_get_genre (genre->ds, "feature");
508
509   if (g_feature != NULL)
510     {
511       CONCORD_Feature p_true_name
512         = concord_genre_get_feature_0 (g_feature, "true-name");
513
514       if (g_feature != NULL)
515         {
516           CONCORD_String_Tank s_true_name;
517           int status
518             = concord_obj_get_feature_value_string (name,
519                                                     p_true_name,
520                                                     &s_true_name);
521           if (status == 0)
522             {
523               char* t_name = alloca (s_true_name.size + 1);
524
525               strncpy (t_name, s_true_name.data, s_true_name.size);
526               t_name[s_true_name.size] = '\0';
527               return concord_genre_get_feature_0 (genre, t_name);
528             }
529         }
530     }
531   return concord_genre_get_feature_0 (genre, name);
532 }
533
534 CONCORD_INDEX
535 concord_genre_get_index (CONCORD_Genre genre, const char* name)
536 {
537   CONCORD_INDEX index;
538
539   index = concord_name_table_get (genre->index_names, name);
540   if (index != NULL)
541     return index;
542
543   index = concord_genre_open_index (genre, name);
544   if (index == NULL)
545     return NULL;
546
547   if (concord_name_table_put (genre->index_names, name, index))
548     {
549       concord_close_index (index);
550       return NULL;
551     }
552   return index;
553 }
554
555
556 struct COS_Feature_ent
557 {
558   COS_Object_Header header;
559   CONCORD_Genre genre;
560   char* name;
561   DB* db;
562   u_int32_t access;
563 };
564
565 CONCORD_Feature
566 concord_genre_open_feature (CONCORD_Genre genre, const char* feature)
567 {
568   CONCORD_Feature table;
569   size_t len = strlen (feature);
570
571   if (genre == NULL)
572     return NULL;
573
574 #if 0
575   table = (CONCORD_Feature)malloc (sizeof (CONCORD_Feature_ent));
576   if (table == NULL)
577     return NULL;
578
579   table->header.prefix = 0xff;
580   table->header.type = CONCORD_Object_Type_Feature;
581 #else
582   table = COS_ALLOCATE_OBJECT (Feature);
583   if (table == NULL)
584     return NULL;
585 #endif
586
587   table->genre = genre;
588   table->db = NULL;
589   table->access = 0;
590   table->name = (char*)malloc (len + 1);
591   if (table->name == NULL)
592     {
593       free (table);
594       return NULL;
595     }
596   strcpy (table->name, feature);
597   return table;
598 }
599
600 int
601 concord_close_feature (CONCORD_Feature feature)
602 {
603   int status;
604
605   if (feature == NULL)
606     return -1;
607
608   if (feature->db == NULL)
609     status = -1;
610   else
611     status = CONCORD_BDB_close (feature->db);
612
613   if (feature->name == NULL)
614     status = -1;
615   else
616     {
617       free (feature->name);
618       status = 0;
619     }
620   free (feature);
621   return status;
622 }
623
624 char*
625 concord_feature_get_name (CONCORD_Feature feature)
626 {
627   return feature->name;
628 }
629
630 CONCORD_Genre
631 concord_feature_get_genre (CONCORD_Feature feature)
632 {
633   return feature->genre;
634 }
635
636 int
637 concord_feature_setup_db (CONCORD_Feature feature, int writable)
638 {
639   u_int32_t access;
640
641   if (feature == NULL)
642     return -1;
643
644   if (writable)
645     {
646       if ((feature->access & DB_CREATE) == 0)
647         {
648           if (feature->db != NULL)
649             {
650               CONCORD_BDB_close (feature->db);
651               feature->db = NULL;
652             }
653           feature->access = 0;
654         }
655       access = DB_CREATE;
656     }
657   else
658     access = DB_RDONLY;
659
660   if (feature->db == NULL)
661     {
662       CONCORD_Genre genre = feature->genre;
663
664       feature->db
665         = CONCORD_BDB_open (genre->ds->location, genre->name,
666                             "feature", feature->name,
667                             genre->ds->subtype,
668                             access, genre->ds->modemask);
669       if (feature->db == NULL)
670         return -1;
671       feature->access = access;
672     }
673   return 0;
674 }
675
676 int
677 concord_feature_sync (CONCORD_Feature feature)
678 {
679   int status;
680
681   if (feature->db == NULL)
682     status = 0;
683   else
684     status = CONCORD_BDB_close (feature->db);
685   feature->db = NULL;
686   feature->access = 0;
687   return status;
688 }
689
690 int
691 concord_obj_put_feature_value_str (const char* object_id,
692                                    CONCORD_Feature feature,
693                                    unsigned char* value)
694 {
695   if (feature == NULL)
696     return -1;
697   if (concord_feature_setup_db (feature, 1))
698     return -1;
699   return CONCORD_BDB_put (feature->db, object_id, value);
700 }
701
702 int
703 concord_obj_get_feature_value_string (const char* object_id,
704                                       CONCORD_Feature feature,
705                                       CONCORD_String value)
706 {
707   int status;
708
709   if (concord_feature_setup_db (feature, 0))
710     return -1;
711   status = CONCORD_BDB_get (feature->db, object_id, value);
712   return status;
713 }
714
715 CONCORD_Object
716 concord_obj_get_feature_value (const char* object_id,
717                                CONCORD_Feature feature)
718 {
719   DBT valdatum;
720   int status;
721
722   if (concord_feature_setup_db (feature, 0))
723     return feature->genre->ds->object_nil;
724   status = CONCORD_BDB_get (feature->db, object_id, &valdatum);
725   if (status)
726     return feature->genre->ds->object_nil;
727   return (*feature->genre->ds->read_object)(valdatum.data, valdatum.size);
728 }
729
730 unsigned char*
731 concord_obj_gets_feature_value (const char* object_id,
732                                 CONCORD_Feature feature,
733                                 unsigned char* dst, size_t size)
734 {
735   DBT valdatum;
736   int status;
737
738   if (concord_feature_setup_db (feature, 0))
739     return NULL;
740   status = CONCORD_BDB_get (feature->db, object_id, &valdatum);
741   if (status)
742     return NULL;
743   if (size < valdatum.size)
744     return NULL;
745   strncpy ((char*)dst, valdatum.data, valdatum.size);
746   dst[valdatum.size] = '\0';
747   return dst;
748 }
749
750 int
751 concord_feature_foreach_obj_string (CONCORD_Feature feature,
752                                     int (*func)(CONCORD_String object_id,
753                                                 CONCORD_Feature feature,
754                                                 CONCORD_String value))
755 {
756   CONCORD_String_Tank key, value;
757   DBC *dbcp;
758   int status;
759
760   if (concord_feature_setup_db (feature, 0))
761     return -1;
762   xzero (key);
763   xzero (value);
764
765   status = feature->db->cursor (feature->db, NULL, &dbcp, 0);
766   for (status = dbcp->c_get (dbcp, &key, &value, DB_FIRST);
767        status == 0;
768        status = dbcp->c_get (dbcp, &key, &value, DB_NEXT))
769     {
770       int ret = func (&key, feature, &value);
771
772       if (ret)
773         break;
774     }
775   dbcp->c_close (dbcp);
776   return 0;
777 }
778
779
780 struct COS_Feature_INDEX_ent
781 {
782   COS_Object_Header header;
783   CONCORD_Genre genre;
784   char *name;
785   DB* db;
786   u_int32_t access;
787 };
788
789 CONCORD_INDEX
790 concord_genre_open_index (CONCORD_Genre genre, const char* index)
791 {
792   CONCORD_INDEX table;
793   size_t len = strlen (index);
794
795   if (genre == NULL)
796     return NULL;
797
798 #if 0
799   table = (CONCORD_INDEX)malloc (sizeof (CONCORD_INDEX_ent));
800   if (table == NULL)
801     return NULL;
802
803   table->header.prefix = 0xff;
804   table->header.type = CONCORD_Object_Type_INDEX;
805 #else
806   table = COS_ALLOCATE_OBJECT (Feature_INDEX);
807   if (table == NULL)
808     return NULL;
809 #endif
810
811   table->genre = genre;
812   table->db = NULL;
813   table->access = 0;
814   table->name = (char*)malloc (len + 1);
815   if (table->name == NULL)
816     {
817       free (table);
818       return NULL;
819     }
820   strcpy (table->name, index);
821   return table;
822 }
823
824 int
825 concord_close_index (CONCORD_INDEX table)
826 {
827   int status;
828
829   if (table == NULL)
830     return -1;
831
832   if (table->db == NULL)
833     status = 0;
834   else
835     status = CONCORD_BDB_close (table->db);
836
837   if (table->name == NULL)
838     status = -1;
839   else
840     {
841       free (table->name);
842       status = 0;
843     }
844   free (table);
845   return status;
846 }
847
848 char*
849 concord_index_get_name (CONCORD_INDEX index)
850 {
851   return index->name;
852 }
853
854 CONCORD_Genre
855 concord_index_get_genre (CONCORD_INDEX index)
856 {
857   return index->genre;
858 }
859
860 int
861 concord_index_setup_db (CONCORD_INDEX index, int writable)
862 {
863   u_int32_t access;
864
865   if (index == NULL)
866     return -1;
867
868   if (writable)
869     {
870       if ((index->access & DB_CREATE) == 0)
871         {
872           if (index->db != NULL)
873             {
874               CONCORD_BDB_close (index->db);
875               index->db = NULL;
876             }
877           index->access = 0;
878         }
879       access = DB_CREATE;
880     }
881   else
882     access = DB_RDONLY;
883
884   if (index->db == NULL)
885     {
886       CONCORD_Genre genre = index->genre;
887
888       index->db
889         = CONCORD_BDB_open (genre->ds->location, genre->name,
890                             "index", index->name,
891                             genre->ds->subtype,
892                             access, genre->ds->modemask);
893       if (index->db == NULL)
894         return -1;
895       index->access = access;
896     }
897   return 0;
898 }
899
900 int
901 concord_index_sync (CONCORD_INDEX index)
902 {
903   int status;
904
905   if (index->db == NULL)
906     status = 0;
907   else
908     status = CONCORD_BDB_close (index->db);
909   index->db = NULL;
910   index->access = 0;
911   return status;
912 }
913
914 int
915 concord_index_strid_put_obj (CONCORD_INDEX index,
916                              const char* strid, char* object_id)
917 {
918   if (index == NULL)
919     return -1;
920
921   if (concord_index_setup_db (index, 1))
922     return -1;  
923
924   return CONCORD_BDB_put (index->db, strid, (unsigned char*)object_id);
925 }
926
927 int
928 concord_index_strid_get_obj_string (CONCORD_INDEX index,
929                                     const char* strid,
930                                     CONCORD_String object_id)
931 {
932   if (index == NULL)
933     return -1;
934
935   if (concord_index_setup_db (index, 0))
936     return -1;  
937
938   return CONCORD_BDB_get (index->db, strid, object_id);
939 }