(chise_open_data_source): Add new arguments `subtype' and `modemask'.
[chise/libchise.git] / chise.c
1 #ifdef HAVE_CONFIG_H
2 #  include "config.h"
3 #endif
4
5 #ifndef HAVE_STRNLEN
6 /* original in mysql, strings/strnlen.c.
7 uint strnlen(register const char *s, register uint maxlen)
8 {
9   const char *end= (const char *)memchr(s, '\0', maxlen);
10   return end ? (uint) (end - s) : maxlen;
11 }
12 */
13 static inline int
14 strnlen (register const char *s, register int maxlen)
15 {
16   const char *end= (const char *)memchr(s, '\0', maxlen);
17   return end ? (int) (end - s) : maxlen;
18 }
19 #endif
20
21 #include "chise.h"
22 #include "chise-name.h"
23
24 typedef DB CHISE_Attribute_Table;
25
26 CHISE_Attribute_Table*
27 chise_open_attribute_table (const unsigned char *db_dir,
28                             const char *encoding, const char *feature,
29                             DBTYPE real_subtype,
30                             u_int32_t accessmask, int modemask);
31
32 int chise_close_attribute_table (CHISE_Attribute_Table *db);
33
34 int chise_get_attribute_table (CHISE_Attribute_Table *db,
35                                char *key, CHISE_Value *valdatum);
36
37 int chise_put_attribute_table (CHISE_Attribute_Table *db,
38                                char *key, unsigned char *value);
39
40
41 #define xzero(lvalue) ((void) memset (&(lvalue), '\0', sizeof (lvalue)))
42
43 CHISE_Char_ID
44 chise_char_id_parse_c_string (unsigned char *str, int len)
45 {
46   int i = 0;
47
48   if ( (len >= 2) && (str[i++] == '?') )
49     {
50       unsigned char c = str[i++];
51       int counter;
52       CHISE_Char_ID cid;
53
54       if (c == '\\')
55         {
56           if (len < 3)
57             return -1;
58           c = str[i++];
59           if (c == '^')
60             {
61               if (len < 4)
62                 return -1;
63               c = str[i++];
64               if (c == '?')
65                 return 0x7F;
66               else
67                 return c & (0x80 | 0x1F);
68             }
69         }
70       if ( c < 0xC0 )
71         {
72           cid = c;
73           counter = 0;
74         }
75       else if ( c < 0xE0 )
76         {
77           cid = c & 0x1f;
78           counter = 1;
79         }
80       else if ( c < 0xF0 )
81         {
82           cid = c & 0x0f;
83           counter = 2;
84         }
85       else if ( c < 0xF8 )
86         {
87           cid = c & 0x07;
88           counter = 3;
89         }
90       else if ( c < 0xFC )
91         {
92           cid = c & 0x03;
93           counter = 4;
94         }
95       else
96         {
97           cid = c & 0x01;
98           counter = 5;
99         }
100
101       if (counter + 2 <= len)
102         {
103           int j;
104
105           for (j = 0; j < counter; j++)
106             cid = (cid << 6) | (str[j + i] & 0x3F);
107           return cid;
108         }
109     }
110   return -1;
111 }
112
113 int
114 chise_format_char_id (CHISE_Char_ID cid, unsigned char *dest, int len)
115 {
116   int i = 0;
117
118   dest[i++] = '?';
119   if (cid == '\t')
120     {
121       dest[i++] = '\\';
122       dest[i++] = 't';
123       dest[i] = '\0';
124       return i;
125     }
126   else if (cid == '\n')
127     {
128       dest[i++] = '\\';
129       dest[i++] = 'n';
130       dest[i] = '\0';
131       return i;
132     }
133   else if (cid == '\r')
134     {
135       dest[i++] = '\\';
136       dest[i++] = 'r';
137       dest[i] = '\0';
138       return i;
139     }
140   else if (cid == 0x1C)
141     {
142       dest[i++] = '\\';
143       dest[i++] = '^';
144       dest[i++] = '\\';
145       dest[i++] = '\\';
146       dest[i] = '\0';
147       return i;
148     }
149   else if (cid <= 0x1F)
150     {
151       dest[i++] = '\\';
152       dest[i++] = '^';
153       dest[i++] = '@' + cid;
154       dest[i] = '\0';
155       return i;
156     }
157   else if ( (cid == ' ') || (cid == '"') ||
158             (cid == '#') || (cid == '\'') ||
159             (cid == '(') || (cid == ')') ||
160             (cid == ',') || (cid == '.') ||
161             (cid == ';') || (cid == '?') ||
162             (cid == '[') || (cid == '\\') ||
163             (cid == ']') || (cid == '`') )
164     {
165       dest[i++] = '\\';
166       dest[i++] = cid;
167       dest[i] = '\0';
168       return i;
169     }
170   else if (cid <= 0x7E)
171     {
172       dest[i++] = cid;
173       dest[i] = '\0';
174       return i;
175     }
176   else if (cid == 0x7F)
177     {
178       dest[i++] = '\\';
179       dest[i++] = '^';
180       dest[i++] = '?';
181       dest[i] = '\0';
182       return i;
183     }
184   else if (cid <= 0x9F)
185     {
186       dest[i++] = '\\';
187       dest[i++] = '^';
188       dest[i++] = ((cid + '@') >> 6) | 0xC0;
189       dest[i++] = ((cid + '@') & 0x3F) | 0x80;
190       dest[i] = '\0';
191       return i;
192     }
193   else if (cid <= 0x7FF)
194     {
195       dest[i++] = (cid >> 6) | 0xC0;
196       dest[i++] = (cid & 0x3F) | 0x80;
197       dest[i] = '\0';
198       return i;
199     }
200   else if (cid <= 0xFFFF)
201     {
202       dest[i++] = (cid >> 12) | 0xE0;
203       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
204       dest[i++]=  (cid        & 0x3F) | 0x80;
205       dest[i] = '\0';
206       return i;
207     }
208   else if (cid <= 0x1FFFFF)
209     {
210       dest[i++]=  (cid >> 18) | 0xF0;
211       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
212       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
213       dest[i++]=  (cid        & 0x3F) | 0x80;
214       dest[i] = '\0';
215       return i;
216     }
217   else if (cid <= 0x3FFFFFF)
218     {
219       dest[i++]=  (cid >> 24) | 0xF8;
220       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
221       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
222       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
223       dest[i++]=  (cid        & 0x3F) | 0x80;
224       dest[i] = '\0';
225       return i;
226     }
227   else
228     {
229       dest[i++]=  (cid >> 30) | 0xFC;
230       dest[i++]= ((cid >> 24) & 0x3F) | 0x80;
231       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
232       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
233       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
234       dest[i++]=  (cid        & 0x3F) | 0x80;
235       dest[i] = '\0';
236       return i;
237     }
238 }
239
240
241 struct CHISE_DS
242 {
243   CHISE_DS_Type type;
244   unsigned char *location;
245   CHISE_NAME_TABLE* feature_names;
246   CHISE_NAME_TABLE* ccs_names;
247 };
248
249 CHISE_DS*
250 chise_open_data_source (CHISE_DS_Type type, char *location)
251 {
252   CHISE_DS *ds = (CHISE_DS*)malloc (sizeof (CHISE_DS));
253   size_t len = strlen (location);
254
255   if (ds == NULL)
256     return NULL;
257
258   ds->type = type;
259   ds->location = (unsigned char*)malloc (len + 1);
260   if (ds->location == NULL)
261     {
262       free (ds);
263       return NULL;
264     }
265   strcpy (ds->location, location);
266
267   ds->feature_names = chise_make_name_table ();
268   if (ds->feature_names == NULL)
269     {
270       free (ds->location);
271       free (ds);
272     }
273
274   ds->ccs_names = chise_make_name_table ();
275   if (ds->ccs_names == NULL)
276     {
277       free (ds->feature_names);
278       free (ds->location);
279       free (ds);
280     }
281   return ds;
282 }
283
284 int
285 chise_ds_close (CHISE_DS *ds)
286 {
287   if (ds->location != NULL)
288     free (ds->location);
289   if (ds->feature_names != NULL)
290     free (ds->feature_names);
291   if (ds->ccs_names != NULL)
292     free (ds->ccs_names);
293   free (ds);
294   return 0;
295 }
296
297 CHISE_Feature_Table*
298 chise_ds_get_feature (CHISE_DS *ds, const unsigned char *feature,
299                       DBTYPE real_subtype,
300                       u_int32_t accessmask, int modemask)
301 {
302   CHISE_Feature_Table* ft;
303
304   ft = chise_name_table_get (ds->feature_names, feature);
305   if (ft != NULL)
306     return ft;
307
308   ft = chise_ds_open_feature_table (ds, feature,
309                                     real_subtype,
310                                     accessmask, modemask);
311   if (ft == NULL)
312     return NULL;
313
314   if (chise_name_table_put (ds->feature_names, feature, ft))
315     {
316       chise_ft_close (ft);
317       return NULL;
318     }
319   return ft;
320 }
321
322 CHISE_CCS_Table*
323 chise_ds_get_ccs (CHISE_DS *ds, const unsigned char *ccs,
324                   DBTYPE real_subtype,
325                   u_int32_t accessmask, int modemask)
326 {
327   CHISE_CCS_Table* ct;
328
329   ct = chise_name_table_get (ds->ccs_names, ccs);
330   if (ct != NULL)
331     return ct;
332
333   ct = chise_ds_open_ccs_table (ds, ccs,
334                                 real_subtype,
335                                 accessmask, modemask);
336   if (ct == NULL)
337     return NULL;
338
339   if (chise_name_table_put (ds->ccs_names, ccs, ct))
340     {
341       chise_ccst_close (ct);
342       return NULL;
343     }
344   return ct;
345 }
346
347
348 struct CHISE_Feature_Table
349 {
350   CHISE_DS *ds;
351   CHISE_Attribute_Table *db;
352 };
353
354 CHISE_Feature_Table*
355 chise_ds_open_feature_table (CHISE_DS *ds, const char *feature,
356                              DBTYPE real_subtype,
357                              u_int32_t accessmask, int modemask)
358 {
359   CHISE_Feature_Table* table;
360
361   if (ds == NULL)
362     return NULL;
363
364   table = (CHISE_Feature_Table*)malloc (sizeof (CHISE_Feature_Table));
365   if (table == NULL)
366     return NULL;
367
368   table->ds = ds;
369   table->db = chise_open_attribute_table (ds->location,
370                                           "system-char-id", feature,
371                                           real_subtype,
372                                           accessmask, modemask);
373   if (table->db == NULL)
374     {
375       free (table);
376       return NULL;
377     }
378   return table;
379 }
380
381 int
382 chise_ft_close (CHISE_Feature_Table *table)
383 {
384   int status;
385
386   if (table == NULL)
387     return -1;
388
389   if (table->db == NULL)
390     status = -1;
391   else
392     status = chise_close_attribute_table (table->db);
393   free (table);
394   return status;
395 }
396
397 int
398 chise_char_load_feature_value (CHISE_Char_ID cid,
399                                CHISE_Feature_Table *table,
400                                CHISE_Value *valdatum)
401 {
402   unsigned char key_buf[8];
403
404   chise_format_char_id (cid, key_buf, 8);
405   return chise_get_attribute_table (table->db,
406                                     key_buf, valdatum);
407 }
408
409 unsigned char*
410 chise_char_gets_feature_value (CHISE_Char_ID cid,
411                                CHISE_Feature_Table *table,
412                                unsigned char *buf, size_t size)
413 {
414   CHISE_Value valdatum;
415   unsigned char key_buf[8];
416   int status;
417
418   chise_format_char_id (cid, key_buf, 8);
419   status = chise_get_attribute_table (table->db,
420                                       key_buf, &valdatum);
421   if (status)
422     return NULL;
423   if (size < valdatum.size)
424     return NULL;
425   strncpy (buf, valdatum.data, valdatum.size);
426   buf[valdatum.size] = '\0';
427   return buf;
428 }
429
430 void
431 chise_char_feature_value_iterate (CHISE_Feature feature,
432                                   int (*func) (CHISE_Char_ID cid,
433                                                CHISE_Feature feature,
434                                                CHISE_Value *valdatum))
435 {
436   DBT keydatum, valdatum;
437   DBC *dbcp;
438   int status;
439
440   xzero (keydatum);
441   xzero (valdatum);
442
443   status = feature->db->cursor (feature->db, NULL, &dbcp, 0);
444   for (status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_FIRST);
445        status == 0;
446        status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_NEXT))
447     {
448       unsigned char *key_str = (unsigned char *)keydatum.data;
449       int key_len = strnlen (key_str, keydatum.size);
450       CHISE_Char_ID key = chise_char_id_parse_c_string (key_str, key_len);
451       int ret;
452
453       if (ret = func (key, feature, &valdatum))
454         break;
455     }
456   dbcp->c_close (dbcp);
457 }
458
459
460 struct CHISE_CCS_Table
461 {
462   CHISE_DS *ds;
463   CHISE_Attribute_Table *db;
464 };
465
466 CHISE_CCS_Table*
467 chise_ds_open_ccs_table (CHISE_DS *ds, const char *ccs,
468                          DBTYPE real_subtype,
469                          u_int32_t accessmask, int modemask)
470 {
471   CHISE_CCS_Table* table;
472
473   if (ds == NULL)
474     return NULL;
475
476   table = (CHISE_CCS_Table*)malloc (sizeof (CHISE_CCS_Table));
477   if (table == NULL)
478     return NULL;
479
480   table->ds = ds;
481   table->db = chise_open_attribute_table (ds->location,
482                                           ccs, "system-char-id",
483                                           real_subtype,
484                                           accessmask, modemask);
485   if (table->db == NULL)
486     {
487       free (table);
488       return NULL;
489     }
490   return table;
491 }
492
493 int
494 chise_ccst_close (CHISE_CCS_Table *table)
495 {
496   int status;
497
498   if (table == NULL)
499     return -1;
500
501   if (table->db == NULL)
502     status = -1;
503   else
504     status = chise_close_attribute_table (table->db);
505   free (table);
506   return status;
507 }
508
509 CHISE_Char_ID
510 chise_ccs_decode (CHISE_CCS ccs, int code_point)
511 {
512   CHISE_Value valdatum;
513   int status = 0;
514   char key_buf[16];
515
516   sprintf(key_buf, "%d", code_point);
517   status = chise_get_attribute_table (ccs->db, key_buf, &valdatum);
518   if (!status)
519     {
520       unsigned char *str
521         = (unsigned char *)chise_value_data (&valdatum);
522       int len = strnlen (str, chise_value_size (&valdatum));
523
524       return chise_char_id_parse_c_string (str, len);
525     }
526   return -1;
527 }
528
529 int
530 chise_ccst_put_char (CHISE_CCS_Table *table,
531                      int code_point, CHISE_Char_ID cid)
532 {
533   CHISE_Value valdatum;
534   char key_buf[16], val_buf[8];
535
536   sprintf(key_buf, "%d", code_point);
537   chise_format_char_id (cid, val_buf, 8);
538   return chise_put_attribute_table (table->db, key_buf, val_buf);
539 }
540
541
542 CHISE_Attribute_Table*
543 chise_open_attribute_table (const unsigned char *db_dir,
544                             const char *encoding, const char *feature,
545                             DBTYPE real_subtype,
546                             u_int32_t accessmask, int modemask)
547 {
548   DB* dbase;
549   int status;
550   int len, flen, i;
551   int size;
552   char *db_file_name, *sp;
553
554   status = db_create (&dbase, NULL, 0);
555   if (status)
556     return NULL;
557
558   len = strlen (db_dir);
559   flen = strlen (feature);
560   size = len + strlen (encoding) + flen * 3 + 4;
561   db_file_name = alloca (size);
562   strcpy (db_file_name, db_dir);
563   if (db_file_name[len - 1] != '/')
564     {
565       db_file_name[len++] = '/';
566       db_file_name[len] = '\0';
567     }
568   strcat (db_file_name, encoding);
569   strcat (db_file_name, "/");
570   /* strcat (db_file_name, feature); */
571   sp = &db_file_name[strlen (db_file_name)];
572   for (i = 0; i < flen; i++)
573     {
574       int c = feature[i];
575
576       if ( (c == '/') || (c == '%') )
577         {
578           sprintf (sp, "%%%02X", c);
579           sp += 3;
580         }
581       else
582         *sp++ = c;
583     }
584   *sp = '\0';
585   status = dbase->open (dbase, db_file_name, NULL,
586                         real_subtype, accessmask, modemask);
587   if (status)
588     {
589       dbase->close (dbase, 0);
590       return NULL;
591     }
592   return dbase;
593 }
594
595 int
596 chise_close_attribute_table (CHISE_Attribute_Table *db)
597 {
598   if (db)
599     {
600       db->sync  (db, 0);
601       db->close (db, 0);
602     }
603   return 0;
604 }
605
606 int
607 chise_get_attribute_table (CHISE_Attribute_Table *db,
608                            char *key, CHISE_Value *valdatum)
609 {
610   DBT keydatum;
611   int status = 0;
612
613   /* DB Version 2 requires DBT's to be zeroed before use. */
614   xzero (keydatum);
615   xzero (*valdatum);
616
617   keydatum.data = key;
618   keydatum.size = strlen (key);
619
620   status = db->get (db, NULL, &keydatum, valdatum, 0);
621   return status;
622 }
623
624 int
625 chise_put_attribute_table (CHISE_Attribute_Table *db,
626                            char *key, unsigned char *value)
627 {
628   DBT keydatum, valdatum;
629   int status = 0;
630
631   /* DB Version 2 requires DBT's to be zeroed before use. */
632   xzero (keydatum);
633   xzero (valdatum);
634
635   keydatum.data = key;
636   keydatum.size = strlen (key);
637
638   valdatum.data = value;
639   valdatum.size = strlen (value);
640
641   status = db->put (db, NULL, &keydatum, &valdatum, 0);
642   return status;
643 }