Generate correct file which defines FOPEN_MAX also when included by
[glibc/history.git] / iconv / gconv_db.c
blobb1320ac7e8a385b38df8bf3a48e700b7e0332cef
1 /* Provide access to the collection of available transformation modules.
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.
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. */
21 #include <gconv.h>
22 #include <search.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <bits/libc-lock.h>
28 /* Simple data structure for alias mapping. We have two names, `from'
29 and `to'. */
30 void *__gconv_alias_db;
32 /* Array with available modules. */
33 size_t __gconv_nmodules;
34 struct gconv_module **__gconv_modules_db;
37 /* Function for searching alias. */
38 int
39 __gconv_alias_compare (const void *p1, const void *p2)
41 struct gconv_alias *s1 = (struct gconv_alias *) p1;
42 struct gconv_alias *s2 = (struct gconv_alias *) p2;
43 return __strcasecmp (s1->fromname, s2->fromname);
47 /* To search for a derivation we create a list of intermediate steps.
48 Each element contains a pointer to the element which precedes it
49 in the derivation order. */
50 struct derivation_step
52 const char *result_set;
53 struct gconv_module *code;
54 struct derivation_step *last;
55 struct derivation_step *next;
58 #define NEW_STEP(result, module, last_mod) \
59 ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
60 newp->result_set = result; \
61 newp->code = module; \
62 newp->last = last_mod; \
63 newp->next = NULL; \
64 newp; })
67 /* If a specific transformation is used more than once we should not need
68 to start looking for it again. Instead cache each successful result. */
69 struct known_derivation
71 const char *from;
72 const char *to;
73 struct gconv_step *steps;
74 size_t nsteps;
77 /* Compare function for database of found derivations. */
78 static int
79 derivation_compare (const void *p1, const void *p2)
81 struct known_derivation *s1 = (struct known_derivation *) p1;
82 struct known_derivation *s2 = (struct known_derivation *) p2;
83 int result;
85 result = strcmp (s1->from, s2->from);
86 if (result == 0)
87 result = strcmp (s1->to, s2->to);
88 return result;
91 /* The search tree for known derivations. */
92 static void *known_derivations;
94 /* Look up whether given transformation was already requested before. */
95 static int
96 derivation_lookup (const char *fromset, const char *toset,
97 struct gconv_step **handle, size_t *nsteps)
99 struct known_derivation key = { fromset, toset, NULL, 0 };
100 struct known_derivation *result;
102 result = __tfind (&key, &known_derivations, derivation_compare);
104 if (result == NULL)
105 return GCONV_NOCONV;
107 *handle = result->steps;
108 *nsteps = result->nsteps;
110 /* Please note that we return GCONV_OK even if the last search for
111 this transformation was unsuccessful. */
112 return GCONV_OK;
115 /* Add new derivation to list of known ones. */
116 static void
117 add_derivation (const char *fromset, const char *toset,
118 struct gconv_step *handle, size_t nsteps)
120 struct known_derivation *new_deriv;
121 size_t fromset_len = strlen (fromset) + 1;
122 size_t toset_len = strlen (toset) + 1;
124 new_deriv = (struct known_derivation *)
125 malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
126 if (new_deriv != NULL)
128 new_deriv->from = memcpy ((char *) new_deriv
129 + sizeof (struct known_derivation),
130 fromset, fromset_len);
131 new_deriv->to = memcpy ((char *) new_deriv->from + fromset_len,
132 toset, toset_len);
134 new_deriv->steps = handle;
135 new_deriv->nsteps = nsteps;
137 __tsearch (new_deriv, &known_derivations, derivation_compare);
139 /* Please note that we don't complain if the allocation failed. This
140 is not tragically but in case we use the memory debugging facilities
141 not all memory will be freed. */
144 static void
145 free_derivation (void *p)
147 struct known_derivation *deriv = (struct known_derivation *) p;
149 free ((struct gconv_step *) deriv->steps);
150 free (deriv);
154 static int
155 internal_function
156 gen_steps (struct derivation_step *best, const char *toset,
157 const char *fromset, struct gconv_step **handle, size_t *nsteps)
159 size_t step_cnt = 0;
160 struct gconv_step *result;
161 struct derivation_step *current;
162 int status = GCONV_NOMEM;
164 /* First determine number of steps. */
165 for (current = best; current->last != NULL; current = current->last)
166 ++step_cnt;
168 result = (struct gconv_step *) malloc (sizeof (struct gconv_step)
169 * step_cnt);
170 if (result != NULL)
172 int failed = 0;
174 *nsteps = step_cnt;
175 current = best;
176 while (step_cnt-- > 0)
178 result[step_cnt].from_name = (step_cnt == 0
179 ? __strdup (fromset)
180 : current->last->result_set);
181 result[step_cnt].to_name = (step_cnt + 1 == *nsteps
182 ? __strdup (current->result_set)
183 : result[step_cnt + 1].from_name);
185 if (current->code->module_name[0] == '/')
187 /* Load the module, return handle for it. */
188 void *shlib_handle =
189 __gconv_find_shlib (current->code->module_name);
191 if (shlib_handle == NULL)
193 failed = 1;
194 break;
197 result[step_cnt].shlib_handle = shlib_handle;
199 result[step_cnt].fct = __gconv_find_func (shlib_handle, "gconv");
200 if (result[step_cnt].fct == NULL)
202 /* Argh, no conversion function. There is something
203 wrong here. */
204 __gconv_release_shlib (result[step_cnt].shlib_handle);
205 failed = 1;
206 break;
209 result[step_cnt].init_fct = __gconv_find_func (shlib_handle,
210 "gconv_init");
211 result[step_cnt].end_fct = __gconv_find_func (shlib_handle,
212 "gconv_end");
214 else
215 /* It's a builtin transformation. */
216 __gconv_get_builtin_trans (current->code->module_name,
217 &result[step_cnt]);
219 current = current->last;
222 if (failed != 0)
224 /* Something went wrong while initializing the modules. */
225 while (++step_cnt < *nsteps)
226 __gconv_release_shlib (result[step_cnt].shlib_handle);
227 free (result);
228 *nsteps = 0;
229 status = GCONV_NOCONV;
231 else
233 *handle = result;
234 status = GCONV_OK;
238 return status;
242 /* The main function: find a possible derivation from the `fromset' (either
243 the given name or the alias) to the `toset' (again with alias). */
244 static int
245 internal_function
246 find_derivation (const char *toset, const char *toset_expand,
247 const char *fromset, const char *fromset_expand,
248 struct gconv_step **handle, size_t *nsteps)
250 __libc_lock_define_initialized (static, lock)
251 struct derivation_step *first, *current, **lastp, *best = NULL;
252 int best_cost = 0;
253 int result;
255 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
256 handle, nsteps);
257 if (result == GCONV_OK)
258 return result;
260 __libc_lock_lock (lock);
262 /* There is a small chance that this derivation is meanwhile found. This
263 can happen if in `find_derivation' we look for this derivation, didn't
264 find it but at the same time another thread looked for this derivation. */
265 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
266 handle, nsteps);
267 if (result == GCONV_OK)
268 return result;
270 /* ### TODO
271 For now we use a simple algorithm with quadratic runtime behaviour.
272 The task is to match the `toset' with any of the available. */
273 if (fromset_expand != NULL)
275 first = NEW_STEP (fromset_expand, NULL, NULL);
276 first->next = NEW_STEP (fromset, NULL, NULL);
277 lastp = &first->next->next;
279 else
281 first = NEW_STEP (fromset, NULL, NULL);
282 lastp = &first->next;
285 current = first;
286 while (current != NULL)
288 /* Now match all the available module specifications against the
289 current charset name. If any of them matches check whether
290 we already have a derivation for this charset. If yes, use the
291 one with the lower costs. Otherwise add the new charset at the
292 end. */
293 size_t cnt;
295 for (cnt = 0; cnt < __gconv_nmodules; ++cnt)
297 const char *result_set = NULL;
299 if (__gconv_modules_db[cnt]->from_pattern == NULL)
301 if (__strcasecmp (current->result_set,
302 __gconv_modules_db[cnt]->from_constpfx) == 0)
304 if (strcmp (__gconv_modules_db[cnt]->to_string, "-") == 0)
305 result_set = toset_expand ?: toset;
306 else
307 result_set = __gconv_modules_db[cnt]->to_string;
310 else
311 /* We have a regular expression. First see if the prefix
312 matches. */
313 if (__strncasecmp (current->result_set,
314 __gconv_modules_db[cnt]->from_constpfx,
315 __gconv_modules_db[cnt]->from_constpfx_len)
316 == 0)
318 /* First compile the regex if not already done. */
319 if (__gconv_modules_db[cnt]->from_regex == NULL)
321 regex_t *newp = (regex_t *) malloc (sizeof (regex_t));
323 if (regcomp (newp, __gconv_modules_db[cnt]->from_pattern,
324 REG_EXTENDED | REG_ICASE) != 0)
326 /* Something is wrong. Remember this. */
327 free (newp);
328 __gconv_modules_db[cnt]->from_regex = (regex_t *) -1L;
330 else
331 __gconv_modules_db[cnt]->from_regex = newp;
334 if (__gconv_modules_db[cnt]->from_regex != (regex_t *) -1L)
336 /* Try to match the from name. */
337 regmatch_t match[4];
339 if (regexec (__gconv_modules_db[cnt]->from_regex,
340 current->result_set, 4, match, 0) == 0
341 && match[0].rm_so == 0
342 && current->result_set[match[0].rm_eo] == '\0')
344 /* At least the whole <from> string is matched.
345 We must now match sed-like possible
346 subexpressions from the match to the
347 toset expression. */
348 #define ENSURE_LEN(LEN) \
349 if (wp + (LEN) >= constr + len - 1) \
351 char *newp = alloca (len += 128); \
352 memcpy (newp, constr, wp - constr); \
353 wp = newp + (wp - constr); \
354 constr = newp; \
356 size_t len = 128;
357 char *constr = alloca (len);
358 char *wp = constr;
359 const char *cp = __gconv_modules_db[cnt]->to_string;
361 while (*cp != '\0')
363 if (*cp != '\\')
365 ENSURE_LEN (1);
366 *wp++ = *cp++;
368 else if (cp[1] == '\0')
369 /* Backslash at end of string. */
370 break;
371 else
373 ++cp;
374 if (*cp == '\\')
376 *wp++ = *cp++;
377 ENSURE_LEN (1);
379 else if (*cp < '1' || *cp > '3')
380 break;
381 else
383 int idx = *cp - '0';
384 if (match[idx].rm_so == -1)
385 /* No match. */
386 break;
388 ENSURE_LEN (match[idx].rm_eo
389 - match[idx].rm_so);
390 wp = __mempcpy (wp,
391 &current->result_set[match[idx].rm_so],
392 match[idx].rm_eo
393 - match[idx].rm_so);
394 ++cp;
398 if (*cp == '\0' && wp != constr)
400 /* Terminate the constructed string. */
401 *wp = '\0';
402 result_set = constr;
408 if (result_set != NULL)
410 /* We managed to find a derivation. First see whether
411 this is what we are looking for. */
412 if (__strcasecmp (result_set, toset) == 0
413 || (toset_expand != NULL
414 && __strcasecmp (result_set, toset_expand) == 0))
416 /* Determine the costs. If they are lower than the
417 previous solution (or this is the first solution)
418 remember this solution. */
419 int cost = __gconv_modules_db[cnt]->cost;
420 struct derivation_step *runp = current;
421 while (runp->code != NULL)
423 cost += runp->code->cost;
424 runp = runp->last;
426 if (best == NULL || cost < best_cost)
428 best = NEW_STEP (result_set, __gconv_modules_db[cnt],
429 current);
430 best_cost = cost;
433 else
435 /* Append at the end if there is no entry with this name. */
436 struct derivation_step *runp = first;
438 while (runp != NULL)
440 if (__strcasecmp (result_set, runp->result_set) == 0)
441 break;
442 runp = runp->next;
445 if (runp == NULL)
447 *lastp = NEW_STEP (result_set, __gconv_modules_db[cnt],
448 current);
449 lastp = &(*lastp)->next;
455 /* Go on with the next entry. */
456 current = current->next;
459 if (best != NULL)
460 /* We really found a way to do the transformation. Now build a data
461 structure describing the transformation steps.*/
462 result = gen_steps (best, toset_expand ?: toset, fromset_expand ?: fromset,
463 handle, nsteps);
464 else
466 /* We haven't found a transformation. Clear the result values. */
467 *handle = NULL;
468 *nsteps = 0;
471 /* Add result in any case to list of known derivations. */
472 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
473 *handle, *nsteps);
475 __libc_lock_unlock (lock);
477 return result;
482 __gconv_find_transform (const char *toset, const char *fromset,
483 struct gconv_step **handle, size_t *nsteps)
485 __libc_once_define (static, once);
486 const char *fromset_expand = NULL;
487 const char *toset_expand = NULL;
488 int result;
490 /* Ensure that the configuration data is read. */
491 __libc_once (once, __gconv_read_conf);
493 /* If we don't have a module database return with an error. */
494 if (__gconv_modules_db == NULL)
495 return GCONV_NOCONV;
497 /* See whether the names are aliases. */
498 if (__gconv_alias_db != NULL)
500 struct gconv_alias key;
501 struct gconv_alias **found;
503 key.fromname = fromset;
504 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
505 fromset_expand = found != NULL ? (*found)->toname : NULL;
507 key.fromname = toset;
508 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
509 toset_expand = found != NULL ? (*found)->toname : NULL;
512 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
513 handle, nsteps);
515 /* The following code is necessary since `find_derivation' will return
516 GCONV_OK even when no derivation was found but the same request
517 was processed before. I.e., negative results will also be cached. */
518 return (result == GCONV_OK
519 ? (*handle == NULL ? GCONV_NOCONV : GCONV_OK)
520 : result);
524 /* Release the entries of the modules list. */
526 __gconv_close_transform (struct gconv_step *steps, size_t nsteps)
528 int result = GCONV_OK;
530 while (nsteps-- > 0)
531 if (steps[nsteps].shlib_handle != NULL)
533 result = __gconv_release_shlib (steps[nsteps].shlib_handle);
534 if (result != GCONV_OK)
535 break;
538 return result;
542 /* Free all resources if necessary. */
543 static void __attribute__ ((unused))
544 free_mem (void)
546 size_t cnt;
548 if (__gconv_alias_db != NULL)
549 __tdestroy (__gconv_alias_db, free);
551 for (cnt = 0; cnt < __gconv_nmodules; ++cnt)
553 if (__gconv_modules_db[cnt]->from_regex != NULL)
554 regfree ((regex_t *) __gconv_modules_db[cnt]->from_regex);
556 /* Modules which names do not start with a slash are builtin
557 transformations and the memory is not allocated dynamically. */
558 if (__gconv_modules_db[cnt]->module_name[0] == '/')
559 free (__gconv_modules_db[cnt]);
562 if (known_derivations != NULL)
563 __tdestroy (known_derivations, free_derivation);
566 text_set_element (__libc_subfreeres, free_mem);