96660b2607cd180dc9f488e3fee085db4eee391e
[gnulib.git] / tests / test-striconveha.c
1 /* Test of character set conversion with error handling and autodetection.
2    Copyright (C) 2007-2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
18
19 #include <config.h>
20
21 #include "striconveha.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
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           free (result);
318         }
319     }
320
321   /* autodetect_jp is only supported when iconv() support ISO-2022-JP-2.  */
322 # if defined _LIBICONV_VERSION || !(defined _AIX || defined __sgi || defined __hpux || defined __osf__ || defined __sun)
323   /* Test conversions from autodetect_jp to UTF-8.  */
324   for (h = 0; h < SIZEOF (handlers); h++)
325     {
326       enum iconv_ilseq_handler handler = handlers[h];
327       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
328       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
329       for (o = 0; o < 2; o++)
330         {
331           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
332           char *result = NULL;
333           size_t length = 0;
334           int retval = mem_iconveha (input, strlen (input),
335                                      "autodetect_jp", "UTF-8",
336                                      false, handler,
337                                      offsets,
338                                      &result, &length);
339           ASSERT (retval == 0);
340           ASSERT (length == strlen (expected));
341           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
342           if (o)
343             {
344               for (i = 0; i < 10; i++)
345                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
346               ASSERT (offsets[10] == MAGIC);
347               free (offsets);
348             }
349           free (result);
350         }
351     }
352   for (h = 0; h < SIZEOF (handlers); h++)
353     {
354       enum iconv_ilseq_handler handler = handlers[h];
355       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
356       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
357       for (o = 0; o < 2; o++)
358         {
359           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
360           char *result = NULL;
361           size_t length = 0;
362           int retval = mem_iconveha (input, strlen (input),
363                                      "autodetect_jp", "UTF-8",
364                                      false, handler,
365                                      offsets,
366                                      &result, &length);
367           ASSERT (retval == 0);
368           ASSERT (length == strlen (expected));
369           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
370           if (o)
371             {
372               for (i = 0; i < 10; i++)
373                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
374               ASSERT (offsets[10] == MAGIC);
375               free (offsets);
376             }
377           free (result);
378         }
379     }
380   for (h = 0; h < SIZEOF (handlers); h++)
381     {
382       enum iconv_ilseq_handler handler = handlers[h];
383       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
384       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
385       for (o = 0; o < 2; o++)
386         {
387           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
388           char *result = NULL;
389           size_t length = 0;
390           int retval = mem_iconveha (input, strlen (input),
391                                      "autodetect_jp", "UTF-8",
392                                      false, handler,
393                                      offsets,
394                                      &result, &length);
395           ASSERT (retval == 0);
396           ASSERT (length == strlen (expected));
397           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
398           if (o)
399             {
400               for (i = 0; i < 16; i++)
401                 ASSERT (offsets[i] == (i == 0 ? 0 :
402                                        i == 5 ? 3 :
403                                        i == 7 ? 6 :
404                                        i == 9 ? 9 :
405                                        i == 11 ? 12 :
406                                        i == 13 ? 15 :
407                                        (size_t)(-1)));
408               ASSERT (offsets[16] == MAGIC);
409               free (offsets);
410             }
411           free (result);
412         }
413     }
414 # endif
415
416 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
417   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
418   for (h = 0; h < SIZEOF (handlers); h++)
419     {
420       enum iconv_ilseq_handler handler = handlers[h];
421       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
422       static const char expected[] = "Costs: 27 EUR";
423       for (o = 0; o < 2; o++)
424         {
425           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
426           char *result = NULL;
427           size_t length = 0;
428           int retval = mem_iconveha (input, strlen (input),
429                                      "UTF-8", "ISO-8859-1",
430                                      true, handler,
431                                      offsets,
432                                      &result, &length);
433           ASSERT (retval == 0);
434           ASSERT (length == strlen (expected));
435           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
436           if (o)
437             {
438               for (i = 0; i < 13; i++)
439                 ASSERT (offsets[i] == (i < 11 ? i : (size_t)(-1)));
440               ASSERT (offsets[13] == MAGIC);
441               free (offsets);
442             }
443           free (result);
444         }
445     }
446 # endif
447
448   /* ------------------------- Test str_iconveha() ------------------------- */
449
450   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
451   for (h = 0; h < SIZEOF (handlers); h++)
452     {
453       enum iconv_ilseq_handler handler = handlers[h];
454       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
455       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
456       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
457       ASSERT (result != NULL);
458       ASSERT (strcmp (result, expected) == 0);
459       free (result);
460     }
461
462   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
463   for (h = 0; h < SIZEOF (handlers); h++)
464     {
465       enum iconv_ilseq_handler handler = handlers[h];
466       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
467       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
468       switch (handler)
469         {
470         case iconveh_error:
471           ASSERT (result == NULL && errno == EILSEQ);
472           break;
473         case iconveh_question_mark:
474           {
475             static const char expected[] = "Rafa? Maszkowski";
476             ASSERT (result != NULL);
477             ASSERT (strcmp (result, expected) == 0);
478             free (result);
479           }
480           break;
481         case iconveh_escape_sequence:
482           {
483             static const char expected[] = "Rafa\\u0142 Maszkowski";
484             ASSERT (result != NULL);
485             ASSERT (strcmp (result, expected) == 0);
486             free (result);
487           }
488           break;
489         }
490     }
491
492   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
493   for (h = 0; h < SIZEOF (handlers); h++)
494     {
495       enum iconv_ilseq_handler handler = handlers[h];
496       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
497       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
498       char *result = str_iconveha (input, "ISO-8859-1", "UTF-8", false, handler);
499       ASSERT (result != NULL);
500       ASSERT (strcmp (result, expected) == 0);
501       free (result);
502     }
503
504   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
505   for (h = 0; h < SIZEOF (handlers); h++)
506     {
507       enum iconv_ilseq_handler handler = handlers[h];
508       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
509       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
510       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
511       ASSERT (result != NULL);
512       ASSERT (strcmp (result, expected) == 0);
513       free (result);
514     }
515
516   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
517   for (h = 0; h < SIZEOF (handlers); h++)
518     {
519       enum iconv_ilseq_handler handler = handlers[h];
520       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
521       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
522       switch (handler)
523         {
524         case iconveh_error:
525           ASSERT (result == NULL && errno == EILSEQ);
526           break;
527         case iconveh_question_mark:
528           {
529             static const char expected[] = "Costs: 27 ?";
530             ASSERT (result != NULL);
531             ASSERT (strcmp (result, expected) == 0);
532             free (result);
533           }
534           break;
535         case iconveh_escape_sequence:
536           {
537             static const char expected[] = "Costs: 27 \\u20AC";
538             ASSERT (result != NULL);
539             ASSERT (strcmp (result, expected) == 0);
540             free (result);
541           }
542           break;
543         }
544     }
545
546   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
547   for (h = 0; h < SIZEOF (handlers); h++)
548     {
549       enum iconv_ilseq_handler handler = handlers[h];
550       static const char input[] = "\342";
551       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
552       ASSERT (result != NULL);
553       ASSERT (strcmp (result, "") == 0);
554       free (result);
555     }
556
557   /* autodetect_jp is only supported when iconv() support ISO-2022-JP-2.  */
558 # if defined _LIBICONV_VERSION || !(defined _AIX || defined __sgi || defined __hpux || defined __osf__ || defined __sun)
559   /* Test conversions from autodetect_jp to UTF-8.  */
560   for (h = 0; h < SIZEOF (handlers); h++)
561     {
562       enum iconv_ilseq_handler handler = handlers[h];
563       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
564       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
565       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
566       ASSERT (result != NULL);
567       ASSERT (strcmp (result, expected) == 0);
568       free (result);
569     }
570   for (h = 0; h < SIZEOF (handlers); h++)
571     {
572       enum iconv_ilseq_handler handler = handlers[h];
573       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
574       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
575       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
576       ASSERT (result != NULL);
577       ASSERT (strcmp (result, expected) == 0);
578       free (result);
579     }
580   for (h = 0; h < SIZEOF (handlers); h++)
581     {
582       enum iconv_ilseq_handler handler = handlers[h];
583       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
584       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
585       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
586       ASSERT (result != NULL);
587       ASSERT (strcmp (result, expected) == 0);
588       free (result);
589     }
590 # endif
591
592 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
593   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
594   for (h = 0; h < SIZEOF (handlers); h++)
595     {
596       enum iconv_ilseq_handler handler = handlers[h];
597       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
598       static const char expected[] = "Costs: 27 EUR";
599       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", true, handler);
600       ASSERT (result != NULL);
601       ASSERT (strcmp (result, expected) == 0);
602       free (result);
603     }
604 # endif
605
606 #endif
607
608   return 0;
609 }