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