Avoid test failure with libiconv-1.13.
[gnulib.git] / tests / test-striconveh.c
1 /* Test of character set conversion with error handling.
2    Copyright (C) 2007-2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
18
19 #include <config.h>
20
21 #include "striconveh.h"
22
23 #if HAVE_ICONV
24 # include <iconv.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #define SIZEOF(array) (sizeof (array) / sizeof (array[0]))
33 #define ASSERT(expr) \
34   do                                                                         \
35     {                                                                        \
36       if (!(expr))                                                           \
37         {                                                                    \
38           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
39           fflush (stderr);                                                   \
40           abort ();                                                          \
41         }                                                                    \
42     }                                                                        \
43   while (0)
44
45 /* Magic number for detecting bounds violations.  */
46 #define MAGIC 0x1983EFF1
47
48 static size_t *
49 new_offsets (size_t n)
50 {
51   size_t *offsets = (size_t *) malloc ((n + 1) * sizeof (size_t));
52   offsets[n] = MAGIC;
53   return offsets;
54 }
55
56 int
57 main ()
58 {
59   static enum iconv_ilseq_handler handlers[] =
60     { iconveh_error, iconveh_question_mark, iconveh_escape_sequence };
61   size_t h;
62   size_t o;
63   size_t i;
64
65 #if HAVE_ICONV
66   /* Assume that iconv() supports at least the encodings ASCII, ISO-8859-1,
67      ISO-8859-2, and UTF-8.  */
68   iconv_t cd_88591_to_88592 = iconv_open ("ISO-8859-2", "ISO-8859-1");
69   iconv_t cd_88592_to_88591 = iconv_open ("ISO-8859-1", "ISO-8859-2");
70   iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1");
71   iconv_t cd_utf8_to_88591 = iconv_open ("ISO-8859-1", "UTF-8");
72   iconv_t cd_88592_to_utf8 = iconv_open ("UTF-8", "ISO-8859-2");
73   iconv_t cd_utf8_to_88592 = iconv_open ("ISO-8859-2", "UTF-8");
74   iconv_t cd_utf7_to_utf8 = iconv_open ("UTF-8", "UTF-7");
75
76   ASSERT (cd_88591_to_utf8 != (iconv_t)(-1));
77   ASSERT (cd_utf8_to_88591 != (iconv_t)(-1));
78   ASSERT (cd_88592_to_utf8 != (iconv_t)(-1));
79   ASSERT (cd_utf8_to_88592 != (iconv_t)(-1));
80
81   /* ------------------------ Test mem_cd_iconveh() ------------------------ */
82
83   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
84   for (h = 0; h < SIZEOF (handlers); h++)
85     {
86       enum iconv_ilseq_handler handler = handlers[h];
87       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
88       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
89       for (o = 0; o < 2; o++)
90         {
91           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
92           char *result = NULL;
93           size_t length = 0;
94           int retval = mem_cd_iconveh (input, strlen (input),
95                                        cd_88592_to_88591,
96                                        cd_88592_to_utf8, cd_utf8_to_88591,
97                                        handler,
98                                        offsets,
99                                        &result, &length);
100           ASSERT (retval == 0);
101           ASSERT (length == strlen (expected));
102           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
103           if (o)
104             {
105               for (i = 0; i < 37; i++)
106                 ASSERT (offsets[i] == i);
107               ASSERT (offsets[37] == MAGIC);
108               free (offsets);
109             }
110           free (result);
111         }
112     }
113
114   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
115   for (h = 0; h < SIZEOF (handlers); h++)
116     {
117       enum iconv_ilseq_handler handler = handlers[h];
118       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
119       for (o = 0; o < 2; o++)
120         {
121           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
122           char *result = NULL;
123           size_t length = 0;
124           int retval = mem_cd_iconveh (input, strlen (input),
125                                        cd_88592_to_88591,
126                                        cd_88592_to_utf8, cd_utf8_to_88591,
127                                        handler,
128                                        offsets,
129                                        &result, &length);
130           switch (handler)
131             {
132             case iconveh_error:
133               ASSERT (retval == -1 && errno == EILSEQ);
134               ASSERT (result == NULL);
135               if (o)
136                 free (offsets);
137               break;
138             case iconveh_question_mark:
139               {
140                 static const char expected[] = "Rafa? Maszkowski";
141                 ASSERT (retval == 0);
142                 ASSERT (length == strlen (expected));
143                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
144                 if (o)
145                   {
146                     for (i = 0; i < 16; i++)
147                       ASSERT (offsets[i] == i);
148                     ASSERT (offsets[16] == MAGIC);
149                     free (offsets);
150                   }
151                 free (result);
152               }
153               break;
154             case iconveh_escape_sequence:
155               {
156                 static const char expected[] = "Rafa\\u0142 Maszkowski";
157                 ASSERT (retval == 0);
158                 ASSERT (length == strlen (expected));
159                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
160                 if (o)
161                   {
162                     for (i = 0; i < 16; i++)
163                       ASSERT (offsets[i] == (i < 5 ? i :
164                                              i + 5));
165                     ASSERT (offsets[16] == MAGIC);
166                     free (offsets);
167                   }
168                 free (result);
169               }
170               break;
171             }
172         }
173     }
174
175   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
176   for (h = 0; h < SIZEOF (handlers); h++)
177     {
178       enum iconv_ilseq_handler handler = handlers[h];
179       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
180       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
181       for (o = 0; o < 2; o++)
182         {
183           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
184           char *result = NULL;
185           size_t length = 0;
186           int retval = mem_cd_iconveh (input, strlen (input),
187                                        cd_88591_to_utf8,
188                                        cd_88591_to_utf8, (iconv_t)(-1),
189                                        handler,
190                                        offsets,
191                                        &result, &length);
192           ASSERT (retval == 0);
193           ASSERT (length == strlen (expected));
194           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
195           if (o)
196             {
197               for (i = 0; i < 37; i++)
198                 ASSERT (offsets[i] == (i < 1 ? i :
199                                        i < 12 ? i + 1 :
200                                        i < 18 ? i + 2 :
201                                        i + 3));
202               ASSERT (offsets[37] == MAGIC);
203               free (offsets);
204             }
205           free (result);
206         }
207     }
208
209   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
210   for (h = 0; h < SIZEOF (handlers); h++)
211     {
212       enum iconv_ilseq_handler handler = handlers[h];
213       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
214       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
215       for (o = 0; o < 2; o++)
216         {
217           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
218           char *result = NULL;
219           size_t length = 0;
220           int retval = mem_cd_iconveh (input, strlen (input),
221                                        cd_utf8_to_88591,
222                                        (iconv_t)(-1), cd_utf8_to_88591,
223                                        handler,
224                                        offsets,
225                                        &result, &length);
226           ASSERT (retval == 0);
227           ASSERT (length == strlen (expected));
228           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
229           if (o)
230             {
231               for (i = 0; i < 41; i++)
232                 ASSERT (offsets[i] == (i < 1 ? i :
233                                        i == 1 ? (size_t)(-1) :
234                                        i < 13 ? i - 1 :
235                                        i == 13 ? (size_t)(-1) :
236                                        i < 20 ? i - 2 :
237                                        i == 20 ? (size_t)(-1) :
238                                        i < 40 ? i - 3 :
239                                        (size_t)(-1)));
240               ASSERT (offsets[41] == MAGIC);
241               free (offsets);
242             }
243           free (result);
244         }
245     }
246
247   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
248   for (h = 0; h < SIZEOF (handlers); h++)
249     {
250       enum iconv_ilseq_handler handler = handlers[h];
251       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
252       for (o = 0; o < 2; o++)
253         {
254           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
255           char *result = NULL;
256           size_t length = 0;
257           int retval = mem_cd_iconveh (input, strlen (input),
258                                        cd_utf8_to_88591,
259                                        (iconv_t)(-1), cd_utf8_to_88591,
260                                        handler,
261                                        offsets,
262                                        &result, &length);
263           switch (handler)
264             {
265             case iconveh_error:
266               ASSERT (retval == -1 && errno == EILSEQ);
267               ASSERT (result == NULL);
268               if (o)
269                 free (offsets);
270               break;
271             case iconveh_question_mark:
272               {
273                 static const char expected[] = "Rafa? Maszkowski";
274                 ASSERT (retval == 0);
275                 ASSERT (length == strlen (expected));
276                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
277                 if (o)
278                   {
279                     for (i = 0; i < 17; i++)
280                       ASSERT (offsets[i] == (i < 5 ? i :
281                                              i == 5 ? (size_t)(-1) :
282                                              i - 1));
283                     ASSERT (offsets[17] == MAGIC);
284                     free (offsets);
285                   }
286                 free (result);
287               }
288               break;
289             case iconveh_escape_sequence:
290               {
291                 static const char expected[] = "Rafa\\u0142 Maszkowski";
292                 ASSERT (retval == 0);
293                 ASSERT (length == strlen (expected));
294                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
295                 if (o)
296                   {
297                     for (i = 0; i < 17; i++)
298                       ASSERT (offsets[i] == (i < 5 ? i :
299                                              i == 5 ? (size_t)(-1) :
300                                              i + 4));
301                     ASSERT (offsets[17] == MAGIC);
302                     free (offsets);
303                   }
304                 free (result);
305               }
306               break;
307             }
308         }
309     }
310
311   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
312   for (h = 0; h < SIZEOF (handlers); h++)
313     {
314       enum iconv_ilseq_handler handler = handlers[h];
315       static const char input[] = "\342";
316       for (o = 0; o < 2; o++)
317         {
318           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
319           char *result = NULL;
320           size_t length = 0;
321           int retval = mem_cd_iconveh (input, strlen (input),
322                                        cd_utf8_to_88591,
323                                        (iconv_t)(-1), cd_utf8_to_88591,
324                                        handler,
325                                        offsets,
326                                        &result, &length);
327           ASSERT (retval == 0);
328           ASSERT (length == 0);
329           if (o)
330             {
331               ASSERT (offsets[0] == 0);
332               ASSERT (offsets[1] == MAGIC);
333               free (offsets);
334             }
335           free (result);
336         }
337     }
338
339   if (cd_utf7_to_utf8 != (iconv_t)(-1))
340     {
341       /* Disabled on Solaris, because Solaris 9 iconv() is buggy: it returns
342          -1 / EILSEQ when converting the 7th byte of the input "+VDLYP9hA".  */
343 # if !(defined __sun && !defined _LIBICONV_VERSION)
344       /* Test conversion from UTF-7 to UTF-8 with EINVAL.  */
345       for (h = 0; h < SIZEOF (handlers); h++)
346         {
347           enum iconv_ilseq_handler handler = handlers[h];
348           /* This is base64 encoded 0x54 0x32 0xD8 0x3F 0xD8 0x40.  It would
349              convert to U+5432 U+D83F U+D840 but these are Unicode surrogates.  */
350           static const char input[] = "+VDLYP9hA";
351           static const char expected1[] = "\345\220\262"; /* 吲 glibc */
352           static const char expected2[] = ""; /* libiconv */
353           char *result = NULL;
354           size_t length = 0;
355           int retval = mem_cd_iconveh (input, 7,
356                                        cd_utf7_to_utf8,
357                                        cd_utf7_to_utf8, (iconv_t)(-1),
358                                        handler,
359                                        NULL,
360                                        &result, &length);
361           ASSERT (retval == 0);
362           ASSERT (length == strlen (expected1) || length == strlen (expected2));
363           ASSERT (result != NULL);
364           if (length == strlen (expected1))
365             ASSERT (memcmp (result, expected1, strlen (expected1)) == 0);
366           else
367             ASSERT (memcmp (result, expected2, strlen (expected2)) == 0);
368           free (result);
369         }
370
371       /* Test conversion from UTF-7 to UTF-8 with EILSEQ.  */
372       for (h = 0; h < SIZEOF (handlers); h++)
373         {
374           enum iconv_ilseq_handler handler = handlers[h];
375           /* This is base64 encoded 0xD8 0x3F 0xD8 0x40 0xD8 0x41.  It would
376              convert to U+D83F U+D840 U+D841 but these are Unicode surrogates.  */
377           static const char input[] = "+2D/YQNhB";
378           char *result = NULL;
379           size_t length = 0;
380           int retval = mem_cd_iconveh (input, strlen (input),
381                                        cd_utf7_to_utf8,
382                                        cd_utf7_to_utf8, (iconv_t)(-1),
383                                        handler,
384                                        NULL,
385                                        &result, &length);
386           switch (handler)
387             {
388             case iconveh_error:
389               ASSERT (retval == -1 && errno == EILSEQ);
390               ASSERT (result == NULL);
391               break;
392             case iconveh_question_mark:
393             case iconveh_escape_sequence:
394               {
395                 /* glibc result */
396                 static const char expected1[] = "?????";
397                 /* libiconv <= 1.12 result */
398                 static const char expected2[] = "?2D/YQNhB";
399                 /* libiconv behaviour changed in version 1.13: the result is
400                    '?' U+0FF6 U+1036; this is U+D83F U+D840 U+D841 shifted left
401                    by 6 bits.  */
402                 static const char expected3[] = "?\340\277\266\341\200\266";
403                 ASSERT (retval == 0);
404                 ASSERT (length == strlen (expected1)
405                         || length == strlen (expected2)
406                         || length == strlen (expected3));
407                 ASSERT (result != NULL);
408                 if (length == strlen (expected1))
409                   ASSERT (memcmp (result, expected1, strlen (expected1)) == 0);
410                 else if (length == strlen (expected2))
411                   ASSERT (memcmp (result, expected2, strlen (expected2)) == 0);
412                 else
413                   ASSERT (memcmp (result, expected3, strlen (expected3)) == 0);
414                 free (result);
415               }
416               break;
417             }
418         }
419 # endif
420     }
421
422   /* ------------------------ Test str_cd_iconveh() ------------------------ */
423
424   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
425   for (h = 0; h < SIZEOF (handlers); h++)
426     {
427       enum iconv_ilseq_handler handler = handlers[h];
428       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
429       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
430       char *result = str_cd_iconveh (input,
431                                      cd_88592_to_88591,
432                                      cd_88592_to_utf8, cd_utf8_to_88591,
433                                      handler);
434       ASSERT (result != NULL);
435       ASSERT (strcmp (result, expected) == 0);
436       free (result);
437     }
438
439   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
440   for (h = 0; h < SIZEOF (handlers); h++)
441     {
442       enum iconv_ilseq_handler handler = handlers[h];
443       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
444       char *result = str_cd_iconveh (input,
445                                      cd_88592_to_88591,
446                                      cd_88592_to_utf8, cd_utf8_to_88591,
447                                      handler);
448       switch (handler)
449         {
450         case iconveh_error:
451           ASSERT (result == NULL && errno == EILSEQ);
452           break;
453         case iconveh_question_mark:
454           {
455             static const char expected[] = "Rafa? Maszkowski";
456             ASSERT (result != NULL);
457             ASSERT (strcmp (result, expected) == 0);
458             free (result);
459           }
460           break;
461         case iconveh_escape_sequence:
462           {
463             static const char expected[] = "Rafa\\u0142 Maszkowski";
464             ASSERT (result != NULL);
465             ASSERT (strcmp (result, expected) == 0);
466             free (result);
467           }
468           break;
469         }
470     }
471
472   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
473   for (h = 0; h < SIZEOF (handlers); h++)
474     {
475       enum iconv_ilseq_handler handler = handlers[h];
476       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
477       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
478       char *result = str_cd_iconveh (input,
479                                      cd_88591_to_utf8,
480                                      cd_88591_to_utf8, (iconv_t)(-1),
481                                      handler);
482       ASSERT (result != NULL);
483       ASSERT (strcmp (result, expected) == 0);
484       free (result);
485     }
486
487   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
488   for (h = 0; h < SIZEOF (handlers); h++)
489     {
490       enum iconv_ilseq_handler handler = handlers[h];
491       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
492       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
493       char *result = str_cd_iconveh (input,
494                                      cd_utf8_to_88591,
495                                      (iconv_t)(-1), cd_utf8_to_88591,
496                                      handler);
497       ASSERT (result != NULL);
498       ASSERT (strcmp (result, expected) == 0);
499       free (result);
500     }
501
502   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
503   for (h = 0; h < SIZEOF (handlers); h++)
504     {
505       enum iconv_ilseq_handler handler = handlers[h];
506       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
507       char *result = str_cd_iconveh (input,
508                                      cd_utf8_to_88591,
509                                      (iconv_t)(-1), cd_utf8_to_88591,
510                                      handler);
511       switch (handler)
512         {
513         case iconveh_error:
514           ASSERT (result == NULL && errno == EILSEQ);
515           break;
516         case iconveh_question_mark:
517           {
518             static const char expected[] = "Costs: 27 ?";
519             ASSERT (result != NULL);
520             ASSERT (strcmp (result, expected) == 0);
521             free (result);
522           }
523           break;
524         case iconveh_escape_sequence:
525           {
526             static const char expected[] = "Costs: 27 \\u20AC";
527             ASSERT (result != NULL);
528             ASSERT (strcmp (result, expected) == 0);
529             free (result);
530           }
531           break;
532         }
533     }
534
535   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
536   for (h = 0; h < SIZEOF (handlers); h++)
537     {
538       enum iconv_ilseq_handler handler = handlers[h];
539       static const char input[] = "\342";
540       char *result = str_cd_iconveh (input,
541                                      cd_utf8_to_88591,
542                                      (iconv_t)(-1), cd_utf8_to_88591,
543                                      handler);
544       ASSERT (result != NULL);
545       ASSERT (strcmp (result, "") == 0);
546       free (result);
547     }
548
549   if (cd_88591_to_88592 != (iconv_t)(-1))
550     iconv_close (cd_88591_to_88592);
551   if (cd_88592_to_88591 != (iconv_t)(-1))
552     iconv_close (cd_88592_to_88591);
553   iconv_close (cd_88591_to_utf8);
554   iconv_close (cd_utf8_to_88591);
555   iconv_close (cd_88592_to_utf8);
556   iconv_close (cd_utf8_to_88592);
557
558   /* ------------------------- Test mem_iconveh() ------------------------- */
559
560   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
561   for (h = 0; h < SIZEOF (handlers); h++)
562     {
563       enum iconv_ilseq_handler handler = handlers[h];
564       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
565       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
566       for (o = 0; o < 2; o++)
567         {
568           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
569           char *result = NULL;
570           size_t length = 0;
571           int retval = mem_iconveh (input, strlen (input),
572                                     "ISO-8859-2", "ISO-8859-1",
573                                     handler,
574                                     offsets,
575                                     &result, &length);
576           ASSERT (retval == 0);
577           ASSERT (length == strlen (expected));
578           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
579           if (o)
580             {
581               for (i = 0; i < 37; i++)
582                 ASSERT (offsets[i] == i);
583               ASSERT (offsets[37] == MAGIC);
584               free (offsets);
585             }
586           free (result);
587         }
588     }
589
590   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
591   for (h = 0; h < SIZEOF (handlers); h++)
592     {
593       enum iconv_ilseq_handler handler = handlers[h];
594       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
595       for (o = 0; o < 2; o++)
596         {
597           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
598           char *result = NULL;
599           size_t length = 0;
600           int retval = mem_iconveh (input, strlen (input),
601                                     "ISO-8859-2", "ISO-8859-1",
602                                     handler,
603                                     offsets,
604                                     &result, &length);
605           switch (handler)
606             {
607             case iconveh_error:
608               ASSERT (retval == -1 && errno == EILSEQ);
609               ASSERT (result == NULL);
610               if (o)
611                 free (offsets);
612               break;
613             case iconveh_question_mark:
614               {
615                 static const char expected[] = "Rafa? Maszkowski";
616                 ASSERT (retval == 0);
617                 ASSERT (length == strlen (expected));
618                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
619                 if (o)
620                   {
621                     for (i = 0; i < 16; i++)
622                       ASSERT (offsets[i] == i);
623                     ASSERT (offsets[16] == MAGIC);
624                     free (offsets);
625                   }
626                 free (result);
627               }
628               break;
629             case iconveh_escape_sequence:
630               {
631                 static const char expected[] = "Rafa\\u0142 Maszkowski";
632                 ASSERT (retval == 0);
633                 ASSERT (length == strlen (expected));
634                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
635                 if (o)
636                   {
637                     for (i = 0; i < 16; i++)
638                       ASSERT (offsets[i] == (i < 5 ? i :
639                                              i + 5));
640                     ASSERT (offsets[16] == MAGIC);
641                     free (offsets);
642                   }
643                 free (result);
644               }
645               break;
646             }
647         }
648     }
649
650   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
651   for (h = 0; h < SIZEOF (handlers); h++)
652     {
653       enum iconv_ilseq_handler handler = handlers[h];
654       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
655       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
656       for (o = 0; o < 2; o++)
657         {
658           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
659           char *result = NULL;
660           size_t length = 0;
661           int retval = mem_iconveh (input, strlen (input),
662                                     "ISO-8859-1", "UTF-8",
663                                     handler,
664                                     offsets,
665                                     &result, &length);
666           ASSERT (retval == 0);
667           ASSERT (length == strlen (expected));
668           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
669           if (o)
670             {
671               for (i = 0; i < 37; i++)
672                 ASSERT (offsets[i] == (i < 1 ? i :
673                                        i < 12 ? i + 1 :
674                                        i < 18 ? i + 2 :
675                                        i + 3));
676               ASSERT (offsets[37] == MAGIC);
677               free (offsets);
678             }
679           free (result);
680         }
681     }
682
683   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
684   for (h = 0; h < SIZEOF (handlers); h++)
685     {
686       enum iconv_ilseq_handler handler = handlers[h];
687       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
688       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
689       for (o = 0; o < 2; o++)
690         {
691           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
692           char *result = NULL;
693           size_t length = 0;
694           int retval = mem_iconveh (input, strlen (input),
695                                     "UTF-8", "ISO-8859-1",
696                                     handler,
697                                     offsets,
698                                     &result, &length);
699           ASSERT (retval == 0);
700           ASSERT (length == strlen (expected));
701           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
702           if (o)
703             {
704               for (i = 0; i < 41; i++)
705                 ASSERT (offsets[i] == (i < 1 ? i :
706                                        i == 1 ? (size_t)(-1) :
707                                        i < 13 ? i - 1 :
708                                        i == 13 ? (size_t)(-1) :
709                                        i < 20 ? i - 2 :
710                                        i == 20 ? (size_t)(-1) :
711                                        i < 40 ? i - 3 :
712                                        (size_t)(-1)));
713               ASSERT (offsets[41] == MAGIC);
714               free (offsets);
715             }
716           free (result);
717         }
718     }
719
720   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
721   for (h = 0; h < SIZEOF (handlers); h++)
722     {
723       enum iconv_ilseq_handler handler = handlers[h];
724       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
725       for (o = 0; o < 2; o++)
726         {
727           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
728           char *result = NULL;
729           size_t length = 0;
730           int retval = mem_iconveh (input, strlen (input),
731                                     "UTF-8", "ISO-8859-1",
732                                     handler,
733                                     offsets,
734                                     &result, &length);
735           switch (handler)
736             {
737             case iconveh_error:
738               ASSERT (retval == -1 && errno == EILSEQ);
739               ASSERT (result == NULL);
740               if (o)
741                 free (offsets);
742               break;
743             case iconveh_question_mark:
744               {
745                 static const char expected[] = "Rafa? Maszkowski";
746                 ASSERT (retval == 0);
747                 ASSERT (length == strlen (expected));
748                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
749                 if (o)
750                   {
751                     for (i = 0; i < 17; i++)
752                       ASSERT (offsets[i] == (i < 5 ? i :
753                                              i == 5 ? (size_t)(-1) :
754                                              i - 1));
755                     ASSERT (offsets[17] == MAGIC);
756                     free (offsets);
757                   }
758                 free (result);
759               }
760               break;
761             case iconveh_escape_sequence:
762               {
763                 static const char expected[] = "Rafa\\u0142 Maszkowski";
764                 ASSERT (retval == 0);
765                 ASSERT (length == strlen (expected));
766                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
767                 if (o)
768                   {
769                     for (i = 0; i < 17; i++)
770                       ASSERT (offsets[i] == (i < 5 ? i :
771                                              i == 5 ? (size_t)(-1) :
772                                              i + 4));
773                     ASSERT (offsets[17] == MAGIC);
774                     free (offsets);
775                   }
776                 free (result);
777               }
778               break;
779             }
780         }
781     }
782
783   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
784   for (h = 0; h < SIZEOF (handlers); h++)
785     {
786       enum iconv_ilseq_handler handler = handlers[h];
787       static const char input[] = "\342";
788       for (o = 0; o < 2; o++)
789         {
790           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
791           char *result = NULL;
792           size_t length = 0;
793           int retval = mem_iconveh (input, strlen (input),
794                                     "UTF-8", "ISO-8859-1",
795                                     handler,
796                                     offsets,
797                                     &result, &length);
798           ASSERT (retval == 0);
799           ASSERT (length == 0);
800           if (o)
801             {
802               ASSERT (offsets[0] == 0);
803               ASSERT (offsets[1] == MAGIC);
804               free (offsets);
805             }
806           free (result);
807         }
808     }
809
810   /* ------------------------- Test str_iconveh() ------------------------- */
811
812   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
813   for (h = 0; h < SIZEOF (handlers); h++)
814     {
815       enum iconv_ilseq_handler handler = handlers[h];
816       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
817       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
818       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
819       ASSERT (result != NULL);
820       ASSERT (strcmp (result, expected) == 0);
821       free (result);
822     }
823
824   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
825   for (h = 0; h < SIZEOF (handlers); h++)
826     {
827       enum iconv_ilseq_handler handler = handlers[h];
828       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
829       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
830       switch (handler)
831         {
832         case iconveh_error:
833           ASSERT (result == NULL && errno == EILSEQ);
834           break;
835         case iconveh_question_mark:
836           {
837             static const char expected[] = "Rafa? Maszkowski";
838             ASSERT (result != NULL);
839             ASSERT (strcmp (result, expected) == 0);
840             free (result);
841           }
842           break;
843         case iconveh_escape_sequence:
844           {
845             static const char expected[] = "Rafa\\u0142 Maszkowski";
846             ASSERT (result != NULL);
847             ASSERT (strcmp (result, expected) == 0);
848             free (result);
849           }
850           break;
851         }
852     }
853
854   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
855   for (h = 0; h < SIZEOF (handlers); h++)
856     {
857       enum iconv_ilseq_handler handler = handlers[h];
858       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
859       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
860       char *result = str_iconveh (input, "ISO-8859-1", "UTF-8", handler);
861       ASSERT (result != NULL);
862       ASSERT (strcmp (result, expected) == 0);
863       free (result);
864     }
865
866   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
867   for (h = 0; h < SIZEOF (handlers); h++)
868     {
869       enum iconv_ilseq_handler handler = handlers[h];
870       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
871       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
872       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
873       ASSERT (result != NULL);
874       ASSERT (strcmp (result, expected) == 0);
875       free (result);
876     }
877
878   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
879   for (h = 0; h < SIZEOF (handlers); h++)
880     {
881       enum iconv_ilseq_handler handler = handlers[h];
882       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
883       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
884       switch (handler)
885         {
886         case iconveh_error:
887           ASSERT (result == NULL && errno == EILSEQ);
888           break;
889         case iconveh_question_mark:
890           {
891             static const char expected[] = "Costs: 27 ?";
892             ASSERT (result != NULL);
893             ASSERT (strcmp (result, expected) == 0);
894             free (result);
895           }
896           break;
897         case iconveh_escape_sequence:
898           {
899             static const char expected[] = "Costs: 27 \\u20AC";
900             ASSERT (result != NULL);
901             ASSERT (strcmp (result, expected) == 0);
902             free (result);
903           }
904           break;
905         }
906     }
907
908   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
909   for (h = 0; h < SIZEOF (handlers); h++)
910     {
911       enum iconv_ilseq_handler handler = handlers[h];
912       static const char input[] = "\342";
913       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
914       ASSERT (result != NULL);
915       ASSERT (strcmp (result, "") == 0);
916       free (result);
917     }
918
919 #endif
920
921   return 0;
922 }