0e5274d68282ad2d6c74b074e69bdce148bb0a46
[chise/libchise.git] / chise.c
1 #include <stdlib.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <unistd.h>
5 #ifdef HAVE_CONFIG_H
6 #  include "config.h"
7 #endif
8 #include "sysdep.h"
9 #include "chise.h"
10 #include "chise-name.h"
11
12 const unsigned char chise_db_dir[] = CHISE_DB_DIR;
13
14 CHISE_Feature_Table*
15 chise_ds_open_feature_table (CHISE_DS *ds, const char *feature);
16
17 int chise_ft_close (CHISE_Feature_Table *table);
18
19
20 CHISE_CCS_Table*
21 chise_ds_open_ccs_table (CHISE_DS *ds, const char *ccs);
22
23 int chise_ccst_close (CHISE_CCS_Table *table);
24
25
26 typedef DB CHISE_Attribute_Table;
27
28 CHISE_Attribute_Table*
29 CHISE_Attribute_Table_open (const unsigned char *db_dir,
30                             const char *encoding, const char *feature,
31                             DBTYPE real_subtype,
32                             u_int32_t accessmask, int modemask);
33
34 int CHISE_Attribute_Table_close (CHISE_Attribute_Table *db);
35
36 int chise_attribute_table_get (CHISE_Attribute_Table *db,
37                                char *key, CHISE_Value *valdatum);
38
39 int chise_attribute_table_put (CHISE_Attribute_Table *db,
40                                char *key, unsigned char *value);
41
42
43 #define xzero(lvalue) ((void) memset (&(lvalue), '\0', sizeof (lvalue)))
44
45 CHISE_Char_ID
46 chise_char_id_parse_c_string (unsigned char *str, size_t len);
47
48 int
49 chise_format_char_id (CHISE_Char_ID cid, unsigned char *dest, size_t len);
50
51
52 struct CHISE_DS
53 {
54   CHISE_DS_Type type;
55   unsigned char *location;
56   CHISE_NAME_TABLE* feature_names;
57   CHISE_NAME_TABLE* ccs_names;
58   DBTYPE subtype;
59   int modemask;
60 };
61
62 CHISE_DS*
63 CHISE_DS_open (CHISE_DS_Type type, const unsigned char *location,
64                DBTYPE subtype, int modemask)
65 {
66   CHISE_DS *ds = (CHISE_DS*)malloc (sizeof (CHISE_DS));
67   size_t len = strlen (location);
68
69   if (ds == NULL)
70     return NULL;
71
72   ds->type = type;
73   ds->subtype = subtype;
74   ds->modemask = modemask;
75   ds->location = (unsigned char*)malloc (len + 1);
76   if (ds->location == NULL)
77     {
78       free (ds);
79       return NULL;
80     }
81   strcpy (ds->location, location);
82
83   ds->feature_names = chise_make_name_table ();
84   if (ds->feature_names == NULL)
85     {
86       free (ds->location);
87       free (ds);
88     }
89
90   ds->ccs_names = chise_make_name_table ();
91   if (ds->ccs_names == NULL)
92     {
93       free (ds->feature_names);
94       free (ds->location);
95       free (ds);
96     }
97   return ds;
98 }
99
100 int
101 CHISE_DS_close (CHISE_DS *ds)
102 {
103   if (ds->location != NULL)
104     free (ds->location);
105   if (ds->feature_names != NULL)
106     free (ds->feature_names);
107   if (ds->ccs_names != NULL)
108     free (ds->ccs_names);
109   free (ds);
110   return 0;
111 }
112
113 CHISE_Feature_Table*
114 chise_ds_get_feature (CHISE_DS *ds, const unsigned char *feature)
115 {
116   CHISE_Feature_Table* ft;
117
118   ft = chise_name_table_get (ds->feature_names, feature);
119   if (ft != NULL)
120     return ft;
121
122   ft = chise_ds_open_feature_table (ds, feature);
123   if (ft == NULL)
124     return NULL;
125
126   if (chise_name_table_put (ds->feature_names, feature, ft))
127     {
128       chise_ft_close (ft);
129       return NULL;
130     }
131   return ft;
132 }
133
134 CHISE_CCS
135 chise_ds_get_ccs (CHISE_DS *ds, const unsigned char *ccs)
136 {
137   CHISE_CCS_Table* ct;
138
139   ct = chise_name_table_get (ds->ccs_names, ccs);
140   if (ct != NULL)
141     return ct;
142
143   ct = chise_ds_open_ccs_table (ds, ccs);
144   if (ct == NULL)
145     return NULL;
146
147   if (chise_name_table_put (ds->ccs_names, ccs, ct))
148     {
149       chise_ccst_close (ct);
150       return NULL;
151     }
152   return ct;
153 }
154
155
156 struct CHISE_Feature_Table
157 {
158   CHISE_DS *ds;
159   unsigned char *name;
160   CHISE_Attribute_Table *db;
161   u_int32_t access;
162 };
163
164 CHISE_Feature_Table*
165 chise_ds_open_feature_table (CHISE_DS *ds, const char *feature)
166 {
167   CHISE_Feature_Table* table;
168   size_t len = strlen (feature);
169
170   if (ds == NULL)
171     return NULL;
172
173   table = (CHISE_Feature_Table*)malloc (sizeof (CHISE_Feature_Table));
174   if (table == NULL)
175     return NULL;
176
177   table->ds = ds;
178   table->db = NULL;
179   table->access = 0;
180   table->name = (unsigned char*)malloc (len + 1);
181   if (table->name == NULL)
182     {
183       free (table);
184       return NULL;
185     }
186   strcpy (table->name, feature);
187   return table;
188 }
189
190 int
191 chise_ft_close (CHISE_Feature_Table *table)
192 {
193   int status;
194
195   if (table == NULL)
196     return -1;
197
198   if (table->db == NULL)
199     status = -1;
200   else
201     status = CHISE_Attribute_Table_close (table->db);
202
203   if (table->name == NULL)
204     status = -1;
205   else
206     {
207       free (table->name);
208       status = 0;
209     }
210   free (table);
211   return status;
212 }
213
214 int
215 chise_feature_setup_db (CHISE_Feature feature, int writable)
216 {
217   u_int32_t access;
218
219   if (feature == NULL)
220     return -1;
221
222   if (writable)
223     {
224       if ((feature->access & DB_CREATE) == 0)
225         {
226           if (feature->db != NULL)
227             {
228               CHISE_Attribute_Table_close (feature->db);
229               feature->db = NULL;
230             }
231           feature->access = 0;
232         }
233       access = DB_CREATE;
234     }
235   else
236     access = DB_RDONLY;
237
238   if (feature->db == NULL)
239     {
240       CHISE_DS *ds = feature->ds;
241
242       feature->db
243         = CHISE_Attribute_Table_open (ds->location,
244                                       "system-char-id", feature->name,
245                                       ds->subtype, access, ds->modemask);
246       if (feature->db == NULL)
247         return -1;
248       feature->access = access;
249     }
250   return 0;
251 }
252
253 int
254 chise_feature_sync (CHISE_Feature feature)
255 {
256   int status;
257
258   if (feature->db == NULL)
259     status = 0;
260   else
261     status = CHISE_Attribute_Table_close (feature->db);
262   feature->db = NULL;
263   feature->access = 0;
264   return status;
265 }
266
267 int
268 chise_char_set_feature_value (CHISE_Char_ID cid,
269                               CHISE_Feature feature,
270                               unsigned char *value)
271 {
272   unsigned char key_buf[8];
273
274   if (feature == NULL)
275     return -1;
276   if (chise_feature_setup_db (feature, 1))
277     return -1;
278   chise_format_char_id (cid, key_buf, 8);
279   return chise_attribute_table_put (feature->db, key_buf, value);
280 }
281
282 int
283 chise_char_load_feature_value (CHISE_Char_ID cid,
284                                CHISE_Feature_Table *table,
285                                CHISE_Value *valdatum)
286 {
287   unsigned char key_buf[8];
288
289   if (chise_feature_setup_db (table, 0))
290     return -1;
291   chise_format_char_id (cid, key_buf, 8);
292   return chise_attribute_table_get (table->db, key_buf, valdatum);
293 }
294
295 unsigned char*
296 chise_char_gets_feature_value (CHISE_Char_ID cid,
297                                CHISE_Feature_Table *table,
298                                unsigned char *buf, size_t size)
299 {
300   CHISE_Value valdatum;
301   unsigned char key_buf[8];
302   int status;
303
304   if (chise_feature_setup_db (table, 0))
305     return NULL;
306   chise_format_char_id (cid, key_buf, 8);
307   status = chise_attribute_table_get (table->db,
308                                       key_buf, &valdatum);
309   if (status)
310     return NULL;
311   if (size < valdatum.size)
312     return NULL;
313   strncpy (buf, valdatum.data, valdatum.size);
314   buf[valdatum.size] = '\0';
315   return buf;
316 }
317
318 int
319 chise_char_feature_value_iterate (CHISE_Feature feature,
320                                   int (*func) (CHISE_Char_ID cid,
321                                                CHISE_Feature feature,
322                                                CHISE_Value *valdatum))
323 {
324   DBT keydatum, valdatum;
325   DBC *dbcp;
326   int status;
327
328   if (chise_feature_setup_db (feature, 0))
329     return -1;
330   xzero (keydatum);
331   xzero (valdatum);
332
333   status = feature->db->cursor (feature->db, NULL, &dbcp, 0);
334   for (status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_FIRST);
335        status == 0;
336        status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_NEXT))
337     {
338       unsigned char *key_str = (unsigned char *)keydatum.data;
339       int key_len = strnlen (key_str, keydatum.size);
340       CHISE_Char_ID key = chise_char_id_parse_c_string (key_str, key_len);
341       int ret = func (key, feature, &valdatum);
342
343       if (ret)
344         break;
345     }
346   dbcp->c_close (dbcp);
347   return 0;
348 }
349
350
351 struct CHISE_CCS_Table
352 {
353   CHISE_DS *ds;
354   unsigned char *name;
355   CHISE_Attribute_Table *db;
356   u_int32_t access;
357 };
358
359 CHISE_CCS_Table*
360 chise_ds_open_ccs_table (CHISE_DS *ds, const char *ccs)
361 {
362   CHISE_CCS_Table* table;
363   size_t len = strlen (ccs);
364
365   if (ds == NULL)
366     return NULL;
367
368   table = (CHISE_CCS_Table*)malloc (sizeof (CHISE_CCS_Table));
369   if (table == NULL)
370     return NULL;
371
372   table->ds = ds;
373   table->db = NULL;
374   table->access = 0;
375   table->name = (unsigned char*)malloc (len + 1);
376   if (table->name == NULL)
377     {
378       free (table);
379       return NULL;
380     }
381   strcpy (table->name, ccs);
382   return table;
383 }
384
385 int
386 chise_ccst_close (CHISE_CCS_Table *table)
387 {
388   int status;
389
390   if (table == NULL)
391     return -1;
392
393   if (table->db == NULL)
394     status = 0;
395   else
396     status = CHISE_Attribute_Table_close (table->db);
397
398   if (table->name == NULL)
399     status = -1;
400   else
401     {
402       free (table->name);
403       status = 0;
404     }
405   free (table);
406   return status;
407 }
408
409 int
410 chise_ccs_setup_db (CHISE_CCS ccs, int writable)
411 {
412   u_int32_t access;
413
414   if (ccs == NULL)
415     return -1;
416
417   if (writable)
418     {
419       if ((ccs->access & DB_CREATE) == 0)
420         {
421           if (ccs->db != NULL)
422             {
423               CHISE_Attribute_Table_close (ccs->db);
424               ccs->db = NULL;
425             }
426           ccs->access = 0;
427         }
428       access = DB_CREATE;
429     }
430   else
431     access = DB_RDONLY;
432
433   if (ccs->db == NULL)
434     {
435       CHISE_DS *ds = ccs->ds;
436
437       ccs->db
438         = CHISE_Attribute_Table_open (ds->location,
439                                       ccs->name, "system-char-id",
440                                       ds->subtype, access, ds->modemask);
441       if (ccs->db == NULL)
442         return -1;
443       ccs->access = access;
444     }
445   return 0;
446 }
447
448 int
449 chise_ccs_sync (CHISE_CCS ccs)
450 {
451   int status;
452
453   if (ccs->db == NULL)
454     status = 0;
455   else
456     status = CHISE_Attribute_Table_close (ccs->db);
457   ccs->db = NULL;
458   ccs->access = 0;
459   return status;
460 }
461
462 CHISE_Char_ID
463 chise_ccs_decode (CHISE_CCS ccs, int code_point)
464 {
465   CHISE_Value valdatum;
466   int status = 0;
467   char key_buf[16];
468
469   if (ccs == NULL)
470     return -1;
471
472   if (chise_ccs_setup_db (ccs, 0))
473     return -1;  
474
475   sprintf(key_buf, "%d", code_point);
476   status = chise_attribute_table_get (ccs->db, key_buf, &valdatum);
477   if (!status)
478     {
479       unsigned char *str
480         = (unsigned char *)chise_value_data (&valdatum);
481       int len = strnlen (str, chise_value_size (&valdatum));
482
483       return chise_char_id_parse_c_string (str, len);
484     }
485   return -1;
486 }
487
488 int
489 chise_ccs_set_decoded_char (CHISE_CCS ccs,
490                             int code_point, CHISE_Char_ID cid)
491 {
492   char key_buf[16], val_buf[8];
493
494   if (ccs == NULL)
495     return -1;
496
497   if (chise_ccs_setup_db (ccs, 1))
498     return -1;  
499
500   sprintf(key_buf, "%d", code_point);
501   chise_format_char_id (cid, val_buf, 8);
502   return chise_attribute_table_put (ccs->db, key_buf, val_buf);
503 }
504
505
506 CHISE_Attribute_Table*
507 CHISE_Attribute_Table_open (const unsigned char *db_dir,
508                             const char *encoding, const char *feature,
509                             DBTYPE real_subtype,
510                             u_int32_t accessmask, int modemask)
511 {
512   DB* dbase;
513   int status;
514   int len, flen, i;
515   int size;
516   char *db_file_name, *sp;
517   struct stat statbuf;
518
519   status = db_create (&dbase, NULL, 0);
520   if (status)
521     return NULL;
522
523   if ( (accessmask & DB_CREATE) && stat (db_dir, &statbuf) )
524     mkdir (db_dir, modemask);
525
526   len = strlen (db_dir);
527   flen = strlen (feature);
528   size = len + strlen (encoding) + flen * 3 + 4;
529   db_file_name = alloca (size);
530   strcpy (db_file_name, db_dir);
531   if (db_file_name[len - 1] != '/')
532     {
533       db_file_name[len++] = '/';
534       db_file_name[len] = '\0';
535     }
536   strcat (db_file_name, encoding);
537
538   if ( (accessmask & DB_CREATE) && stat (db_file_name, &statbuf) )
539     mkdir (db_file_name, modemask);
540
541   strcat (db_file_name, "/");
542   /* strcat (db_file_name, feature); */
543   sp = &db_file_name[strlen (db_file_name)];
544   for (i = 0; i < flen; i++)
545     {
546       int c = feature[i];
547
548       if ( (c == '/') || (c == '%') )
549         {
550           sprintf (sp, "%%%02X", c);
551           sp += 3;
552         }
553       else
554         *sp++ = c;
555     }
556   *sp = '\0';
557   status = dbase->open (dbase, db_file_name, NULL,
558                         real_subtype, accessmask, modemask);
559   if (status)
560     {
561       dbase->close (dbase, 0);
562       return NULL;
563     }
564   return dbase;
565 }
566
567 int
568 CHISE_Attribute_Table_close (CHISE_Attribute_Table *db)
569 {
570   if (db)
571     {
572       db->sync  (db, 0);
573       db->close (db, 0);
574     }
575   return 0;
576 }
577
578 int
579 chise_attribute_table_get (CHISE_Attribute_Table *db,
580                            char *key, CHISE_Value *valdatum)
581 {
582   DBT keydatum;
583   int status = 0;
584
585   /* DB Version 2 requires DBT's to be zeroed before use. */
586   xzero (keydatum);
587   xzero (*valdatum);
588
589   keydatum.data = key;
590   keydatum.size = strlen (key);
591
592   status = db->get (db, NULL, &keydatum, valdatum, 0);
593   return status;
594 }
595
596 int
597 chise_attribute_table_put (CHISE_Attribute_Table *db,
598                            char *key, unsigned char *value)
599 {
600   DBT keydatum, valdatum;
601   int status = 0;
602
603   /* DB Version 2 requires DBT's to be zeroed before use. */
604   xzero (keydatum);
605   xzero (valdatum);
606
607   keydatum.data = key;
608   keydatum.size = strlen (key);
609
610   valdatum.data = value;
611   valdatum.size = strlen (value);
612
613   status = db->put (db, NULL, &keydatum, &valdatum, 0);
614   return status;
615 }
616
617 CHISE_Char_ID
618 chise_char_id_parse_c_string (unsigned char *str, size_t len)
619 {
620   int i = 0;
621
622   if ( (len >= 2) && (str[i++] == '?') )
623     {
624       unsigned char c = str[i++];
625       int counter;
626       CHISE_Char_ID cid;
627
628       if (c == '\\')
629         {
630           if (len < 3)
631             return -1;
632           c = str[i++];
633           if (c == '^')
634             {
635               if (len < 4)
636                 return -1;
637               c = str[i++];
638               if (c == '?')
639                 return 0x7F;
640               else
641                 return c & (0x80 | 0x1F);
642             }
643         }
644       if ( c < 0xC0 )
645         {
646           cid = c;
647           counter = 0;
648         }
649       else if ( c < 0xE0 )
650         {
651           cid = c & 0x1f;
652           counter = 1;
653         }
654       else if ( c < 0xF0 )
655         {
656           cid = c & 0x0f;
657           counter = 2;
658         }
659       else if ( c < 0xF8 )
660         {
661           cid = c & 0x07;
662           counter = 3;
663         }
664       else if ( c < 0xFC )
665         {
666           cid = c & 0x03;
667           counter = 4;
668         }
669       else
670         {
671           cid = c & 0x01;
672           counter = 5;
673         }
674
675       if (counter + 2 <= len)
676         {
677           int j;
678
679           for (j = 0; j < counter; j++)
680             cid = (cid << 6) | (str[j + i] & 0x3F);
681           return cid;
682         }
683     }
684   return -1;
685 }
686
687 int
688 chise_format_char_id (CHISE_Char_ID cid, unsigned char *dest, size_t len)
689 {
690   int i = 0;
691
692   dest[i++] = '?';
693   if (cid == '\t')
694     {
695       dest[i++] = '\\';
696       dest[i++] = 't';
697       dest[i] = '\0';
698       return i;
699     }
700   else if (cid == '\n')
701     {
702       dest[i++] = '\\';
703       dest[i++] = 'n';
704       dest[i] = '\0';
705       return i;
706     }
707   else if (cid == '\r')
708     {
709       dest[i++] = '\\';
710       dest[i++] = 'r';
711       dest[i] = '\0';
712       return i;
713     }
714   else if (cid == 0x1C)
715     {
716       dest[i++] = '\\';
717       dest[i++] = '^';
718       dest[i++] = '\\';
719       dest[i++] = '\\';
720       dest[i] = '\0';
721       return i;
722     }
723   else if (cid <= 0x1F)
724     {
725       dest[i++] = '\\';
726       dest[i++] = '^';
727       dest[i++] = '@' + cid;
728       dest[i] = '\0';
729       return i;
730     }
731   else if ( (cid == ' ') || (cid == '"') ||
732             (cid == '#') || (cid == '\'') ||
733             (cid == '(') || (cid == ')') ||
734             (cid == ',') || (cid == '.') ||
735             (cid == ';') || (cid == '?') ||
736             (cid == '[') || (cid == '\\') ||
737             (cid == ']') || (cid == '`') )
738     {
739       dest[i++] = '\\';
740       dest[i++] = cid;
741       dest[i] = '\0';
742       return i;
743     }
744   else if (cid <= 0x7E)
745     {
746       dest[i++] = cid;
747       dest[i] = '\0';
748       return i;
749     }
750   else if (cid == 0x7F)
751     {
752       dest[i++] = '\\';
753       dest[i++] = '^';
754       dest[i++] = '?';
755       dest[i] = '\0';
756       return i;
757     }
758   else if (cid <= 0x9F)
759     {
760       dest[i++] = '\\';
761       dest[i++] = '^';
762       dest[i++] = ((cid + '@') >> 6) | 0xC0;
763       dest[i++] = ((cid + '@') & 0x3F) | 0x80;
764       dest[i] = '\0';
765       return i;
766     }
767   else if (cid <= 0x7FF)
768     {
769       dest[i++] = (cid >> 6) | 0xC0;
770       dest[i++] = (cid & 0x3F) | 0x80;
771       dest[i] = '\0';
772       return i;
773     }
774   else if (cid <= 0xFFFF)
775     {
776       dest[i++] = (cid >> 12) | 0xE0;
777       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
778       dest[i++]=  (cid        & 0x3F) | 0x80;
779       dest[i] = '\0';
780       return i;
781     }
782   else if (cid <= 0x1FFFFF)
783     {
784       dest[i++]=  (cid >> 18) | 0xF0;
785       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
786       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
787       dest[i++]=  (cid        & 0x3F) | 0x80;
788       dest[i] = '\0';
789       return i;
790     }
791   else if (cid <= 0x3FFFFFF)
792     {
793       dest[i++]=  (cid >> 24) | 0xF8;
794       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
795       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
796       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
797       dest[i++]=  (cid        & 0x3F) | 0x80;
798       dest[i] = '\0';
799       return i;
800     }
801   else
802     {
803       dest[i++]=  (cid >> 30) | 0xFC;
804       dest[i++]= ((cid >> 24) & 0x3F) | 0x80;
805       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
806       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
807       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
808       dest[i++]=  (cid        & 0x3F) | 0x80;
809       dest[i] = '\0';
810       return i;
811     }
812 }