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