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