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