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