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