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