(add_to_readlist): Take locale pointer as extra parameter from which
[kopensolaris-gnu/glibc.git] / locale / programs / repertoire.c
1 /* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <errno.h>
25 #include <error.h>
26 #include <limits.h>
27 #include <obstack.h>
28 #include <search.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "linereader.h"
34 #include "charmap.h"
35 #include "repertoire.h"
36 #include "simple-hash.h"
37 #include "localedef.h"
38
39
40 /* Simple keyword hashing for the repertoiremap.  */
41 static const struct keyword_t *repertoiremap_hash (const char *str,
42                                                    unsigned int len);
43 static void repertoire_new_char (struct linereader *lr, hash_table *ht,
44                                  hash_table *rt, struct obstack *ob,
45                                  uint32_t value, const char *from,
46                                  const char *to, int decimal_ellipsis);
47 static int repertoire_compare (const void *p1, const void *p2);
48
49 /* Already known repertoire maps.  */
50 static void *known;
51
52 /* List of repertoire maps which are not available and which have been
53    reported to not be.  */
54 static void *unavailable;
55
56
57 struct repertoire_t *
58 repertoire_read (const char *filename)
59 {
60   struct linereader *repfile;
61   struct repertoire_t *result;
62   struct repertoire_t **resultp;
63   struct repertoire_t search;
64   int state;
65   char *from_name = NULL;
66   char *to_name = NULL;
67   enum token_t ellipsis = tok_none;
68
69   search.name = filename;
70   resultp = tfind (&search, &known, &repertoire_compare);
71   if (resultp != NULL)
72     return *resultp;
73
74   /* Determine path.  */
75   repfile = lr_open (filename, repertoiremap_hash);
76   if (repfile == NULL)
77     {
78       if (strchr (filename, '/') == NULL)
79         {
80           char *i18npath = getenv ("I18NPATH");
81           if (i18npath != NULL && *i18npath != '\0')
82             {
83               char path[strlen (filename) + 1 + strlen (i18npath)
84                         + sizeof ("/repertoiremaps/") - 1];
85               char *next;
86               i18npath = strdupa (i18npath);
87
88
89               while (repfile == NULL
90                      && (next = strsep (&i18npath, ":")) != NULL)
91                 {
92                   stpcpy (stpcpy (stpcpy (path, next), "/repertoiremaps/"),
93                           filename);
94
95                   repfile = lr_open (path, repertoiremap_hash);
96
97                   if (repfile == NULL)
98                     {
99                       stpcpy (stpcpy (path, next), filename);
100
101                       repfile = lr_open (path, repertoiremap_hash);
102                     }
103                 }
104             }
105
106           if (repfile == NULL)
107             {
108               /* Look in the systems charmap directory.  */
109               char *buf = xmalloc (strlen (filename) + 1
110                                    + sizeof (REPERTOIREMAP_PATH));
111
112               stpcpy (stpcpy (stpcpy (buf, REPERTOIREMAP_PATH), "/"),
113                       filename);
114               repfile = lr_open (buf, repertoiremap_hash);
115
116               if (repfile == NULL)
117                 free (buf);
118             }
119         }
120
121       if (repfile == NULL)
122         return NULL;
123     }
124
125   /* We don't want symbolic names in string to be translated.  */
126   repfile->translate_strings = 0;
127
128   /* Allocate room for result.  */
129   result = (struct repertoire_t *) xmalloc (sizeof (struct repertoire_t));
130   memset (result, '\0', sizeof (struct repertoire_t));
131
132   result->name = xstrdup (filename);
133
134 #define obstack_chunk_alloc malloc
135 #define obstack_chunk_free free
136   obstack_init (&result->mem_pool);
137
138   if (init_hash (&result->char_table, 256)
139       || init_hash (&result->reverse_table, 256)
140       || init_hash (&result->seq_table, 256))
141     {
142       free (result);
143       return NULL;
144     }
145
146   /* We use a state machine to describe the charmap description file
147      format.  */
148   state = 1;
149   while (1)
150     {
151       /* What's on?  */
152       struct token *now = lr_token (repfile, NULL, NULL);
153       enum token_t nowtok = now->tok;
154       struct token *arg;
155
156       if (nowtok == tok_eof)
157         break;
158
159       switch (state)
160         {
161         case 1:
162           /* We haven't yet read any character definition.  This is where
163              we accept escape_char and comment_char definitions.  */
164           if (nowtok == tok_eol)
165             /* Ignore empty lines.  */
166             continue;
167
168           if (nowtok == tok_escape_char || nowtok == tok_comment_char)
169             {
170               /* We know that we need an argument.  */
171               arg = lr_token (repfile, NULL, NULL);
172
173               if (arg->tok != tok_ident)
174                 {
175                   lr_error (repfile, _("syntax error in prolog: %s"),
176                             _("bad argument"));
177
178                   lr_ignore_rest (repfile, 0);
179                   continue;
180                 }
181
182               if (arg->val.str.lenmb != 1)
183                 {
184                   lr_error (repfile, _("\
185 argument to <%s> must be a single character"),
186                             nowtok == tok_escape_char ? "escape_char"
187                                                       : "comment_char");
188
189                   lr_ignore_rest (repfile, 0);
190                   continue;
191                 }
192
193               if (nowtok == tok_escape_char)
194                 repfile->escape_char = *arg->val.str.startmb;
195               else
196                 repfile->comment_char = *arg->val.str.startmb;
197
198               lr_ignore_rest (repfile, 1);
199               continue;
200             }
201
202           if (nowtok == tok_charids)
203             {
204               lr_ignore_rest (repfile, 1);
205
206               state = 2;
207               continue;
208             }
209
210           /* Otherwise we start reading the character definitions.  */
211           state = 2;
212           /* FALLTHROUGH */
213
214         case 2:
215           /* We are now are in the body.  Each line
216              must have the format "%s %s %s\n" or "%s...%s %s %s\n".  */
217           if (nowtok == tok_eol)
218             /* Ignore empty lines.  */
219             continue;
220
221           if (nowtok == tok_end)
222             {
223               state = 90;
224               continue;
225             }
226
227           if (nowtok != tok_bsymbol)
228             {
229               lr_error (repfile,
230                         _("syntax error in repertoire map definition: %s"),
231                         _("no symbolic name given"));
232
233               lr_ignore_rest (repfile, 0);
234               continue;
235             }
236
237           /* If the previous line was not completely correct free the
238              used memory.  */
239           if (from_name != NULL)
240             obstack_free (&result->mem_pool, from_name);
241
242           from_name = (char *) obstack_copy0 (&result->mem_pool,
243                                               now->val.str.startmb,
244                                               now->val.str.lenmb);
245           to_name = NULL;
246
247           state = 3;
248           continue;
249
250         case 3:
251           /* We have two possibilities: We can see an ellipsis or an
252              encoding value.  */
253           if (nowtok == tok_ellipsis3 || nowtok == tok_ellipsis4
254               || nowtok == tok_ellipsis2)
255             {
256               ellipsis = nowtok;
257               state = 4;
258               continue;
259             }
260           /* FALLTHROUGH */
261
262         case 5:
263           /* We expect a value of the form <Uxxxx> or <Uxxxxxxxx> where
264              the xxx mean a hexadecimal value.  */
265           state = 2;
266
267           errno = 0;
268           if (nowtok != tok_ucs4)
269             {
270               lr_error (repfile,
271                         _("syntax error in repertoire map definition: %s"),
272                         _("no <Uxxxx> or <Uxxxxxxxx> value given"));
273
274               lr_ignore_rest (repfile, 0);
275               continue;
276             }
277
278           /* We've found a new valid definition.  */
279           repertoire_new_char (repfile, &result->char_table,
280                                &result->reverse_table, &result->mem_pool,
281                                now->val.ucs4, from_name, to_name,
282                                ellipsis != tok_ellipsis2);
283
284           /* Ignore the rest of the line.  */
285           lr_ignore_rest (repfile, 0);
286
287           from_name = NULL;
288           to_name = NULL;
289
290           continue;
291
292         case 4:
293           if (nowtok != tok_bsymbol)
294             {
295               lr_error (repfile,
296                         _("syntax error in repertoire map definition: %s"),
297                         _("no symbolic name given for end of range"));
298
299               lr_ignore_rest (repfile, 0);
300               state = 2;
301               continue;
302             }
303
304           /* Copy the to-name in a safe place.  */
305           to_name = (char *) obstack_copy0 (&result->mem_pool,
306                                             repfile->token.val.str.startmb,
307                                             repfile->token.val.str.lenmb);
308
309           state = 5;
310           continue;
311
312         case 90:
313           if (nowtok != tok_charids)
314             lr_error (repfile, _("\
315 `%1$s' definition does not end with `END %1$s'"), "CHARIDS");
316
317           lr_ignore_rest (repfile, nowtok == tok_charids);
318           break;
319         }
320
321       break;
322     }
323
324   if (state != 2 && state != 90 && !be_quiet)
325     error (0, 0, _("%s: premature end of file"), repfile->fname);
326
327   lr_close (repfile);
328
329   if (tsearch (result, &known, &repertoire_compare) == NULL)
330     /* Something went wrong.  */
331     error (0, errno, _("cannot safe new repertoire map"));
332
333   return result;
334 }
335
336
337 void
338 repertoire_complain (const char *name)
339 {
340   if (tfind (name, &unavailable, (__compar_fn_t) strcmp) == NULL)
341     {
342       error (0, errno, _("repertoire map file `%s' not found"), name);
343
344       /* Remember that we reported this map.  */
345       tsearch (name, &unavailable, (__compar_fn_t) strcmp);
346     }
347 }
348
349
350 static int
351 repertoire_compare (const void *p1, const void *p2)
352 {
353   struct repertoire_t *r1 = (struct repertoire_t *) p1;
354   struct repertoire_t *r2 = (struct repertoire_t *) p2;
355
356   return strcmp (r1->name, r2->name);
357 }
358
359
360 static const struct keyword_t *
361 repertoiremap_hash (const char *str, unsigned int len)
362 {
363   static const struct keyword_t wordlist[0] =
364   {
365     {"escape_char",      tok_escape_char,     0},
366     {"comment_char",     tok_comment_char,    0},
367     {"CHARIDS",          tok_charids,         0},
368     {"END",              tok_end,             0},
369   };
370
371   if (len == 11 && memcmp (wordlist[0].name, str, 11) == 0)
372     return &wordlist[0];
373   if (len == 12 && memcmp (wordlist[1].name, str, 12) == 0)
374     return &wordlist[1];
375   if (len == 7 && memcmp (wordlist[2].name, str, 7) == 0)
376     return &wordlist[2];
377   if (len == 3 && memcmp (wordlist[3].name, str, 3) == 0)
378     return &wordlist[3];
379
380   return NULL;
381 }
382
383
384 static void
385 repertoire_new_char (struct linereader *lr, hash_table *ht, hash_table *rt,
386                      struct obstack *ob, uint32_t value, const char *from,
387                      const char *to, int decimal_ellipsis)
388 {
389   char *from_end;
390   char *to_end;
391   const char *cp;
392   char *buf = NULL;
393   int prefix_len, len1, len2;
394   unsigned int from_nr, to_nr, cnt;
395
396   if (to == NULL)
397     {
398       insert_entry (ht, from, strlen (from),
399                     (void *) (unsigned long int) value);
400       /* Please note that it isn't a bug if a symbol is defined more
401          than once.  All later definitions are simply discarded.  */
402
403       insert_entry (rt, obstack_copy (ob, &value, sizeof (value)),
404                     sizeof (value), (void *) from);
405
406       return;
407     }
408
409   /* We have a range: the names must have names with equal prefixes
410      and an equal number of digits, where the second number is greater
411      or equal than the first.  */
412   len1 = strlen (from);
413   len2 = strlen (to);
414
415   if (len1 != len2)
416     {
417     invalid_range:
418       lr_error (lr, _("invalid names for character range"));
419       return;
420     }
421
422   cp = &from[len1 - 1];
423   if (decimal_ellipsis)
424     while (isdigit (*cp) && cp >= from)
425       --cp;
426   else
427     while (isxdigit (*cp) && cp >= from)
428       {
429         if (!isdigit (*cp) && !isupper (*cp))
430           lr_error (lr, _("\
431 hexadecimal range format should use only capital characters"));
432         --cp;
433       }
434
435   prefix_len = (cp - from) + 1;
436
437   if (cp == &from[len1 - 1] || strncmp (from, to, prefix_len) != 0)
438     goto invalid_range;
439
440   errno = 0;
441   from_nr = strtoul (&from[prefix_len], &from_end, decimal_ellipsis ? 10 : 16);
442   if (*from_end != '\0' || (from_nr == ULONG_MAX && errno == ERANGE)
443       || ((to_nr = strtoul (&to[prefix_len], &to_end,
444                             decimal_ellipsis ? 10 : 16)) == ULONG_MAX
445           && errno == ERANGE)
446       || *to_end != '\0')
447     {
448       lr_error (lr, _("<%s> and <%s> are invalid names for range"),
449                 from, to);
450       return;
451     }
452
453   if (from_nr > to_nr)
454     {
455       lr_error (lr, _("upper limit in range is not smaller then lower limit"));
456       return;
457     }
458
459   for (cnt = from_nr; cnt <= to_nr; ++cnt)
460     {
461       uint32_t this_value = value + (cnt - from_nr);
462
463       obstack_printf (ob, decimal_ellipsis ? "%.*s%0*d" : "%.*s%0*X",
464                       prefix_len, from, len1 - prefix_len, cnt);
465       obstack_1grow (ob, '\0');
466
467       insert_entry (ht, buf, len1,
468                     (void *) (unsigned long int) this_value);
469       /* Please note we don't examine the return value since it is no error
470          if we have two definitions for a symbol.  */
471
472       insert_entry (rt, obstack_copy (ob, &this_value, sizeof (this_value)),
473                     sizeof (this_value), (void *) from);
474     }
475 }
476
477
478 uint32_t
479 repertoire_find_value (const struct repertoire_t *rep, const char *name,
480                        size_t len)
481 {
482   void *result;
483
484   if (rep == NULL)
485     return ILLEGAL_CHAR_VALUE;
486
487   if (find_entry ((hash_table *) &rep->char_table, name, len, &result) < 0)
488     return ILLEGAL_CHAR_VALUE;
489
490   return (uint32_t) ((unsigned long int) result);
491 }
492
493
494 const char *
495 repertoire_find_symbol (const struct repertoire_t *rep, uint32_t ucs)
496 {
497   void *result;
498
499   if (rep == NULL)
500     return NULL;
501
502   if (find_entry ((hash_table *) &rep->reverse_table, &ucs, sizeof (ucs),
503                   &result) < 0)
504     return NULL;
505
506   return (const char *) result;
507 }
508
509
510 struct charseq *
511 repertoire_find_seq (const struct repertoire_t *rep, uint32_t ucs)
512 {
513   void *result;
514
515   if (rep == NULL)
516     return NULL;
517
518   if (find_entry ((hash_table *) &rep->seq_table, &ucs, sizeof (ucs),
519                   &result) < 0)
520     return NULL;
521
522   return (struct charseq *) result;
523 }