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