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