Character set transformation implementation
[kopensolaris-gnu/glibc.git] / iconv / gconv_conf.c
1 /* Handle configuration data.
2    Copyright (C) 1997 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
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.
10
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.
15
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.  */
20
21 #include <ctype.h>
22 #include <gconv.h>
23 #include <search.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/param.h>
29
30
31 /* This is the default path where we look for module lists.  */
32 static const char default_gconv_path[] = GCONV_PATH;
33
34 /* Name of the file containing the module information in the directories
35    along the path.  */
36 static const char gconv_conf_filename[] = "gconv-modules";
37
38 /* We have a few builtin transformations.  */
39 static struct gconv_module builtin_modules[] =
40 {
41 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
42                                Fct, Init, End) \
43   {                                                                           \
44     from_pattern: From,                                                       \
45     from_constpfx: ConstPfx,                                                  \
46     from_constpfx_len: ConstLen,                                              \
47     from_regex: NULL,                                                         \
48     to_string: To,                                                            \
49     cost: Cost,                                                               \
50     module_name: Name                                                         \
51   },
52
53 #include "gconv_builtin.h"
54 };
55
56
57 /* Function for searching module.  */
58 static int
59 module_compare (const void *p1, const void *p2)
60 {
61   struct gconv_module *s1 = (struct gconv_module *) p1;
62   struct gconv_module *s2 = (struct gconv_module *) p2;
63   int result;
64
65   if (s1->from_pattern == NULL)
66     {
67       if (s2->from_pattern == NULL)
68         result = strcmp (s1->from_constpfx, s2->from_constpfx);
69       else
70         result = -1;
71     }
72   else if (s2->from_pattern == NULL)
73     result = 1;
74   else
75     result = strcmp (s1->from_pattern, s2->from_pattern);
76
77   if (result == 0)
78     result = strcmp (s1->to_string, s2->to_string);
79
80   return result;
81 }
82
83
84 /* Add new alias.  */
85 static inline void
86 add_alias (char *rp)
87 {
88   /* We now expect two more string.  The strings are normalized
89      (converted to UPPER case) and strored in the alias database.  */
90   struct gconv_alias *new_alias;
91   char *from, *to, *wp;
92
93   while (isspace (*rp))
94     ++rp;
95   from = wp = rp;
96   while (*rp != '\0' && !isspace (*rp))
97     ++rp;
98   if (*rp == '\0')
99     /* There is no `to' string on the line.  Ignore it.  */
100     return;
101   *rp++ = '\0';
102   to = wp = rp;
103   while (isspace (*rp))
104     ++rp;
105   while (*rp != '\0' && !isspace (*rp))
106     *wp++ = *rp++;
107   if (to == wp)
108     /* No `to' string, ignore the line.  */
109     return;
110   *wp++ = '\0';
111
112   new_alias = (struct gconv_alias *)
113     malloc (sizeof (struct gconv_alias) + (wp - from));
114   new_alias->fromname = memcpy ((char *) new_alias
115                                 + sizeof (struct gconv_alias),
116                                 from, to - from);
117   new_alias->toname = memcpy ((char *) new_alias + sizeof (struct gconv_alias)
118                               + (to - from), to, wp - to);
119
120   if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare) == NULL)
121     /* Something went wrong, free this entry.  */
122     free (new_alias);
123 }
124
125
126 /* Add new module.  */
127 static inline void
128 add_module (char *rp, const char *directory, size_t dir_len, void **modules,
129             size_t *nmodules)
130 {
131   /* We expect now
132      1. `from' name
133      2. `to' name
134      3. filename of the module
135      4. an optional cost value
136   */
137   struct gconv_module *new_module;
138   char *from, *to, *module, *wp;
139   size_t const_len;
140   int from_is_regex;
141   int cost;
142
143   while (isspace (*rp))
144     ++rp;
145   from = rp;
146   from_is_regex = 0;
147   while (*rp != '\0' && !isspace (*rp))
148     {
149       if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.'
150           && *rp != '_')
151         from_is_regex = 1;
152       ++rp;
153     }
154   if (*rp == '\0')
155     return;
156   *rp++ = '\0';
157   to = wp = rp;
158   while (isspace (*rp))
159     ++rp;
160   while (*rp != '\0' && !isspace (*rp))
161     *wp++ = *rp++;
162   if (*rp == '\0')
163     return;
164   *wp++ = '\0';
165   do
166     ++rp;
167   while (isspace (*rp));
168   module = wp;
169   while (*rp != '\0' && !isspace (*rp))
170     *wp++ = *rp++;
171   if (*rp == '\0')
172     {
173       /* There is no cost, use one by default.  */
174       *wp++ = '\0';
175       cost = 1;
176     }
177   else
178     {
179       /* There might be a cost value.  */
180       char *endp;
181
182       *wp++ = '\0';
183       cost = strtol (rp, &endp, 10);
184       if (rp == endp)
185         /* No useful information.  */
186         cost = 1;
187     }
188
189   if (module[0] == '\0')
190     /* No module name given.  */
191     return;
192   if (module[0] == '/')
193     dir_len = 0;
194   else
195     /* Increment by one for the slash.  */
196     ++dir_len;
197
198   /* We've collected all the information, now create an entry.  */
199
200   const_len = 0;
201   if (from_is_regex)
202     do
203       ++const_len;
204     while (isalnum (from[const_len]) || from[const_len] == '-'
205            || from[const_len] == '/' || from[const_len] == '.'
206            || from[const_len] == '_');
207
208   new_module = (struct gconv_module *) malloc (sizeof (struct gconv_module)
209                                                + (wp - from) + const_len
210                                                + dir_len);
211   if (new_module != NULL)
212     {
213       if (from_is_regex)
214         {
215           new_module->from_pattern = memcpy ((char *) new_module
216                                              + sizeof (struct gconv_module),
217                                              from, to - from);
218           new_module->from_constpfx = memcpy ((char *) new_module->from_pattern
219                                               + (to - from),
220                                               from, const_len);
221           ((char *) new_module->from_constpfx)[const_len] = '\0';
222           new_module->from_constpfx_len = const_len;
223           ++const_len;
224         }
225       else
226         {
227           new_module->from_pattern = NULL;
228           new_module->from_constpfx = memcpy ((char *) new_module
229                                               + sizeof (struct gconv_module),
230                                               from, to - from);
231           new_module->from_constpfx_len = to - from - 1;
232           const_len = to - from;
233         }
234       new_module->from_regex = NULL;
235
236       new_module->to_string = memcpy ((char *) new_module->from_constpfx
237                                       + const_len + 1, to, module - to);
238
239       new_module->cost = cost;
240
241       if (dir_len == 0)
242         new_module->module_name = memcpy ((char *) new_module->to_string
243                                           + (module - to),
244                                           module, wp - module);
245       else
246         {
247           char *tmp;
248           new_module->module_name = ((char *) new_module->to_string
249                                      + (module - to));
250           tmp = __mempcpy ((char *) new_module->module_name,
251                            directory, dir_len - 1);
252           *tmp++ = '/';
253           memcpy (tmp, module, wp - module);
254         }
255
256       if (__tfind (new_module, *modules, module_compare) != NULL)
257         if (__tsearch (new_module, modules, module_compare) == NULL)
258           /* Something went wrong while inserting the new module.  */
259           free (new_module);
260         else
261           ++*nmodules;
262     }
263 }
264
265
266 static void
267 insert_module (const void *nodep, VISIT value, int level)
268 {
269   if (value == preorder || value == leaf)
270     __gconv_modules_db[__gconv_nmodules++] = (struct gconv_module *) nodep;
271 }
272
273 static void
274 nothing (void *unused __attribute__ ((unused)))
275 {
276 }
277
278
279 /* Read the next configuration file.  */
280 static void
281 internal_function
282 read_conf_file (const char *filename, const char *directory, size_t dir_len,
283                 void **modules, size_t *nmodules)
284 {
285   FILE *fp = fopen (filename, "r");
286   char *line = NULL;
287   size_t line_len = 0;
288
289   /* Don't complain if a file is not present or readable, simply silently
290      ignore it.  */
291   if (fp == NULL)
292     return;
293
294   /* Process the known entries of the file.  Comments start with `#' and
295      end with the end of the line.  Empty lines are ignored.  */
296   while (!feof (fp))
297     {
298       char *rp, *endp, *word;
299       ssize_t n = __getdelim (&line, &line_len, '\n', fp);
300       if (n < 0)
301         /* An error occurred.  */
302         break;
303
304       rp = line;
305       while (isspace (*rp))
306         ++rp;
307       /* Terminate the line (excluding comments or newline) by an NUL byte
308          to simplify the following code.  */
309       endp = strchr (rp, '#');
310       if (endp != NULL)
311         *endp = '\0';
312       else
313         {
314           endp = strchr (rp, '\n');
315           if (endp != NULL)
316             *endp = '\0';
317         }
318
319       /* If this is an empty line go on with the next one.  */
320       if (rp == endp)
321         continue;
322
323       word = rp;
324       while (*rp != '\0' && !isspace (*rp))
325         ++rp;
326
327       if (rp - word == sizeof ("alias") - 1
328           && memcpy (word, "alias", sizeof ("alias") - 1) == 0)
329         add_alias (rp);
330       else if (rp - word == sizeof ("module") - 1
331                && memcpy (word, "module", sizeof ("module") - 1) == 0)
332         add_module (rp, directory, dir_len, modules, nmodules);
333       /* else */
334         /* Otherwise ignore the line.  */
335     }
336
337   if (line != NULL)
338     free (line);
339   fclose (fp);
340 }
341
342
343 /* Read all configuration files found in the user-specified and the default
344    path.  */
345 void
346 __gconv_read_conf (void)
347 {
348   const char *user_path = __secure_getenv ("GCONV_PATH");
349   char *gconv_path, *elem;
350   void *modules = NULL;
351   size_t nmodules = 0;
352
353   if (user_path == NULL)
354     /* No user-defined path.  Make a modifiable copy of the default path.  */
355     gconv_path = strdupa (default_gconv_path);
356   else
357     {
358       /* Append the default path to the user-defined path.  */
359       size_t user_len = strlen (user_path);
360       char *tmp;
361
362       gconv_path = alloca (user_len + 1 + sizeof (default_gconv_path));
363       tmp = __mempcpy (gconv_path, user_path, user_len);
364       *tmp++ = ':';
365       __mempcpy (tmp, default_gconv_path, sizeof (default_gconv_path));
366     }
367
368   elem = strtok_r (gconv_path, ":", &gconv_path);
369   while (elem != NULL)
370     {
371       char real_elem[MAXPATHLEN];
372
373       if (realpath (elem, real_elem) != NULL)
374         {
375           size_t elem_len = strlen (real_elem);
376           char *filename, *tmp;
377
378           filename = alloca (elem_len + 1 + sizeof (gconv_conf_filename));
379           tmp = __mempcpy (filename, real_elem, elem_len);
380           *tmp++ = '/';
381           __mempcpy (tmp, gconv_conf_filename, sizeof (gconv_conf_filename));
382
383           /* Read the next configuration file.  */
384           read_conf_file (filename, real_elem, elem_len, &modules, &nmodules);
385         }
386
387       /* Get next element in the path.  */
388       elem = strtok_r (NULL, ":", &gconv_path);
389     }
390
391   /* If the configuration files do not contain any valid module specification
392      remember this by setting the pointer to the module array to NULL.  */
393   nmodules = sizeof (builtin_modules) / sizeof (struct gconv_module);
394   if (nmodules == 0)
395     {
396       __gconv_modules_db = NULL;
397       return;
398     }
399
400   __gconv_modules_db =
401     (struct gconv_module **) malloc (nmodules * sizeof (struct gconv_module));
402   if (__gconv_modules_db == NULL)
403     /* We cannot do anything.  */
404     return;
405
406   /* First insert the builtin transformations.  */
407   while (__gconv_nmodules < (sizeof (builtin_modules)
408                              / sizeof (struct gconv_module)))
409     {
410       __gconv_modules_db[__gconv_nmodules] =
411         &builtin_modules[__gconv_nmodules];
412       ++__gconv_nmodules;
413     }
414
415   /* Insert all module entries into the array.  */
416   __twalk (modules, insert_module);
417
418   /* No remove the tree data structure.  */
419   __tdestroy (modules, nothing);
420 }