1 /* Handle configuration data.
2 Copyright (C) 1997, 1998 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 /* Function for searching module. */
88 module_compare (const void *p1
, const void *p2
)
90 const struct gconv_module
*s1
= (const struct gconv_module
*) p1
;
91 const struct gconv_module
*s2
= (const struct gconv_module
*) p2
;
94 if (s1
->from_pattern
== NULL
)
96 if (s2
->from_pattern
== NULL
)
97 result
= strcmp (s1
->from_constpfx
, s2
->from_constpfx
);
101 else if (s2
->from_pattern
== NULL
)
104 result
= strcmp (s1
->from_pattern
, s2
->from_pattern
);
107 result
= strcmp (s1
->to_string
, s2
->to_string
);
113 /* This function is used to test for a conflict which could be introduced
114 if adding a new alias.
116 This function is a *very* ugly hack. The action-function is not
117 supposed to alter the parameter. But we have to do this. We will if
118 necessary compile the regular expression so that we can see whether it
119 matches the alias name. This is safe in this environment and for the
120 sake of performance we do it this way. The alternative would be to
121 compile all regular expressions right from the start or to forget about
122 the compilation though we might need it later.
124 The second ugliness is that we have no possibility to pass parameters
125 to the function. Therefore we use a global variable. This is no problem
126 since we are for sure alone even in multi-threaded applications. */
128 /* This is alias we want to check. */
129 static const char *alias_to_test
;
131 /* This variable is set to a nonzero value once we have found a matching
133 static int abort_conflict_search
;
136 detect_conflict (const void *p
, VISIT value
, int level
)
138 struct gconv_module
*s
= *(struct gconv_module
**) p
;
140 if ((value
!= endorder
&& value
!= leaf
) || s
->from_constpfx
== NULL
141 || abort_conflict_search
)
144 /* Before we test the whole expression (if this is a regular expression)
145 make sure the constant prefix matches. In case this is no regular
146 expression this is the whole string. */
147 if (strcmp (alias_to_test
, s
->from_constpfx
) == 0)
149 if (s
->from_pattern
== NULL
)
150 /* This is a simple string and therefore we have a conflict. */
151 abort_conflict_search
= 1;
154 /* Make sure the regular expression is compiled (if possible). */
155 if (s
->from_regex
== NULL
)
157 /* Beware, this is what I warned you about in the comment
158 above. We are modifying the object. */
159 if (__regcomp (&s
->from_regex_mem
, s
->from_pattern
,
160 REG_EXTENDED
| REG_ICASE
) != 0)
161 /* Something is wrong. Remember this. */
162 s
->from_regex
= (regex_t
*) -1L;
164 s
->from_regex
= &s
->from_regex_mem
;
167 if (s
->from_regex
!= (regex_t
*) -1L)
171 if (__regexec (s
->from_regex
, alias_to_test
, 1, match
, 0) == 0
172 && match
[0].rm_so
== 0
173 && alias_to_test
[match
[0].rm_eo
] == '\0')
174 /* The whole string matched. This is also a conflict. */
175 abort_conflict_search
= 1;
184 add_alias (char *rp
, void *modules
)
186 /* We now expect two more string. The strings are normalized
187 (converted to UPPER case) and strored in the alias database. */
188 struct gconv_alias
*new_alias
;
189 char *from
, *to
, *wp
;
191 while (isspace (*rp
))
194 while (*rp
!= '\0' && !isspace (*rp
))
197 /* There is no `to' string on the line. Ignore it. */
201 while (isspace (*rp
))
203 while (*rp
!= '\0' && !isspace (*rp
))
206 /* No `to' string, ignore the line. */
210 /* Test whether this alias conflicts with any available module. See
211 the comment before the function `detect_conflict' for a description
212 of this ugly hack. */
213 alias_to_test
= from
;
214 abort_conflict_search
= 0;
215 __twalk (modules
, detect_conflict
);
216 if (abort_conflict_search
)
217 /* It does conflict, don't add the alias. */
220 new_alias
= (struct gconv_alias
*)
221 malloc (sizeof (struct gconv_alias
) + (wp
- from
));
222 if (new_alias
!= NULL
)
224 new_alias
->fromname
= memcpy ((char *) new_alias
225 + sizeof (struct gconv_alias
),
227 new_alias
->toname
= new_alias
->fromname
+ (to
- from
);
229 if (__tsearch (new_alias
, &__gconv_alias_db
, __gconv_alias_compare
)
231 /* Something went wrong, free this entry. */
237 /* Add new module. */
239 add_module (char *rp
, const char *directory
, size_t dir_len
, void **modules
,
240 size_t *nmodules
, int modcounter
)
245 3. filename of the module
246 4. an optional cost value
248 struct gconv_module
*new_module
;
249 char *from
, *to
, *module
, *wp
;
255 while (isspace (*rp
))
259 while (*rp
!= '\0' && !isspace (*rp
))
261 if (!isalnum (*rp
) && *rp
!= '-' && *rp
!= '/' && *rp
!= '.'
270 while (isspace (*rp
))
272 while (*rp
!= '\0' && !isspace (*rp
))
279 while (isspace (*rp
));
281 while (*rp
!= '\0' && !isspace (*rp
))
285 /* There is no cost, use one by default. */
291 /* There might be a cost value. */
295 cost_hi
= strtol (rp
, &endp
, 10);
297 /* No useful information. */
301 if (module
[0] == '\0')
302 /* No module name given. */
304 if (module
[0] == '/')
307 /* Increment by one for the slash. */
310 /* See whether we must add the ending. */
312 if (wp
- module
< sizeof (gconv_module_ext
)
313 || memcmp (wp
- sizeof (gconv_module_ext
), gconv_module_ext
,
314 sizeof (gconv_module_ext
)) != 0)
315 /* We must add the module extension. */
316 need_ext
= sizeof (gconv_module_ext
) - 1;
318 /* We've collected all the information, now create an entry. */
323 while (isalnum (from
[const_len
]) || from
[const_len
] == '-'
324 || from
[const_len
] == '/' || from
[const_len
] == '.'
325 || from
[const_len
] == '_')
329 const_len
= to
- from
- 1;
331 new_module
= (struct gconv_module
*) malloc (sizeof (struct gconv_module
)
333 + dir_len
+ need_ext
);
334 if (new_module
!= NULL
)
338 new_module
->from_constpfx
= memcpy ((char *) new_module
339 + sizeof (struct gconv_module
),
342 new_module
->from_pattern
= new_module
->from_constpfx
;
344 new_module
->from_pattern
= NULL
;
346 new_module
->from_constpfx_len
= const_len
;
348 new_module
->from_regex
= NULL
;
350 new_module
->to_string
= memcpy ((char *) new_module
->from_constpfx
351 + (to
- from
), to
, module
- to
);
353 new_module
->cost_hi
= cost_hi
;
354 new_module
->cost_lo
= modcounter
;
356 new_module
->module_name
= (char *) new_module
->to_string
+ (module
- to
);
359 tmp
= (char *) new_module
->module_name
;
362 tmp
= __mempcpy ((char *) new_module
->module_name
,
363 directory
, dir_len
- 1);
367 tmp
= __mempcpy (tmp
, module
, wp
- module
);
370 memcpy (tmp
- 1, gconv_module_ext
, sizeof (gconv_module_ext
));
372 /* See whether we have already an alias with this name defined.
373 We do allow regular expressions matching this any alias since
374 this expression can also match other names and we test for aliases
375 before testing for modules. */
378 struct gconv_alias fake_alias
;
380 fake_alias
.fromname
= new_module
->from_constpfx
;
382 if (__tfind (&fake_alias
, &__gconv_alias_db
, __gconv_alias_compare
)
385 /* This module duplicates an alias. */
391 if (__tfind (new_module
, modules
, module_compare
) == NULL
)
393 if (__tsearch (new_module
, modules
, module_compare
) == NULL
)
394 /* Something went wrong while inserting the new module. */
404 insert_module (const void *nodep
, VISIT value
, int level
)
406 if (value
== preorder
|| value
== leaf
)
407 __gconv_modules_db
[__gconv_nmodules
++] = *(struct gconv_module
**) nodep
;
411 nothing (void *unused
__attribute__ ((unused
)))
416 /* Read the next configuration file. */
419 read_conf_file (const char *filename
, const char *directory
, size_t dir_len
,
420 void **modules
, size_t *nmodules
)
422 FILE *fp
= fopen (filename
, "r");
427 /* Don't complain if a file is not present or readable, simply silently
432 /* Process the known entries of the file. Comments start with `#' and
433 end with the end of the line. Empty lines are ignored. */
434 while (!feof_unlocked (fp
))
436 char *rp
, *endp
, *word
;
437 ssize_t n
= __getdelim (&line
, &line_len
, '\n', fp
);
439 /* An error occurred. */
443 /* Terminate the line (excluding comments or newline) by an NUL byte
444 to simplify the following code. */
445 endp
= strchr (rp
, '#');
449 if (rp
[n
- 1] == '\n')
452 while (isspace (*rp
))
455 /* If this is an empty line go on with the next one. */
460 while (*rp
!= '\0' && !isspace (*rp
))
463 if (rp
- word
== sizeof ("alias") - 1
464 && memcmp (word
, "alias", sizeof ("alias") - 1) == 0)
465 add_alias (rp
, *modules
);
466 else if (rp
- word
== sizeof ("module") - 1
467 && memcmp (word
, "module", sizeof ("module") - 1) == 0)
468 add_module (rp
, directory
, dir_len
, modules
, nmodules
, modcounter
++);
470 /* Otherwise ignore the line. */
479 /* Read all configuration files found in the user-specified and the default
482 __gconv_read_conf (void)
484 const char *user_path
= __secure_getenv ("GCONV_PATH");
485 char *gconv_path
, *elem
;
486 void *modules
= NULL
;
488 int save_errno
= errno
;
491 if (user_path
== NULL
)
492 /* No user-defined path. Make a modifiable copy of the default path. */
493 gconv_path
= strdupa (default_gconv_path
);
496 /* Append the default path to the user-defined path. */
497 size_t user_len
= strlen (user_path
);
500 gconv_path
= alloca (user_len
+ 1 + sizeof (default_gconv_path
));
501 tmp
= __mempcpy (gconv_path
, user_path
, user_len
);
503 __mempcpy (tmp
, default_gconv_path
, sizeof (default_gconv_path
));
506 elem
= strtok_r (gconv_path
, ":", &gconv_path
);
510 /* We define a reasonable limit. */
511 # define MAXPATHLEN 4096
513 char real_elem
[MAXPATHLEN
];
515 if (__realpath (elem
, real_elem
) != NULL
)
517 size_t elem_len
= strlen (real_elem
);
518 char *filename
, *tmp
;
520 filename
= alloca (elem_len
+ 1 + sizeof (gconv_conf_filename
));
521 tmp
= __mempcpy (filename
, real_elem
, elem_len
);
523 __mempcpy (tmp
, gconv_conf_filename
, sizeof (gconv_conf_filename
));
525 /* Read the next configuration file. */
526 read_conf_file (filename
, real_elem
, elem_len
, &modules
, &nmodules
);
529 /* Get next element in the path. */
530 elem
= strtok_r (NULL
, ":", &gconv_path
);
533 /* If the configuration files do not contain any valid module specification
534 remember this by setting the pointer to the module array to NULL. */
535 nmodules
+= sizeof (builtin_modules
) / sizeof (builtin_modules
[0]);
537 __gconv_modules_db
= NULL
;
541 (struct gconv_module
**) malloc (nmodules
542 * sizeof (struct gconv_module
));
543 if (__gconv_modules_db
!= NULL
)
547 /* Insert all module entries into the array. */
548 __twalk (modules
, insert_module
);
550 /* Finally insert the builtin transformations. */
551 for (cnt
= 0; cnt
< (sizeof (builtin_modules
)
552 / sizeof (struct gconv_module
)); ++cnt
)
553 __gconv_modules_db
[__gconv_nmodules
++] = &builtin_modules
[cnt
];
557 /* Add aliases for builtin conversions. */
558 cnt
= sizeof (builtin_aliases
) / sizeof (builtin_aliases
[0]);
561 char *copy
= strdupa (builtin_aliases
[--cnt
]);
562 add_alias (copy
, modules
);
566 /* Now remove the tree data structure. */
567 __tdestroy (modules
, nothing
);
569 /* Restore the error number. */
570 __set_errno (save_errno
);