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