(concord_open_ds): Use <char*> instead of <unsigned char*> for
[chise/concord.git] / concord.c
1 /* Copyright (C) 2003,2004,2005,2006 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 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 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 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, char* name))
186 {
187   unsigned char* dname = ds->location;
188   DIR *dir;
189   struct dirent *de;
190
191   if ( (dir = opendir (dname)) == NULL)
192     return -1;
193
194   while ( (de = readdir (dir)) != NULL )
195     {
196       if ( (strcmp (de->d_name, ".") != 0) &&
197            (strcmp (de->d_name, "..") != 0) )
198         {
199           int i, need_to_decode = 0;
200           unsigned char *cp;
201           unsigned char *name;
202           unsigned char *np;
203
204           for (cp = de->d_name, i = 0; *cp != '\0'; i++)
205             {
206               if (*cp++ == '%')
207                 need_to_decode = 1;
208             }
209           if (need_to_decode)
210             {
211               int index = -1;
212               int ch, c[2];
213               int hex[2];
214
215               name = (unsigned char *) alloca (i);
216               cp = de->d_name;
217               np = name;
218
219               while ( (ch = *cp++) != '\0')
220                 {
221                   if (ch == '%')
222                     {
223                       if (index >= 0)
224                         {
225                           *np++ = '%';
226                           if (index == 1)
227                             *np++ = c[0];
228                         }
229                       index = 0;
230                     }
231                   else if (index >= 0)
232                     {
233                       c[index] = ch;
234
235                       if ( ('0' <= ch) && (ch <= '9') )
236                         hex[index++] = ch - '0';
237                       else if ( ('A' <= ch) && (ch <= 'F') )
238                         hex[index++] = ch - 'A' + 10;
239                       else if ( ('a' <= ch) && (ch <= 'f') )
240                         hex[index++] = ch - 'a' + 10;
241                       else
242                         {
243                           *np++ = '%';
244                           if (index == 1)
245                             *np++ = c[0];
246                           *np++ = ch;
247                           index = -1;
248                           continue;
249                         }
250                       if (index == 2)
251                         {
252                           *np++ = (hex[0] << 4) | hex[1];
253                           index = -1;
254                           continue;
255                         }
256                     }
257                   else
258                     *np++ = ch;
259                 }
260               *np = '\0';
261             }  
262           else
263             name = de->d_name;
264
265           if (func (ds, name))
266             return closedir (dir);
267         }
268     }
269   return closedir (dir);
270 }
271
272
273 struct CONCORD_Genre_Table
274 {
275   CONCORD_DS ds;
276   unsigned char *name;
277   CONCORD_NAME_TABLE* feature_names;
278   CONCORD_NAME_TABLE* index_names;
279 };
280
281 CONCORD_Genre
282 concord_ds_open_genre (CONCORD_DS ds, const unsigned char* name)
283 {
284   CONCORD_Genre genre;
285   size_t len = strlen (name);
286
287   if (ds == NULL)
288     return NULL;
289
290   genre = (CONCORD_Genre)malloc (sizeof (CONCORD_Genre_Table));
291   if (genre == NULL)
292     return NULL;
293
294   genre->ds = ds;
295   genre->name = (unsigned char*)malloc (len + 1);
296   if (genre->name == NULL)
297     {
298       free (genre);
299       return NULL;
300     }
301   strcpy (genre->name, name);
302
303   genre->feature_names = concord_make_name_table ();
304   if (genre->feature_names == NULL)
305     {
306       free (genre->name);
307       free (genre);
308       return NULL;
309     }
310
311   genre->index_names = concord_make_name_table ();
312   if (genre->index_names == NULL)
313     {
314       free (genre->feature_names);
315       free (genre->name);
316       free (genre);
317       return NULL;
318     }
319   return genre;
320 }
321
322 int
323 concord_close_genre (CONCORD_Genre genre)
324 {
325   int status;
326
327   if (genre == NULL)
328     return -1;
329
330   if (genre->name == NULL)
331     status = -1;
332   else
333     {
334       free (genre->name);
335       status = 0;
336     }
337
338   if (genre->feature_names != NULL)
339     concord_destroy_name_table (genre->feature_names);
340
341   if (genre->index_names != NULL)
342     concord_destroy_name_table (genre->index_names);
343
344   free (genre);
345   return status;
346 }
347
348 char*
349 concord_genre_get_name (CONCORD_Genre genre)
350 {
351   return genre->name;
352 }
353
354 CONCORD_DS
355 concord_genre_get_data_source (CONCORD_Genre genre)
356 {
357   return genre->ds;
358 }
359
360 int
361 concord_genre_foreach_feature_name (CONCORD_Genre genre,
362                                     int (*func) (CONCORD_Genre genre,
363                                                  char* name))
364 {
365   unsigned char *dname
366     = alloca (strlen (genre->ds->location)
367               + 1 + strlen (genre->name) + sizeof ("/feature") + 1);
368   DIR *dir;
369   struct dirent *de;
370
371   strcpy (dname, genre->ds->location);
372   strcat (dname, "/");
373   strcat (dname, genre->name);
374   strcat (dname, "/feature");
375
376   if ( (dir = opendir (dname)) == NULL)
377     return -1;
378
379   while ( (de = readdir (dir)) != NULL )
380     {
381       if ( (strcmp (de->d_name, ".") != 0) &&
382            (strcmp (de->d_name, "..") != 0) )
383         {
384           int i, need_to_decode = 0;
385           unsigned char *cp;
386           unsigned char *name;
387           unsigned char *np;
388
389           for (cp = de->d_name, i = 0; *cp != '\0'; i++)
390             {
391               if (*cp++ == '%')
392                 need_to_decode = 1;
393             }
394           if (need_to_decode)
395             {
396               int index = -1;
397               int ch, c[2];
398               int hex[2];
399
400               name = (unsigned char *) alloca (i);
401               cp = de->d_name;
402               np = name;
403
404               while ( (ch = *cp++) != '\0')
405                 {
406                   if (ch == '%')
407                     {
408                       if (index >= 0)
409                         {
410                           *np++ = '%';
411                           if (index == 1)
412                             *np++ = c[0];
413                         }
414                       index = 0;
415                     }
416                   else if (index >= 0)
417                     {
418                       c[index] = ch;
419
420                       if ( ('0' <= ch) && (ch <= '9') )
421                         hex[index++] = ch - '0';
422                       else if ( ('A' <= ch) && (ch <= 'F') )
423                         hex[index++] = ch - 'A' + 10;
424                       else if ( ('a' <= ch) && (ch <= 'f') )
425                         hex[index++] = ch - 'a' + 10;
426                       else
427                         {
428                           *np++ = '%';
429                           if (index == 1)
430                             *np++ = c[0];
431                           *np++ = ch;
432                           index = -1;
433                           continue;
434                         }
435                       if (index == 2)
436                         {
437                           *np++ = (hex[0] << 4) | hex[1];
438                           index = -1;
439                           continue;
440                         }
441                     }
442                   else
443                     *np++ = ch;
444                 }
445               *np = '\0';
446             }  
447           else
448             name = de->d_name;
449
450           if (func (genre, name))
451             return closedir (dir);
452         }
453     }
454   return closedir (dir);
455 }
456
457 CONCORD_Feature
458 concord_genre_get_feature_0 (CONCORD_Genre genre, const unsigned char* name)
459 {
460   CONCORD_Feature feature;
461
462   feature = concord_name_table_get (genre->feature_names, name);
463   if (feature != NULL)
464     return feature;
465
466   feature = concord_genre_open_feature (genre, name);
467   if (feature == NULL)
468     return NULL;
469
470   if (concord_name_table_put (genre->feature_names, name, feature))
471     {
472       concord_close_feature (feature);
473       return NULL;
474     }
475   return feature;
476 }
477
478 CONCORD_Feature
479 concord_genre_get_feature (CONCORD_Genre genre, const char* name)
480 {
481   CONCORD_Genre g_feature
482     = concord_ds_get_genre (genre->ds, "feature");
483
484   if (g_feature != NULL)
485     {
486       CONCORD_Feature p_true_name
487         = concord_genre_get_feature_0 (g_feature, "true-name");
488
489       if (g_feature != NULL)
490         {
491           CONCORD_String_Tank s_true_name;
492           int status
493             = concord_obj_get_feature_value_string (name,
494                                                     p_true_name,
495                                                     &s_true_name);
496           if (status == 0)
497             {
498               unsigned char* t_name = alloca (s_true_name.size + 1);
499
500               strncpy (t_name, s_true_name.data, s_true_name.size);
501               t_name[s_true_name.size] = '\0';
502               return concord_genre_get_feature_0 (genre, t_name);
503             }
504         }
505     }
506   return concord_genre_get_feature_0 (genre, name);
507 }
508
509 CONCORD_INDEX
510 concord_genre_get_index (CONCORD_Genre genre, const char* name)
511 {
512   CONCORD_INDEX index;
513
514   index = concord_name_table_get (genre->index_names, name);
515   if (index != NULL)
516     return index;
517
518   index = concord_genre_open_index (genre, name);
519   if (index == NULL)
520     return NULL;
521
522   if (concord_name_table_put (genre->index_names, name, index))
523     {
524       concord_close_index (index);
525       return NULL;
526     }
527   return index;
528 }
529
530
531 struct CONCORD_Feature_Table
532 {
533   CONCORD_Genre genre;
534   unsigned char* name;
535   DB* db;
536   u_int32_t access;
537 };
538
539 CONCORD_Feature
540 concord_genre_open_feature (CONCORD_Genre genre, const unsigned char* feature)
541 {
542   CONCORD_Feature table;
543   size_t len = strlen (feature);
544
545   if (genre == NULL)
546     return NULL;
547
548   table = (CONCORD_Feature)malloc (sizeof (CONCORD_Feature_Table));
549   if (table == NULL)
550     return NULL;
551
552   table->genre = genre;
553   table->db = NULL;
554   table->access = 0;
555   table->name = (unsigned char*)malloc (len + 1);
556   if (table->name == NULL)
557     {
558       free (table);
559       return NULL;
560     }
561   strcpy (table->name, feature);
562   return table;
563 }
564
565 int
566 concord_close_feature (CONCORD_Feature feature)
567 {
568   int status;
569
570   if (feature == NULL)
571     return -1;
572
573   if (feature->db == NULL)
574     status = -1;
575   else
576     status = CONCORD_BDB_close (feature->db);
577
578   if (feature->name == NULL)
579     status = -1;
580   else
581     {
582       free (feature->name);
583       status = 0;
584     }
585   free (feature);
586   return status;
587 }
588
589 char*
590 concord_feature_get_name (CONCORD_Feature feature)
591 {
592   return feature->name;
593 }
594
595 CONCORD_Genre
596 concord_feature_get_genre (CONCORD_Feature feature)
597 {
598   return feature->genre;
599 }
600
601 int
602 concord_feature_setup_db (CONCORD_Feature feature, int writable)
603 {
604   u_int32_t access;
605
606   if (feature == NULL)
607     return -1;
608
609   if (writable)
610     {
611       if ((feature->access & DB_CREATE) == 0)
612         {
613           if (feature->db != NULL)
614             {
615               CONCORD_BDB_close (feature->db);
616               feature->db = NULL;
617             }
618           feature->access = 0;
619         }
620       access = DB_CREATE;
621     }
622   else
623     access = DB_RDONLY;
624
625   if (feature->db == NULL)
626     {
627       CONCORD_Genre genre = feature->genre;
628
629       feature->db
630         = CONCORD_BDB_open (genre->ds->location, genre->name,
631                             "feature", feature->name,
632                             genre->ds->subtype,
633                             access, genre->ds->modemask);
634       if (feature->db == NULL)
635         return -1;
636       feature->access = access;
637     }
638   return 0;
639 }
640
641 int
642 concord_feature_sync (CONCORD_Feature feature)
643 {
644   int status;
645
646   if (feature->db == NULL)
647     status = 0;
648   else
649     status = CONCORD_BDB_close (feature->db);
650   feature->db = NULL;
651   feature->access = 0;
652   return status;
653 }
654
655 int
656 concord_obj_put_feature_value_str (const char* object_id,
657                                    CONCORD_Feature feature,
658                                    unsigned char* value)
659 {
660   if (feature == NULL)
661     return -1;
662   if (concord_feature_setup_db (feature, 1))
663     return -1;
664   return CONCORD_BDB_put (feature->db, object_id, value);
665 }
666
667 int
668 concord_obj_get_feature_value_string (const char* object_id,
669                                       CONCORD_Feature feature,
670                                       CONCORD_String value)
671 {
672   int status;
673
674   if (concord_feature_setup_db (feature, 0))
675     return -1;
676   status = CONCORD_BDB_get (feature->db, object_id, value);
677   return status;
678 }
679
680 CONCORD_Object
681 concord_obj_get_feature_value (const char* object_id,
682                                CONCORD_Feature feature)
683 {
684   DBT valdatum;
685   int status;
686
687   if (concord_feature_setup_db (feature, 0))
688     return feature->genre->ds->object_nil;
689   status = CONCORD_BDB_get (feature->db, object_id, &valdatum);
690   if (status)
691     return feature->genre->ds->object_nil;
692   return (*feature->genre->ds->read_object)(valdatum.data, valdatum.size);
693 }
694
695 unsigned char*
696 concord_obj_gets_feature_value (const char* object_id,
697                                 CONCORD_Feature feature,
698                                 unsigned char* dst, size_t size)
699 {
700   DBT valdatum;
701   int status;
702
703   if (concord_feature_setup_db (feature, 0))
704     return NULL;
705   status = CONCORD_BDB_get (feature->db, object_id, &valdatum);
706   if (status)
707     return NULL;
708   if (size < valdatum.size)
709     return NULL;
710   strncpy (dst, valdatum.data, valdatum.size);
711   dst[valdatum.size] = '\0';
712   return dst;
713 }
714
715 int
716 concord_feature_foreach_obj_string (CONCORD_Feature feature,
717                                     int (*func)(CONCORD_String object_id,
718                                                 CONCORD_Feature feature,
719                                                 CONCORD_String value))
720 {
721   CONCORD_String_Tank key, value;
722   DBC *dbcp;
723   int status;
724
725   if (concord_feature_setup_db (feature, 0))
726     return -1;
727   xzero (key);
728   xzero (value);
729
730   status = feature->db->cursor (feature->db, NULL, &dbcp, 0);
731   for (status = dbcp->c_get (dbcp, &key, &value, DB_FIRST);
732        status == 0;
733        status = dbcp->c_get (dbcp, &key, &value, DB_NEXT))
734     {
735       int ret = func (&key, feature, &value);
736
737       if (ret)
738         break;
739     }
740   dbcp->c_close (dbcp);
741   return 0;
742 }
743
744
745 struct CONCORD_INDEX_Table
746 {
747   CONCORD_Genre genre;
748   unsigned char *name;
749   DB* db;
750   u_int32_t access;
751 };
752
753 CONCORD_INDEX
754 concord_genre_open_index (CONCORD_Genre genre, const unsigned char* index)
755 {
756   CONCORD_INDEX table;
757   size_t len = strlen (index);
758
759   if (genre == NULL)
760     return NULL;
761
762   table = (CONCORD_INDEX)malloc (sizeof (CONCORD_INDEX_Table));
763   if (table == NULL)
764     return NULL;
765
766   table->genre = genre;
767   table->db = NULL;
768   table->access = 0;
769   table->name = (unsigned char*)malloc (len + 1);
770   if (table->name == NULL)
771     {
772       free (table);
773       return NULL;
774     }
775   strcpy (table->name, index);
776   return table;
777 }
778
779 int
780 concord_close_index (CONCORD_INDEX table)
781 {
782   int status;
783
784   if (table == NULL)
785     return -1;
786
787   if (table->db == NULL)
788     status = 0;
789   else
790     status = CONCORD_BDB_close (table->db);
791
792   if (table->name == NULL)
793     status = -1;
794   else
795     {
796       free (table->name);
797       status = 0;
798     }
799   free (table);
800   return status;
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, 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 }