1 /* Handle configuration data.
2 Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
29 #include <sys/param.h>
31 #include <gconv_int.h>
34 /* This is the default path where we look for module lists. */
35 static const char default_gconv_path
[] = GCONV_PATH
;
37 /* Name of the file containing the module information in the directories
39 static const char gconv_conf_filename
[] = "gconv-modules";
41 /* Filename extension for the modules. */
43 # define MODULE_EXT ".so"
45 static const char gconv_module_ext
[] = MODULE_EXT
;
47 /* We have a few builtin transformations. */
48 static struct gconv_module builtin_modules
[] =
50 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
51 Fct, Init, End, MinF, MaxF, MinT, MaxT) \
54 from_constpfx: ConstPfx, \
55 from_constpfx_len: ConstLen, \
62 #define BUILTIN_ALIAS(From, To)
64 #include "gconv_builtin.h"
67 #undef BUILTIN_TRANSFORMATION
73 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
74 Fct, Init, End, MinF, MaxF, MinT, MaxT)
75 #define BUILTIN_ALIAS(From, To) From " " To,
77 #include "gconv_builtin.h"
81 # include <libio/libioP.h>
82 # define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
86 /* Test whether there is already a matching module known. */
89 detect_conflict (const char *alias
, size_t alias_len
)
91 struct gconv_module
*node
= __gconv_modules_db
;
95 int cmpres
= strncmp (alias
, node
->from_constpfx
,
96 MIN (alias_len
, node
->from_constpfx_len
));
100 struct gconv_module
*runp
;
102 if (alias_len
< node
->from_constpfx_len
)
103 /* Cannot possibly match. */
106 /* This means the prefix and the alias are identical. If
107 there is now a simple extry or a regular expression
108 matching this name we have found a conflict. If there is
109 no conflict with the elements in the `same' list there
110 cannot be a conflict. */
114 if (runp
->from_pattern
== NULL
)
116 /* This is a simple entry and therefore we have a
117 conflict if the strings are really the same. */
118 if (alias_len
== node
->from_constpfx_len
)
123 /* Compile the regular expression if necessary. */
124 if (runp
->from_regex
== NULL
)
126 if (__regcomp (&runp
->from_regex_mem
,
128 REG_EXTENDED
| REG_ICASE
) != 0)
129 /* Something is wrong. Remember this. */
130 runp
->from_regex
= (regex_t
*) -1L;
132 runp
->from_regex
= &runp
->from_regex_mem
;
135 if (runp
->from_regex
!= (regex_t
*) -1L)
139 /* Try to match the regular expression. */
140 if (__regexec (runp
->from_regex
, alias
, 1, match
, 0) == 0
141 && match
[0].rm_so
== 0
142 && alias
[match
[0].rm_eo
] == '\0')
143 /* They match, therefore it is a conflict. */
150 while (runp
!= NULL
);
152 if (alias_len
== node
->from_constpfx_len
)
155 node
= node
->matching
;
169 add_alias (char *rp
, void *modules
)
171 /* We now expect two more string. The strings are normalized
172 (converted to UPPER case) and strored in the alias database. */
173 struct gconv_alias
*new_alias
;
174 char *from
, *to
, *wp
;
176 while (isspace (*rp
))
179 while (*rp
!= '\0' && !isspace (*rp
))
180 *wp
++ = toupper (*rp
++);
182 /* There is no `to' string on the line. Ignore it. */
186 while (isspace (*rp
))
188 while (*rp
!= '\0' && !isspace (*rp
))
189 *wp
++ = toupper (*rp
++);
191 /* No `to' string, ignore the line. */
195 /* Test whether this alias conflicts with any available module. */
196 if (detect_conflict (from
, to
- from
- 1))
197 /* It does conflict, don't add the alias. */
200 new_alias
= (struct gconv_alias
*)
201 malloc (sizeof (struct gconv_alias
) + (wp
- from
));
202 if (new_alias
!= NULL
)
204 new_alias
->fromname
= memcpy ((char *) new_alias
205 + sizeof (struct gconv_alias
),
207 new_alias
->toname
= new_alias
->fromname
+ (to
- from
);
209 if (__tfind (new_alias
, &__gconv_alias_db
, __gconv_alias_compare
) != NULL
210 || (__tsearch (new_alias
, &__gconv_alias_db
, __gconv_alias_compare
)
212 /* Something went wrong, free this entry. */
218 /* Insert a data structure for a new module in the search tree. */
221 insert_module (struct gconv_module
*newp
)
223 struct gconv_module
**rootp
= &__gconv_modules_db
;
225 while (*rootp
!= NULL
)
227 struct gconv_module
*root
= *rootp
;
228 size_t minlen
= MIN (newp
->from_constpfx_len
, root
->from_constpfx_len
);
231 cmpres
= strncmp (newp
->from_constpfx
, root
->from_constpfx
, minlen
);
234 /* This can mean two things: the prefix is entirely the same or
235 it matches only for the minimum length of both strings. */
236 if (newp
->from_constpfx_len
== root
->from_constpfx_len
)
238 /* Both prefixes are identical. Insert the string at the
239 end of the `same' list if it is not already there. */
240 const char *from_pattern
= (newp
->from_pattern
241 ?: newp
->from_constpfx
);
243 while (strcmp (from_pattern
,
244 root
->from_pattern
?: root
->from_constpfx
) != 0
245 || strcmp (newp
->to_string
, root
->to_string
) != 0)
254 /* This is a no new conversion. */
260 /* The new element either has a prefix which is itself a
261 prefix for the prefix of the current node or vice verse.
262 In the first case we insert the node right here. Otherwise
263 we have to descent further. */
264 if (newp
->from_constpfx_len
< root
->from_constpfx_len
)
266 newp
->matching
= root
;
270 rootp
= &root
->matching
;
275 rootp
= &root
->right
;
278 /* Plug in the new node here. */
283 /* Add new module. */
286 add_module (char *rp
, const char *directory
, size_t dir_len
, void **modules
,
287 size_t *nmodules
, int modcounter
)
292 3. filename of the module
293 4. an optional cost value
295 struct gconv_module
*new_module
;
296 char *from
, *to
, *module
, *wp
;
302 while (isspace (*rp
))
306 while (*rp
!= '\0' && !isspace (*rp
))
308 if (!isalnum (*rp
) && *rp
!= '-' && *rp
!= '/' && *rp
!= '.'
309 && *rp
!= '_' && *rp
!= '(' && *rp
!= ')')
318 while (isspace (*rp
))
320 while (*rp
!= '\0' && !isspace (*rp
))
321 *wp
++ = toupper (*rp
++);
327 while (isspace (*rp
));
329 while (*rp
!= '\0' && !isspace (*rp
))
333 /* There is no cost, use one by default. */
339 /* There might be a cost value. */
343 cost_hi
= strtol (rp
, &endp
, 10);
344 if (rp
== endp
|| cost_hi
< 1)
345 /* No useful information. */
349 if (module
[0] == '\0')
350 /* No module name given. */
352 if (module
[0] == '/')
355 /* Increment by one for the slash. */
358 /* See whether we must add the ending. */
360 if (wp
- module
< sizeof (gconv_module_ext
)
361 || memcmp (wp
- sizeof (gconv_module_ext
), gconv_module_ext
,
362 sizeof (gconv_module_ext
)) != 0)
363 /* We must add the module extension. */
364 need_ext
= sizeof (gconv_module_ext
) - 1;
366 /* We've collected all the information, now create an entry. */
371 while (isalnum (from
[const_len
]) || from
[const_len
] == '-'
372 || from
[const_len
] == '/' || from
[const_len
] == '.'
373 || from
[const_len
] == '_')
377 const_len
= to
- from
- 1;
379 new_module
= (struct gconv_module
*) calloc (1,
380 sizeof (struct gconv_module
)
382 + dir_len
+ need_ext
);
383 if (new_module
!= NULL
)
387 new_module
->from_constpfx
= memcpy ((char *) new_module
388 + sizeof (struct gconv_module
),
391 new_module
->from_pattern
= new_module
->from_constpfx
;
393 new_module
->from_constpfx_len
= const_len
;
395 new_module
->to_string
= memcpy ((char *) new_module
->from_constpfx
396 + (to
- from
), to
, module
- to
);
398 new_module
->cost_hi
= cost_hi
;
399 new_module
->cost_lo
= modcounter
;
401 new_module
->module_name
= (char *) new_module
->to_string
+ (module
- to
);
404 tmp
= (char *) new_module
->module_name
;
407 tmp
= __mempcpy ((char *) new_module
->module_name
,
408 directory
, dir_len
- 1);
412 tmp
= __mempcpy (tmp
, module
, wp
- module
);
415 memcpy (tmp
- 1, gconv_module_ext
, sizeof (gconv_module_ext
));
417 /* See whether we have already an alias with this name defined.
418 We do allow regular expressions matching this any alias since
419 this expression can also match other names and we test for aliases
420 before testing for modules. */
423 struct gconv_alias fake_alias
;
425 fake_alias
.fromname
= new_module
->from_constpfx
;
427 if (__tfind (&fake_alias
, &__gconv_alias_db
, __gconv_alias_compare
)
430 /* This module duplicates an alias. */
436 /* Now insert the new module data structure in our search tree. */
437 insert_module (new_module
);
442 /* Read the next configuration file. */
445 read_conf_file (const char *filename
, const char *directory
, size_t dir_len
,
446 void **modules
, size_t *nmodules
)
448 FILE *fp
= fopen (filename
, "r");
453 /* Don't complain if a file is not present or readable, simply silently
458 /* Process the known entries of the file. Comments start with `#' and
459 end with the end of the line. Empty lines are ignored. */
460 while (!feof_unlocked (fp
))
462 char *rp
, *endp
, *word
;
463 ssize_t n
= __getdelim (&line
, &line_len
, '\n', fp
);
465 /* An error occurred. */
469 /* Terminate the line (excluding comments or newline) by an NUL byte
470 to simplify the following code. */
471 endp
= strchr (rp
, '#');
475 if (rp
[n
- 1] == '\n')
478 while (isspace (*rp
))
481 /* If this is an empty line go on with the next one. */
486 while (*rp
!= '\0' && !isspace (*rp
))
489 if (rp
- word
== sizeof ("alias") - 1
490 && memcmp (word
, "alias", sizeof ("alias") - 1) == 0)
491 add_alias (rp
, *modules
);
492 else if (rp
- word
== sizeof ("module") - 1
493 && memcmp (word
, "module", sizeof ("module") - 1) == 0)
494 add_module (rp
, directory
, dir_len
, modules
, nmodules
, modcounter
++);
496 /* Otherwise ignore the line. */
505 /* Read all configuration files found in the user-specified and the default
508 __gconv_read_conf (void)
510 const char *user_path
= __secure_getenv ("GCONV_PATH");
511 char *gconv_path
, *elem
;
512 void *modules
= NULL
;
514 int save_errno
= errno
;
517 if (user_path
== NULL
)
518 /* No user-defined path. Make a modifiable copy of the default path. */
519 gconv_path
= strdupa (default_gconv_path
);
522 /* Append the default path to the user-defined path. */
523 size_t user_len
= strlen (user_path
);
525 gconv_path
= alloca (user_len
+ 1 + sizeof (default_gconv_path
));
526 __mempcpy (__mempcpy (__mempcpy (gconv_path
, user_path
, user_len
),
528 default_gconv_path
, sizeof (default_gconv_path
));
531 elem
= __strtok_r (gconv_path
, ":", &gconv_path
);
535 /* We define a reasonable limit. */
536 # define MAXPATHLEN 4096
538 char real_elem
[MAXPATHLEN
];
540 if (__realpath (elem
, real_elem
) != NULL
)
542 size_t elem_len
= strlen (real_elem
);
545 filename
= alloca (elem_len
+ 1 + sizeof (gconv_conf_filename
));
546 __mempcpy (__mempcpy (__mempcpy (filename
, real_elem
, elem_len
),
548 gconv_conf_filename
, sizeof (gconv_conf_filename
));
550 /* Read the next configuration file. */
551 read_conf_file (filename
, real_elem
, elem_len
, &modules
, &nmodules
);
554 /* Get next element in the path. */
555 elem
= __strtok_r (NULL
, ":", &gconv_path
);
558 /* Add the internal modules. */
559 for (cnt
= 0; cnt
< sizeof (builtin_modules
) / sizeof (builtin_modules
[0]);
562 if (builtin_modules
[cnt
].from_pattern
== NULL
)
564 struct gconv_alias fake_alias
;
566 fake_alias
.fromname
= builtin_modules
[cnt
].from_constpfx
;
568 if (__tfind (&fake_alias
, &__gconv_alias_db
, __gconv_alias_compare
)
570 /* It'll conflict so don't add it. */
574 insert_module (&builtin_modules
[cnt
]);
577 /* Add aliases for builtin conversions. */
578 cnt
= sizeof (builtin_aliases
) / sizeof (builtin_aliases
[0]);
581 char *copy
= strdupa (builtin_aliases
[--cnt
]);
582 add_alias (copy
, modules
);
585 /* Restore the error number. */
586 __set_errno (save_errno
);