Changes for 64-bit cleanliness, loosely based on patch from Mark Murnane.
[platform/upstream/glib.git] / tests / unicode-encoding.c
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <glib.h>
6
7 static gint exit_status = 0;
8
9 static void
10 croak (char *format, ...)
11 {
12   va_list va;
13   
14   va_start (va, format);
15   vfprintf (stderr, format, va);
16   va_end (va);
17
18   exit (1);
19 }
20
21 static void
22 fail (char *format, ...)
23 {
24   va_list va;
25   
26   va_start (va, format);
27   vfprintf (stderr, format, va);
28   va_end (va);
29
30   exit_status |= 1;
31 }
32
33 typedef enum
34 {
35   VALID,
36   INCOMPLETE,
37   NOTUNICODE,
38   OVERLONG,
39   MALFORMED
40 } Status;
41
42 static gboolean
43 ucs4_equal (gunichar *a, gunichar *b)
44 {
45   while (*a && *b && (*a == *b))
46     {
47       a++;
48       b++;
49     }
50
51   return (*a == *b);
52 }
53
54 static gboolean
55 utf16_equal (gunichar2 *a, gunichar2 *b)
56 {
57   while (*a && *b && (*a == *b))
58     {
59       a++;
60       b++;
61     }
62
63   return (*a == *b);
64 }
65
66 static gint
67 utf16_count (gunichar2 *a)
68 {
69   gint result = 0;
70   
71   while (a[result])
72     result++;
73
74   return result;
75 }
76
77 static void
78 process (gint      line,
79          gchar    *utf8,
80          Status    status,
81          gunichar *ucs4,
82          gint      ucs4_len)
83 {
84   const gchar *end;
85   gboolean is_valid = g_utf8_validate (utf8, -1, &end);
86   GError *error = NULL;
87   glong items_read, items_written;
88
89   switch (status)
90     {
91     case VALID:
92       if (!is_valid)
93         {
94           fail ("line %d: valid but g_utf8_validate returned FALSE\n", line);
95           return;
96         }
97       break;
98     case NOTUNICODE:
99     case INCOMPLETE:
100     case OVERLONG:
101     case MALFORMED:
102       if (is_valid)
103         {
104           fail ("line %d: invalid but g_utf8_validate returned TRUE\n", line);
105           return;
106         }
107       break;
108     }
109
110   if (status == INCOMPLETE)
111     {
112       gunichar *ucs4_result;      
113
114       ucs4_result = g_utf8_to_ucs4 (utf8, -1, NULL, NULL, &error);
115
116       if (!error || !g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT))
117         {
118           fail ("line %d: incomplete input not properly detected\n", line);
119           return;
120         }
121       g_clear_error (&error);
122
123       ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, NULL, &error);
124
125       if (!ucs4_result || items_read == strlen (utf8))
126         {
127           fail ("line %d: incomplete input not properly detected\n", line);
128           return;
129         }
130
131       g_free (ucs4_result);
132     }
133
134   if (status == VALID || status == NOTUNICODE)
135     {
136       gunichar *ucs4_result;
137       gchar *utf8_result;
138
139       ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, &items_written, &error);
140       if (!ucs4_result)
141         {
142           fail ("line %d: conversion to ucs4 failed: %s\n", line, error->message);
143           return;
144         }
145       
146       if (!ucs4_equal (ucs4_result, ucs4) ||
147           items_read != strlen (utf8) ||
148           items_written != ucs4_len)
149         {
150           fail ("line %d: results of conversion to ucs4 do not match expected.\n", line);
151           return;
152         }
153
154       g_free (ucs4_result);
155
156       ucs4_result = g_utf8_to_ucs4_fast (utf8, -1, &items_written);
157       
158       if (!ucs4_equal (ucs4_result, ucs4) ||
159           items_written != ucs4_len)
160         {
161           fail ("line %d: results of conversion to ucs4 do not match expected.\n", line);
162           return;
163         }
164
165       utf8_result = g_ucs4_to_utf8 (ucs4_result, -1, &items_read, &items_written, &error);
166       if (!utf8_result)
167         {
168           fail ("line %d: conversion back to utf8 failed: %s", line, error->message);
169           return;
170         }
171
172       if (strcmp (utf8_result, utf8) != 0 ||
173           items_read != ucs4_len ||
174           items_written != strlen (utf8))
175         {
176           fail ("line %d: conversion back to utf8 did not match original\n", line);
177           return;
178         }
179
180       g_free (utf8_result);
181       g_free (ucs4_result);
182     }
183
184   if (status == VALID)
185     {
186       gunichar2 *utf16_expected_tmp;
187       gunichar2 *utf16_expected;
188       gunichar2 *utf16_from_utf8;
189       gunichar2 *utf16_from_ucs4;
190       gunichar *ucs4_result;
191       gsize bytes_written;
192       gint n_chars;
193       gchar *utf8_result;
194
195 #ifdef G_OS_WIN32
196 #define TARGET "UTF-16LE"
197 #else
198 #define TARGET "UTF-16"
199 #endif
200
201       if (!(utf16_expected_tmp = (gunichar2 *)g_convert (utf8, -1, TARGET, "UTF-8",
202                                                          NULL, &bytes_written, NULL)))
203         {
204           fail ("line %d: could not convert to UTF-16 via g_convert\n", line);
205           return;
206         }
207
208       /* zero-terminate and remove BOM
209        */
210       n_chars = bytes_written / 2;
211       if (utf16_expected_tmp[0] == 0xfeff) /* BOM */
212         {
213           n_chars--;
214           utf16_expected = g_new (gunichar2, n_chars + 1);
215           memcpy (utf16_expected, utf16_expected_tmp + 1, sizeof(gunichar2) * n_chars);
216         }
217       else if (utf16_expected_tmp[0] == 0xfffe) /* ANTI-BOM */
218         {
219           fail ("line %d: conversion via iconv to \"UTF-16\" is not native-endian\n", line);
220           return;
221         }
222       else
223         {
224           utf16_expected = g_new (gunichar2, n_chars + 1);
225           memcpy (utf16_expected, utf16_expected_tmp, sizeof(gunichar2) * n_chars);
226         }
227
228       utf16_expected[n_chars] = '\0';
229       
230       if (!(utf16_from_utf8 = g_utf8_to_utf16 (utf8, -1, &items_read, &items_written, &error)))
231         {
232           fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
233           return;
234         }
235
236       if (items_read != strlen (utf8) ||
237           utf16_count (utf16_from_utf8) != items_written)
238         {
239           fail ("line %d: length error in conversion to ucs16\n", line);
240           return;
241         }
242
243       if (!(utf16_from_ucs4 = g_ucs4_to_utf16 (ucs4, -1, &items_read, &items_written, &error)))
244         {
245           fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
246           return;
247         }
248
249       if (items_read != ucs4_len ||
250           utf16_count (utf16_from_ucs4) != items_written)
251         {
252           fail ("line %d: length error in conversion to ucs16\n", line);
253           return;
254         }
255
256       if (!utf16_equal (utf16_from_utf8, utf16_expected) ||
257           !utf16_equal (utf16_from_ucs4, utf16_expected))
258         {
259           fail ("line %d: results of conversion to ucs16 do not match\n", line);
260           return;
261         }
262
263       if (!(utf8_result = g_utf16_to_utf8 (utf16_from_utf8, -1, &items_read, &items_written, &error)))
264         {
265           fail ("line %d: conversion back to utf8 failed: %s\n", line, error->message);
266           return;
267         }
268
269       if (items_read != utf16_count (utf16_from_utf8) ||
270           items_written != strlen (utf8))
271         {
272           fail ("line %d: length error in conversion from ucs16 to utf8\n", line);
273           return;
274         }
275
276       if (!(ucs4_result = g_utf16_to_ucs4 (utf16_from_ucs4, -1, &items_read, &items_written, &error)))
277         {
278           fail ("line %d: conversion back to utf8/ucs4 failed\n", line);
279           return;
280         }
281
282       if (items_read != utf16_count (utf16_from_utf8) ||
283           items_written != ucs4_len)
284         {
285           fail ("line %d: length error in conversion from ucs16 to ucs4\n", line);
286           return;
287         }
288
289       if (strcmp (utf8, utf8_result) != 0 ||
290           !ucs4_equal (ucs4, ucs4_result))
291         {
292           fail ("line %d: conversion back to utf8/ucs4 did not match original\n", line);
293           return;
294         }
295       
296       g_free (utf16_expected_tmp);
297       g_free (utf16_expected);
298       g_free (utf16_from_utf8);
299       g_free (utf16_from_ucs4);
300       g_free (utf8_result);
301       g_free (ucs4_result);
302     }
303 }
304
305 int
306 main (int argc, char **argv)
307 {
308   gchar *srcdir = getenv ("srcdir");
309   gchar *testfile;
310   gchar *contents;
311   GError *error = NULL;
312   gchar *p, *end;
313   char *tmp;
314   gint state = 0;
315   gint line = 1;
316   gint start_line = 0;          /* Quiet GCC */
317   gchar *utf8 = NULL;           /* Quiet GCC */
318   GArray *ucs4;
319   Status status = VALID;        /* Quiet GCC */
320
321   if (!srcdir)
322     srcdir = ".";
323   
324   testfile = g_strconcat (srcdir, G_DIR_SEPARATOR_S "utf8.txt", NULL);
325   
326   g_file_get_contents (testfile, &contents, NULL, &error);
327   if (error)
328     croak ("Cannot open utf8.txt: %s", error->message);
329
330   ucs4 = g_array_new (TRUE, FALSE, sizeof(gunichar));
331
332   p = contents;
333
334   /* Loop over lines */
335   while (*p)
336     {
337       while (*p && (*p == ' ' || *p == '\t'))
338         p++;
339
340       end = p;
341       while (*end && (*end != '\r' && *end != '\n'))
342         end++;
343       
344       if (!*p || *p == '#' || *p == '\r' || *p == '\n')
345         goto next_line;
346
347       tmp = g_strstrip (g_strndup (p, end - p));
348       
349       switch (state)
350         {
351         case 0:
352           /* UTF-8 string */
353           start_line = line;
354           utf8 = tmp;
355           tmp = NULL;
356           break;
357           
358         case 1:
359           /* Status */
360           if (!strcmp (tmp, "VALID"))
361             status = VALID;
362           else if (!strcmp (tmp, "INCOMPLETE"))
363             status = INCOMPLETE;
364           else if (!strcmp (tmp, "NOTUNICODE"))
365             status = NOTUNICODE;
366           else if (!strcmp (tmp, "OVERLONG"))
367             status = OVERLONG;
368           else if (!strcmp (tmp, "MALFORMED"))
369             status = MALFORMED;
370           else
371             croak ("Invalid status on line %d\n", line);
372
373           if (status != VALID && status != NOTUNICODE)
374             state++;            /* No UCS-4 data */
375           
376           break;
377           
378         case 2:
379           /* UCS-4 version */
380
381           p = strtok (tmp, " \t");
382           while (p)
383             {
384               gchar *endptr;
385               
386               gunichar ch = strtoul (p, &endptr, 16);
387               if (*endptr != '\0')
388                 croak ("Invalid UCS-4 character on line %d\n", line);
389
390               g_array_append_val (ucs4, ch);
391               
392               p = strtok (NULL, " \t");
393             }
394
395           break;
396         }
397
398       g_free (tmp);
399       state = (state + 1) % 3;
400
401       if (state == 0)
402         {
403           process (start_line, utf8, status, (gunichar *)ucs4->data, ucs4->len);
404           g_array_set_size (ucs4, 0);
405           g_free (utf8);
406         }
407       
408     next_line:
409       p = end;
410       if (*p && *p == '\r')
411         p++;
412       if (*p && *p == '\n')
413         p++;
414       
415       line++;
416     }
417
418   return 0;
419 }