Check behaviour when converting from UTF-7.
[gnulib.git] / tests / test-striconveh.c
1 /* Test of character set conversion with error handling.
2    Copyright (C) 2007-2008 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                 static const char expected1[] = "?????"; /* glibc */
396                 static const char expected2[] = "?2D/YQNhB"; /* libiconv */
397                 ASSERT (retval == 0);
398                 ASSERT (length == strlen (expected1) || length == strlen (expected2));
399                 ASSERT (result != NULL);
400                 if (length == strlen (expected1))
401                   ASSERT (memcmp (result, expected1, strlen (expected1)) == 0);
402                 else
403                   ASSERT (memcmp (result, expected2, strlen (expected2)) == 0);
404                 free (result);
405               }
406               break;
407             }
408         }
409 # endif
410     }
411
412   /* ------------------------ Test str_cd_iconveh() ------------------------ */
413
414   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
415   for (h = 0; h < SIZEOF (handlers); h++)
416     {
417       enum iconv_ilseq_handler handler = handlers[h];
418       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
419       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
420       char *result = str_cd_iconveh (input,
421                                      cd_88592_to_88591,
422                                      cd_88592_to_utf8, cd_utf8_to_88591,
423                                      handler);
424       ASSERT (result != NULL);
425       ASSERT (strcmp (result, expected) == 0);
426       free (result);
427     }
428
429   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
430   for (h = 0; h < SIZEOF (handlers); h++)
431     {
432       enum iconv_ilseq_handler handler = handlers[h];
433       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
434       char *result = str_cd_iconveh (input,
435                                      cd_88592_to_88591,
436                                      cd_88592_to_utf8, cd_utf8_to_88591,
437                                      handler);
438       switch (handler)
439         {
440         case iconveh_error:
441           ASSERT (result == NULL && errno == EILSEQ);
442           break;
443         case iconveh_question_mark:
444           {
445             static const char expected[] = "Rafa? Maszkowski";
446             ASSERT (result != NULL);
447             ASSERT (strcmp (result, expected) == 0);
448             free (result);
449           }
450           break;
451         case iconveh_escape_sequence:
452           {
453             static const char expected[] = "Rafa\\u0142 Maszkowski";
454             ASSERT (result != NULL);
455             ASSERT (strcmp (result, expected) == 0);
456             free (result);
457           }
458           break;
459         }
460     }
461
462   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
463   for (h = 0; h < SIZEOF (handlers); h++)
464     {
465       enum iconv_ilseq_handler handler = handlers[h];
466       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
467       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
468       char *result = str_cd_iconveh (input,
469                                      cd_88591_to_utf8,
470                                      cd_88591_to_utf8, (iconv_t)(-1),
471                                      handler);
472       ASSERT (result != NULL);
473       ASSERT (strcmp (result, expected) == 0);
474       free (result);
475     }
476
477   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
478   for (h = 0; h < SIZEOF (handlers); h++)
479     {
480       enum iconv_ilseq_handler handler = handlers[h];
481       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
482       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
483       char *result = str_cd_iconveh (input,
484                                      cd_utf8_to_88591,
485                                      (iconv_t)(-1), cd_utf8_to_88591,
486                                      handler);
487       ASSERT (result != NULL);
488       ASSERT (strcmp (result, expected) == 0);
489       free (result);
490     }
491
492   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
493   for (h = 0; h < SIZEOF (handlers); h++)
494     {
495       enum iconv_ilseq_handler handler = handlers[h];
496       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
497       char *result = str_cd_iconveh (input,
498                                      cd_utf8_to_88591,
499                                      (iconv_t)(-1), cd_utf8_to_88591,
500                                      handler);
501       switch (handler)
502         {
503         case iconveh_error:
504           ASSERT (result == NULL && errno == EILSEQ);
505           break;
506         case iconveh_question_mark:
507           {
508             static const char expected[] = "Costs: 27 ?";
509             ASSERT (result != NULL);
510             ASSERT (strcmp (result, expected) == 0);
511             free (result);
512           }
513           break;
514         case iconveh_escape_sequence:
515           {
516             static const char expected[] = "Costs: 27 \\u20AC";
517             ASSERT (result != NULL);
518             ASSERT (strcmp (result, expected) == 0);
519             free (result);
520           }
521           break;
522         }
523     }
524
525   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
526   for (h = 0; h < SIZEOF (handlers); h++)
527     {
528       enum iconv_ilseq_handler handler = handlers[h];
529       static const char input[] = "\342";
530       char *result = str_cd_iconveh (input,
531                                      cd_utf8_to_88591,
532                                      (iconv_t)(-1), cd_utf8_to_88591,
533                                      handler);
534       ASSERT (result != NULL);
535       ASSERT (strcmp (result, "") == 0);
536       free (result);
537     }
538
539   if (cd_88591_to_88592 != (iconv_t)(-1))
540     iconv_close (cd_88591_to_88592);
541   if (cd_88592_to_88591 != (iconv_t)(-1))
542     iconv_close (cd_88592_to_88591);
543   iconv_close (cd_88591_to_utf8);
544   iconv_close (cd_utf8_to_88591);
545   iconv_close (cd_88592_to_utf8);
546   iconv_close (cd_utf8_to_88592);
547
548   /* ------------------------- Test mem_iconveh() ------------------------- */
549
550   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
551   for (h = 0; h < SIZEOF (handlers); h++)
552     {
553       enum iconv_ilseq_handler handler = handlers[h];
554       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
555       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
556       for (o = 0; o < 2; o++)
557         {
558           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
559           char *result = NULL;
560           size_t length = 0;
561           int retval = mem_iconveh (input, strlen (input),
562                                     "ISO-8859-2", "ISO-8859-1",
563                                     handler,
564                                     offsets,
565                                     &result, &length);
566           ASSERT (retval == 0);
567           ASSERT (length == strlen (expected));
568           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
569           if (o)
570             {
571               for (i = 0; i < 37; i++)
572                 ASSERT (offsets[i] == i);
573               ASSERT (offsets[37] == MAGIC);
574               free (offsets);
575             }
576           free (result);
577         }
578     }
579
580   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
581   for (h = 0; h < SIZEOF (handlers); h++)
582     {
583       enum iconv_ilseq_handler handler = handlers[h];
584       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
585       for (o = 0; o < 2; o++)
586         {
587           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
588           char *result = NULL;
589           size_t length = 0;
590           int retval = mem_iconveh (input, strlen (input),
591                                     "ISO-8859-2", "ISO-8859-1",
592                                     handler,
593                                     offsets,
594                                     &result, &length);
595           switch (handler)
596             {
597             case iconveh_error:
598               ASSERT (retval == -1 && errno == EILSEQ);
599               ASSERT (result == NULL);
600               if (o)
601                 free (offsets);
602               break;
603             case iconveh_question_mark:
604               {
605                 static const char expected[] = "Rafa? Maszkowski";
606                 ASSERT (retval == 0);
607                 ASSERT (length == strlen (expected));
608                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
609                 if (o)
610                   {
611                     for (i = 0; i < 16; i++)
612                       ASSERT (offsets[i] == i);
613                     ASSERT (offsets[16] == MAGIC);
614                     free (offsets);
615                   }
616                 free (result);
617               }
618               break;
619             case iconveh_escape_sequence:
620               {
621                 static const char expected[] = "Rafa\\u0142 Maszkowski";
622                 ASSERT (retval == 0);
623                 ASSERT (length == strlen (expected));
624                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
625                 if (o)
626                   {
627                     for (i = 0; i < 16; i++)
628                       ASSERT (offsets[i] == (i < 5 ? i :
629                                              i + 5));
630                     ASSERT (offsets[16] == MAGIC);
631                     free (offsets);
632                   }
633                 free (result);
634               }
635               break;
636             }
637         }
638     }
639
640   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
641   for (h = 0; h < SIZEOF (handlers); h++)
642     {
643       enum iconv_ilseq_handler handler = handlers[h];
644       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
645       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
646       for (o = 0; o < 2; o++)
647         {
648           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
649           char *result = NULL;
650           size_t length = 0;
651           int retval = mem_iconveh (input, strlen (input),
652                                     "ISO-8859-1", "UTF-8",
653                                     handler,
654                                     offsets,
655                                     &result, &length);
656           ASSERT (retval == 0);
657           ASSERT (length == strlen (expected));
658           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
659           if (o)
660             {
661               for (i = 0; i < 37; i++)
662                 ASSERT (offsets[i] == (i < 1 ? i :
663                                        i < 12 ? i + 1 :
664                                        i < 18 ? i + 2 :
665                                        i + 3));
666               ASSERT (offsets[37] == MAGIC);
667               free (offsets);
668             }
669           free (result);
670         }
671     }
672
673   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
674   for (h = 0; h < SIZEOF (handlers); h++)
675     {
676       enum iconv_ilseq_handler handler = handlers[h];
677       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
678       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
679       for (o = 0; o < 2; o++)
680         {
681           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
682           char *result = NULL;
683           size_t length = 0;
684           int retval = mem_iconveh (input, strlen (input),
685                                     "UTF-8", "ISO-8859-1",
686                                     handler,
687                                     offsets,
688                                     &result, &length);
689           ASSERT (retval == 0);
690           ASSERT (length == strlen (expected));
691           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
692           if (o)
693             {
694               for (i = 0; i < 41; i++)
695                 ASSERT (offsets[i] == (i < 1 ? i :
696                                        i == 1 ? (size_t)(-1) :
697                                        i < 13 ? i - 1 :
698                                        i == 13 ? (size_t)(-1) :
699                                        i < 20 ? i - 2 :
700                                        i == 20 ? (size_t)(-1) :
701                                        i < 40 ? i - 3 :
702                                        (size_t)(-1)));
703               ASSERT (offsets[41] == MAGIC);
704               free (offsets);
705             }
706           free (result);
707         }
708     }
709
710   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
711   for (h = 0; h < SIZEOF (handlers); h++)
712     {
713       enum iconv_ilseq_handler handler = handlers[h];
714       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
715       for (o = 0; o < 2; o++)
716         {
717           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
718           char *result = NULL;
719           size_t length = 0;
720           int retval = mem_iconveh (input, strlen (input),
721                                     "UTF-8", "ISO-8859-1",
722                                     handler,
723                                     offsets,
724                                     &result, &length);
725           switch (handler)
726             {
727             case iconveh_error:
728               ASSERT (retval == -1 && errno == EILSEQ);
729               ASSERT (result == NULL);
730               if (o)
731                 free (offsets);
732               break;
733             case iconveh_question_mark:
734               {
735                 static const char expected[] = "Rafa? Maszkowski";
736                 ASSERT (retval == 0);
737                 ASSERT (length == strlen (expected));
738                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
739                 if (o)
740                   {
741                     for (i = 0; i < 17; i++)
742                       ASSERT (offsets[i] == (i < 5 ? i :
743                                              i == 5 ? (size_t)(-1) :
744                                              i - 1));
745                     ASSERT (offsets[17] == MAGIC);
746                     free (offsets);
747                   }
748                 free (result);
749               }
750               break;
751             case iconveh_escape_sequence:
752               {
753                 static const char expected[] = "Rafa\\u0142 Maszkowski";
754                 ASSERT (retval == 0);
755                 ASSERT (length == strlen (expected));
756                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
757                 if (o)
758                   {
759                     for (i = 0; i < 17; i++)
760                       ASSERT (offsets[i] == (i < 5 ? i :
761                                              i == 5 ? (size_t)(-1) :
762                                              i + 4));
763                     ASSERT (offsets[17] == MAGIC);
764                     free (offsets);
765                   }
766                 free (result);
767               }
768               break;
769             }
770         }
771     }
772
773   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
774   for (h = 0; h < SIZEOF (handlers); h++)
775     {
776       enum iconv_ilseq_handler handler = handlers[h];
777       static const char input[] = "\342";
778       for (o = 0; o < 2; o++)
779         {
780           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
781           char *result = NULL;
782           size_t length = 0;
783           int retval = mem_iconveh (input, strlen (input),
784                                     "UTF-8", "ISO-8859-1",
785                                     handler,
786                                     offsets,
787                                     &result, &length);
788           ASSERT (retval == 0);
789           ASSERT (length == 0);
790           if (o)
791             {
792               ASSERT (offsets[0] == 0);
793               ASSERT (offsets[1] == MAGIC);
794               free (offsets);
795             }
796           free (result);
797         }
798     }
799
800   /* ------------------------- Test str_iconveh() ------------------------- */
801
802   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
803   for (h = 0; h < SIZEOF (handlers); h++)
804     {
805       enum iconv_ilseq_handler handler = handlers[h];
806       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
807       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
808       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
809       ASSERT (result != NULL);
810       ASSERT (strcmp (result, expected) == 0);
811       free (result);
812     }
813
814   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
815   for (h = 0; h < SIZEOF (handlers); h++)
816     {
817       enum iconv_ilseq_handler handler = handlers[h];
818       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
819       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
820       switch (handler)
821         {
822         case iconveh_error:
823           ASSERT (result == NULL && errno == EILSEQ);
824           break;
825         case iconveh_question_mark:
826           {
827             static const char expected[] = "Rafa? Maszkowski";
828             ASSERT (result != NULL);
829             ASSERT (strcmp (result, expected) == 0);
830             free (result);
831           }
832           break;
833         case iconveh_escape_sequence:
834           {
835             static const char expected[] = "Rafa\\u0142 Maszkowski";
836             ASSERT (result != NULL);
837             ASSERT (strcmp (result, expected) == 0);
838             free (result);
839           }
840           break;
841         }
842     }
843
844   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
845   for (h = 0; h < SIZEOF (handlers); h++)
846     {
847       enum iconv_ilseq_handler handler = handlers[h];
848       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
849       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
850       char *result = str_iconveh (input, "ISO-8859-1", "UTF-8", handler);
851       ASSERT (result != NULL);
852       ASSERT (strcmp (result, expected) == 0);
853       free (result);
854     }
855
856   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
857   for (h = 0; h < SIZEOF (handlers); h++)
858     {
859       enum iconv_ilseq_handler handler = handlers[h];
860       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
861       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
862       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
863       ASSERT (result != NULL);
864       ASSERT (strcmp (result, expected) == 0);
865       free (result);
866     }
867
868   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
869   for (h = 0; h < SIZEOF (handlers); h++)
870     {
871       enum iconv_ilseq_handler handler = handlers[h];
872       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
873       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
874       switch (handler)
875         {
876         case iconveh_error:
877           ASSERT (result == NULL && errno == EILSEQ);
878           break;
879         case iconveh_question_mark:
880           {
881             static const char expected[] = "Costs: 27 ?";
882             ASSERT (result != NULL);
883             ASSERT (strcmp (result, expected) == 0);
884             free (result);
885           }
886           break;
887         case iconveh_escape_sequence:
888           {
889             static const char expected[] = "Costs: 27 \\u20AC";
890             ASSERT (result != NULL);
891             ASSERT (strcmp (result, expected) == 0);
892             free (result);
893           }
894           break;
895         }
896     }
897
898   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
899   for (h = 0; h < SIZEOF (handlers); h++)
900     {
901       enum iconv_ilseq_handler handler = handlers[h];
902       static const char input[] = "\342";
903       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
904       ASSERT (result != NULL);
905       ASSERT (strcmp (result, "") == 0);
906       free (result);
907     }
908
909 #endif
910
911   return 0;
912 }