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