7fac3c54dacb3e4eaeb2df4fe4a21ca445d01615
[yaz-moved-to-github.git] / test / tsticonv.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: tsticonv.c,v 1.27 2007-03-17 00:10:41 adam Exp $
6  */
7
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <ctype.h>
16
17 #include <yaz/yaz-util.h>
18 #include <yaz/test.h>
19
20 static int compare_buffers(char *msg, int no,
21                            int expect_len, const char *expect_buf,
22                            int got_len, const char *got_buf)
23 {
24     if (expect_len == got_len
25         && !memcmp(expect_buf, got_buf, expect_len))
26         return 1;
27     
28     if (0) /* use 1 see how the buffers differ (for debug purposes) */
29     {
30         int i;
31         printf("tsticonv test=%s i=%d failed\n", msg, no);
32         printf("off got exp\n");
33         for (i = 0; i<got_len || i<expect_len; i++)
34         {
35             char got_char[10];
36             char expect_char[10];
37             
38             if (i < got_len)
39                 sprintf(got_char, "%02X", got_buf[i]);
40             else
41                 sprintf(got_char, "?  ");
42             
43             if (i < expect_len)
44                 sprintf(expect_char, "%02X", expect_buf[i]);
45             else
46                 sprintf(expect_char, "?  ");
47             
48             printf("%02d  %s  %s %c\n",
49                    i, got_char, expect_char, got_buf[i] == expect_buf[i] ?
50                    ' ' : '*');
51             
52         }
53     }
54     return 0;
55 }
56
57 static int tst_convert_l(yaz_iconv_t cd, size_t in_len, const char *in_buf,
58                          size_t expect_len, const char *expect_buf)
59 {
60     size_t r;
61     char *inbuf= (char*) in_buf;
62     size_t inbytesleft = in_len > 0 ? in_len : strlen(in_buf);
63     char outbuf0[64];
64     char *outbuf = outbuf0;
65
66     while (inbytesleft)
67     {
68         size_t outbytesleft = outbuf0 + sizeof(outbuf0) - outbuf;
69         if (outbytesleft > 12)
70             outbytesleft = 12;
71         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
72         if (r == (size_t) (-1))
73         {
74             int e = yaz_iconv_error(cd);
75             if (e != YAZ_ICONV_E2BIG)
76                 return 0;
77         }
78         else
79             break;
80     }
81     return compare_buffers("tsticonv 22", 0,
82                            expect_len, expect_buf,
83                            outbuf - outbuf0, outbuf0);
84 }
85
86 static int tst_convert(yaz_iconv_t cd, const char *buf, const char *cmpbuf)
87 {
88     int ret = 0;
89     WRBUF b = wrbuf_alloc();
90     char outbuf[12];
91     size_t inbytesleft = strlen(buf);
92     const char *inp = buf;
93     int rounds = 0;
94     for (rounds = 0; inbytesleft && rounds < sizeof(outbuf); rounds++)
95     {
96         size_t outbytesleft = sizeof(outbuf);
97         char *outp = outbuf;
98         size_t r = yaz_iconv(cd, (char**) &inp,  &inbytesleft,
99                              &outp, &outbytesleft);
100         wrbuf_write(b, outbuf, outp - outbuf);
101         if (r == (size_t) (-1))
102         {
103             int e = yaz_iconv_error(cd);
104             if (e != YAZ_ICONV_E2BIG)
105                 break;
106         }
107     }
108     if (wrbuf_len(b) == strlen(cmpbuf) 
109         && !memcmp(cmpbuf, wrbuf_buf(b), wrbuf_len(b)))
110         ret = 1;
111     else
112         yaz_log(YLOG_LOG, "GOT (%.*s)", wrbuf_len(b), wrbuf_buf(b));
113     wrbuf_free(b, 1);
114     return ret;
115 }
116
117
118 /* some test strings in ISO-8859-1 format */
119 static const char *iso_8859_1_a[] = {
120     "ax" ,
121     "\xd8",
122     "eneb\346r",
123     "\xe5" "\xd8",
124     "\xe5" "\xd8" "b",
125     "\xe5" "\xe5",
126     0 };
127
128 static void tst_marc8_to_ucs4b(void)
129 {
130     yaz_iconv_t cd = yaz_iconv_open("UCS4", "MARC8");
131     YAZ_CHECK(cd);
132     if (!cd)
133         return;
134     
135     YAZ_CHECK(tst_convert_l(
136                   cd,
137                   0,
138                   "\033$1" "\x21\x2B\x3B" /* FF1F */ "\033(B" "o",
139                   8, 
140                   "\x00\x00\xFF\x1F" "\x00\x00\x00o"));
141     YAZ_CHECK(tst_convert_l(
142                   cd,
143                   0,
144                   "\033$1" "\x6F\x77\x29" /* AE0E */
145                   "\x6F\x52\x7C" /* c0F4 */ "\033(B",
146                   8,
147                   "\x00\x00\xAE\x0E" "\x00\x00\xC0\xF4"));
148     YAZ_CHECK(tst_convert_l(
149                   cd,
150                   0,
151                   "\033$1"
152                   "\x21\x50\x6E"  /* UCS 7CFB */
153                   "\x21\x51\x31"  /* UCS 7D71 */
154                   "\x21\x3A\x67"  /* UCS 5B89 */
155                   "\x21\x33\x22"  /* UCS 5168 */
156                   "\x21\x33\x53"  /* UCS 5206 */
157                   "\x21\x44\x2B"  /* UCS 6790 */
158                   "\033(B",
159                   24, 
160                   "\x00\x00\x7C\xFB"
161                   "\x00\x00\x7D\x71"
162                   "\x00\x00\x5B\x89"
163                   "\x00\x00\x51\x68"
164                   "\x00\x00\x52\x06"
165                   "\x00\x00\x67\x90"));
166
167     YAZ_CHECK(tst_convert_l(
168                   cd,
169                   0,
170                   "\xB0\xB2",     /* AYN and oSLASH */
171                   8, 
172                   "\x00\x00\x02\xBB"  "\x00\x00\x00\xF8"));
173     YAZ_CHECK(tst_convert_l(
174                   cd,
175                   0,
176                   "\xF6\x61",     /* a underscore */
177                   8, 
178                   "\x00\x00\x00\x61"  "\x00\x00\x03\x32"));
179
180     YAZ_CHECK(tst_convert_l(
181                   cd,
182                   0,
183                   "\x61\xC2",     /* a, phonorecord mark */
184                   8,
185                   "\x00\x00\x00\x61"  "\x00\x00\x21\x17"));
186
187     /* bug #258 */
188     YAZ_CHECK(tst_convert_l(
189                   cd,
190                   0,
191                   "el" "\xe8" "am\xe8" "an", /* elaman where a is a" */
192                   32,
193                   "\x00\x00\x00" "e"
194                   "\x00\x00\x00" "l"
195                   "\x00\x00\x00" "a"
196                   "\x00\x00\x03\x08"
197                   "\x00\x00\x00" "m"
198                   "\x00\x00\x00" "a"
199                   "\x00\x00\x03\x08"
200                   "\x00\x00\x00" "n"));
201     /* bug #260 */
202     YAZ_CHECK(tst_convert_l(
203                   cd,
204                   0,
205                   "\xe5\xe8\x41",
206                   12, 
207                   "\x00\x00\x00\x41" "\x00\x00\x03\x04" "\x00\x00\x03\x08"));
208     /* bug #416 */
209     YAZ_CHECK(tst_convert_l(
210                   cd,
211                   0,
212                   "\xEB\x74\xEC\x73",
213                   12,
214                   "\x00\x00\x00\x74" "\x00\x00\x03\x61" "\x00\x00\x00\x73"));
215     /* bug #416 */
216     YAZ_CHECK(tst_convert_l(
217                   cd,
218                   0,
219                   "\xFA\x74\xFB\x73",
220                   12, 
221                   "\x00\x00\x00\x74" "\x00\x00\x03\x60" "\x00\x00\x00\x73"));
222
223     yaz_iconv_close(cd);
224 }
225
226 static void tst_ucs4b_to_utf8(void)
227 {
228     yaz_iconv_t cd = yaz_iconv_open("UTF8", "UCS4");
229     YAZ_CHECK(cd);
230     if (!cd)
231         return;
232     YAZ_CHECK(tst_convert_l(
233                   cd,
234                   8,
235                   "\x00\x00\xFF\x1F\x00\x00\x00o",
236                   4,
237                   "\xEF\xBC\x9F\x6F"));
238
239     YAZ_CHECK(tst_convert_l(
240                   cd,
241                   8, 
242                   "\x00\x00\xAE\x0E\x00\x00\xC0\xF4",
243                   6,
244                   "\xEA\xB8\x8E\xEC\x83\xB4"));
245     yaz_iconv_close(cd);
246 }
247
248 static void dconvert(int mandatory, const char *tmpcode)
249 {
250     int i;
251     int ret;
252     yaz_iconv_t cd;
253     for (i = 0; iso_8859_1_a[i]; i++)
254     {
255         size_t r;
256         char *inbuf = (char*) iso_8859_1_a[i];
257         size_t inbytesleft = strlen(inbuf);
258         char outbuf0[24];
259         char outbuf1[10];
260         char *outbuf = outbuf0;
261         size_t outbytesleft = sizeof(outbuf0);
262
263         cd = yaz_iconv_open(tmpcode, "ISO-8859-1");
264         YAZ_CHECK(cd || !mandatory);
265         if (!cd)
266             return;
267         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
268         YAZ_CHECK(r != (size_t) (-1));
269         yaz_iconv_close(cd);
270         if (r == (size_t) (-1))
271             return;
272         
273         cd = yaz_iconv_open("ISO-8859-1", tmpcode);
274         YAZ_CHECK(cd || !mandatory);
275         if (!cd)
276             return;
277         inbuf = outbuf0;
278         inbytesleft = sizeof(outbuf0) - outbytesleft;
279
280         outbuf = outbuf1;
281         outbytesleft = sizeof(outbuf1);
282         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
283         YAZ_CHECK(r != (size_t) (-1));
284         if (r != (size_t)(-1)) 
285         {
286             ret = compare_buffers("dconvert", i,
287                                   strlen(iso_8859_1_a[i]), iso_8859_1_a[i],
288                               sizeof(outbuf1) - outbytesleft, outbuf1);
289             YAZ_CHECK(ret);
290         }
291         yaz_iconv_close(cd);
292     }
293 }
294
295 int utf8_check(unsigned c)
296 {
297     if (sizeof(c) >= 4)
298     {
299         size_t r;
300         char src[4];
301         char dst[4];
302         char utf8buf[6];
303         char *inbuf = src;
304         size_t inbytesleft = 4;
305         char *outbuf = utf8buf;
306         size_t outbytesleft = sizeof(utf8buf);
307         int i;
308         yaz_iconv_t cd = yaz_iconv_open("UTF-8", "UCS4LE");
309         if (!cd)
310             return 0;
311         for (i = 0; i<4; i++)
312             src[i] = c >> (i*8);
313         
314         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
315         yaz_iconv_close(cd);
316
317         if (r == (size_t)(-1))
318             return 0;
319
320         cd = yaz_iconv_open("UCS4LE", "UTF-8");
321         if (!cd)
322             return 0;
323         inbytesleft = sizeof(utf8buf) - outbytesleft;
324         inbuf = utf8buf;
325
326         outbuf = dst;
327         outbytesleft = 4;
328
329         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
330         if (r == (size_t)(-1))
331             return 0;
332
333         yaz_iconv_close(cd);
334
335         if (memcmp(src, dst, 4))
336             return 0;
337     }
338     return 1;
339 }
340         
341 static void tst_marc8_to_utf8(void)
342 {
343     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC8");
344
345     YAZ_CHECK(cd);
346     if (!cd)
347         return;
348
349     YAZ_CHECK(tst_convert(cd, "Cours de math", 
350                           "Cours de math"));
351     /* COMBINING ACUTE ACCENT */
352     YAZ_CHECK(tst_convert(cd, "Cours de mathâe", 
353                           "Cours de mathe\xcc\x81"));
354
355
356     YAZ_CHECK(tst_convert(cd, "a\xea\x1e", "a\x1e\xcc\x8a"));
357
358     YAZ_CHECK(tst_convert(cd, "a\xea", "a"));
359     yaz_iconv_close(cd);
360 }
361
362 static void tst_marc8s_to_utf8(void)
363 {
364     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC8s");
365
366     YAZ_CHECK(cd);
367     if (!cd)
368         return;
369
370     YAZ_CHECK(tst_convert(cd, "Cours de math", 
371                           "Cours de math"));
372     /* E9: LATIN SMALL LETTER E WITH ACUTE */
373     YAZ_CHECK(tst_convert(cd, "Cours de mathâe", 
374                           "Cours de math\xc3\xa9"));
375
376     yaz_iconv_close(cd);
377 }
378
379
380 static void tst_marc8_to_latin1(void)
381 {
382     yaz_iconv_t cd = yaz_iconv_open("ISO-8859-1", "MARC8");
383
384     YAZ_CHECK(cd);
385     if (!cd)
386         return;
387
388     YAZ_CHECK(tst_convert(cd, "ax", "ax"));
389
390     /* latin capital letter o with stroke */
391     YAZ_CHECK(tst_convert(cd, "\xa2", "\xd8"));
392
393     /* with latin small letter ae */
394     YAZ_CHECK(tst_convert(cd, "eneb\xb5r", "eneb\346r"));
395
396     YAZ_CHECK(tst_convert(cd, "\xea" "a\xa2", "\xe5" "\xd8"));
397
398     YAZ_CHECK(tst_convert(cd, "\xea" "a\xa2" "b", "\xe5" "\xd8" "b"));
399
400     YAZ_CHECK(tst_convert(cd, "\xea" "a"  "\xea" "a", "\xe5" "\xe5"));
401
402     YAZ_CHECK(tst_convert(cd, "Cours de math", 
403                           "Cours de math"));
404     YAZ_CHECK(tst_convert(cd, "Cours de mathâe", 
405                           "Cours de mathé"));
406     YAZ_CHECK(tst_convert(cd, "12345678âe", 
407                           "12345678é"));
408     YAZ_CHECK(tst_convert(cd, "123456789âe", 
409                           "123456789é"));
410     YAZ_CHECK(tst_convert(cd, "1234567890âe", 
411                           "1234567890é"));
412     YAZ_CHECK(tst_convert(cd, "12345678901âe", 
413                           "12345678901é"));
414     YAZ_CHECK(tst_convert(cd, "Cours de mathâem", 
415                           "Cours de mathém"));
416     YAZ_CHECK(tst_convert(cd, "Cours de mathâematiques", 
417                           "Cours de mathématiques"));
418
419     yaz_iconv_close(cd);
420 }
421
422 static void tst_utf8_to_marc8(void)
423 {
424     yaz_iconv_t cd = yaz_iconv_open("MARC8", "UTF-8");
425
426     YAZ_CHECK(cd);
427     if (!cd)
428         return;
429
430     YAZ_CHECK(tst_convert(cd, "Cours ", "Cours "));
431
432     /** Pure ASCII. 11 characters (sizeof(outbuf)-1) */
433     YAZ_CHECK(tst_convert(cd, "Cours de mat", "Cours de mat"));
434
435     /** Pure ASCII. 12 characters (sizeof(outbuf)) */
436     YAZ_CHECK(tst_convert(cd, "Cours de math", "Cours de math"));
437
438     /** Pure ASCII. 13 characters (sizeof(outbuf)) */
439     YAZ_CHECK(tst_convert(cd, "Cours de math.", "Cours de math."));
440
441     /** UPPERCASE SCANDINAVIAN O */
442     YAZ_CHECK(tst_convert(cd, "S\xc3\x98", "S\xa2"));
443
444     /** ARING */
445     YAZ_CHECK(tst_convert(cd, "A" "\xCC\x8A", "\xEA" "A"));
446
447     /** A MACRON + UMLAUT, DIAERESIS */
448     YAZ_CHECK(tst_convert(cd, "A" "\xCC\x84" "\xCC\x88",
449                           "\xE5\xE8\x41"));
450     
451     /* Ligature spanning two characters */
452     YAZ_CHECK(tst_convert(cd,
453                           "\x74" "\xCD\xA1" "\x73",  /* UTF-8 */
454                           "\xEB\x74\xEC\x73"));      /* MARC-8 */
455
456     /* Double title spanning two characters */
457     YAZ_CHECK(tst_convert(cd,
458                           "\x74" "\xCD\xA0" "\x73",  /* UTF-8 */
459                           "\xFA\x74\xFB\x73"));      /* MARC-8 */
460
461     /** Ideographic question mark (Unicode FF1F) */
462     YAZ_CHECK(tst_convert(cd,
463                           "\xEF\xBC\x9F" "o",        /* UTF-8 */
464                           "\033$1" "\x21\x2B\x3B" "\033(B" "o" ));
465
466
467     /** Superscript 0 . bug #642 */
468     YAZ_CHECK(tst_convert(cd,
469                           "(\xe2\x81\xb0)",        /* UTF-8 */
470                           "(\033p0\x1bs)"));
471     
472  
473     {
474         char *inbuf0 = "\xe2\x81\xb0";
475         char *inbuf = inbuf0;
476         size_t inbytesleft = strlen(inbuf);
477         char outbuf0[64];
478         char *outbuf = outbuf0;
479         size_t outbytesleft = sizeof(outbuf0)-1;
480         size_t r;
481 #if 0
482         int i;
483 #endif
484         r = yaz_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
485         YAZ_CHECK(r != (size_t) (-1));
486
487 #if 0
488         *outbuf = '\0';  /* so we know when to stop printing */
489         for (i = 0; outbuf0[i]; i++)
490         {
491             int ch = outbuf0[i] & 0xff;
492             yaz_log(YLOG_LOG, "ch%d %02X %c", i, ch, ch >= ' ' ? ch : '?');
493         }
494 #endif
495
496         r = yaz_iconv(cd, 0, 0, &outbuf, &outbytesleft);
497         YAZ_CHECK(r != (size_t) (-1));
498         *outbuf = '\0';  /* for strcmp test below and printing */
499 #if 0
500         for (i = 0; outbuf0[i]; i++)
501         {
502             int ch = outbuf0[i] & 0xff;
503             yaz_log(YLOG_LOG, "ch%d %02X %c", i, ch, ch >= ' ' ? ch : '?');
504         }
505 #endif
506         YAZ_CHECK(strcmp("\033p0\x1bs", outbuf0) == 0);
507     }
508     yaz_iconv_close(cd);
509 }
510
511 static void tst_advance_to_utf8(void)
512 {
513     yaz_iconv_t cd = yaz_iconv_open("utf-8", "advancegreek");
514
515     YAZ_CHECK(cd);
516     if (!cd)
517         return;
518
519     YAZ_CHECK(tst_convert(cd, "Cours ", "Cours "));
520     yaz_iconv_close(cd);
521 }
522
523 static void tst_utf8_to_advance(void)
524 {
525     yaz_iconv_t cd = yaz_iconv_open("advancegreek", "utf-8");
526
527     YAZ_CHECK(cd);
528     if (!cd)
529         return;
530
531     YAZ_CHECK(tst_convert(cd, "Cours ", "Cours "));
532     yaz_iconv_close(cd);
533 }
534
535 static void tst_latin1_to_marc8(void)
536 {
537     yaz_iconv_t cd = yaz_iconv_open("MARC8", "ISO-8859-1");
538
539     YAZ_CHECK(cd);
540     if (!cd)
541         return;
542
543     YAZ_CHECK(tst_convert(cd, "Cours ", "Cours "));
544
545     /** Pure ASCII. 11 characters (sizeof(outbuf)-1) */
546     YAZ_CHECK(tst_convert(cd, "Cours de mat", "Cours de mat"));
547
548     /** Pure ASCII. 12 characters (sizeof(outbuf)) */
549     YAZ_CHECK(tst_convert(cd, "Cours de math", "Cours de math"));
550
551     /** Pure ASCII. 13 characters (sizeof(outbuf)) */
552     YAZ_CHECK(tst_convert(cd, "Cours de math.", "Cours de math."));
553
554     /** D8: UPPERCASE SCANDINAVIAN O */
555     YAZ_CHECK(tst_convert(cd, "S\xd8", "S\xa2"));
556
557     /** E9: LATIN SMALL LETTER E WITH ACUTE */
558     YAZ_CHECK(tst_convert(cd, "Cours de math\xe9", "Cours de mathâe"));
559     YAZ_CHECK(tst_convert(cd, "Cours de math", "Cours de math"
560                   ));
561     YAZ_CHECK(tst_convert(cd, "Cours de mathé", "Cours de mathâe" ));
562     YAZ_CHECK(tst_convert(cd, "12345678é","12345678âe"));
563     YAZ_CHECK(tst_convert(cd, "123456789é", "123456789âe"));
564     YAZ_CHECK(tst_convert(cd, "1234567890é","1234567890âe"));
565     YAZ_CHECK(tst_convert(cd, "12345678901é", "12345678901âe"));
566     YAZ_CHECK(tst_convert(cd, "Cours de mathém", "Cours de mathâem"));
567     YAZ_CHECK(tst_convert(cd, "Cours de mathématiques",
568                           "Cours de mathâematiques"));
569     yaz_iconv_close(cd);
570 }
571
572 static void tst_utf8_codes(void)
573 {
574     YAZ_CHECK(utf8_check(3));
575     YAZ_CHECK(utf8_check(127));
576     YAZ_CHECK(utf8_check(128));
577     YAZ_CHECK(utf8_check(255));
578     YAZ_CHECK(utf8_check(256));
579     YAZ_CHECK(utf8_check(900));
580     YAZ_CHECK(utf8_check(1000));
581     YAZ_CHECK(utf8_check(10000));
582     YAZ_CHECK(utf8_check(100000));
583     YAZ_CHECK(utf8_check(1000000));
584     YAZ_CHECK(utf8_check(10000000));
585     YAZ_CHECK(utf8_check(100000000));
586 }
587
588 int main (int argc, char **argv)
589 {
590     YAZ_CHECK_INIT(argc, argv);
591
592     tst_utf8_codes();
593
594     tst_marc8_to_utf8();
595
596     tst_marc8s_to_utf8();
597
598     tst_marc8_to_latin1();
599
600     tst_advance_to_utf8();
601     tst_utf8_to_advance();
602
603     tst_utf8_to_marc8();
604
605     tst_latin1_to_marc8();
606
607     tst_marc8_to_ucs4b();
608     tst_ucs4b_to_utf8();
609
610     dconvert(1, "UTF-8");
611     dconvert(1, "ISO-8859-1");
612     dconvert(1, "UCS4");
613     dconvert(1, "UCS4LE");
614     dconvert(0, "CP865");
615
616     YAZ_CHECK_TERM;
617 }
618 /*
619  * Local variables:
620  * c-basic-offset: 4
621  * indent-tabs-mode: nil
622  * End:
623  * vim: shiftwidth=4 tabstop=8 expandtab
624  */