Merge branch 'upstream' into stable
[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 indirect;
62   size_t h;
63   size_t o;
64   size_t i;
65
66 #if HAVE_ICONV
67   /* Assume that iconv() supports at least the encodings ASCII, ISO-8859-1,
68      ISO-8859-2, and UTF-8.  */
69   iconv_t cd_ascii_to_88591 = iconv_open ("ISO-8859-1", "ASCII");
70   iconv_t cd_88591_to_88592 = iconv_open ("ISO-8859-2", "ISO-8859-1");
71   iconv_t cd_88592_to_88591 = iconv_open ("ISO-8859-1", "ISO-8859-2");
72   iconv_t cd_ascii_to_utf8 = iconv_open ("UTF-8", "ASCII");
73   iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1");
74   iconv_t cd_utf8_to_88591 = iconv_open ("ISO-8859-1", "UTF-8");
75   iconv_t cd_88592_to_utf8 = iconv_open ("UTF-8", "ISO-8859-2");
76   iconv_t cd_utf8_to_88592 = iconv_open ("ISO-8859-2", "UTF-8");
77   iconv_t cd_utf7_to_utf8 = iconv_open ("UTF-8", "UTF-7");
78   iconveh_t cdeh_ascii_to_88591;
79   iconveh_t cdeh_ascii_to_88591_indirectly;
80   iconveh_t cdeh_88592_to_88591;
81   iconveh_t cdeh_88592_to_88591_indirectly;
82   iconveh_t cdeh_ascii_to_utf8;
83   iconveh_t cdeh_88591_to_utf8;
84   iconveh_t cdeh_utf8_to_88591;
85   iconveh_t cdeh_utf7_to_utf8;
86
87   ASSERT (cd_ascii_to_utf8 != (iconv_t)(-1));
88   ASSERT (cd_88591_to_utf8 != (iconv_t)(-1));
89   ASSERT (cd_utf8_to_88591 != (iconv_t)(-1));
90   ASSERT (cd_88592_to_utf8 != (iconv_t)(-1));
91   ASSERT (cd_utf8_to_88592 != (iconv_t)(-1));
92
93   cdeh_ascii_to_88591.cd = cd_ascii_to_88591;
94   cdeh_ascii_to_88591.cd1 = cd_ascii_to_utf8;
95   cdeh_ascii_to_88591.cd2 = cd_utf8_to_88591;
96
97   cdeh_ascii_to_88591_indirectly.cd = (iconv_t)(-1);
98   cdeh_ascii_to_88591_indirectly.cd1 = cd_ascii_to_utf8;
99   cdeh_ascii_to_88591_indirectly.cd2 = cd_utf8_to_88591;
100
101   cdeh_88592_to_88591.cd = cd_88592_to_88591;
102   cdeh_88592_to_88591.cd1 = cd_88592_to_utf8;
103   cdeh_88592_to_88591.cd2 = cd_utf8_to_88591;
104
105   cdeh_88592_to_88591_indirectly.cd = (iconv_t)(-1);
106   cdeh_88592_to_88591_indirectly.cd1 = cd_88592_to_utf8;
107   cdeh_88592_to_88591_indirectly.cd2 = cd_utf8_to_88591;
108
109   cdeh_ascii_to_utf8.cd = cd_ascii_to_utf8;
110   cdeh_ascii_to_utf8.cd1 = cd_ascii_to_utf8;
111   cdeh_ascii_to_utf8.cd2 = (iconv_t)(-1);
112
113   cdeh_88591_to_utf8.cd = cd_88591_to_utf8;
114   cdeh_88591_to_utf8.cd1 = cd_88591_to_utf8;
115   cdeh_88591_to_utf8.cd2 = (iconv_t)(-1);
116
117   cdeh_utf8_to_88591.cd = cd_utf8_to_88591;
118   cdeh_utf8_to_88591.cd1 = (iconv_t)(-1);
119   cdeh_utf8_to_88591.cd2 = cd_utf8_to_88591;
120
121   cdeh_utf7_to_utf8.cd = cd_utf7_to_utf8;
122   cdeh_utf7_to_utf8.cd1 = cd_utf7_to_utf8;
123   cdeh_utf7_to_utf8.cd2 = (iconv_t)(-1);
124
125   /* ------------------------ Test mem_cd_iconveh() ------------------------ */
126
127   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
128   for (indirect = 0; indirect <= 1; indirect++)
129     {
130       for (h = 0; h < SIZEOF (handlers); h++)
131         {
132           enum iconv_ilseq_handler handler = handlers[h];
133           static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
134           static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
135           for (o = 0; o < 2; o++)
136             {
137               size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
138               char *result = NULL;
139               size_t length = 0;
140               int retval = mem_cd_iconveh (input, strlen (input),
141                                            (indirect
142                                             ? &cdeh_88592_to_88591_indirectly
143                                             : &cdeh_88592_to_88591),
144                                            handler,
145                                            offsets,
146                                            &result, &length);
147               ASSERT (retval == 0);
148               ASSERT (length == strlen (expected));
149               ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
150               if (o)
151                 {
152                   for (i = 0; i < 37; i++)
153                     ASSERT (offsets[i] == i);
154                   ASSERT (offsets[37] == MAGIC);
155                   free (offsets);
156                 }
157               free (result);
158             }
159         }
160     }
161
162   /* Test conversion from ASCII to ISO-8859-1 with invalid input (EILSEQ).  */
163   for (indirect = 0; indirect <= 1; indirect++)
164     {
165       for (h = 0; h < SIZEOF (handlers); h++)
166         {
167           enum iconv_ilseq_handler handler = handlers[h];
168           static const char input[] = "Rafa\263 Maszkowski"; /* Rafa? Maszkowski */
169           for (o = 0; o < 2; o++)
170             {
171               size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
172               char *result = NULL;
173               size_t length = 0;
174               int retval = mem_cd_iconveh (input, strlen (input),
175                                            (indirect
176                                             ? &cdeh_ascii_to_88591_indirectly
177                                             : &cdeh_ascii_to_88591),
178                                            handler,
179                                            offsets,
180                                            &result, &length);
181               switch (handler)
182                 {
183                 case iconveh_error:
184                   ASSERT (retval == -1 && errno == EILSEQ);
185                   ASSERT (result == NULL);
186                   if (o)
187                     free (offsets);
188                   break;
189                 case iconveh_question_mark:
190                 case iconveh_escape_sequence:
191                   {
192                     static const char expected[] = "Rafa? Maszkowski";
193                     ASSERT (retval == 0);
194                     ASSERT (length == strlen (expected));
195                     ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
196                     if (o)
197                       {
198                         for (i = 0; i < 16; i++)
199                           ASSERT (offsets[i] == i);
200                         ASSERT (offsets[16] == MAGIC);
201                         free (offsets);
202                       }
203                     free (result);
204                   }
205                   break;
206                 }
207             }
208         }
209     }
210
211   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
212   for (indirect = 0; indirect <= 1; indirect++)
213     {
214       for (h = 0; h < SIZEOF (handlers); h++)
215         {
216           enum iconv_ilseq_handler handler = handlers[h];
217           static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
218           for (o = 0; o < 2; o++)
219             {
220               size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
221               char *result = NULL;
222               size_t length = 0;
223               int retval = mem_cd_iconveh (input, strlen (input),
224                                            (indirect
225                                             ? &cdeh_88592_to_88591_indirectly
226                                             : &cdeh_88592_to_88591),
227                                            handler,
228                                            offsets,
229                                            &result, &length);
230               switch (handler)
231                 {
232                 case iconveh_error:
233                   ASSERT (retval == -1 && errno == EILSEQ);
234                   ASSERT (result == NULL);
235                   if (o)
236                     free (offsets);
237                   break;
238                 case iconveh_question_mark:
239                   {
240                     static const char expected[] = "Rafa? Maszkowski";
241                     ASSERT (retval == 0);
242                     ASSERT (length == strlen (expected));
243                     ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
244                     if (o)
245                       {
246                         for (i = 0; i < 16; i++)
247                           ASSERT (offsets[i] == i);
248                         ASSERT (offsets[16] == MAGIC);
249                         free (offsets);
250                       }
251                     free (result);
252                   }
253                   break;
254                 case iconveh_escape_sequence:
255                   {
256                     static const char expected[] = "Rafa\\u0142 Maszkowski";
257                     ASSERT (retval == 0);
258                     ASSERT (length == strlen (expected));
259                     ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
260                     if (o)
261                       {
262                         for (i = 0; i < 16; i++)
263                           ASSERT (offsets[i] == (i < 5 ? i :
264                                                  i + 5));
265                         ASSERT (offsets[16] == MAGIC);
266                         free (offsets);
267                       }
268                     free (result);
269                   }
270                   break;
271                 }
272             }
273         }
274     }
275
276   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
277   for (h = 0; h < SIZEOF (handlers); h++)
278     {
279       enum iconv_ilseq_handler handler = handlers[h];
280       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
281       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
282       for (o = 0; o < 2; o++)
283         {
284           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
285           char *result = NULL;
286           size_t length = 0;
287           int retval = mem_cd_iconveh (input, strlen (input),
288                                        &cdeh_88591_to_utf8,
289                                        handler,
290                                        offsets,
291                                        &result, &length);
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 < 37; i++)
298                 ASSERT (offsets[i] == (i < 1 ? i :
299                                        i < 12 ? i + 1 :
300                                        i < 18 ? i + 2 :
301                                        i + 3));
302               ASSERT (offsets[37] == MAGIC);
303               free (offsets);
304             }
305           free (result);
306         }
307     }
308
309   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
310   for (h = 0; h < SIZEOF (handlers); h++)
311     {
312       enum iconv_ilseq_handler handler = handlers[h];
313       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
314       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
315       for (o = 0; o < 2; o++)
316         {
317           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
318           char *result = NULL;
319           size_t length = 0;
320           int retval = mem_cd_iconveh (input, strlen (input),
321                                        &cdeh_utf8_to_88591,
322                                        handler,
323                                        offsets,
324                                        &result, &length);
325           ASSERT (retval == 0);
326           ASSERT (length == strlen (expected));
327           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
328           if (o)
329             {
330               for (i = 0; i < 41; i++)
331                 ASSERT (offsets[i] == (i < 1 ? i :
332                                        i == 1 ? (size_t)(-1) :
333                                        i < 13 ? i - 1 :
334                                        i == 13 ? (size_t)(-1) :
335                                        i < 20 ? i - 2 :
336                                        i == 20 ? (size_t)(-1) :
337                                        i < 40 ? i - 3 :
338                                        (size_t)(-1)));
339               ASSERT (offsets[41] == MAGIC);
340               free (offsets);
341             }
342           free (result);
343         }
344     }
345
346   /* Test conversion from ASCII to UTF-8 with invalid input (EILSEQ).  */
347   for (h = 0; h < SIZEOF (handlers); h++)
348     {
349       enum iconv_ilseq_handler handler = handlers[h];
350       static const char input[] = "Rafa\263 Maszkowski"; /* Rafa? Maszkowski */
351       for (o = 0; o < 2; o++)
352         {
353           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
354           char *result = NULL;
355           size_t length = 0;
356           int retval = mem_cd_iconveh (input, strlen (input),
357                                        &cdeh_ascii_to_utf8,
358                                        handler,
359                                        offsets,
360                                        &result, &length);
361           switch (handler)
362             {
363             case iconveh_error:
364               ASSERT (retval == -1 && errno == EILSEQ);
365               ASSERT (result == NULL);
366               if (o)
367                 free (offsets);
368               break;
369             case iconveh_question_mark:
370             case iconveh_escape_sequence:
371               {
372                 static const char expected[] = "Rafa? Maszkowski";
373                 ASSERT (retval == 0);
374                 ASSERT (length == strlen (expected));
375                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
376                 if (o)
377                   {
378                     for (i = 0; i < 16; i++)
379                       ASSERT (offsets[i] == i);
380                     ASSERT (offsets[16] == MAGIC);
381                     free (offsets);
382                   }
383                 free (result);
384               }
385               break;
386             }
387         }
388     }
389
390   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
391   for (h = 0; h < SIZEOF (handlers); h++)
392     {
393       enum iconv_ilseq_handler handler = handlers[h];
394       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
395       for (o = 0; o < 2; o++)
396         {
397           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
398           char *result = NULL;
399           size_t length = 0;
400           int retval = mem_cd_iconveh (input, strlen (input),
401                                        &cdeh_utf8_to_88591,
402                                        handler,
403                                        offsets,
404                                        &result, &length);
405           switch (handler)
406             {
407             case iconveh_error:
408               ASSERT (retval == -1 && errno == EILSEQ);
409               ASSERT (result == NULL);
410               if (o)
411                 free (offsets);
412               break;
413             case iconveh_question_mark:
414               {
415                 static const char expected[] = "Rafa? Maszkowski";
416                 ASSERT (retval == 0);
417                 ASSERT (length == strlen (expected));
418                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
419                 if (o)
420                   {
421                     for (i = 0; i < 17; i++)
422                       ASSERT (offsets[i] == (i < 5 ? i :
423                                              i == 5 ? (size_t)(-1) :
424                                              i - 1));
425                     ASSERT (offsets[17] == MAGIC);
426                     free (offsets);
427                   }
428                 free (result);
429               }
430               break;
431             case iconveh_escape_sequence:
432               {
433                 static const char expected[] = "Rafa\\u0142 Maszkowski";
434                 ASSERT (retval == 0);
435                 ASSERT (length == strlen (expected));
436                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
437                 if (o)
438                   {
439                     for (i = 0; i < 17; i++)
440                       ASSERT (offsets[i] == (i < 5 ? i :
441                                              i == 5 ? (size_t)(-1) :
442                                              i + 4));
443                     ASSERT (offsets[17] == MAGIC);
444                     free (offsets);
445                   }
446                 free (result);
447               }
448               break;
449             }
450         }
451     }
452
453   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
454   for (h = 0; h < SIZEOF (handlers); h++)
455     {
456       enum iconv_ilseq_handler handler = handlers[h];
457       static const char input[] = "\342";
458       for (o = 0; o < 2; o++)
459         {
460           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
461           char *result = NULL;
462           size_t length = 0;
463           int retval = mem_cd_iconveh (input, strlen (input),
464                                        &cdeh_utf8_to_88591,
465                                        handler,
466                                        offsets,
467                                        &result, &length);
468           ASSERT (retval == 0);
469           ASSERT (length == 0);
470           if (o)
471             {
472               ASSERT (offsets[0] == 0);
473               ASSERT (offsets[1] == MAGIC);
474               free (offsets);
475             }
476           free (result);
477         }
478     }
479
480   if (cd_utf7_to_utf8 != (iconv_t)(-1))
481     {
482       /* Disabled on Solaris, because Solaris 9 iconv() is buggy: it returns
483          -1 / EILSEQ when converting the 7th byte of the input "+VDLYP9hA".  */
484 # if !(defined __sun && !defined _LIBICONV_VERSION)
485       /* Test conversion from UTF-7 to UTF-8 with EINVAL.  */
486       for (h = 0; h < SIZEOF (handlers); h++)
487         {
488           enum iconv_ilseq_handler handler = handlers[h];
489           /* This is base64 encoded 0x54 0x32 0xD8 0x3F 0xD8 0x40.  It would
490              convert to U+5432 U+D83F U+D840 but these are Unicode surrogates.  */
491           static const char input[] = "+VDLYP9hA";
492           static const char expected1[] = "\345\220\262"; /* 吲 glibc */
493           static const char expected2[] = ""; /* libiconv */
494           char *result = NULL;
495           size_t length = 0;
496           int retval = mem_cd_iconveh (input, 7,
497                                        &cdeh_utf7_to_utf8,
498                                        handler,
499                                        NULL,
500                                        &result, &length);
501           ASSERT (retval == 0);
502           ASSERT (length == strlen (expected1) || length == strlen (expected2));
503           ASSERT (result != NULL);
504           if (length == strlen (expected1))
505             ASSERT (memcmp (result, expected1, strlen (expected1)) == 0);
506           else
507             ASSERT (memcmp (result, expected2, strlen (expected2)) == 0);
508           free (result);
509         }
510
511       /* Test conversion from UTF-7 to UTF-8 with EILSEQ.  */
512       for (h = 0; h < SIZEOF (handlers); h++)
513         {
514           enum iconv_ilseq_handler handler = handlers[h];
515           /* This is base64 encoded 0xD8 0x3F 0xD8 0x40 0xD8 0x41.  It would
516              convert to U+D83F U+D840 U+D841 but these are Unicode surrogates.  */
517           static const char input[] = "+2D/YQNhB";
518           char *result = NULL;
519           size_t length = 0;
520           int retval = mem_cd_iconveh (input, strlen (input),
521                                        &cdeh_utf7_to_utf8,
522                                        handler,
523                                        NULL,
524                                        &result, &length);
525           switch (handler)
526             {
527             case iconveh_error:
528               ASSERT (retval == -1 && errno == EILSEQ);
529               ASSERT (result == NULL);
530               break;
531             case iconveh_question_mark:
532             case iconveh_escape_sequence:
533               {
534                 /* glibc result */
535                 static const char expected1[] = "?????";
536                 /* libiconv <= 1.12 result */
537                 static const char expected2[] = "?2D/YQNhB";
538                 /* libiconv behaviour changed in version 1.13: the result is
539                    '?' U+0FF6 U+1036; this is U+D83F U+D840 U+D841 shifted left
540                    by 6 bits.  */
541                 static const char expected3[] = "?\340\277\266\341\200\266";
542                 ASSERT (retval == 0);
543                 ASSERT (length == strlen (expected1)
544                         || length == strlen (expected2)
545                         || length == strlen (expected3));
546                 ASSERT (result != NULL);
547                 if (length == strlen (expected1))
548                   ASSERT (memcmp (result, expected1, strlen (expected1)) == 0);
549                 else if (length == strlen (expected2))
550                   ASSERT (memcmp (result, expected2, strlen (expected2)) == 0);
551                 else
552                   ASSERT (memcmp (result, expected3, strlen (expected3)) == 0);
553                 free (result);
554               }
555               break;
556             }
557         }
558 # endif
559     }
560
561   /* ------------------------ Test str_cd_iconveh() ------------------------ */
562
563   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
564   for (indirect = 0; indirect <= 1; indirect++)
565     {
566       for (h = 0; h < SIZEOF (handlers); h++)
567         {
568           enum iconv_ilseq_handler handler = handlers[h];
569           static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
570           static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
571           char *result = str_cd_iconveh (input,
572                                          (indirect
573                                           ? &cdeh_88592_to_88591_indirectly
574                                           : &cdeh_88592_to_88591),
575                                          handler);
576           ASSERT (result != NULL);
577           ASSERT (strcmp (result, expected) == 0);
578           free (result);
579         }
580     }
581
582   /* Test conversion from ASCII to ISO-8859-1 with invalid input (EILSEQ).  */
583   for (indirect = 0; indirect <= 1; indirect++)
584     {
585       for (h = 0; h < SIZEOF (handlers); h++)
586         {
587           enum iconv_ilseq_handler handler = handlers[h];
588           static const char input[] = "Rafa\263 Maszkowski"; /* Rafa? Maszkowski */
589           char *result = str_cd_iconveh (input,
590                                          (indirect
591                                           ? &cdeh_ascii_to_88591_indirectly
592                                           : &cdeh_ascii_to_88591),
593                                          handler);
594           switch (handler)
595             {
596             case iconveh_error:
597               ASSERT (result == NULL && errno == EILSEQ);
598               break;
599             case iconveh_question_mark:
600             case iconveh_escape_sequence:
601               {
602                 static const char expected[] = "Rafa? Maszkowski";
603                 ASSERT (result != NULL);
604                 ASSERT (strcmp (result, expected) == 0);
605                 free (result);
606               }
607               break;
608             }
609         }
610     }
611
612   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
613   for (indirect = 0; indirect <= 1; indirect++)
614     {
615       for (h = 0; h < SIZEOF (handlers); h++)
616         {
617           enum iconv_ilseq_handler handler = handlers[h];
618           static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
619           char *result = str_cd_iconveh (input,
620                                          (indirect
621                                           ? &cdeh_88592_to_88591_indirectly
622                                           : &cdeh_88592_to_88591),
623                                          handler);
624           switch (handler)
625             {
626             case iconveh_error:
627               ASSERT (result == NULL && errno == EILSEQ);
628               break;
629             case iconveh_question_mark:
630               {
631                 static const char expected[] = "Rafa? Maszkowski";
632                 ASSERT (result != NULL);
633                 ASSERT (strcmp (result, expected) == 0);
634                 free (result);
635               }
636               break;
637             case iconveh_escape_sequence:
638               {
639                 static const char expected[] = "Rafa\\u0142 Maszkowski";
640                 ASSERT (result != NULL);
641                 ASSERT (strcmp (result, expected) == 0);
642                 free (result);
643               }
644               break;
645             }
646         }
647     }
648
649   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
650   for (h = 0; h < SIZEOF (handlers); h++)
651     {
652       enum iconv_ilseq_handler handler = handlers[h];
653       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
654       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
655       char *result = str_cd_iconveh (input,
656                                      &cdeh_88591_to_utf8,
657                                      handler);
658       ASSERT (result != NULL);
659       ASSERT (strcmp (result, expected) == 0);
660       free (result);
661     }
662
663   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
664   for (h = 0; h < SIZEOF (handlers); h++)
665     {
666       enum iconv_ilseq_handler handler = handlers[h];
667       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
668       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
669       char *result = str_cd_iconveh (input,
670                                      &cdeh_utf8_to_88591,
671                                      handler);
672       ASSERT (result != NULL);
673       ASSERT (strcmp (result, expected) == 0);
674       free (result);
675     }
676
677   /* Test conversion from ASCII to UTF-8 with invalid input (EILSEQ).  */
678   for (h = 0; h < SIZEOF (handlers); h++)
679     {
680       enum iconv_ilseq_handler handler = handlers[h];
681       static const char input[] = "Rafa\263 Maszkowski"; /* Rafa? Maszkowski */
682       char *result = str_cd_iconveh (input,
683                                      &cdeh_ascii_to_utf8,
684                                      handler);
685       switch (handler)
686         {
687         case iconveh_error:
688           ASSERT (result == NULL && errno == EILSEQ);
689           break;
690         case iconveh_question_mark:
691         case iconveh_escape_sequence:
692           {
693             static const char expected[] = "Rafa? Maszkowski";
694             ASSERT (result != NULL);
695             ASSERT (strcmp (result, expected) == 0);
696             free (result);
697           }
698           break;
699         }
700     }
701
702   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
703   for (h = 0; h < SIZEOF (handlers); h++)
704     {
705       enum iconv_ilseq_handler handler = handlers[h];
706       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
707       char *result = str_cd_iconveh (input,
708                                      &cdeh_utf8_to_88591,
709                                      handler);
710       switch (handler)
711         {
712         case iconveh_error:
713           ASSERT (result == NULL && errno == EILSEQ);
714           break;
715         case iconveh_question_mark:
716           {
717             static const char expected[] = "Costs: 27 ?";
718             ASSERT (result != NULL);
719             ASSERT (strcmp (result, expected) == 0);
720             free (result);
721           }
722           break;
723         case iconveh_escape_sequence:
724           {
725             static const char expected[] = "Costs: 27 \\u20AC";
726             ASSERT (result != NULL);
727             ASSERT (strcmp (result, expected) == 0);
728             free (result);
729           }
730           break;
731         }
732     }
733
734   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
735   for (h = 0; h < SIZEOF (handlers); h++)
736     {
737       enum iconv_ilseq_handler handler = handlers[h];
738       static const char input[] = "\342";
739       char *result = str_cd_iconveh (input,
740                                      &cdeh_utf8_to_88591,
741                                      handler);
742       ASSERT (result != NULL);
743       ASSERT (strcmp (result, "") == 0);
744       free (result);
745     }
746
747   if (cd_88591_to_88592 != (iconv_t)(-1))
748     iconv_close (cd_88591_to_88592);
749   if (cd_88592_to_88591 != (iconv_t)(-1))
750     iconv_close (cd_88592_to_88591);
751   iconv_close (cd_88591_to_utf8);
752   iconv_close (cd_utf8_to_88591);
753   iconv_close (cd_88592_to_utf8);
754   iconv_close (cd_utf8_to_88592);
755
756   /* ------------------------- Test mem_iconveh() ------------------------- */
757
758   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
759   for (h = 0; h < SIZEOF (handlers); h++)
760     {
761       enum iconv_ilseq_handler handler = handlers[h];
762       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
763       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
764       for (o = 0; o < 2; o++)
765         {
766           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
767           char *result = NULL;
768           size_t length = 0;
769           int retval = mem_iconveh (input, strlen (input),
770                                     "ISO-8859-2", "ISO-8859-1",
771                                     handler,
772                                     offsets,
773                                     &result, &length);
774           ASSERT (retval == 0);
775           ASSERT (length == strlen (expected));
776           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
777           if (o)
778             {
779               for (i = 0; i < 37; i++)
780                 ASSERT (offsets[i] == i);
781               ASSERT (offsets[37] == MAGIC);
782               free (offsets);
783             }
784           free (result);
785         }
786     }
787
788   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
789   for (h = 0; h < SIZEOF (handlers); h++)
790     {
791       enum iconv_ilseq_handler handler = handlers[h];
792       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
793       for (o = 0; o < 2; o++)
794         {
795           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
796           char *result = NULL;
797           size_t length = 0;
798           int retval = mem_iconveh (input, strlen (input),
799                                     "ISO-8859-2", "ISO-8859-1",
800                                     handler,
801                                     offsets,
802                                     &result, &length);
803           switch (handler)
804             {
805             case iconveh_error:
806               ASSERT (retval == -1 && errno == EILSEQ);
807               ASSERT (result == NULL);
808               if (o)
809                 free (offsets);
810               break;
811             case iconveh_question_mark:
812               {
813                 static const char expected[] = "Rafa? Maszkowski";
814                 ASSERT (retval == 0);
815                 ASSERT (length == strlen (expected));
816                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
817                 if (o)
818                   {
819                     for (i = 0; i < 16; i++)
820                       ASSERT (offsets[i] == i);
821                     ASSERT (offsets[16] == MAGIC);
822                     free (offsets);
823                   }
824                 free (result);
825               }
826               break;
827             case iconveh_escape_sequence:
828               {
829                 static const char expected[] = "Rafa\\u0142 Maszkowski";
830                 ASSERT (retval == 0);
831                 ASSERT (length == strlen (expected));
832                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
833                 if (o)
834                   {
835                     for (i = 0; i < 16; i++)
836                       ASSERT (offsets[i] == (i < 5 ? i :
837                                              i + 5));
838                     ASSERT (offsets[16] == MAGIC);
839                     free (offsets);
840                   }
841                 free (result);
842               }
843               break;
844             }
845         }
846     }
847
848   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
849   for (h = 0; h < SIZEOF (handlers); h++)
850     {
851       enum iconv_ilseq_handler handler = handlers[h];
852       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
853       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
854       for (o = 0; o < 2; o++)
855         {
856           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
857           char *result = NULL;
858           size_t length = 0;
859           int retval = mem_iconveh (input, strlen (input),
860                                     "ISO-8859-1", "UTF-8",
861                                     handler,
862                                     offsets,
863                                     &result, &length);
864           ASSERT (retval == 0);
865           ASSERT (length == strlen (expected));
866           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
867           if (o)
868             {
869               for (i = 0; i < 37; i++)
870                 ASSERT (offsets[i] == (i < 1 ? i :
871                                        i < 12 ? i + 1 :
872                                        i < 18 ? i + 2 :
873                                        i + 3));
874               ASSERT (offsets[37] == MAGIC);
875               free (offsets);
876             }
877           free (result);
878         }
879     }
880
881   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
882   for (h = 0; h < SIZEOF (handlers); h++)
883     {
884       enum iconv_ilseq_handler handler = handlers[h];
885       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
886       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
887       for (o = 0; o < 2; o++)
888         {
889           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
890           char *result = NULL;
891           size_t length = 0;
892           int retval = mem_iconveh (input, strlen (input),
893                                     "UTF-8", "ISO-8859-1",
894                                     handler,
895                                     offsets,
896                                     &result, &length);
897           ASSERT (retval == 0);
898           ASSERT (length == strlen (expected));
899           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
900           if (o)
901             {
902               for (i = 0; i < 41; i++)
903                 ASSERT (offsets[i] == (i < 1 ? i :
904                                        i == 1 ? (size_t)(-1) :
905                                        i < 13 ? i - 1 :
906                                        i == 13 ? (size_t)(-1) :
907                                        i < 20 ? i - 2 :
908                                        i == 20 ? (size_t)(-1) :
909                                        i < 40 ? i - 3 :
910                                        (size_t)(-1)));
911               ASSERT (offsets[41] == MAGIC);
912               free (offsets);
913             }
914           free (result);
915         }
916     }
917
918   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
919   for (h = 0; h < SIZEOF (handlers); h++)
920     {
921       enum iconv_ilseq_handler handler = handlers[h];
922       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
923       for (o = 0; o < 2; o++)
924         {
925           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
926           char *result = NULL;
927           size_t length = 0;
928           int retval = mem_iconveh (input, strlen (input),
929                                     "UTF-8", "ISO-8859-1",
930                                     handler,
931                                     offsets,
932                                     &result, &length);
933           switch (handler)
934             {
935             case iconveh_error:
936               ASSERT (retval == -1 && errno == EILSEQ);
937               ASSERT (result == NULL);
938               if (o)
939                 free (offsets);
940               break;
941             case iconveh_question_mark:
942               {
943                 static const char expected[] = "Rafa? Maszkowski";
944                 ASSERT (retval == 0);
945                 ASSERT (length == strlen (expected));
946                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
947                 if (o)
948                   {
949                     for (i = 0; i < 17; i++)
950                       ASSERT (offsets[i] == (i < 5 ? i :
951                                              i == 5 ? (size_t)(-1) :
952                                              i - 1));
953                     ASSERT (offsets[17] == MAGIC);
954                     free (offsets);
955                   }
956                 free (result);
957               }
958               break;
959             case iconveh_escape_sequence:
960               {
961                 static const char expected[] = "Rafa\\u0142 Maszkowski";
962                 ASSERT (retval == 0);
963                 ASSERT (length == strlen (expected));
964                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
965                 if (o)
966                   {
967                     for (i = 0; i < 17; i++)
968                       ASSERT (offsets[i] == (i < 5 ? i :
969                                              i == 5 ? (size_t)(-1) :
970                                              i + 4));
971                     ASSERT (offsets[17] == MAGIC);
972                     free (offsets);
973                   }
974                 free (result);
975               }
976               break;
977             }
978         }
979     }
980
981   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
982   for (h = 0; h < SIZEOF (handlers); h++)
983     {
984       enum iconv_ilseq_handler handler = handlers[h];
985       static const char input[] = "\342";
986       for (o = 0; o < 2; o++)
987         {
988           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
989           char *result = NULL;
990           size_t length = 0;
991           int retval = mem_iconveh (input, strlen (input),
992                                     "UTF-8", "ISO-8859-1",
993                                     handler,
994                                     offsets,
995                                     &result, &length);
996           ASSERT (retval == 0);
997           ASSERT (length == 0);
998           if (o)
999             {
1000               ASSERT (offsets[0] == 0);
1001               ASSERT (offsets[1] == MAGIC);
1002               free (offsets);
1003             }
1004           free (result);
1005         }
1006     }
1007
1008   /* ------------------------- Test str_iconveh() ------------------------- */
1009
1010   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
1011   for (h = 0; h < SIZEOF (handlers); h++)
1012     {
1013       enum iconv_ilseq_handler handler = handlers[h];
1014       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
1015       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
1016       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
1017       ASSERT (result != NULL);
1018       ASSERT (strcmp (result, expected) == 0);
1019       free (result);
1020     }
1021
1022   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
1023   for (h = 0; h < SIZEOF (handlers); h++)
1024     {
1025       enum iconv_ilseq_handler handler = handlers[h];
1026       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
1027       char *result = str_iconveh (input, "ISO-8859-2", "ISO-8859-1", handler);
1028       switch (handler)
1029         {
1030         case iconveh_error:
1031           ASSERT (result == NULL && errno == EILSEQ);
1032           break;
1033         case iconveh_question_mark:
1034           {
1035             static const char expected[] = "Rafa? Maszkowski";
1036             ASSERT (result != NULL);
1037             ASSERT (strcmp (result, expected) == 0);
1038             free (result);
1039           }
1040           break;
1041         case iconveh_escape_sequence:
1042           {
1043             static const char expected[] = "Rafa\\u0142 Maszkowski";
1044             ASSERT (result != NULL);
1045             ASSERT (strcmp (result, expected) == 0);
1046             free (result);
1047           }
1048           break;
1049         }
1050     }
1051
1052   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
1053   for (h = 0; h < SIZEOF (handlers); h++)
1054     {
1055       enum iconv_ilseq_handler handler = handlers[h];
1056       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
1057       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
1058       char *result = str_iconveh (input, "ISO-8859-1", "UTF-8", handler);
1059       ASSERT (result != NULL);
1060       ASSERT (strcmp (result, expected) == 0);
1061       free (result);
1062     }
1063
1064   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
1065   for (h = 0; h < SIZEOF (handlers); h++)
1066     {
1067       enum iconv_ilseq_handler handler = handlers[h];
1068       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
1069       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
1070       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
1071       ASSERT (result != NULL);
1072       ASSERT (strcmp (result, expected) == 0);
1073       free (result);
1074     }
1075
1076   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
1077   for (h = 0; h < SIZEOF (handlers); h++)
1078     {
1079       enum iconv_ilseq_handler handler = handlers[h];
1080       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
1081       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
1082       switch (handler)
1083         {
1084         case iconveh_error:
1085           ASSERT (result == NULL && errno == EILSEQ);
1086           break;
1087         case iconveh_question_mark:
1088           {
1089             static const char expected[] = "Costs: 27 ?";
1090             ASSERT (result != NULL);
1091             ASSERT (strcmp (result, expected) == 0);
1092             free (result);
1093           }
1094           break;
1095         case iconveh_escape_sequence:
1096           {
1097             static const char expected[] = "Costs: 27 \\u20AC";
1098             ASSERT (result != NULL);
1099             ASSERT (strcmp (result, expected) == 0);
1100             free (result);
1101           }
1102           break;
1103         }
1104     }
1105
1106   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
1107   for (h = 0; h < SIZEOF (handlers); h++)
1108     {
1109       enum iconv_ilseq_handler handler = handlers[h];
1110       static const char input[] = "\342";
1111       char *result = str_iconveh (input, "UTF-8", "ISO-8859-1", handler);
1112       ASSERT (result != NULL);
1113       ASSERT (strcmp (result, "") == 0);
1114       free (result);
1115     }
1116
1117 #endif
1118
1119   return 0;
1120 }