Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-tests / uniname / test-uninames.c
1 /* Test the Unicode character name functions.
2    Copyright (C) 2000-2003, 2005, 2007, 2009-2015 Free Software Foundation,
3    Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "xalloc.h"
25 #include "uniname.h"
26 #include "progname.h"
27
28 /* The names according to the UnicodeData.txt file, modified to contain the
29    Hangul syllable names, as described in the Unicode 3.0 book.  */
30 static const char * unicode_names [0x110000];
31
32 /* Maximum entries in unicode_aliases.  */
33 #define ALIASLEN 0x200
34
35 /* The aliases according to the NameAliases.txt file.  */
36 struct unicode_alias
37 {
38   const char *name;
39   unsigned int uc;
40 };
41
42 static struct unicode_alias unicode_aliases [ALIASLEN];
43 static int aliases_count;
44
45 /* Stores in unicode_names[] the relevant contents of the UnicodeData.txt
46    file.  */
47 static void
48 fill_names (const char *unicodedata_filename)
49 {
50   FILE *stream;
51   char *field0;
52   char *field1;
53   char line[1024];
54   int lineno = 0;
55
56   stream = fopen (unicodedata_filename, "r");
57   if (stream == NULL)
58     {
59       fprintf (stderr, "error during fopen of '%s'\n", unicodedata_filename);
60       exit (EXIT_FAILURE);
61     }
62
63   while (fgets (line, sizeof line, stream))
64     {
65       char *p;
66       char *comment;
67       unsigned int i;
68
69       lineno++;
70
71       comment = strchr (line, '#');
72       if (comment != NULL)
73         *comment = '\0';
74       if (line[strspn (line, " \t\r\n")] == '\0')
75         continue;
76
77       field0 = p = line;
78       p = strchr (p, ';');
79       if (!p)
80         {
81           fprintf (stderr, "short line in '%s':%d\n",
82                    unicodedata_filename, lineno);
83           exit (EXIT_FAILURE);
84         }
85       *p++ = '\0';
86
87       field1 = p;
88       if (*field1 == '<')
89         continue;
90       p = strchr (p, ';');
91       if (!p)
92         {
93           fprintf (stderr, "short line in '%s':%d\n",
94                    unicodedata_filename, lineno);
95           exit (EXIT_FAILURE);
96         }
97       *p = '\0';
98       i = strtoul (field0, NULL, 16);
99       if (i >= 0x110000)
100         {
101           fprintf (stderr, "index too large\n");
102           exit (EXIT_FAILURE);
103         }
104       unicode_names[i] = xstrdup (field1);
105     }
106   if (ferror (stream) || fclose (stream))
107     {
108       fprintf (stderr, "error reading from '%s'\n", unicodedata_filename);
109       exit (1);
110     }
111 }
112
113 /* Stores in unicode_aliases[] the relevant contents of the NameAliases.txt
114    file.  */
115 static void
116 fill_aliases (const char *namealiases_filename)
117 {
118   FILE *stream;
119   char *field0;
120   char *field1;
121   char line[1024];
122   int lineno = 0;
123
124   stream = fopen (namealiases_filename, "r");
125   if (stream == NULL)
126     {
127       fprintf (stderr, "error during fopen of '%s'\n", namealiases_filename);
128       exit (EXIT_FAILURE);
129     }
130
131   while (fgets (line, sizeof line, stream))
132     {
133       char *p;
134       char *comment;
135       unsigned int uc;
136
137       comment = strchr (line, '#');
138       if (comment != NULL)
139         *comment = '\0';
140       if (line[strspn (line, " \t\r\n")] == '\0')
141         continue;
142
143       lineno++;
144
145       field0 = p = line;
146       p = strchr (p, ';');
147       if (!p)
148         {
149           fprintf (stderr, "short line in '%s':%d\n",
150                    namealiases_filename, lineno);
151           exit (EXIT_FAILURE);
152         }
153       *p++ = '\0';
154
155       field1 = p;
156       p = strchr (p, ';');
157       if (!p)
158         {
159           fprintf (stderr, "short line in '%s':%d\n",
160                    namealiases_filename, lineno);
161           exit (EXIT_FAILURE);
162         }
163       *p = '\0';
164
165       uc = strtoul (field0, NULL, 16);
166       if (uc >= 0x110000)
167         {
168           fprintf (stderr, "index too large\n");
169           exit (EXIT_FAILURE);
170         }
171
172       if (aliases_count == ALIASLEN)
173         {
174           fprintf (stderr, "too many aliases\n");
175           exit (EXIT_FAILURE);
176         }
177       unicode_aliases[aliases_count].name = xstrdup (field1);
178       unicode_aliases[aliases_count].uc = uc;
179       aliases_count++;
180     }
181   if (ferror (stream) || fclose (stream))
182     {
183       fprintf (stderr, "error reading from '%s'\n", namealiases_filename);
184       exit (1);
185     }
186 }
187
188 static int
189 name_has_alias (unsigned int uc)
190 {
191   int i;
192   for (i = 0; i < ALIASLEN; i++)
193     if (unicode_aliases[i].uc == uc)
194       return 1;
195   return 0;
196 }
197
198 /* Perform an exhaustive test of the unicode_character_name function.  */
199 static int
200 test_name_lookup ()
201 {
202   int error = 0;
203   unsigned int i;
204   char buf[UNINAME_MAX];
205
206   for (i = 0; i < 0x11000; i++)
207     {
208       char *result = unicode_character_name (i, buf);
209
210       if (unicode_names[i] != NULL)
211         {
212           if (result == NULL)
213             {
214               fprintf (stderr, "\\u%04X name lookup failed!\n", i);
215               error = 1;
216             }
217           else if (strcmp (result, unicode_names[i]) != 0)
218             {
219               fprintf (stderr, "\\u%04X name lookup returned wrong name: %s\n",
220                                i, result);
221               error = 1;
222             }
223         }
224       else
225         {
226           if (result != NULL)
227             {
228               fprintf (stderr, "\\u%04X name lookup returned wrong name: %s\n",
229                                i, result);
230               error = 1;
231             }
232         }
233     }
234
235   for (i = 0x110000; i < 0x1000000; i++)
236     {
237       char *result = unicode_character_name (i, buf);
238
239       if (result != NULL)
240         {
241           fprintf (stderr, "\\u%04X name lookup returned wrong name: %s\n",
242                            i, result);
243           error = 1;
244         }
245     }
246
247   return error;
248 }
249
250 /* Perform a test of the unicode_name_character function.  */
251 static int
252 test_inverse_lookup ()
253 {
254   int error = 0;
255   unsigned int i;
256
257   /* First, verify all valid character names are recognized.  */
258   for (i = 0; i < 0x110000; i++)
259     if (unicode_names[i] != NULL)
260       {
261         unsigned int result = unicode_name_character (unicode_names[i]);
262         if (result != i)
263           {
264             if (result == UNINAME_INVALID)
265               fprintf (stderr, "inverse name lookup of \"%s\" failed\n",
266                        unicode_names[i]);
267             else
268               fprintf (stderr,
269                        "inverse name lookup of \"%s\" returned 0x%04X\n",
270                        unicode_names[i], result);
271             error = 1;
272           }
273       }
274
275   /* Second, generate random but likely names and verify they are not
276      recognized unless really valid.  */
277   for (i = 0; i < 10000; i++)
278     {
279       unsigned int i1, i2;
280       const char *s1;
281       const char *s2;
282       unsigned int l1, l2, j1, j2;
283       char buf[2*UNINAME_MAX];
284       unsigned int result;
285
286       do i1 = ((rand () % 0x11) << 16)
287               + ((rand () & 0xff) << 8)
288               + (rand () & 0xff);
289       while (unicode_names[i1] == NULL);
290
291       do i2 = ((rand () % 0x11) << 16)
292               + ((rand () & 0xff) << 8)
293               + (rand () & 0xff);
294       while (unicode_names[i2] == NULL);
295
296       s1 = unicode_names[i1];
297       l1 = strlen (s1);
298       s2 = unicode_names[i2];
299       l2 = strlen (s2);
300
301       /* Concatenate a starting piece of s1 with an ending piece of s2.  */
302       for (j1 = 1; j1 <= l1; j1++)
303         if (j1 == l1 || s1[j1] == ' ')
304           for (j2 = 0; j2 < l2; j2++)
305             if (j2 == 0 || s2[j2-1] == ' ')
306               {
307                 memcpy (buf, s1, j1);
308                 buf[j1] = ' ';
309                 memcpy (buf + j1 + 1, s2 + j2, l2 - j2 + 1);
310
311                 result = unicode_name_character (buf);
312                 if (result != UNINAME_INVALID
313                     && !name_has_alias (result)
314                     && !(unicode_names[result] != NULL
315                          && strcmp (unicode_names[result], buf) == 0))
316                   {
317                     fprintf (stderr,
318                              "inverse name lookup of \"%s\" returned 0x%04X\n",
319                              unicode_names[i], result);
320                     error = 1;
321                   }
322               }
323     }
324
325   /* Third, some extreme case that used to loop.  */
326   if (unicode_name_character ("A A") != UNINAME_INVALID)
327     error = 1;
328
329   return error;
330 }
331
332 /* Perform a test of the unicode_name_character function for aliases.  */
333 static int
334 test_alias_lookup ()
335 {
336   int error = 0;
337   unsigned int i;
338   char buf[UNINAME_MAX];
339
340   /* Verify all valid character names are recognized.  */
341   for (i = 0; i < ALIASLEN; i++)
342     if (unicode_aliases[i].uc != UNINAME_INVALID
343         /* Skip if the character has no canonical name (e.g. control
344            characters).  */
345         && unicode_character_name (unicode_aliases[i].uc, buf))
346       {
347         unsigned int result = unicode_name_character (unicode_aliases[i].name);
348         if (result != unicode_aliases[i].uc)
349           {
350             if (result == UNINAME_INVALID)
351               fprintf (stderr, "inverse name lookup of \"%s\" failed\n",
352                        unicode_aliases[i].name);
353             else
354               fprintf (stderr,
355                        "inverse name lookup of \"%s\" returned 0x%04X\n",
356                        unicode_aliases[i].name, result);
357             error = 1;
358           }
359       }
360
361   return error;
362 }
363
364 int
365 main (int argc, char *argv[])
366 {
367   int error = 0;
368   int i;
369
370   set_program_name (argv[0]);
371
372   for (i = 1; i < argc && strcmp (argv[i], "--") != 0; i++)
373     fill_names (argv[i]);
374
375   if (i < argc)
376     {
377       int j;
378       for (j = 0; j < ALIASLEN; j++)
379         unicode_aliases[j].uc = UNINAME_INVALID;
380
381       i++;
382       for (; i < argc; i++)
383         fill_aliases (argv[i]);
384     }
385
386   error |= test_name_lookup ();
387   error |= test_inverse_lookup ();
388
389   if (aliases_count > 0)
390     error |= test_alias_lookup ();
391
392   return error;
393 }