fixed memory violation in ICU chain code, restructuring tokenizer to allow for non...
[yaz-moved-to-github.git] / src / icu_I18N.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: icu_I18N.c,v 1.3 2007-10-24 07:41:48 marc Exp $
6  */
7
8 #if HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #define USE_TIMING 0
13 #if USE_TIMING
14 #include <yaz/timing.h>
15 #endif
16
17
18 #if HAVE_ICU
19 #include <yaz/icu_I18N.h>
20
21 #include <yaz/log.h>
22
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26
27 #include <unicode/ustring.h>  /* some more string fcns*/
28 #include <unicode/uchar.h>    /* char names           */
29
30
31 #include <unicode/ucol.h> 
32
33
34 int icu_check_status (UErrorCode status)
35 {
36     if(U_FAILURE(status)){
37         yaz_log(YLOG_WARN, 
38                 "ICU: %d %s\n", status, u_errorName(status));
39         return 0;   
40     }
41     return 1;
42     
43 }
44
45
46
47 struct icu_buf_utf16 * icu_buf_utf16_create(size_t capacity)
48 {
49     struct icu_buf_utf16 * buf16 
50         = (struct icu_buf_utf16 *) malloc(sizeof(struct icu_buf_utf16));
51
52     buf16->utf16 = 0;
53     buf16->utf16_len = 0;
54     buf16->utf16_cap = 0;
55
56     if (capacity > 0){
57         buf16->utf16 = (UChar *) malloc(sizeof(UChar) * capacity);
58         buf16->utf16[0] = (UChar) 0;
59         buf16->utf16_cap = capacity;
60     }
61     return buf16;
62 }
63
64 struct icu_buf_utf16 * icu_buf_utf16_clear(struct icu_buf_utf16 * buf16)
65 {
66     if (buf16){
67         if (buf16->utf16)
68             buf16->utf16[0] = (UChar) 0;
69         buf16->utf16_len = 0;
70     }
71     return buf16;
72 }
73
74 struct icu_buf_utf16 * icu_buf_utf16_resize(struct icu_buf_utf16 * buf16,
75                                             size_t capacity)
76 {
77     if (!buf16)
78         return 0;
79     
80     if (capacity >  0){
81         if (0 == buf16->utf16)
82             buf16->utf16 = (UChar *) malloc(sizeof(UChar) * capacity);
83         else
84             buf16->utf16 
85                 = (UChar *) realloc(buf16->utf16, sizeof(UChar) * capacity);
86
87         icu_buf_utf16_clear(buf16);
88         buf16->utf16_cap = capacity;
89     } 
90     else { 
91         if (buf16->utf16)
92             free(buf16->utf16);
93         buf16->utf16 = 0;
94         buf16->utf16_len = 0;
95         buf16->utf16_cap = 0;
96     }
97
98     return buf16;
99 }
100
101
102 struct icu_buf_utf16 * icu_buf_utf16_copy(struct icu_buf_utf16 * dest16,
103                                           struct icu_buf_utf16 * src16)
104 {
105     if(!dest16 || !src16
106        || dest16 == src16)
107         return 0;
108
109     if (dest16->utf16_cap < src16->utf16_len)
110         icu_buf_utf16_resize(dest16, src16->utf16_len * 2);
111
112     u_strncpy(dest16->utf16, src16->utf16, src16->utf16_len);
113     dest16->utf16_len = src16->utf16_len;
114
115     return dest16;
116 }
117
118
119 void icu_buf_utf16_destroy(struct icu_buf_utf16 * buf16)
120 {
121     if (buf16){
122         if (buf16->utf16)
123             free(buf16->utf16);
124         free(buf16);
125     }
126 }
127
128
129
130
131
132
133 struct icu_buf_utf8 * icu_buf_utf8_create(size_t capacity)
134 {
135     struct icu_buf_utf8 * buf8 
136         = (struct icu_buf_utf8 *) malloc(sizeof(struct icu_buf_utf8));
137
138     buf8->utf8 = 0;
139     buf8->utf8_len = 0;
140     buf8->utf8_cap = 0;
141
142     if (capacity > 0){
143         buf8->utf8 = (uint8_t *) malloc(sizeof(uint8_t) * capacity);
144         buf8->utf8[0] = (uint8_t) 0;
145         buf8->utf8_cap = capacity;
146     }
147     return buf8;
148 }
149
150
151 struct icu_buf_utf8 * icu_buf_utf8_clear(struct icu_buf_utf8 * buf8)
152 {
153     if (buf8){
154         if (buf8->utf8)
155             buf8->utf8[0] = (uint8_t) 0;
156         buf8->utf8_len = 0;
157     }
158     return buf8;
159 }
160
161
162 struct icu_buf_utf8 * icu_buf_utf8_resize(struct icu_buf_utf8 * buf8,
163                                           size_t capacity)
164 {
165     if (!buf8)
166         return 0;
167
168     if (capacity >  0){
169         if (0 == buf8->utf8)
170             buf8->utf8 = (uint8_t *) malloc(sizeof(uint8_t) * capacity);
171         else
172             buf8->utf8 
173                 = (uint8_t *) realloc(buf8->utf8, sizeof(uint8_t) * capacity);
174
175         icu_buf_utf8_clear(buf8);
176         buf8->utf8_cap = capacity;
177     } 
178     else { 
179         if (buf8->utf8)
180             free(buf8->utf8);
181         buf8->utf8 = 0;
182         buf8->utf8_len = 0;
183         buf8->utf8_cap = 0;
184     }
185     
186     return buf8;
187 }
188
189
190 struct icu_buf_utf8 * icu_buf_utf8_copy(struct icu_buf_utf8 * dest8,
191                                           struct icu_buf_utf8 * src8)
192 {
193     if(!dest8 || !src8
194        || dest8 == src8)
195         return 0;
196     
197
198     if (dest8->utf8_cap < src8->utf8_len)
199         icu_buf_utf8_resize(dest8, src8->utf8_len * 2);
200
201     strncpy((char*) dest8->utf8, (char*) src8->utf8, src8->utf8_len);
202
203     return dest8;
204 }
205
206
207 const char *icu_buf_utf8_to_cstr(struct icu_buf_utf8 *src8)
208 {
209     if (!src8 || src8->utf8_len == 0)
210         return "";
211
212     if (src8->utf8_len == src8->utf8_cap)
213         src8 = icu_buf_utf8_resize(src8, src8->utf8_len * 2 + 1);
214
215     src8->utf8[src8->utf8_len] = '\0';
216
217     return (const char *) src8->utf8;
218 }
219
220
221 void icu_buf_utf8_destroy(struct icu_buf_utf8 * buf8)
222 {
223     if (buf8){
224         if (buf8->utf8)
225             free(buf8->utf8);
226         free(buf8);
227     }
228 }
229
230
231
232 UErrorCode icu_utf16_from_utf8(struct icu_buf_utf16 * dest16,
233                                struct icu_buf_utf8 * src8,
234                                UErrorCode * status)
235 {
236     int32_t utf16_len = 0;
237   
238     u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
239                   &utf16_len,
240                   (const char *) src8->utf8, src8->utf8_len, status);
241   
242     /* check for buffer overflow, resize and retry */
243     if (*status == U_BUFFER_OVERFLOW_ERROR)
244     {
245         icu_buf_utf16_resize(dest16, utf16_len * 2);
246         *status = U_ZERO_ERROR;
247         u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
248                       &utf16_len,
249                       (const char *) src8->utf8, src8->utf8_len, status);
250     }
251
252     if (U_SUCCESS(*status)  
253         && utf16_len <= dest16->utf16_cap)
254         dest16->utf16_len = utf16_len;
255     else 
256         icu_buf_utf16_clear(dest16);
257   
258     return *status;
259 }
260
261  
262
263 UErrorCode icu_utf16_from_utf8_cstr(struct icu_buf_utf16 * dest16,
264                                     const char * src8cstr,
265                                     UErrorCode * status)
266 {
267     size_t src8cstr_len = 0;
268     int32_t utf16_len = 0;
269
270     src8cstr_len = strlen(src8cstr);
271   
272     u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
273                   &utf16_len,
274                   src8cstr, src8cstr_len, status);
275   
276     /* check for buffer overflow, resize and retry */
277     if (*status == U_BUFFER_OVERFLOW_ERROR)
278     {
279         icu_buf_utf16_resize(dest16, utf16_len * 2);
280         *status = U_ZERO_ERROR;
281         u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
282                       &utf16_len,
283                       src8cstr, src8cstr_len, status);
284     }
285
286     if (U_SUCCESS(*status)  
287         && utf16_len <= dest16->utf16_cap)
288         dest16->utf16_len = utf16_len;
289     else 
290         icu_buf_utf16_clear(dest16);
291   
292     return *status;
293 }
294
295
296
297
298 UErrorCode icu_utf16_to_utf8(struct icu_buf_utf8 * dest8,
299                              struct icu_buf_utf16 * src16,
300                              UErrorCode * status)
301 {
302     int32_t utf8_len = 0;
303   
304     u_strToUTF8((char *) dest8->utf8, dest8->utf8_cap,
305                 &utf8_len,
306                 src16->utf16, src16->utf16_len, status);
307   
308     /* check for buffer overflow, resize and retry */
309     if (*status == U_BUFFER_OVERFLOW_ERROR)
310     {
311         icu_buf_utf8_resize(dest8, utf8_len * 2);
312         *status = U_ZERO_ERROR;
313         u_strToUTF8((char *) dest8->utf8, dest8->utf8_cap,
314                     &utf8_len,
315                     src16->utf16, src16->utf16_len, status);
316
317     }
318
319     if (U_SUCCESS(*status)  
320         && utf8_len <= dest8->utf8_cap)
321         dest8->utf8_len = utf8_len;
322     else 
323         icu_buf_utf8_clear(dest8);
324   
325     return *status;
326 }
327
328
329
330 struct icu_casemap * icu_casemap_create(const char *locale, char action,
331                                         UErrorCode *status)
332 {    
333     struct icu_casemap * casemap
334         = (struct icu_casemap *) malloc(sizeof(struct icu_casemap));
335     strcpy(casemap->locale, locale);
336     casemap->action = action;
337
338     switch(casemap->action) {    
339     case 'l':   
340         break;
341     case 'u':   
342         break;
343     case 't':  
344         break;
345     case 'f':  
346         break;
347     default:
348         icu_casemap_destroy(casemap);
349         return 0;
350     }
351
352     return casemap;
353 }
354
355 void icu_casemap_destroy(struct icu_casemap * casemap)
356 {
357     if (casemap) 
358         free(casemap);
359 }
360
361
362 int icu_casemap_casemap(struct icu_casemap * casemap,
363                         struct icu_buf_utf16 * dest16,
364                         struct icu_buf_utf16 * src16,
365                         UErrorCode *status)
366 {
367     if(!casemap)
368         return 0;
369     
370     return icu_utf16_casemap(dest16, src16,
371                              casemap->locale, casemap->action, status);
372 }
373
374
375 int icu_utf16_casemap(struct icu_buf_utf16 * dest16,
376                       struct icu_buf_utf16 * src16,
377                       const char *locale, char action,
378                       UErrorCode *status)
379 {
380     int32_t dest16_len = 0;
381
382
383     if (!src16->utf16_len){           //guarding for empty source string
384         if (dest16->utf16)
385             dest16->utf16[0] = (UChar) 0;
386         dest16->utf16_len = 0;
387         return U_ZERO_ERROR;
388     }
389
390     
391     switch(action) {    
392     case 'l':    
393     case 'L':    
394         dest16_len = u_strToLower(dest16->utf16, dest16->utf16_cap,
395                                   src16->utf16, src16->utf16_len, 
396                                   locale, status);
397         break;
398     case 'u':    
399     case 'U':    
400         dest16_len = u_strToUpper(dest16->utf16, dest16->utf16_cap,
401                                   src16->utf16, src16->utf16_len, 
402                                   locale, status);
403         break;
404     case 't':    
405     case 'T':    
406         dest16_len = u_strToTitle(dest16->utf16, dest16->utf16_cap,
407                                   src16->utf16, src16->utf16_len,
408                                   0, locale, status);
409         break;
410     case 'f':    
411     case 'F':    
412         dest16_len = u_strFoldCase(dest16->utf16, dest16->utf16_cap,
413                                    src16->utf16, src16->utf16_len,
414                                    U_FOLD_CASE_DEFAULT, status);
415         break;
416         
417     default:
418         return U_UNSUPPORTED_ERROR;
419         break;
420     }
421
422     /* check for buffer overflow, resize and retry */
423     if (*status == U_BUFFER_OVERFLOW_ERROR
424         && dest16 != src16        /* do not resize if in-place conversion */
425         ){
426         icu_buf_utf16_resize(dest16, dest16_len * 2);
427         *status = U_ZERO_ERROR;
428
429     
430         switch(action) {    
431         case 'l':    
432         case 'L':    
433             dest16_len = u_strToLower(dest16->utf16, dest16->utf16_cap,
434                                       src16->utf16, src16->utf16_len, 
435                                       locale, status);
436             break;
437         case 'u':    
438         case 'U':    
439             dest16_len = u_strToUpper(dest16->utf16, dest16->utf16_cap,
440                                       src16->utf16, src16->utf16_len, 
441                                       locale, status);
442             break;
443         case 't':    
444         case 'T':    
445             dest16_len = u_strToTitle(dest16->utf16, dest16->utf16_cap,
446                                       src16->utf16, src16->utf16_len,
447                                       0, locale, status);
448             break;
449         case 'f':    
450         case 'F':    
451             dest16_len = u_strFoldCase(dest16->utf16, dest16->utf16_cap,
452                                        src16->utf16, src16->utf16_len,
453                                        U_FOLD_CASE_DEFAULT, status);
454             break;
455         
456         default:
457             return U_UNSUPPORTED_ERROR;
458             break;
459         }
460     }
461     
462     if (U_SUCCESS(*status)
463         && dest16_len <= dest16->utf16_cap)
464         dest16->utf16_len = dest16_len;
465     else {
466         if (dest16->utf16)
467             dest16->utf16[0] = (UChar) 0;
468         dest16->utf16_len = 0;
469     }
470   
471     return *status;
472 }
473
474
475
476 UErrorCode icu_sortkey8_from_utf16(UCollator *coll,
477                                    struct icu_buf_utf8 * dest8, 
478                                    struct icu_buf_utf16 * src16,
479                                    UErrorCode * status)
480
481   
482     int32_t sortkey_len = 0;
483
484     sortkey_len = ucol_getSortKey(coll, src16->utf16, src16->utf16_len,
485                                   dest8->utf8, dest8->utf8_cap);
486
487     /* check for buffer overflow, resize and retry */
488     if (sortkey_len > dest8->utf8_cap) {
489         icu_buf_utf8_resize(dest8, sortkey_len * 2);
490         sortkey_len = ucol_getSortKey(coll, src16->utf16, src16->utf16_len,
491                                       dest8->utf8, dest8->utf8_cap);
492     }
493
494     if (U_SUCCESS(*status)
495         && sortkey_len > 0)
496         dest8->utf8_len = sortkey_len;
497     else 
498         icu_buf_utf8_clear(dest8);
499     
500     return sortkey_len;
501 }
502
503
504
505 struct icu_tokenizer * icu_tokenizer_create(const char *locale, char action,
506                                             UErrorCode *status)
507 {
508     struct icu_tokenizer * tokenizer
509         = (struct icu_tokenizer *) malloc(sizeof(struct icu_tokenizer));
510
511     strcpy(tokenizer->locale, locale);
512     tokenizer->action = action;
513     tokenizer->bi = 0;
514     tokenizer->buf16 = 0;
515     tokenizer->token_count = 0;
516     tokenizer->token_id = 0;
517     tokenizer->token_start = 0;
518     tokenizer->token_end = 0;
519
520
521     switch(tokenizer->action) {    
522     case 'l':
523     case 'L':
524         tokenizer->bi
525             = ubrk_open(UBRK_LINE, tokenizer->locale,
526                         0, 0, status);
527         break;
528     case 's':
529     case 'S':
530         tokenizer->bi
531             = ubrk_open(UBRK_SENTENCE, tokenizer->locale,
532                         0, 0, status);
533         break;
534     case 'w':
535     case 'W':
536         tokenizer->bi 
537             = ubrk_open(UBRK_WORD, tokenizer->locale,
538                         0, 0, status);
539         break;
540     case 'c':
541     case 'C':
542         tokenizer->bi 
543             = ubrk_open(UBRK_CHARACTER, tokenizer->locale,
544                         0, 0, status);
545         break;
546     case 't':
547     case 'T':
548         tokenizer->bi 
549             = ubrk_open(UBRK_TITLE, tokenizer->locale,
550                         0, 0, status);
551         break;
552     default:
553         *status = U_UNSUPPORTED_ERROR;
554         return 0;
555         break;
556     }
557     
558     /* ICU error stuff is a very  funny business */
559     if (U_SUCCESS(*status))
560         return tokenizer;
561
562     /* freeing if failed */
563     icu_tokenizer_destroy(tokenizer);
564     return 0;
565 }
566
567 void icu_tokenizer_destroy(struct icu_tokenizer * tokenizer)
568 {
569     if (tokenizer) {
570         if (tokenizer->bi)
571             ubrk_close(tokenizer->bi);
572         free(tokenizer);
573     }
574 }
575
576 int icu_tokenizer_attach(struct icu_tokenizer * tokenizer, 
577                          struct icu_buf_utf16 * src16, 
578                          UErrorCode *status)
579 {
580     if (!tokenizer || !tokenizer->bi || !src16)
581         return 0;
582
583
584     tokenizer->buf16 = src16;
585     tokenizer->token_count = 0;
586     tokenizer->token_id = 0;
587     tokenizer->token_start = 0;
588     tokenizer->token_end = 0;
589
590     ubrk_setText(tokenizer->bi, src16->utf16, src16->utf16_len, status);
591     
592  
593     if (U_FAILURE(*status))
594         return 0;
595
596     return 1;
597 };
598
599 int32_t icu_tokenizer_next_token(struct icu_tokenizer * tokenizer, 
600                          struct icu_buf_utf16 * tkn16, 
601                          UErrorCode *status)
602 {
603     int32_t tkn_start = 0;
604     int32_t tkn_end = 0;
605     int32_t tkn_len = 0;
606     
607
608     if (!tokenizer || !tokenizer->bi
609         || !tokenizer->buf16 || !tokenizer->buf16->utf16_len)
610         return 0;
611
612     /*
613     never change tokenizer->buf16 and keep always invariant
614     0 <= tokenizer->token_start 
615        <= tokenizer->token_end 
616        <= tokenizer->buf16->utf16_len
617     returns length of token
618     */
619
620     if (0 == tokenizer->token_end) /* first call */
621         tkn_start = ubrk_first(tokenizer->bi);
622     else /* successive calls */
623         tkn_start = tokenizer->token_end;
624
625     /* get next position */
626     tkn_end = ubrk_next(tokenizer->bi);
627
628     /* repairing invariant at end of ubrk, which is UBRK_DONE = -1 */
629     if (UBRK_DONE == tkn_end)
630         tkn_end = tokenizer->buf16->utf16_len;
631
632     /* copy out if everything is well */
633     if(U_FAILURE(*status))
634         return 0;        
635     
636     /* everything OK, now update internal state */
637     tkn_len = tkn_end - tkn_start;
638
639     if (0 < tkn_len){
640         tokenizer->token_count++;
641         tokenizer->token_id++;
642     } else {
643         tokenizer->token_id = 0;    
644     }
645     tokenizer->token_start = tkn_start;
646     tokenizer->token_end = tkn_end;
647     
648
649     /* copying into token buffer if it exists */
650     if (tkn16){
651         if (tkn16->utf16_cap < tkn_len)
652             icu_buf_utf16_resize(tkn16, (size_t) tkn_len * 2);
653
654         u_strncpy(tkn16->utf16, &(tokenizer->buf16->utf16)[tkn_start], 
655                   tkn_len);
656
657         tkn16->utf16_len = tkn_len;
658     }
659
660     return tkn_len;
661 }
662
663
664 int32_t icu_tokenizer_token_id(struct icu_tokenizer * tokenizer)
665 {
666     return tokenizer->token_id;
667 }
668
669 int32_t icu_tokenizer_token_start(struct icu_tokenizer * tokenizer)
670 {
671     return tokenizer->token_start;
672 }
673
674 int32_t icu_tokenizer_token_end(struct icu_tokenizer * tokenizer)
675 {
676     return tokenizer->token_end;
677 }
678
679 int32_t icu_tokenizer_token_length(struct icu_tokenizer * tokenizer)
680 {
681     return (tokenizer->token_end - tokenizer->token_start);
682 }
683
684 int32_t icu_tokenizer_token_count(struct icu_tokenizer * tokenizer)
685 {
686     return tokenizer->token_count;
687 }
688
689
690
691 struct icu_normalizer * icu_normalizer_create(const char *rules, char action,
692                                               UErrorCode *status)
693 {
694
695     struct icu_normalizer * normalizer
696         = (struct icu_normalizer *) malloc(sizeof(struct icu_normalizer));
697
698     normalizer->action = action;
699     normalizer->trans = 0;
700     normalizer->rules16 =  icu_buf_utf16_create(0);
701     icu_utf16_from_utf8_cstr(normalizer->rules16, rules, status);
702      
703     switch(normalizer->action) {    
704     case 'f':
705     case 'F':
706         normalizer->trans
707             = utrans_openU(normalizer->rules16->utf16, 
708                            normalizer->rules16->utf16_len,
709                            UTRANS_FORWARD,
710                            0, 0, 
711                            normalizer->parse_error, status);
712         break;
713     case 'r':
714     case 'R':
715         normalizer->trans
716             = utrans_openU(normalizer->rules16->utf16,
717                            normalizer->rules16->utf16_len,
718                            UTRANS_REVERSE ,
719                            0, 0,
720                            normalizer->parse_error, status);
721         break;
722     default:
723         *status = U_UNSUPPORTED_ERROR;
724         return 0;
725         break;
726     }
727     
728     if (U_SUCCESS(*status))
729         return normalizer;
730
731     /* freeing if failed */
732     icu_normalizer_destroy(normalizer);
733     return 0;
734 }
735
736
737 void icu_normalizer_destroy(struct icu_normalizer * normalizer){
738     if (normalizer) {
739         if (normalizer->rules16) 
740             icu_buf_utf16_destroy(normalizer->rules16);
741         if (normalizer->trans)
742             utrans_close(normalizer->trans);
743         free(normalizer);
744     }
745 }
746
747
748
749 int icu_normalizer_normalize(struct icu_normalizer * normalizer,
750                              struct icu_buf_utf16 * dest16,
751                              struct icu_buf_utf16 * src16,
752                              UErrorCode *status)
753 {
754     if (!normalizer || !normalizer->trans 
755         || !src16
756         || !dest16)
757         return 0;
758
759     if (!src16->utf16_len){           //guarding for empty source string
760         icu_buf_utf16_clear(dest16);
761         return 0;
762     }
763
764     if (!icu_buf_utf16_copy(dest16, src16))
765         return 0;
766
767    
768     utrans_transUChars (normalizer->trans, 
769                         dest16->utf16, &(dest16->utf16_len),
770                         dest16->utf16_cap,
771                         0, &(src16->utf16_len), status);
772
773     if (U_FAILURE(*status))
774         icu_buf_utf16_clear(dest16);
775     
776     return dest16->utf16_len;
777 }
778
779
780
781
782 struct icu_chain_step * icu_chain_step_create(struct icu_chain * chain,
783                                               enum icu_chain_step_type type,
784                                               const uint8_t * rule,
785                                               struct icu_buf_utf16 * buf16,
786                                               UErrorCode *status)
787 {
788     struct icu_chain_step * step = 0;
789     
790     if(!chain || !type || !rule)
791         return 0;
792
793     step = (struct icu_chain_step *) malloc(sizeof(struct icu_chain_step));
794
795     step->type = type;
796
797     step->buf16 = buf16;
798
799     /* create auxilary objects */
800     switch(step->type) {
801     case ICU_chain_step_type_display:
802         break;
803     case ICU_chain_step_type_index:
804         break;
805     case ICU_chain_step_type_sortkey:
806         break;
807     case ICU_chain_step_type_casemap:
808         step->u.casemap = icu_casemap_create((char *) chain->locale, 
809                                              (char) rule[0], status);
810         break;
811     case ICU_chain_step_type_normalize:
812         step->u.normalizer = icu_normalizer_create((char *) rule, 'f', status);
813         break;
814     case ICU_chain_step_type_tokenize:
815         step->u.tokenizer = icu_tokenizer_create((char *) chain->locale, 
816                                                  (char) rule[0], status);
817         break;
818     default:
819         break;
820     }
821
822     return step;
823 }
824
825
826 void icu_chain_step_destroy(struct icu_chain_step * step){
827     
828     if (!step)
829         return;
830
831     icu_chain_step_destroy(step->previous);
832
833     switch(step->type) {
834     case ICU_chain_step_type_display:
835         break;
836     case ICU_chain_step_type_index:
837         break;
838     case ICU_chain_step_type_sortkey:
839         break;
840     case ICU_chain_step_type_casemap:
841         icu_casemap_destroy(step->u.casemap);
842         icu_buf_utf16_destroy(step->buf16);
843         break;
844     case ICU_chain_step_type_normalize:
845         icu_normalizer_destroy(step->u.normalizer);
846         icu_buf_utf16_destroy(step->buf16);
847         break;
848     case ICU_chain_step_type_tokenize:
849         icu_tokenizer_destroy(step->u.tokenizer);
850         icu_buf_utf16_destroy(step->buf16);
851         break;
852     default:
853         break;
854     }
855     free(step);
856 }
857
858
859
860 struct icu_chain * icu_chain_create(const uint8_t * identifier,
861                                     const uint8_t * locale)
862 {
863
864     struct icu_chain * chain 
865         = (struct icu_chain *) malloc(sizeof(struct icu_chain));
866
867     strncpy((char *) chain->identifier, (const char *) identifier, 128);
868     chain->identifier[128 - 1] = '\0';
869     strncpy((char *) chain->locale, (const char *) locale, 16);    
870     chain->locale[16 - 1] = '\0';
871
872     chain->token_count = 0;
873
874     chain->display8 = icu_buf_utf8_create(0);
875     chain->norm8 = icu_buf_utf8_create(0);
876     chain->sort8 = icu_buf_utf8_create(0);
877
878     chain->src16 = icu_buf_utf16_create(0);
879
880     chain->steps = 0;
881
882     return chain;
883 }
884
885
886 void icu_chain_destroy(struct icu_chain * chain)
887 {
888     if (chain){
889         icu_buf_utf8_destroy(chain->display8);
890         icu_buf_utf8_destroy(chain->norm8);
891         icu_buf_utf8_destroy(chain->sort8);
892         
893         icu_buf_utf16_destroy(chain->src16);
894     
895         icu_chain_step_destroy(chain->steps);
896         free(chain);
897     }
898 }
899
900
901
902 struct icu_chain * icu_chain_xml_config(xmlNode *xml_node, 
903                                         UErrorCode * status){
904
905     xmlNode *node = 0;
906     struct icu_chain * chain = 0;
907    
908     if (!xml_node 
909         ||xml_node->type != XML_ELEMENT_NODE 
910         || strcmp((const char *) xml_node->name, "icu_chain"))
911
912         return 0;
913
914     {    
915         xmlChar *xml_id = xmlGetProp(xml_node, (xmlChar *) "id");
916         xmlChar *xml_locale = xmlGetProp(xml_node, (xmlChar *) "locale");
917         
918         if (!xml_id || !strlen((const char *) xml_id) 
919             || !xml_locale || !strlen((const char *) xml_locale))
920             return 0;
921         
922         chain = icu_chain_create((const uint8_t *) xml_id, 
923                                  (const uint8_t *) xml_locale);
924         
925         xmlFree(xml_id);
926         xmlFree(xml_locale);
927     }
928     if (!chain)
929         return 0;
930         
931     for (node = xml_node->children; node; node = node->next)
932     {
933         xmlChar *xml_rule;
934         struct icu_chain_step * step = 0;
935
936         if (node->type != XML_ELEMENT_NODE)
937             continue;
938
939         xml_rule = xmlGetProp(node, (xmlChar *) "rule");
940
941         if (!strcmp((const char *) node->name, 
942                     (const char *) "casemap")){
943             step = icu_chain_insert_step(chain, ICU_chain_step_type_casemap, 
944                                          (const uint8_t *) xml_rule, status);
945         }
946         else if (!strcmp((const char *) node->name,
947                          (const char *) "normalize")){
948             step = icu_chain_insert_step(chain, ICU_chain_step_type_normalize, 
949                                          (const uint8_t *) xml_rule, status);
950         }
951         else if (!strcmp((const char *) node->name,
952                          (const char *) "tokenize")){
953             step = icu_chain_insert_step(chain, ICU_chain_step_type_tokenize, 
954                                          (const uint8_t *) xml_rule, status);
955         }
956         else if (!strcmp((const char *) node->name,
957                          (const char *) "display")){
958             step = icu_chain_insert_step(chain, ICU_chain_step_type_display, 
959                                          (const uint8_t *) "", status);
960         }
961         else if (!strcmp((const char *) node->name,
962                          (const char *) "index")){
963             step = icu_chain_insert_step(chain, ICU_chain_step_type_index, 
964                                          (const uint8_t *) "", status);
965         }
966         else if (!strcmp((const char *) node->name,
967                          (const char *) "sortkey")){
968             step = icu_chain_insert_step(chain, ICU_chain_step_type_sortkey, 
969                                          (const uint8_t *) "", status);
970         }
971
972         xmlFree(xml_rule);
973         if (!step || U_FAILURE(*status)){
974             icu_chain_destroy(chain);
975             return 0;
976         }
977         
978
979     }
980
981     return chain;
982 }
983
984
985
986 struct icu_chain_step * icu_chain_insert_step(struct icu_chain * chain,
987                                               enum icu_chain_step_type type,
988                                               const uint8_t * rule,
989                                               UErrorCode *status)
990 {    
991     struct icu_chain_step * step = 0;
992     struct icu_buf_utf16 * src16 = 0;
993     struct icu_buf_utf16 * buf16 = 0;
994
995     if (!chain || !type || !rule)
996         return 0;
997
998     /* assign utf16 src buffers as needed */
999     if (chain->steps && chain->steps->buf16)
1000         src16 = chain->steps->buf16;
1001     else if (chain->src16)
1002         src16 = chain->src16;
1003     else
1004         return 0;
1005
1006     
1007     /* create utf16 destination buffers as needed, or */
1008     switch(type) {
1009     case ICU_chain_step_type_display:
1010         buf16 = src16;
1011         break;
1012     case ICU_chain_step_type_index:
1013         buf16 = src16;
1014         break;
1015     case ICU_chain_step_type_sortkey:
1016         buf16 = src16;
1017         break;
1018     case ICU_chain_step_type_casemap:
1019         buf16 = icu_buf_utf16_create(0);
1020         break;
1021     case ICU_chain_step_type_normalize:
1022         buf16 = icu_buf_utf16_create(0);
1023         break;
1024     case ICU_chain_step_type_tokenize:
1025         buf16 = icu_buf_utf16_create(0);
1026         break;
1027     default:
1028         break;
1029     }
1030
1031     /* create actual chain step with this buffer */
1032     step = icu_chain_step_create(chain, type, rule, buf16, status);
1033
1034     step->previous = chain->steps;
1035     chain->steps = step;
1036
1037     return step;
1038 }
1039
1040
1041 int icu_chain_step_next_token(struct icu_chain * chain,
1042                               struct icu_chain_step * step,
1043                               UErrorCode *status)
1044 {
1045     struct icu_buf_utf16 * src16 = 0;
1046     
1047     if (!chain || !chain->src16 || !step || !step->more_tokens)
1048         return 0;
1049
1050     /* assign utf16 src buffers as neeed, advance in previous steps
1051        tokens until non-zero token met, and setting stop condition
1052     */
1053     if (step->previous){
1054         src16 = step->previous->buf16;
1055         if (step->need_new_token)
1056             step->more_tokens 
1057                 = icu_chain_step_next_token(chain, step->previous, status);
1058     }
1059     else { /* first step can only work once on chain->src16 input buffer */
1060         src16 = chain->src16;
1061         step->more_tokens = 1;
1062     }
1063
1064     /* stop if nothing to process 
1065        i.e new token source was not properly assigned
1066        or I did not get any tokens from previous step
1067    */
1068     if (!step->more_tokens || !src16)
1069         return 0;
1070
1071     /* perform the work, eventually put this steps output in 
1072        step->buf16 or the chains UTF8 output buffers  */
1073     switch(step->type) {
1074     case ICU_chain_step_type_display:
1075         icu_utf16_to_utf8(chain->display8, src16, status);
1076         break;
1077     case ICU_chain_step_type_index:
1078         icu_utf16_to_utf8(chain->norm8, src16, status);
1079         break;
1080     case ICU_chain_step_type_sortkey:
1081         icu_utf16_to_utf8(chain->sort8, src16, status);
1082         break;
1083     case ICU_chain_step_type_casemap:
1084         icu_casemap_casemap(step->u.casemap,
1085                             step->buf16, src16, status);
1086         break;
1087     case ICU_chain_step_type_normalize:
1088         icu_normalizer_normalize(step->u.normalizer,
1089                                  step->buf16, src16, status);
1090         break;
1091     case ICU_chain_step_type_tokenize:
1092         /* attach to new src16 token only first time during splitting */
1093         if (step->need_new_token){
1094             icu_tokenizer_attach(step->u.tokenizer, src16, status);
1095             step->need_new_token = 0;
1096         }
1097         /* splitting one src16 token into multiple buf16 tokens */
1098         step->more_tokens
1099             = icu_tokenizer_next_token(step->u.tokenizer,
1100                                        step->buf16, status);
1101         /* make sure to get new previous token if this one had been used up */
1102         if (step->previous && !step->more_tokens){
1103             if (icu_chain_step_next_token(chain, step->previous, status)){
1104                 icu_tokenizer_attach(step->u.tokenizer, src16, status);
1105                 step->need_new_token = 0;   
1106                 step->more_tokens
1107                     = icu_tokenizer_next_token(step->u.tokenizer,
1108                                                step->buf16, status);
1109             }
1110         }
1111         if (0 == step->more_tokens)
1112             return 0;
1113         break;
1114     default:
1115         return 0;
1116         break;
1117     }
1118
1119     /* stop further token processing if last step and 
1120        new tokens can ot be obtained from last non-existing step 
1121     */
1122     if (!step->previous)
1123         step->more_tokens = 0;
1124
1125     if (U_FAILURE(*status))
1126         return 0;
1127
1128     // result buf empty, got no token TODO: make while loop around all token
1129     // fetching operations
1130     //if (!step->buf16->utf16_len)
1131     //    return 0;
1132
1133     return 1;
1134 }
1135
1136
1137
1138 int icu_chain_assign_cstr(struct icu_chain * chain,
1139                           const char * src8cstr, 
1140                           UErrorCode *status)
1141 {
1142     struct icu_chain_step * stp = 0; 
1143
1144     if (!chain || !src8cstr)
1145         return 0;
1146
1147     stp = chain->steps;
1148     
1149     /* clear token count */
1150     chain->token_count = 0;
1151
1152     /* clear all steps stop states */
1153     while (stp){
1154         stp->more_tokens = 1;
1155         stp->need_new_token = 1;
1156         stp = stp->previous;
1157     }
1158     
1159     /* finally convert UTF8 to UTF16 string */
1160     icu_utf16_from_utf8_cstr(chain->src16, src8cstr, status);
1161             
1162     if (U_FAILURE(*status))
1163         return 0;
1164
1165     return 1;
1166 }
1167
1168
1169
1170 int icu_chain_next_token(struct icu_chain * chain,
1171                          UErrorCode *status)
1172 {
1173     int got_token = 0;
1174     
1175     if (!chain || !chain->steps)
1176         return 0;
1177
1178     got_token = icu_chain_step_next_token(chain, chain->steps, status);
1179     
1180     if (got_token){
1181         chain->token_count++;
1182         return chain->token_count;
1183     }
1184
1185     return 0;
1186 }
1187
1188 int icu_chain_get_token_count(struct icu_chain * chain)
1189 {
1190     if (!chain)
1191         return 0;
1192     
1193     return chain->token_count;
1194 }
1195
1196
1197
1198 const char * icu_chain_get_display(struct icu_chain * chain)
1199 {
1200     if (chain->display8)
1201         return icu_buf_utf8_to_cstr(chain->display8);
1202     
1203     return 0;
1204 }
1205
1206 const char * icu_chain_get_norm(struct icu_chain * chain)
1207 {
1208     if (chain->norm8)
1209         return icu_buf_utf8_to_cstr(chain->norm8);
1210     
1211     return 0;
1212 }
1213
1214 const char * icu_chain_get_sort(struct icu_chain * chain)
1215 {
1216     if (chain->sort8)
1217         return icu_buf_utf8_to_cstr(chain->sort8);
1218     
1219     return 0;
1220 }
1221
1222
1223 #endif /* HAVE_ICU */
1224
1225
1226
1227
1228 /*
1229  * Local variables:
1230  * c-basic-offset: 4
1231  * indent-tabs-mode: nil
1232  * End:
1233  * vim: shiftwidth=4 tabstop=8 expandtab
1234  */