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