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