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