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