gconv_db.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. /* Provide access to the collection of available transformation modules.
  2. Copyright (C) 1997-2026 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #include <assert.h>
  16. #include <limits.h>
  17. #include <search.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sys/param.h>
  21. #include <libc-lock.h>
  22. #include <locale/localeinfo.h>
  23. #include <dlfcn.h>
  24. #include <gconv_int.h>
  25. #include <pointer_guard.h>
  26. /* Simple data structure for alias mapping. We have two names, `from'
  27. and `to'. */
  28. void *__gconv_alias_db;
  29. /* Array with available modules. */
  30. struct gconv_module *__gconv_modules_db;
  31. /* We modify global data. */
  32. __libc_lock_define_initialized (, __gconv_lock)
  33. /* Provide access to module database. */
  34. struct gconv_module *
  35. __gconv_get_modules_db (void)
  36. {
  37. return __gconv_modules_db;
  38. }
  39. void *
  40. __gconv_get_alias_db (void)
  41. {
  42. return __gconv_alias_db;
  43. }
  44. /* Function for searching alias. */
  45. int
  46. __gconv_alias_compare (const void *p1, const void *p2)
  47. {
  48. const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
  49. const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
  50. return strcmp (s1->fromname, s2->fromname);
  51. }
  52. /* To search for a derivation we create a list of intermediate steps.
  53. Each element contains a pointer to the element which precedes it
  54. in the derivation order. */
  55. struct derivation_step
  56. {
  57. const char *result_set;
  58. size_t result_set_len;
  59. int cost_lo;
  60. int cost_hi;
  61. struct gconv_module *code;
  62. struct derivation_step *last;
  63. struct derivation_step *next;
  64. };
  65. #define NEW_STEP(result, hi, lo, module, last_mod) \
  66. ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
  67. newp->result_set = result; \
  68. newp->result_set_len = strlen (result); \
  69. newp->cost_hi = hi; \
  70. newp->cost_lo = lo; \
  71. newp->code = module; \
  72. newp->last = last_mod; \
  73. newp->next = NULL; \
  74. newp; })
  75. /* If a specific transformation is used more than once we should not need
  76. to start looking for it again. Instead cache each successful result. */
  77. struct known_derivation
  78. {
  79. const char *from;
  80. const char *to;
  81. struct __gconv_step *steps;
  82. size_t nsteps;
  83. };
  84. /* Compare function for database of found derivations. */
  85. static int
  86. derivation_compare (const void *p1, const void *p2)
  87. {
  88. const struct known_derivation *s1 = (const struct known_derivation *) p1;
  89. const struct known_derivation *s2 = (const struct known_derivation *) p2;
  90. int result;
  91. result = strcmp (s1->from, s2->from);
  92. if (result == 0)
  93. result = strcmp (s1->to, s2->to);
  94. return result;
  95. }
  96. /* The search tree for known derivations. */
  97. static void *known_derivations;
  98. /* Look up whether given transformation was already requested before. */
  99. static int
  100. derivation_lookup (const char *fromset, const char *toset,
  101. struct __gconv_step **handle, size_t *nsteps)
  102. {
  103. struct known_derivation key = { fromset, toset, NULL, 0 };
  104. struct known_derivation **result;
  105. result = __tfind (&key, &known_derivations, derivation_compare);
  106. if (result == NULL)
  107. return __GCONV_NOCONV;
  108. *handle = (*result)->steps;
  109. *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;
  113. }
  114. /* Add new derivation to list of known ones. */
  115. static void
  116. add_derivation (const char *fromset, const char *toset,
  117. struct __gconv_step *handle, size_t nsteps)
  118. {
  119. struct known_derivation *new_deriv;
  120. size_t fromset_len = strlen (fromset) + 1;
  121. size_t toset_len = strlen (toset) + 1;
  122. new_deriv = (struct known_derivation *)
  123. malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
  124. if (new_deriv != NULL)
  125. {
  126. new_deriv->from = (char *) (new_deriv + 1);
  127. new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
  128. toset, toset_len);
  129. new_deriv->steps = handle;
  130. new_deriv->nsteps = nsteps;
  131. if (__tsearch (new_deriv, &known_derivations, derivation_compare)
  132. == NULL)
  133. /* There is some kind of memory allocation problem. */
  134. free (new_deriv);
  135. }
  136. /* Please note that we don't complain if the allocation failed. This
  137. is not tragically but in case we use the memory debugging facilities
  138. not all memory will be freed. */
  139. }
  140. static void
  141. free_derivation (void *p)
  142. {
  143. struct known_derivation *deriv = (struct known_derivation *) p;
  144. size_t cnt;
  145. for (cnt = 0; cnt < deriv->nsteps; ++cnt)
  146. if (deriv->steps[cnt].__counter > 0
  147. && deriv->steps[cnt].__shlib_handle != NULL)
  148. {
  149. __gconv_end_fct end_fct = deriv->steps[cnt].__end_fct;
  150. PTR_DEMANGLE (end_fct);
  151. if (end_fct != NULL)
  152. DL_CALL_FCT (end_fct, (&deriv->steps[cnt]));
  153. }
  154. /* Free the name strings. */
  155. if (deriv->steps != NULL)
  156. {
  157. free ((char *) deriv->steps[0].__from_name);
  158. free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
  159. free ((struct __gconv_step *) deriv->steps);
  160. }
  161. free (deriv);
  162. }
  163. /* Decrement the reference count for a single step in a steps array. */
  164. void
  165. __gconv_release_step (struct __gconv_step *step)
  166. {
  167. /* Skip builtin modules; they are not reference counted. */
  168. if (step->__shlib_handle != NULL && --step->__counter == 0)
  169. {
  170. /* Call the destructor. */
  171. __gconv_end_fct end_fct = step->__end_fct;
  172. PTR_DEMANGLE (end_fct);
  173. if (end_fct != NULL)
  174. DL_CALL_FCT (end_fct, (step));
  175. #ifndef STATIC_GCONV
  176. /* Release the loaded module. */
  177. __gconv_release_shlib (step->__shlib_handle);
  178. step->__shlib_handle = NULL;
  179. #endif
  180. }
  181. else if (step->__shlib_handle == NULL)
  182. /* Builtin modules should not have end functions. */
  183. assert (step->__end_fct == NULL);
  184. }
  185. static int
  186. gen_steps (struct derivation_step *best, const char *toset,
  187. const char *fromset, struct __gconv_step **handle, size_t *nsteps)
  188. {
  189. size_t step_cnt = 0;
  190. struct __gconv_step *result;
  191. struct derivation_step *current;
  192. int status = __GCONV_NOMEM;
  193. char *from_name = NULL;
  194. char *to_name = NULL;
  195. /* First determine number of steps. */
  196. for (current = best; current->last != NULL; current = current->last)
  197. ++step_cnt;
  198. result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
  199. * step_cnt);
  200. if (result != NULL)
  201. {
  202. int failed = 0;
  203. status = __GCONV_OK;
  204. *nsteps = step_cnt;
  205. current = best;
  206. while (step_cnt-- > 0)
  207. {
  208. if (step_cnt == 0)
  209. {
  210. result[step_cnt].__from_name = from_name = __strdup (fromset);
  211. if (from_name == NULL)
  212. {
  213. failed = 1;
  214. break;
  215. }
  216. }
  217. else
  218. result[step_cnt].__from_name = (char *)current->last->result_set;
  219. if (step_cnt + 1 == *nsteps)
  220. {
  221. result[step_cnt].__to_name = to_name
  222. = __strdup (current->result_set);
  223. if (to_name == NULL)
  224. {
  225. failed = 1;
  226. break;
  227. }
  228. }
  229. else
  230. result[step_cnt].__to_name = result[step_cnt + 1].__from_name;
  231. result[step_cnt].__counter = 1;
  232. result[step_cnt].__data = NULL;
  233. #ifndef STATIC_GCONV
  234. if (current->code->module_name[0] == '/')
  235. {
  236. /* Load the module, return handle for it. */
  237. struct __gconv_loaded_object *shlib_handle =
  238. __gconv_find_shlib (current->code->module_name);
  239. if (shlib_handle == NULL)
  240. {
  241. failed = 1;
  242. break;
  243. }
  244. result[step_cnt].__shlib_handle = shlib_handle;
  245. result[step_cnt].__modname = shlib_handle->name;
  246. result[step_cnt].__fct = shlib_handle->fct;
  247. result[step_cnt].__init_fct = shlib_handle->init_fct;
  248. result[step_cnt].__end_fct = shlib_handle->end_fct;
  249. /* These settings can be overridden by the init function. */
  250. result[step_cnt].__btowc_fct = NULL;
  251. /* Call the init function. */
  252. __gconv_init_fct init_fct = result[step_cnt].__init_fct;
  253. PTR_DEMANGLE (init_fct);
  254. if (init_fct != NULL)
  255. {
  256. status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
  257. if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
  258. {
  259. failed = 1;
  260. /* Do not call the end function because the init
  261. function has failed. */
  262. result[step_cnt].__end_fct = NULL;
  263. PTR_MANGLE (result[step_cnt].__end_fct);
  264. /* Make sure we unload this module. */
  265. --step_cnt;
  266. break;
  267. }
  268. }
  269. PTR_MANGLE (result[step_cnt].__btowc_fct);
  270. }
  271. else
  272. #endif
  273. /* It's a builtin transformation. */
  274. __gconv_get_builtin_trans (current->code->module_name,
  275. &result[step_cnt]);
  276. current = current->last;
  277. }
  278. if (__builtin_expect (failed, 0) != 0)
  279. {
  280. /* Something went wrong while initializing the modules. */
  281. while (++step_cnt < *nsteps)
  282. __gconv_release_step (&result[step_cnt]);
  283. free (result);
  284. free (from_name);
  285. free (to_name);
  286. *nsteps = 0;
  287. *handle = NULL;
  288. if (status == __GCONV_OK)
  289. status = __GCONV_NOCONV;
  290. }
  291. else
  292. *handle = result;
  293. }
  294. else
  295. {
  296. *nsteps = 0;
  297. *handle = NULL;
  298. }
  299. return status;
  300. }
  301. #ifndef STATIC_GCONV
  302. static int
  303. increment_counter (struct __gconv_step *steps, size_t nsteps)
  304. {
  305. /* Increment the user counter. */
  306. size_t cnt = nsteps;
  307. int result = __GCONV_OK;
  308. while (cnt-- > 0)
  309. {
  310. struct __gconv_step *step = &steps[cnt];
  311. if (step->__counter++ == 0)
  312. {
  313. /* Skip builtin modules. */
  314. if (step->__modname != NULL)
  315. {
  316. /* Reopen a previously used module. */
  317. step->__shlib_handle = __gconv_find_shlib (step->__modname);
  318. if (step->__shlib_handle == NULL)
  319. {
  320. /* Oops, this is the second time we use this module
  321. (after unloading) and this time loading failed!? */
  322. --step->__counter;
  323. while (++cnt < nsteps)
  324. __gconv_release_step (&steps[cnt]);
  325. result = __GCONV_NOCONV;
  326. break;
  327. }
  328. /* The function addresses defined by the module may
  329. have changed. */
  330. step->__fct = step->__shlib_handle->fct;
  331. step->__init_fct = step->__shlib_handle->init_fct;
  332. step->__end_fct = step->__shlib_handle->end_fct;
  333. /* These settings can be overridden by the init function. */
  334. step->__btowc_fct = NULL;
  335. /* Call the init function. */
  336. __gconv_init_fct init_fct = step->__init_fct;
  337. PTR_DEMANGLE (init_fct);
  338. if (init_fct != NULL)
  339. DL_CALL_FCT (init_fct, (step));
  340. PTR_MANGLE (step->__btowc_fct);
  341. }
  342. }
  343. }
  344. return result;
  345. }
  346. #endif
  347. /* The main function: find a possible derivation from the `fromset' (either
  348. the given name or the alias) to the `toset' (again with alias). */
  349. static int
  350. find_derivation (const char *toset, const char *toset_expand,
  351. const char *fromset, const char *fromset_expand,
  352. struct __gconv_step **handle, size_t *nsteps)
  353. {
  354. struct derivation_step *first, *current, **lastp, *solution = NULL;
  355. int best_cost_hi = INT_MAX;
  356. int best_cost_lo = INT_MAX;
  357. int result;
  358. /* Look whether an earlier call to `find_derivation' has already
  359. computed a possible derivation. If so, return it immediately. */
  360. result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
  361. handle, nsteps);
  362. if (result == __GCONV_OK)
  363. {
  364. #ifndef STATIC_GCONV
  365. result = increment_counter (*handle, *nsteps);
  366. #endif
  367. return result;
  368. }
  369. /* The task is to find a sequence of transformations, backed by the
  370. existing modules - whether builtin or dynamically loadable -,
  371. starting at `fromset' (or `fromset_expand') and ending at `toset'
  372. (or `toset_expand'), and with minimal cost.
  373. For computer scientists, this is a shortest path search in the
  374. graph where the nodes are all possible charsets and the edges are
  375. the transformations listed in __gconv_modules_db.
  376. For now we use a simple algorithm with quadratic runtime behaviour.
  377. A breadth-first search, starting at `fromset' and `fromset_expand'.
  378. The list starting at `first' contains all nodes that have been
  379. visited up to now, in the order in which they have been visited --
  380. excluding the goal nodes `toset' and `toset_expand' which get
  381. managed in the list starting at `solution'.
  382. `current' walks through the list starting at `first' and looks
  383. which nodes are reachable from the current node, adding them to
  384. the end of the list [`first' or `solution' respectively] (if
  385. they are visited the first time) or updating them in place (if
  386. they have have already been visited).
  387. In each node of either list, cost_lo and cost_hi contain the
  388. minimum cost over any paths found up to now, starting at `fromset'
  389. or `fromset_expand', ending at that node. best_cost_lo and
  390. best_cost_hi represent the minimum over the elements of the
  391. `solution' list. */
  392. if (fromset_expand != NULL)
  393. {
  394. first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
  395. first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
  396. lastp = &first->next->next;
  397. }
  398. else
  399. {
  400. first = NEW_STEP (fromset, 0, 0, NULL, NULL);
  401. lastp = &first->next;
  402. }
  403. for (current = first; current != NULL; current = current->next)
  404. {
  405. /* Now match all the available module specifications against the
  406. current charset name. If any of them matches check whether
  407. we already have a derivation for this charset. If yes, use the
  408. one with the lower costs. Otherwise add the new charset at the
  409. end.
  410. The module database is organized in a tree form which allows
  411. searching for prefixes. So we search for the first entry with a
  412. matching prefix and any other matching entry can be found from
  413. this place. */
  414. struct gconv_module *node;
  415. /* Maybe it is not necessary anymore to look for a solution for
  416. this entry since the cost is already as high (or higher) as
  417. the cost for the best solution so far. */
  418. if (current->cost_hi > best_cost_hi
  419. || (current->cost_hi == best_cost_hi
  420. && current->cost_lo >= best_cost_lo))
  421. continue;
  422. node = __gconv_modules_db;
  423. while (node != NULL)
  424. {
  425. int cmpres = strcmp (current->result_set, node->from_string);
  426. if (cmpres == 0)
  427. {
  428. /* Walk through the list of modules with this prefix and
  429. try to match the name. */
  430. struct gconv_module *runp;
  431. /* Check all the modules with this prefix. */
  432. runp = node;
  433. do
  434. {
  435. const char *result_set = (strcmp (runp->to_string, "-") == 0
  436. ? (toset_expand ?: toset)
  437. : runp->to_string);
  438. int cost_hi = runp->cost_hi + current->cost_hi;
  439. int cost_lo = runp->cost_lo + current->cost_lo;
  440. struct derivation_step *step;
  441. /* We managed to find a derivation. First see whether
  442. we have reached one of the goal nodes. */
  443. if (strcmp (result_set, toset) == 0
  444. || (toset_expand != NULL
  445. && strcmp (result_set, toset_expand) == 0))
  446. {
  447. /* Append to the `solution' list if there
  448. is no entry with this name. */
  449. for (step = solution; step != NULL; step = step->next)
  450. if (strcmp (result_set, step->result_set) == 0)
  451. break;
  452. if (step == NULL)
  453. {
  454. step = NEW_STEP (result_set,
  455. cost_hi, cost_lo,
  456. runp, current);
  457. step->next = solution;
  458. solution = step;
  459. }
  460. else if (step->cost_hi > cost_hi
  461. || (step->cost_hi == cost_hi
  462. && step->cost_lo > cost_lo))
  463. {
  464. /* A better path was found for the node,
  465. on the `solution' list. */
  466. step->code = runp;
  467. step->last = current;
  468. step->cost_hi = cost_hi;
  469. step->cost_lo = cost_lo;
  470. }
  471. /* Update best_cost accordingly. */
  472. if (cost_hi < best_cost_hi
  473. || (cost_hi == best_cost_hi
  474. && cost_lo < best_cost_lo))
  475. {
  476. best_cost_hi = cost_hi;
  477. best_cost_lo = cost_lo;
  478. }
  479. }
  480. else if (cost_hi < best_cost_hi
  481. || (cost_hi == best_cost_hi
  482. && cost_lo < best_cost_lo))
  483. {
  484. /* Append at the end of the `first' list if there
  485. is no entry with this name. */
  486. for (step = first; step != NULL; step = step->next)
  487. if (strcmp (result_set, step->result_set) == 0)
  488. break;
  489. if (step == NULL)
  490. {
  491. *lastp = NEW_STEP (result_set,
  492. cost_hi, cost_lo,
  493. runp, current);
  494. lastp = &(*lastp)->next;
  495. }
  496. else if (step->cost_hi > cost_hi
  497. || (step->cost_hi == cost_hi
  498. && step->cost_lo > cost_lo))
  499. {
  500. /* A better path was found for the node,
  501. on the `first' list. */
  502. step->code = runp;
  503. step->last = current;
  504. /* Update the cost for all steps. */
  505. for (step = first; step != NULL;
  506. step = step->next)
  507. /* But don't update the start nodes. */
  508. if (step->code != NULL)
  509. {
  510. struct derivation_step *back;
  511. int hi, lo;
  512. hi = step->code->cost_hi;
  513. lo = step->code->cost_lo;
  514. for (back = step->last; back->code != NULL;
  515. back = back->last)
  516. {
  517. hi += back->code->cost_hi;
  518. lo += back->code->cost_lo;
  519. }
  520. step->cost_hi = hi;
  521. step->cost_lo = lo;
  522. }
  523. /* Likewise for the nodes on the solution list.
  524. Also update best_cost accordingly. */
  525. for (step = solution; step != NULL;
  526. step = step->next)
  527. {
  528. step->cost_hi = (step->code->cost_hi
  529. + step->last->cost_hi);
  530. step->cost_lo = (step->code->cost_lo
  531. + step->last->cost_lo);
  532. if (step->cost_hi < best_cost_hi
  533. || (step->cost_hi == best_cost_hi
  534. && step->cost_lo < best_cost_lo))
  535. {
  536. best_cost_hi = step->cost_hi;
  537. best_cost_lo = step->cost_lo;
  538. }
  539. }
  540. }
  541. }
  542. runp = runp->same;
  543. }
  544. while (runp != NULL);
  545. break;
  546. }
  547. else if (cmpres < 0)
  548. node = node->left;
  549. else
  550. node = node->right;
  551. }
  552. }
  553. if (solution != NULL)
  554. {
  555. /* We really found a way to do the transformation. */
  556. /* Choose the best solution. This is easy because we know that
  557. the solution list has at most length 2 (one for every possible
  558. goal node). */
  559. if (solution->next != NULL)
  560. {
  561. struct derivation_step *solution2 = solution->next;
  562. if (solution2->cost_hi < solution->cost_hi
  563. || (solution2->cost_hi == solution->cost_hi
  564. && solution2->cost_lo < solution->cost_lo))
  565. solution = solution2;
  566. }
  567. /* Now build a data structure describing the transformation steps. */
  568. result = gen_steps (solution, toset_expand ?: toset,
  569. fromset_expand ?: fromset, handle, nsteps);
  570. }
  571. else
  572. {
  573. /* We haven't found a transformation. Clear the result values. */
  574. *handle = NULL;
  575. *nsteps = 0;
  576. }
  577. /* Add result in any case to list of known derivations. */
  578. add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
  579. *handle, *nsteps);
  580. return result;
  581. }
  582. static const char *
  583. do_lookup_alias (const char *name)
  584. {
  585. struct gconv_alias key;
  586. struct gconv_alias **found;
  587. key.fromname = (char *) name;
  588. found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
  589. return found != NULL ? (*found)->toname : NULL;
  590. }
  591. int
  592. __gconv_compare_alias (const char *name1, const char *name2)
  593. {
  594. int result;
  595. /* Ensure that the configuration data is read. */
  596. __gconv_load_conf ();
  597. if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
  598. result = strcmp (do_lookup_alias (name1) ?: name1,
  599. do_lookup_alias (name2) ?: name2);
  600. return result;
  601. }
  602. int
  603. __gconv_find_transform (const char *toset, const char *fromset,
  604. struct __gconv_step **handle, size_t *nsteps,
  605. int flags)
  606. {
  607. const char *fromset_expand;
  608. const char *toset_expand;
  609. int result;
  610. /* Ensure that the configuration data is read. */
  611. __gconv_load_conf ();
  612. /* Acquire the lock. */
  613. __libc_lock_lock (__gconv_lock);
  614. result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
  615. if (result != __GCONV_NODB)
  616. {
  617. /* We have a cache and could resolve the request, successful or not. */
  618. __libc_lock_unlock (__gconv_lock);
  619. return result;
  620. }
  621. /* If we don't have a module database return with an error. */
  622. if (__gconv_modules_db == NULL)
  623. {
  624. __libc_lock_unlock (__gconv_lock);
  625. return __GCONV_NOCONV;
  626. }
  627. /* See whether the names are aliases. */
  628. fromset_expand = do_lookup_alias (fromset);
  629. toset_expand = do_lookup_alias (toset);
  630. if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
  631. /* We are not supposed to create a pseudo transformation (means
  632. copying) when the input and output character set are the same. */
  633. && (strcmp (toset, fromset) == 0
  634. || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
  635. || (fromset_expand != NULL
  636. && (strcmp (toset, fromset_expand) == 0
  637. || (toset_expand != NULL
  638. && strcmp (toset_expand, fromset_expand) == 0)))))
  639. {
  640. /* Both character sets are the same. */
  641. __libc_lock_unlock (__gconv_lock);
  642. return __GCONV_NULCONV;
  643. }
  644. result = find_derivation (toset, toset_expand, fromset, fromset_expand,
  645. handle, nsteps);
  646. /* Release the lock. */
  647. __libc_lock_unlock (__gconv_lock);
  648. /* The following code is necessary since `find_derivation' will return
  649. GCONV_OK even when no derivation was found but the same request
  650. was processed before. I.e., negative results will also be cached. */
  651. return (result == __GCONV_OK
  652. ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
  653. : result);
  654. }
  655. /* Release the entries of the modules list. */
  656. int
  657. __gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
  658. {
  659. int result = __GCONV_OK;
  660. size_t cnt;
  661. /* Acquire the lock. */
  662. __libc_lock_lock (__gconv_lock);
  663. #ifndef STATIC_GCONV
  664. cnt = nsteps;
  665. while (cnt-- > 0)
  666. __gconv_release_step (&steps[cnt]);
  667. #endif
  668. /* If we use the cache we free a bit more since we don't keep any
  669. transformation records around, they are cheap enough to
  670. recreate. */
  671. __gconv_release_cache (steps, nsteps);
  672. /* Release the lock. */
  673. __libc_lock_unlock (__gconv_lock);
  674. return result;
  675. }
  676. /* Free the modules mentioned. */
  677. static void
  678. free_modules_db (struct gconv_module *node)
  679. {
  680. if (node->left != NULL)
  681. free_modules_db (node->left);
  682. if (node->right != NULL)
  683. free_modules_db (node->right);
  684. do
  685. {
  686. struct gconv_module *act = node;
  687. node = node->same;
  688. if (act->module_name[0] == '/')
  689. free (act);
  690. }
  691. while (node != NULL);
  692. }
  693. /* Free all resources if necessary. */
  694. void
  695. __gconv_db_freemem (void)
  696. {
  697. /* First free locale memory. This needs to be done before freeing
  698. derivations, as ctype cleanup functions dereference steps arrays which we
  699. free below. */
  700. _nl_locale_subfreeres ();
  701. /* finddomain.c has similar problem. */
  702. extern void _nl_finddomain_subfreeres (void) attribute_hidden;
  703. _nl_finddomain_subfreeres ();
  704. if (__gconv_alias_db != NULL)
  705. __tdestroy (__gconv_alias_db, free);
  706. if (__gconv_modules_db != NULL)
  707. free_modules_db (__gconv_modules_db);
  708. if (known_derivations != NULL)
  709. __tdestroy (known_derivations, free_derivation);
  710. }