(add_to_readlist): Take locale pointer as extra parameter from which
[kopensolaris-gnu/glibc.git] / locale / programs / charmap.c
1 /* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
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 <ctype.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <libintl.h>
28 #include <limits.h>
29 #include <obstack.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "error.h"
35 #include "linereader.h"
36 #include "charmap.h"
37 #include "locfile.h"
38 #include "repertoire.h"
39
40 #include <assert.h>
41
42
43 /* Define the lookup function.  */
44 #include "charmap-kw.h"
45
46
47 extern void *xmalloc (size_t __n);
48
49 /* Prototypes for local functions.  */
50 static struct charmap_t *parse_charmap (struct linereader *cmfile);
51 static void new_width (struct linereader *cmfile, struct charmap_t *result,
52                        const char *from, const char *to,
53                        unsigned long int width);
54 static void charmap_new_char (struct linereader *lr, struct charmap_t *cm,
55                               int nbytes, char *bytes, const char *from,
56                               const char *to, int decimal_ellipsis, int step);
57
58
59 struct charmap_t *
60 charmap_read (const char *filename)
61 {
62   struct charmap_t *result = NULL;
63
64   if (filename != NULL)
65     {
66       struct linereader *cmfile;
67
68       /* First try the name as found in the parameter.  */
69       cmfile = lr_open (filename, charmap_hash);
70       if (cmfile == NULL)
71         {
72           /* No successful.  So start looking through the directories
73              in the I18NPATH if this is a simple name.  */
74           if (strchr (filename, '/') == NULL)
75             {
76               char *i18npath = getenv ("I18NPATH");
77               if (i18npath != NULL && *i18npath != '\0')
78                 {
79                   char path[strlen (filename) + 1 + strlen (i18npath)
80                            + sizeof ("/charmaps/") - 1];
81                   char *next;
82                   i18npath = strdupa (i18npath);
83
84
85                   while (cmfile == NULL
86                          && (next = strsep (&i18npath, ":")) != NULL)
87                     {
88                       stpcpy (stpcpy (stpcpy (path, next), "/charmaps/"),
89                               filename);
90
91                       cmfile = lr_open (path, charmap_hash);
92
93                       if (cmfile == NULL)
94                         {
95                           /* Try without the "/charmaps" part.  */
96                           stpcpy (stpcpy (path, next), filename);
97
98                           cmfile = lr_open (path, charmap_hash);
99                         }
100                     }
101                 }
102
103               if (cmfile == NULL)
104                 {
105                   /* Try the default directory.  */
106                   char path[sizeof (CHARMAP_PATH) + strlen (filename) + 1];
107
108                   stpcpy (stpcpy (stpcpy (path, CHARMAP_PATH), "/"), filename);
109                   cmfile = lr_open (path, charmap_hash);
110                 }
111             }
112         }
113
114       if (cmfile != NULL)
115         {
116           result = parse_charmap (cmfile);
117
118           if (result == NULL && !be_quiet)
119             error (0, errno, _("character map file `%s' not found"), filename);
120         }
121     }
122
123   if (result == NULL)
124     {
125       /* OK, one more try.  We also accept the names given to the
126          character sets in the files.  Sometimes they differ from the
127          file name.  */
128       DIR *dir;
129       struct dirent *dirent;
130
131       dir = opendir (CHARMAP_PATH);
132       if (dir != NULL)
133         {
134           while ((dirent = readdir (dir)) != NULL)
135             if (strcmp (dirent->d_name, ".") != 0
136                 && strcmp (dirent->d_name, "..") != 0)
137               {
138                 char buf[sizeof (CHARMAP_PATH)
139                         + strlen (dirent->d_name) + 1];
140                 FILE *fp;
141 #ifdef _DIRENT_HAVE_D_TYPE
142                 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_REG)
143                   continue;
144 #endif
145                 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
146                         dirent->d_name);
147
148                 fp = fopen (buf, "r");
149                 if (fp != NULL)
150                   {
151                     char *name = NULL;
152
153                     while (!feof (fp))
154                       {
155                         char junk[BUFSIZ];
156
157                         if (fscanf (fp, " <code_set_name> %as", &name) == 1
158                             || (fscanf (fp, " <code_set_name> \"%as\"", &name)
159                                 == 1)
160                             || fscanf (fp, "%% alias %as", &name) == 1)
161                           {
162                             if (strcasecmp (name, filename) == 0)
163                               break;
164
165                             free (name);
166                             name = NULL;
167                           }
168
169                         if (fgets (junk, sizeof junk, fp) != NULL)
170                           {
171                             if (strstr (junk, "CHARMAP") != NULL)
172                               /* We cannot expect more aliases from now on.  */
173                               break;
174
175                             while (strchr (junk, '\n') == NULL
176                                    && fgets (junk, sizeof junk, fp) != NULL)
177                               continue;
178                           }
179                       }
180
181                     fclose (fp);
182
183                     if (name != NULL)
184                       {
185                         struct linereader *cmfile;
186
187                         cmfile = lr_open (buf, charmap_hash);
188                         result = (cmfile == NULL
189                                   ? NULL : parse_charmap (cmfile));
190
191                         if (result)
192                           return result;
193
194                         break;
195                       }
196                   }
197               }
198
199           closedir (dir);
200         }
201     }
202
203   if (result == NULL)
204     {
205       struct linereader *cmfile;
206
207       cmfile = lr_open (CHARMAP_PATH "/" DEFAULT_CHARMAP, charmap_hash);
208
209       result = cmfile == NULL ? NULL : parse_charmap (cmfile);
210
211       if (result == NULL)
212         error (4, errno, _("default character map file `%s' not found"),
213                DEFAULT_CHARMAP);
214     }
215
216   return result;
217 }
218
219
220 static struct charmap_t *
221 parse_charmap (struct linereader *cmfile)
222 {
223   struct charmap_t *result;
224   int state;
225   enum token_t expected_tok = tok_error;
226   const char *expected_str = NULL;
227   char *from_name = NULL;
228   char *to_name = NULL;
229   enum token_t ellipsis = 0;
230   int step = 1;
231
232   /* We don't want symbolic names in string to be translated.  */
233   cmfile->translate_strings = 0;
234
235   /* Allocate room for result.  */
236   result = (struct charmap_t *) xmalloc (sizeof (struct charmap_t));
237   memset (result, '\0', sizeof (struct charmap_t));
238   /* The default DEFAULT_WIDTH is 1.  */
239   result->width_default = 1;
240
241 #define obstack_chunk_alloc malloc
242 #define obstack_chunk_free free
243   obstack_init (&result->mem_pool);
244
245   if (init_hash (&result->char_table, 256)
246       || init_hash (&result->byte_table, 256))
247     {
248       free (result);
249       return NULL;
250     }
251
252   /* We use a state machine to describe the charmap description file
253      format.  */
254   state = 1;
255   while (1)
256     {
257       /* What's on?  */
258       struct token *now = lr_token (cmfile, NULL, NULL);
259       enum token_t nowtok = now->tok;
260       struct token *arg;
261
262       if (nowtok == tok_eof)
263         break;
264
265       switch (state)
266         {
267         case 1:
268           /* The beginning.  We expect the special declarations, EOL or
269              `CHARMAP'.  */
270           if (nowtok == tok_eol)
271             /* Ignore empty lines.  */
272             continue;
273
274           if (nowtok == tok_charmap)
275             {
276               from_name = NULL;
277               to_name = NULL;
278
279               /* We have to set up the real work.  Fill in some
280                  default values.  */
281               if (result->mb_cur_max == 0)
282                 result->mb_cur_max = 1;
283               if (result->mb_cur_min == 0)
284                 result->mb_cur_min = result->mb_cur_max;
285               if (result->mb_cur_min > result->mb_cur_max)
286                 {
287                   if (!be_quiet)
288                     error (0, 0, _("\
289 %s: <mb_cur_max> must be greater than <mb_cur_min>\n"),
290                            cmfile->fname);
291
292                   result->mb_cur_min = result->mb_cur_max;
293                 }
294
295               lr_ignore_rest (cmfile, 1);
296
297               state = 2;
298               continue;
299             }
300
301           if (nowtok != tok_code_set_name && nowtok != tok_mb_cur_max
302               && nowtok != tok_mb_cur_min && nowtok != tok_escape_char
303               && nowtok != tok_comment_char && nowtok != tok_g0esc
304               && nowtok != tok_g1esc && nowtok != tok_g2esc
305               && nowtok != tok_g3esc && nowtok != tok_repertoiremap
306               && nowtok != tok_include)
307             {
308               lr_error (cmfile, _("syntax error in prolog: %s"),
309                         _("invalid definition"));
310
311               lr_ignore_rest (cmfile, 0);
312               continue;
313             }
314
315           /* We know that we need an argument.  */
316           arg = lr_token (cmfile, NULL, NULL);
317
318           switch (nowtok)
319             {
320             case tok_code_set_name:
321             case tok_repertoiremap:
322               if (arg->tok != tok_ident && arg->tok != tok_string)
323                 {
324                 badarg:
325                   lr_error (cmfile, _("syntax error in prolog: %s"),
326                             _("bad argument"));
327
328                   lr_ignore_rest (cmfile, 0);
329                   continue;
330                 }
331
332               if (nowtok == tok_code_set_name)
333                 result->code_set_name = obstack_copy0 (&result->mem_pool,
334                                                        arg->val.str.startmb,
335                                                        arg->val.str.lenmb);
336               else
337                 result->repertoiremap = obstack_copy0 (&result->mem_pool,
338                                                        arg->val.str.startmb,
339                                                        arg->val.str.lenmb);
340
341               lr_ignore_rest (cmfile, 1);
342               continue;
343
344             case tok_mb_cur_max:
345             case tok_mb_cur_min:
346               if (arg->tok != tok_number)
347                 goto badarg;
348
349               if (verbose
350                   && ((nowtok == tok_mb_cur_max
351                        && result->mb_cur_max != 0)
352                       || (nowtok == tok_mb_cur_max
353                           && result->mb_cur_max != 0)))
354                 lr_error (cmfile, _("duplicate definition of <%s>"),
355                           nowtok == tok_mb_cur_min
356                           ? "mb_cur_min" : "mb_cur_max");
357
358               if (arg->val.num < 1)
359                 {
360                   lr_error (cmfile,
361                             _("value for <%s> must be 1 or greater"),
362                             nowtok == tok_mb_cur_min
363                             ? "mb_cur_min" : "mb_cur_max");
364
365                   lr_ignore_rest (cmfile, 0);
366                   continue;
367                 }
368               if ((nowtok == tok_mb_cur_max && result->mb_cur_min != 0
369                    && (int) arg->val.num < result->mb_cur_min)
370                   || (nowtok == tok_mb_cur_min && result->mb_cur_max != 0
371                       && (int) arg->val.num > result->mb_cur_max))
372                 {
373                   lr_error (cmfile, _("\
374 value of <%s> must be greater or equal than the value of <%s>"),
375                             "mb_cur_max", "mb_cur_min");
376
377                   lr_ignore_rest (cmfile, 0);
378                   continue;
379                 }
380
381               if (nowtok == tok_mb_cur_max)
382                 result->mb_cur_max = arg->val.num;
383               else
384                 result->mb_cur_min = arg->val.num;
385
386               lr_ignore_rest (cmfile, 1);
387               continue;
388
389             case tok_escape_char:
390             case tok_comment_char:
391               if (arg->tok != tok_ident)
392                 goto badarg;
393
394               if (arg->val.str.lenmb != 1)
395                 {
396                   lr_error (cmfile, _("\
397 argument to <%s> must be a single character"),
398                             nowtok == tok_escape_char ? "escape_char"
399                                                       : "comment_char");
400
401                   lr_ignore_rest (cmfile, 0);
402                   continue;
403                 }
404
405               if (nowtok == tok_escape_char)
406                 cmfile->escape_char = *arg->val.str.startmb;
407               else
408                 cmfile->comment_char = *arg->val.str.startmb;
409
410               lr_ignore_rest (cmfile, 1);
411               continue;
412
413             case tok_g0esc:
414             case tok_g1esc:
415             case tok_g2esc:
416             case tok_g3esc:
417             case tok_escseq:
418               lr_ignore_rest (cmfile, 0); /* XXX */
419               continue;
420
421             case tok_include:
422               lr_error (cmfile, _("\
423 character sets with locking states are not supported"));
424               exit (4);
425
426             default:
427               /* Cannot happen.  */
428               assert (! "Should not happen");
429             }
430           break;
431
432         case 2:
433           /* We have seen `CHARMAP' and now are in the body.  Each line
434              must have the format "%s %s %s\n" or "%s...%s %s %s\n".  */
435           if (nowtok == tok_eol)
436             /* Ignore empty lines.  */
437             continue;
438
439           if (nowtok == tok_end)
440             {
441               expected_tok = tok_charmap;
442               expected_str = "CHARMAP";
443               state = 90;
444               continue;
445             }
446
447           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
448             {
449               lr_error (cmfile, _("syntax error in %s definition: %s"),
450                         "CHARMAP", _("no symbolic name given"));
451
452               lr_ignore_rest (cmfile, 0);
453               continue;
454             }
455
456           /* If the previous line was not completely correct free the
457              used memory.  */
458           if (from_name != NULL)
459             obstack_free (&result->mem_pool, from_name);
460
461           if (nowtok == tok_bsymbol)
462             from_name = (char *) obstack_copy0 (&result->mem_pool,
463                                                 now->val.str.startmb,
464                                                 now->val.str.lenmb);
465           else
466             {
467               obstack_printf (&result->mem_pool, "U%08X",
468                               cmfile->token.val.ucs4);
469               obstack_1grow (&result->mem_pool, '\0');
470               from_name = (char *) obstack_finish (&result->mem_pool);
471             }
472           to_name = NULL;
473
474           state = 3;
475           continue;
476
477         case 3:
478           /* We have two possibilities: We can see an ellipsis or an
479              encoding value.  */
480           if (nowtok == tok_ellipsis3 || nowtok == tok_ellipsis4
481               || nowtok == tok_ellipsis2 || nowtok == tok_ellipsis4_2
482               || nowtok == tok_ellipsis2_2)
483             {
484               ellipsis = nowtok;
485               if (nowtok == tok_ellipsis4_2)
486                 {
487                   step = 2;
488                   nowtok = tok_ellipsis4;
489                 }
490               else if (nowtok == tok_ellipsis2_2)
491                 {
492                   step = 2;
493                   nowtok = tok_ellipsis2;
494                 }
495               state = 4;
496               continue;
497             }
498           /* FALLTHROUGH */
499
500         case 5:
501           if (nowtok != tok_charcode)
502             {
503               lr_error (cmfile, _("syntax error in %s definition: %s"),
504                         "CHARMAP", _("invalid encoding given"));
505
506               lr_ignore_rest (cmfile, 0);
507
508               state = 2;
509               continue;
510             }
511
512           if (now->val.charcode.nbytes < result->mb_cur_min)
513             lr_error (cmfile, _("too few bytes in character encoding"));
514           else if (now->val.charcode.nbytes > result->mb_cur_max)
515             lr_error (cmfile, _("too many bytes in character encoding"));
516           else
517             charmap_new_char (cmfile, result, now->val.charcode.nbytes,
518                               now->val.charcode.bytes, from_name, to_name,
519                               ellipsis != tok_ellipsis2, step);
520
521           /* Ignore trailing comment silently.  */
522           lr_ignore_rest (cmfile, 0);
523
524           from_name = NULL;
525           to_name = NULL;
526           ellipsis = tok_none;
527           step = 1;
528
529           state = 2;
530           continue;
531
532         case 4:
533           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
534             {
535               lr_error (cmfile, _("syntax error in %s definition: %s"),
536                         "CHARMAP",
537                         _("no symbolic name given for end of range"));
538
539               lr_ignore_rest (cmfile, 0);
540               continue;
541             }
542
543           /* Copy the to-name in a safe place.  */
544           if (nowtok == tok_bsymbol)
545             to_name = (char *) obstack_copy0 (&result->mem_pool,
546                                               cmfile->token.val.str.startmb,
547                                               cmfile->token.val.str.lenmb);
548           else
549             {
550               obstack_printf (&result->mem_pool, "U%08X",
551                               cmfile->token.val.ucs4);
552               obstack_1grow (&result->mem_pool, '\0');
553               to_name = (char *) obstack_finish (&result->mem_pool);
554             }
555
556           state = 5;
557           continue;
558
559         case 90:
560           if (nowtok != expected_tok)
561             lr_error (cmfile, _("\
562 `%1$s' definition does not end with `END %1$s'"), expected_str);
563
564           lr_ignore_rest (cmfile, nowtok == expected_tok);
565           state = 91;
566           continue;
567
568         case 91:
569           /* Waiting for WIDTH... */
570           if (nowtok == tok_eol)
571             /* Ignore empty lines.  */
572             continue;
573
574           if (nowtok == tok_width_default)
575             {
576               state = 92;
577               continue;
578             }
579
580           if (nowtok == tok_width)
581             {
582               lr_ignore_rest (cmfile, 1);
583               state = 93;
584               continue;
585             }
586
587           if (nowtok == tok_width_variable)
588             {
589               lr_ignore_rest (cmfile, 1);
590               state = 98;
591               continue;
592             }
593
594           lr_error (cmfile, _("\
595 only WIDTH definitions are allowed to follow the CHARMAP definition"));
596
597           lr_ignore_rest (cmfile, 0);
598           continue;
599
600         case 92:
601           if (nowtok != tok_number)
602             lr_error (cmfile, _("value for %s must be an integer"),
603                       "WIDTH_DEFAULT");
604           else
605             result->width_default = now->val.num;
606
607           lr_ignore_rest (cmfile, nowtok == tok_number);
608
609           state = 91;
610           continue;
611
612         case 93:
613           /* We now expect `END WIDTH' or lines of the format "%s %d\n" or
614              "%s...%s %d\n".  */
615           if (nowtok == tok_eol)
616             /* ignore empty lines.  */
617             continue;
618
619           if (nowtok == tok_end)
620             {
621               expected_tok = tok_width;
622               expected_str = "WIDTH";
623               state = 90;
624               continue;
625             }
626
627           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
628             {
629               lr_error (cmfile, _("syntax error in %s definition: %s"),
630                         "WIDTH", _("no symbolic name given"));
631
632               lr_ignore_rest (cmfile, 0);
633               continue;
634             }
635
636           if (from_name != NULL)
637             obstack_free (&result->mem_pool, from_name);
638
639           if (nowtok == tok_bsymbol)
640             from_name = (char *) obstack_copy0 (&result->mem_pool,
641                                                 now->val.str.startmb,
642                                                 now->val.str.lenmb);
643           else
644             {
645               obstack_printf (&result->mem_pool, "U%08X",
646                               cmfile->token.val.ucs4);
647               obstack_1grow (&result->mem_pool, '\0');
648               from_name = (char *) obstack_finish (&result->mem_pool);
649             }
650
651           to_name = NULL;
652
653           state = 94;
654           continue;
655
656         case 94:
657           if (nowtok == tok_ellipsis3)
658             {
659               state = 95;
660               continue;
661             }
662
663         case 96:
664           if (nowtok != tok_number)
665             lr_error (cmfile, _("value for %s must be an integer"),
666                       "WIDTH");
667           else
668             {
669               /* Store width for chars.  */
670               new_width (cmfile, result, from_name, to_name, now->val.num);
671
672               from_name = NULL;
673               to_name = NULL;
674             }
675
676           lr_ignore_rest (cmfile, nowtok == tok_number);
677
678           state = 93;
679           continue;
680
681         case 95:
682           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
683             {
684               lr_error (cmfile, _("syntax error in %s definition: %s"),
685                         "WIDTH", _("no symbolic name given for end of range"));
686
687               lr_ignore_rest (cmfile, 0);
688
689               state = 93;
690               continue;
691             }
692
693           if (nowtok == tok_bsymbol)
694             to_name = (char *) obstack_copy0 (&result->mem_pool,
695                                               now->val.str.startmb,
696                                               now->val.str.lenmb);
697           else
698             {
699               obstack_printf (&result->mem_pool, "U%08X",
700                               cmfile->token.val.ucs4);
701               obstack_1grow (&result->mem_pool, '\0');
702               to_name = (char *) obstack_finish (&result->mem_pool);
703             }
704
705           state = 96;
706           continue;
707
708         case 98:
709           /* We now expect `END WIDTH_VARIABLE' or lines of the format
710              "%s\n" or "%s...%s\n".  */
711           if (nowtok == tok_eol)
712             /* ignore empty lines.  */
713             continue;
714
715           if (nowtok == tok_end)
716             {
717               expected_tok = tok_width_variable;
718               expected_str = "WIDTH_VARIABLE";
719               state = 90;
720               continue;
721             }
722
723           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
724             {
725               lr_error (cmfile, _("syntax error in %s definition: %s"),
726                         "WIDTH_VARIABLE", _("no symbolic name given"));
727
728               lr_ignore_rest (cmfile, 0);
729
730               continue;
731             }
732
733           if (from_name != NULL)
734             obstack_free (&result->mem_pool, from_name);
735
736           if (nowtok == tok_bsymbol)
737             from_name = (char *) obstack_copy0 (&result->mem_pool,
738                                                 now->val.str.startmb,
739                                                 now->val.str.lenmb);
740           else
741             {
742               obstack_printf (&result->mem_pool, "U%08X",
743                               cmfile->token.val.ucs4);
744               obstack_1grow (&result->mem_pool, '\0');
745               from_name = (char *) obstack_finish (&result->mem_pool);
746             }
747           to_name = NULL;
748
749           state = 99;
750           continue;
751
752         case 99:
753           if (nowtok == tok_ellipsis3)
754             state = 100;
755
756           /* Store info.  */
757           from_name = NULL;
758
759           /* Warn */
760           state = 98;
761           continue;
762
763         case 100:
764           if (nowtok != tok_bsymbol && nowtok != tok_ucs4)
765             {
766               lr_error (cmfile, _("syntax error in %s definition: %s"),
767                         "WIDTH_VARIABLE",
768                         _("no symbolic name given for end of range"));
769               lr_ignore_rest (cmfile, 0);
770               continue;
771             }
772
773           if (nowtok == tok_bsymbol)
774             to_name = (char *) obstack_copy0 (&result->mem_pool,
775                                               now->val.str.startmb,
776                                               now->val.str.lenmb);
777           else
778             {
779               obstack_printf (&result->mem_pool, "U%08X",
780                               cmfile->token.val.ucs4);
781               obstack_1grow (&result->mem_pool, '\0');
782               to_name = (char *) obstack_finish (&result->mem_pool);
783             }
784
785           /* XXX Enter value into table.  */
786
787           lr_ignore_rest (cmfile, 1);
788
789           state = 98;
790           continue;
791
792         default:
793           error (5, 0, _("%s: error in state machine"), __FILE__);
794           /* NOTREACHED */
795         }
796       break;
797     }
798
799   if (state != 91 && !be_quiet)
800     error (0, 0, _("%s: premature end of file"), cmfile->fname);
801
802   lr_close (cmfile);
803
804   return result;
805 }
806
807
808 static void
809 new_width (struct linereader *cmfile, struct charmap_t *result,
810            const char *from, const char *to, unsigned long int width)
811 {
812   struct charseq *from_val;
813   struct charseq *to_val;
814
815   from_val = charmap_find_value (result, from, strlen (from));
816   if (from_val == NULL)
817     {
818       lr_error (cmfile, _("unknown character `%s'"), from);
819       return;
820     }
821
822   if (to == NULL)
823     to_val = from_val;
824   else
825     {
826       to_val = charmap_find_value (result, to, strlen (to));
827       if (to_val == NULL)
828         {
829           lr_error (cmfile, _("unknown character `%s'"), to);
830           return;
831         }
832     }
833
834   if (result->nwidth_rules >= result->nwidth_rules_max)
835     {
836       size_t new_size = result->nwidth_rules + 32;
837       struct width_rule *new_rules =
838         (struct width_rule *) obstack_alloc (&result->mem_pool,
839                                              (new_size
840                                               * sizeof (struct width_rule)));
841
842       memcpy (new_rules, result->width_rules,
843               result->nwidth_rules_max * sizeof (struct width_rule));
844
845       result->width_rules = new_rules;
846       result->nwidth_rules_max = new_size;
847     }
848
849   result->width_rules[result->nwidth_rules].from = from_val;
850   result->width_rules[result->nwidth_rules].to = to_val;
851   result->width_rules[result->nwidth_rules].width = (unsigned int) width;
852   ++result->nwidth_rules;
853 }
854
855
856 struct charseq *
857 charmap_find_value (const struct charmap_t *cm, const char *name, size_t len)
858 {
859   void *result;
860
861   return (find_entry ((hash_table *) &cm->char_table, name, len, &result)
862           < 0 ? NULL : (struct charseq *) result);
863 }
864
865
866 static void
867 charmap_new_char (struct linereader *lr, struct charmap_t *cm,
868                   int nbytes, char *bytes, const char *from, const char *to,
869                   int decimal_ellipsis, int step)
870 {
871   hash_table *ht = &cm->char_table;
872   hash_table *bt = &cm->byte_table;
873   struct obstack *ob = &cm->mem_pool;
874   char *from_end;
875   char *to_end;
876   const char *cp;
877   int prefix_len, len1, len2;
878   unsigned int from_nr, to_nr, cnt;
879   struct charseq *newp;
880
881   len1 = strlen (from);
882
883   if (to == NULL)
884     {
885       newp = (struct charseq *) obstack_alloc (ob, sizeof (*newp) + nbytes);
886       newp->nbytes = nbytes;
887       memcpy (newp->bytes, bytes, nbytes);
888       newp->name = from;
889
890       newp->ucs4 = UNINITIALIZED_CHAR_VALUE;
891       if ((from[0] == 'U' || from[0] == 'P') && (len1 == 5 || len1 == 9))
892         {
893           /* Maybe the name is of the form `Uxxxx' or `Uxxxxxxxx' where
894              xxxx and xxxxxxxx are hexadecimal numbers.  In this case
895              we use the value of xxxx or xxxxxxxx as the UCS4 value of
896              this character and we don't have to consult the repertoire
897              map.
898
899              If the name is of the form `Pxxxx' or `Pxxxxxxxx' the xxxx
900              and xxxxxxxx also give the code point in UCS4 but this must
901              be in the private, i.e., unassigned, area.  This should be
902              used for characters which do not (yet) have an equivalent
903              in ISO 10646 and Unicode.  */
904           char *endp;
905
906           errno = 0;
907           newp->ucs4 = strtoul (from + 1, &endp, 16);
908           if (endp - from != len1
909               || (newp->ucs4 == ULONG_MAX && errno == ERANGE)
910               || newp->ucs4 >= 0x80000000)
911             /* This wasn't successful.  Signal this name cannot be a
912                correct UCS value.  */
913             newp->ucs4 = UNINITIALIZED_CHAR_VALUE;
914         }
915
916       insert_entry (ht, from, len1, newp);
917       insert_entry (bt, newp->bytes, nbytes, newp);
918       /* Please note that it isn't a bug if a symbol is defined more
919          than once.  All later definitions are simply discarded.  */
920       return;
921     }
922
923   /* We have a range: the names must have names with equal prefixes
924      and an equal number of digits, where the second number is greater
925      or equal than the first.  */
926   len2 = strlen (to);
927
928   if (len1 != len2)
929     {
930     illegal_range:
931       lr_error (lr, _("invalid names for character range"));
932       return;
933     }
934
935   cp = &from[len1 - 1];
936   if (decimal_ellipsis)
937     while (isdigit (*cp) && cp >= from)
938       --cp;
939   else
940     while (isxdigit (*cp) && cp >= from)
941       {
942         if (!isdigit (*cp) && !isupper (*cp))
943           lr_error (lr, _("\
944 hexadecimal range format should use only capital characters"));
945         --cp;
946       }
947
948   prefix_len = (cp - from) + 1;
949
950   if (cp == &from[len1 - 1] || strncmp (from, to, prefix_len) != 0)
951     goto illegal_range;
952
953   errno = 0;
954   from_nr = strtoul (&from[prefix_len], &from_end, decimal_ellipsis ? 10 : 16);
955   if (*from_end != '\0' || (from_nr == ULONG_MAX && errno == ERANGE)
956       || ((to_nr = strtoul (&to[prefix_len], &to_end,
957                             decimal_ellipsis ? 10 : 16)) == ULONG_MAX
958           && errno == ERANGE)
959       || *to_end != '\0')
960     {
961       lr_error (lr, _("<%s> and <%s> are illegal names for range"), from, to);
962       return;
963     }
964
965   if (from_nr > to_nr)
966     {
967       lr_error (lr, _("upper limit in range is not higher then lower limit"));
968       return;
969     }
970
971   for (cnt = from_nr; cnt <= to_nr; cnt += step)
972     {
973       char *name_end;
974       obstack_printf (ob, decimal_ellipsis ? "%.*s%0*d" : "%.*s%0*X",
975                       prefix_len, from, len1 - prefix_len, cnt);
976       obstack_1grow (ob, '\0');
977       name_end = obstack_finish (ob);
978
979       newp = (struct charseq *) obstack_alloc (ob, sizeof (*newp) + nbytes);
980       newp->nbytes = nbytes;
981       memcpy (newp->bytes, bytes, nbytes);
982       newp->name = name_end;
983
984       newp->ucs4 = UNINITIALIZED_CHAR_VALUE;
985       if ((name_end[0] == 'U' || name_end[0] == 'P')
986           && (len1 == 5 || len1 == 9))
987         {
988           /* Maybe the name is of the form `Uxxxx' or `Uxxxxxxxx' where
989              xxxx and xxxxxxxx are hexadecimal numbers.  In this case
990              we use the value of xxxx or xxxxxxxx as the UCS4 value of
991              this character and we don't have to consult the repertoire
992              map.
993
994              If the name is of the form `Pxxxx' or `Pxxxxxxxx' the xxxx
995              and xxxxxxxx also give the code point in UCS4 but this must
996              be in the private, i.e., unassigned, area.  This should be
997              used for characters which do not (yet) have an equivalent
998              in ISO 10646 and Unicode.  */
999           char *endp;
1000
1001           errno = 0;
1002           newp->ucs4 = strtoul (name_end, &endp, 16);
1003           if (endp - name_end != len1
1004               || (newp->ucs4 == ULONG_MAX && errno == ERANGE)
1005               || newp->ucs4 >= 0x80000000)
1006             /* This wasn't successful.  Signal this name cannot be a
1007                correct UCS value.  */
1008             newp->ucs4 = UNINITIALIZED_CHAR_VALUE;
1009         }
1010
1011       insert_entry (ht, name_end, len1, newp);
1012       insert_entry (bt, newp->bytes, nbytes, newp);
1013       /* Please note we don't examine the return value since it is no error
1014          if we have two definitions for a symbol.  */
1015
1016       /* Increment the value in the byte sequence.  */
1017       if (++bytes[nbytes - 1] == '\0')
1018         {
1019           int b = nbytes - 2;
1020
1021           do
1022             if (b < 0)
1023               {
1024                 lr_error (lr,
1025                           _("resulting bytes for range not representable."));
1026                 return;
1027               }
1028           while (++bytes[b--] == 0);
1029         }
1030     }
1031 }
1032
1033
1034 struct charseq *
1035 charmap_find_symbol (const struct charmap_t *cm, const char *bytes,
1036                      size_t nbytes)
1037 {
1038   void *result;
1039
1040   return (find_entry ((hash_table *) &cm->byte_table, bytes, nbytes, &result)
1041           < 0 ? NULL : (struct charseq *) result);
1042 }