* MODULES.html.sh: Add include_next.
[gnulib.git] / tests / test-striconveha.c
1 /* Test of character set conversion with error handling and autodetection.
2    Copyright (C) 2007 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 2, or (at your option)
7    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, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
19
20 #include <config.h>
21
22 #include "striconveha.h"
23
24 #if HAVE_ICONV
25 # include <iconv.h>
26 #endif
27
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #define SIZEOF(array) (sizeof (array) / sizeof (array[0]))
34 #define ASSERT(expr) \
35   do                                                                         \
36     {                                                                        \
37       if (!(expr))                                                           \
38         {                                                                    \
39           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
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
69   /* ------------------------- Test mem_iconveha() ------------------------- */
70
71   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
72   for (h = 0; h < SIZEOF (handlers); h++)
73     {
74       enum iconv_ilseq_handler handler = handlers[h];
75       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
76       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
77       for (o = 0; o < 2; o++)
78         {
79           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
80           char *result = NULL;
81           size_t length = 0;
82           int retval = mem_iconveha (input, strlen (input),
83                                      "ISO-8859-2", "ISO-8859-1",
84                                      false, handler,
85                                      offsets,
86                                      &result, &length);
87           ASSERT (retval == 0);
88           ASSERT (length == strlen (expected));
89           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
90           if (o)
91             {
92               for (i = 0; i < 37; i++)
93                 ASSERT (offsets[i] == i);
94               ASSERT (offsets[37] == MAGIC);
95               free (offsets);
96             }
97           free (result);
98         }
99     }
100
101   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
102   for (h = 0; h < SIZEOF (handlers); h++)
103     {
104       enum iconv_ilseq_handler handler = handlers[h];
105       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
106       for (o = 0; o < 2; o++)
107         {
108           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
109           char *result = NULL;
110           size_t length = 0;
111           int retval = mem_iconveha (input, strlen (input),
112                                      "ISO-8859-2", "ISO-8859-1",
113                                      false, handler,
114                                      offsets,
115                                      &result, &length);
116           switch (handler)
117             {
118             case iconveh_error:
119               ASSERT (retval == -1 && errno == EILSEQ);
120               ASSERT (result == NULL);
121               if (o)
122                 free (offsets);
123               break;
124             case iconveh_question_mark:
125               {
126                 static const char expected[] = "Rafa? Maszkowski";
127                 ASSERT (retval == 0);
128                 ASSERT (length == strlen (expected));
129                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
130                 if (o)
131                   {
132                     for (i = 0; i < 16; i++)
133                       ASSERT (offsets[i] == i);
134                     ASSERT (offsets[16] == MAGIC);
135                     free (offsets);
136                   }
137                 free (result);
138               }
139               break;
140             case iconveh_escape_sequence:
141               {
142                 static const char expected[] = "Rafa\\u0142 Maszkowski";
143                 ASSERT (retval == 0);
144                 ASSERT (length == strlen (expected));
145                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
146                 if (o)
147                   {
148                     for (i = 0; i < 16; i++)
149                       ASSERT (offsets[i] == (i < 5 ? i :
150                                              i + 5));
151                     ASSERT (offsets[16] == MAGIC);
152                     free (offsets);
153                   }
154                 free (result);
155               }
156               break;
157             }
158         }
159     }
160
161   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
162   for (h = 0; h < SIZEOF (handlers); h++)
163     {
164       enum iconv_ilseq_handler handler = handlers[h];
165       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
166       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
167       for (o = 0; o < 2; o++)
168         {
169           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
170           char *result = NULL;
171           size_t length = 0;
172           int retval = mem_iconveha (input, strlen (input),
173                                      "ISO-8859-1", "UTF-8",
174                                      false, handler,
175                                      offsets,
176                                      &result, &length);
177           ASSERT (retval == 0);
178           ASSERT (length == strlen (expected));
179           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
180           if (o)
181             {
182               for (i = 0; i < 37; i++)
183                 ASSERT (offsets[i] == (i < 1 ? i :
184                                        i < 12 ? i + 1 :
185                                        i < 18 ? i + 2 :
186                                        i + 3));
187               ASSERT (offsets[37] == MAGIC);
188               free (offsets);
189             }
190           free (result);
191         }
192     }
193
194   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
195   for (h = 0; h < SIZEOF (handlers); h++)
196     {
197       enum iconv_ilseq_handler handler = handlers[h];
198       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
199       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
200       for (o = 0; o < 2; o++)
201         {
202           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
203           char *result = NULL;
204           size_t length = 0;
205           int retval = mem_iconveha (input, strlen (input),
206                                      "UTF-8", "ISO-8859-1",
207                                      false, handler,
208                                      offsets,
209                                      &result, &length);
210           ASSERT (retval == 0);
211           ASSERT (length == strlen (expected));
212           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
213           if (o)
214             {
215               for (i = 0; i < 41; i++)
216                 ASSERT (offsets[i] == (i < 1 ? i :
217                                        i == 1 ? (size_t)(-1) :
218                                        i < 13 ? i - 1 :
219                                        i == 13 ? (size_t)(-1) :
220                                        i < 20 ? i - 2 :
221                                        i == 20 ? (size_t)(-1) :
222                                        i < 40 ? i - 3 :
223                                        (size_t)(-1)));
224               ASSERT (offsets[41] == MAGIC);
225               free (offsets);
226             }
227           free (result);
228         }
229     }
230
231   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
232   for (h = 0; h < SIZEOF (handlers); h++)
233     {
234       enum iconv_ilseq_handler handler = handlers[h];
235       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
236       for (o = 0; o < 2; o++)
237         {
238           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
239           char *result = NULL;
240           size_t length = 0;
241           int retval = mem_iconveha (input, strlen (input),
242                                      "UTF-8", "ISO-8859-1",
243                                      false, handler,
244                                      offsets,
245                                      &result, &length);
246           switch (handler)
247             {
248             case iconveh_error:
249               ASSERT (retval == -1 && errno == EILSEQ);
250               ASSERT (result == NULL);
251               if (o)
252                 free (offsets);
253               break;
254             case iconveh_question_mark:
255               {
256                 static const char expected[] = "Rafa? 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 < 17; i++)
263                       ASSERT (offsets[i] == (i < 5 ? i :
264                                              i == 5 ? (size_t)(-1) :
265                                              i - 1));
266                     ASSERT (offsets[17] == MAGIC);
267                     free (offsets);
268                   }
269                 free (result);
270               }
271               break;
272             case iconveh_escape_sequence:
273               {
274                 static const char expected[] = "Rafa\\u0142 Maszkowski";
275                 ASSERT (retval == 0);
276                 ASSERT (length == strlen (expected));
277                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
278                 if (o)
279                   {
280                     for (i = 0; i < 17; i++)
281                       ASSERT (offsets[i] == (i < 5 ? i :
282                                              i == 5 ? (size_t)(-1) :
283                                              i + 4));
284                     ASSERT (offsets[17] == MAGIC);
285                     free (offsets);
286                   }
287                 free (result);
288               }
289               break;
290             }
291         }
292     }
293
294   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
295   for (h = 0; h < SIZEOF (handlers); h++)
296     {
297       enum iconv_ilseq_handler handler = handlers[h];
298       static const char input[] = "\342";
299       for (o = 0; o < 2; o++)
300         {
301           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
302           char *result = NULL;
303           size_t length = 0;
304           int retval = mem_iconveha (input, strlen (input),
305                                      "UTF-8", "ISO-8859-1",
306                                      false, handler,
307                                      offsets,
308                                      &result, &length);
309           ASSERT (retval == 0);
310           ASSERT (length == 0);
311           if (o)
312             {
313               ASSERT (offsets[0] == 0);
314               ASSERT (offsets[1] == MAGIC);
315               free (offsets);
316             }
317           if (result != NULL)
318             free (result);
319         }
320     }
321
322   /* autodetect_jp is only supported when iconv() support ISO-2022-JP-2.  */
323 # if defined _LIBICONV_VERSION || !(defined _AIX || defined __sgi || defined __hpux || defined __osf__ || defined __sun)
324   /* Test conversions from autodetect_jp to UTF-8.  */
325   for (h = 0; h < SIZEOF (handlers); h++)
326     {
327       enum iconv_ilseq_handler handler = handlers[h];
328       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
329       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
330       for (o = 0; o < 2; o++)
331         {
332           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
333           char *result = NULL;
334           size_t length = 0;
335           int retval = mem_iconveha (input, strlen (input),
336                                      "autodetect_jp", "UTF-8",
337                                      false, handler,
338                                      offsets,
339                                      &result, &length);
340           ASSERT (retval == 0);
341           ASSERT (length == strlen (expected));
342           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
343           if (o)
344             {
345               for (i = 0; i < 10; i++)
346                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
347               ASSERT (offsets[10] == MAGIC);
348               free (offsets);
349             }
350           free (result);
351         }
352     }
353   for (h = 0; h < SIZEOF (handlers); h++)
354     {
355       enum iconv_ilseq_handler handler = handlers[h];
356       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
357       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
358       for (o = 0; o < 2; o++)
359         {
360           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
361           char *result = NULL;
362           size_t length = 0;
363           int retval = mem_iconveha (input, strlen (input),
364                                      "autodetect_jp", "UTF-8",
365                                      false, handler,
366                                      offsets,
367                                      &result, &length);
368           ASSERT (retval == 0);
369           ASSERT (length == strlen (expected));
370           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
371           if (o)
372             {
373               for (i = 0; i < 10; i++)
374                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
375               ASSERT (offsets[10] == MAGIC);
376               free (offsets);
377             }
378           free (result);
379         }
380     }
381   for (h = 0; h < SIZEOF (handlers); h++)
382     {
383       enum iconv_ilseq_handler handler = handlers[h];
384       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
385       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
386       for (o = 0; o < 2; o++)
387         {
388           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
389           char *result = NULL;
390           size_t length = 0;
391           int retval = mem_iconveha (input, strlen (input),
392                                      "autodetect_jp", "UTF-8",
393                                      false, handler,
394                                      offsets,
395                                      &result, &length);
396           ASSERT (retval == 0);
397           ASSERT (length == strlen (expected));
398           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
399           if (o)
400             {
401               for (i = 0; i < 16; i++)
402                 ASSERT (offsets[i] == (i == 0 ? 0 :
403                                        i == 5 ? 3 :
404                                        i == 7 ? 6 :
405                                        i == 9 ? 9 :
406                                        i == 11 ? 12 :
407                                        i == 13 ? 15 :
408                                        (size_t)(-1)));
409               ASSERT (offsets[16] == MAGIC);
410               free (offsets);
411             }
412           free (result);
413         }
414     }
415 # endif
416
417 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
418   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
419   for (h = 0; h < SIZEOF (handlers); h++)
420     {
421       enum iconv_ilseq_handler handler = handlers[h];
422       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
423       static const char expected[] = "Costs: 27 EUR";
424       for (o = 0; o < 2; o++)
425         {
426           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
427           char *result = NULL;
428           size_t length = 0;
429           int retval = mem_iconveha (input, strlen (input),
430                                      "UTF-8", "ISO-8859-1",
431                                      true, handler,
432                                      offsets,
433                                      &result, &length);
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 < 13; i++)
440                 ASSERT (offsets[i] == (i < 11 ? i : (size_t)(-1)));
441               ASSERT (offsets[13] == MAGIC);
442               free (offsets);
443             }
444           free (result);
445         }
446     }
447 # endif
448
449   /* ------------------------- Test str_iconveha() ------------------------- */
450
451   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
452   for (h = 0; h < SIZEOF (handlers); h++)
453     {
454       enum iconv_ilseq_handler handler = handlers[h];
455       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
456       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
457       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
458       ASSERT (result != NULL);
459       ASSERT (strcmp (result, expected) == 0);
460       free (result);
461     }
462
463   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
464   for (h = 0; h < SIZEOF (handlers); h++)
465     {
466       enum iconv_ilseq_handler handler = handlers[h];
467       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
468       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
469       switch (handler)
470         {
471         case iconveh_error:
472           ASSERT (result == NULL && errno == EILSEQ);
473           break;
474         case iconveh_question_mark:
475           {
476             static const char expected[] = "Rafa? Maszkowski";
477             ASSERT (result != NULL);
478             ASSERT (strcmp (result, expected) == 0);
479             free (result);
480           }
481           break;
482         case iconveh_escape_sequence:
483           {
484             static const char expected[] = "Rafa\\u0142 Maszkowski";
485             ASSERT (result != NULL);
486             ASSERT (strcmp (result, expected) == 0);
487             free (result);
488           }
489           break;
490         }
491     }
492
493   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
494   for (h = 0; h < SIZEOF (handlers); h++)
495     {
496       enum iconv_ilseq_handler handler = handlers[h];
497       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
498       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
499       char *result = str_iconveha (input, "ISO-8859-1", "UTF-8", false, handler);
500       ASSERT (result != NULL);
501       ASSERT (strcmp (result, expected) == 0);
502       free (result);
503     }
504
505   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
506   for (h = 0; h < SIZEOF (handlers); h++)
507     {
508       enum iconv_ilseq_handler handler = handlers[h];
509       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
510       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
511       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
512       ASSERT (result != NULL);
513       ASSERT (strcmp (result, expected) == 0);
514       free (result);
515     }
516
517   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
518   for (h = 0; h < SIZEOF (handlers); h++)
519     {
520       enum iconv_ilseq_handler handler = handlers[h];
521       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
522       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
523       switch (handler)
524         {
525         case iconveh_error:
526           ASSERT (result == NULL && errno == EILSEQ);
527           break;
528         case iconveh_question_mark:
529           {
530             static const char expected[] = "Costs: 27 ?";
531             ASSERT (result != NULL);
532             ASSERT (strcmp (result, expected) == 0);
533             free (result);
534           }
535           break;
536         case iconveh_escape_sequence:
537           {
538             static const char expected[] = "Costs: 27 \\u20AC";
539             ASSERT (result != NULL);
540             ASSERT (strcmp (result, expected) == 0);
541             free (result);
542           }
543           break;
544         }
545     }
546
547   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
548   for (h = 0; h < SIZEOF (handlers); h++)
549     {
550       enum iconv_ilseq_handler handler = handlers[h];
551       static const char input[] = "\342";
552       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
553       ASSERT (result != NULL);
554       ASSERT (strcmp (result, "") == 0);
555       free (result);
556     }
557
558   /* autodetect_jp is only supported when iconv() support ISO-2022-JP-2.  */
559 # if defined _LIBICONV_VERSION || !(defined _AIX || defined __sgi || defined __hpux || defined __osf__ || defined __sun)
560   /* Test conversions from autodetect_jp to UTF-8.  */
561   for (h = 0; h < SIZEOF (handlers); h++)
562     {
563       enum iconv_ilseq_handler handler = handlers[h];
564       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
565       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
566       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
567       ASSERT (result != NULL);
568       ASSERT (strcmp (result, expected) == 0);
569       free (result);
570     }
571   for (h = 0; h < SIZEOF (handlers); h++)
572     {
573       enum iconv_ilseq_handler handler = handlers[h];
574       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
575       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
576       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
577       ASSERT (result != NULL);
578       ASSERT (strcmp (result, expected) == 0);
579       free (result);
580     }
581   for (h = 0; h < SIZEOF (handlers); h++)
582     {
583       enum iconv_ilseq_handler handler = handlers[h];
584       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
585       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
586       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
587       ASSERT (result != NULL);
588       ASSERT (strcmp (result, expected) == 0);
589       free (result);
590     }
591 # endif
592
593 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
594   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
595   for (h = 0; h < SIZEOF (handlers); h++)
596     {
597       enum iconv_ilseq_handler handler = handlers[h];
598       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
599       static const char expected[] = "Costs: 27 EUR";
600       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", true, handler);
601       ASSERT (result != NULL);
602       ASSERT (strcmp (result, expected) == 0);
603       free (result);
604     }
605 # endif
606
607 #endif
608
609   return 0;
610 }