1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21: #include <assert.h>
22: #include <ctype.h>
23: #include <errno.h>
24: #include <limits.h>
25: #include <locale.h>
26: #include <search.h>
27: #include <stddef.h>
28: #include <stdio.h>
29: #include <stdio_ext.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <unistd.h>
33: #include <sys/param.h>
34:
35: #include <bits/libc-lock.h>
36: #include <gconv_int.h>
37:
38:
39:
40: static const char default_gconv_path[] = GCONV_PATH;
41:
42:
43:
44: struct path_elem *__gconv_path_elem;
45:
46: size_t __gconv_max_path_elem_len;
47:
48:
49: static const struct path_elem empty_path_elem = { NULL, 0 };
50:
51:
52:
53: static const char gconv_conf_filename[] = "gconv-modules";
54:
55:
56: #ifndef MODULE_EXT
57: # define MODULE_EXT ".so"
58: #endif
59: static const char gconv_module_ext[] = MODULE_EXT;
60:
61:
62: static struct gconv_module builtin_modules[] =
63: {
64: #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
65: MinF, MaxF, MinT, MaxT) \
66: { \
67: .from_string = From, \
68: .to_string = To, \
69: .cost_hi = Cost, \
70: .cost_lo = INT_MAX, \
71: .module_name = Name \
72: },
73: #define BUILTIN_ALIAS(From, To)
74:
75: #include "gconv_builtin.h"
76:
77: #undef BUILTIN_TRANSFORMATION
78: #undef BUILTIN_ALIAS
79: };
80:
81: static const char builtin_aliases[] =
82: {
83: #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
84: MinF, MaxF, MinT, MaxT)
85: #define BUILTIN_ALIAS(From, To) From "\0" To "\0"
86:
87: #include "gconv_builtin.h"
88:
89: #undef BUILTIN_TRANSFORMATION
90: #undef BUILTIN_ALIAS
91: };
92:
93: #ifdef USE_IN_LIBIO
94: # include <libio/libioP.h>
95: # define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
96: #endif
97:
98:
99:
100: const char *__gconv_path_envvar;
101:
102:
103:
104: static int
105: internal_function
106: detect_conflict (const char *alias)
107: {
108: struct gconv_module *node = __gconv_modules_db;
109:
110: while (node != NULL)
111: {
112: int cmpres = strcmp (alias, node->from_string);
113:
114: if (cmpres == 0)
115:
116: return 1;
117: else if (cmpres < 0)
118: node = node->left;
119: else
120: node = node->right;
121: }
122:
123: return node != NULL;
124: }
125:
126:
127:
128: static void
129: add_alias2 (const char *from, const char *to, const char *wp, void *modules)
130: {
131:
132: if (detect_conflict (from))
133:
134: return;
135:
136: struct gconv_alias *new_alias = (struct gconv_alias *)
137: malloc (sizeof (struct gconv_alias) + (wp - from));
138: if (new_alias != NULL)
139: {
140: void **inserted;
141:
142: new_alias->fromname = memcpy ((char *) new_alias
143: + sizeof (struct gconv_alias),
144: from, wp - from);
145: new_alias->toname = new_alias->fromname + (to - from);
146:
147: inserted = (void **) __tsearch (new_alias, &__gconv_alias_db,
148: __gconv_alias_compare);
149: if (inserted == NULL || *inserted != new_alias)
150:
151: free (new_alias);
152: }
153: }
154:
155:
156:
157: static void
158: add_alias (char *rp, void *modules)
159: {
160:
161:
162: char *from, *to, *wp;
163:
164: while (__isspace_l (*rp, _nl_C_locobj_ptr))
165: ++rp;
166: from = wp = rp;
167: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
168: *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
169: if (*rp == '\0')
170:
171: return;
172: *wp++ = '\0';
173: to = ++rp;
174: while (__isspace_l (*rp, _nl_C_locobj_ptr))
175: ++rp;
176: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
177: *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
178: if (to == wp)
179:
180: return;
181: *wp++ = '\0';
182:
183: add_alias2 (from, to, wp, modules);
184: }
185:
186:
187:
188: static void
189: internal_function
190: insert_module (struct gconv_module *newp, int tobefreed)
191: {
192: struct gconv_module **rootp = &__gconv_modules_db;
193:
194: while (*rootp != NULL)
195: {
196: struct gconv_module *root = *rootp;
197: int cmpres;
198:
199: cmpres = strcmp (newp->from_string, root->from_string);
200: if (cmpres == 0)
201: {
202:
203:
204: while (strcmp (newp->from_string, root->from_string) != 0
205: || strcmp (newp->to_string, root->to_string) != 0)
206: {
207: rootp = &root->same;
208: root = *rootp;
209: if (root == NULL)
210: break;
211: }
212:
213: if (root != NULL)
214: {
215:
216:
217: if (newp->cost_hi < root->cost_hi
218: || (newp->cost_hi == root->cost_hi
219: && newp->cost_lo < root->cost_lo))
220: {
221: newp->left = root->left;
222: newp->right = root->right;
223: newp->same = root->same;
224: *rootp = newp;
225:
226: free (root);
227: }
228: else if (tobefreed)
229: free (newp);
230: return;
231: }
232:
233: break;
234: }
235: else if (cmpres < 0)
236: rootp = &root->left;
237: else
238: rootp = &root->right;
239: }
240:
241:
242: *rootp = newp;
243: }
244:
245:
246:
247: static void
248: internal_function
249: add_module (char *rp, const char *directory, size_t dir_len, void **modules,
250: size_t *nmodules, int modcounter)
251: {
252:
253:
254:
255:
256:
257:
258: struct gconv_alias fake_alias;
259: struct gconv_module *new_module;
260: char *from, *to, *module, *wp;
261: int need_ext;
262: int cost_hi;
263:
264: while (__isspace_l (*rp, _nl_C_locobj_ptr))
265: ++rp;
266: from = rp;
267: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
268: {
269: *rp = __toupper_l (*rp, _nl_C_locobj_ptr);
270: ++rp;
271: }
272: if (*rp == '\0')
273: return;
274: *rp++ = '\0';
275: to = wp = rp;
276: while (__isspace_l (*rp, _nl_C_locobj_ptr))
277: ++rp;
278: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
279: *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
280: if (*rp == '\0')
281: return;
282: *wp++ = '\0';
283: do
284: ++rp;
285: while (__isspace_l (*rp, _nl_C_locobj_ptr));
286: module = wp;
287: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
288: *wp++ = *rp++;
289: if (*rp == '\0')
290: {
291:
292: *wp++ = '\0';
293: cost_hi = 1;
294: }
295: else
296: {
297:
298: char *endp;
299:
300: *wp++ = '\0';
301: cost_hi = strtol (rp, &endp, 10);
302: if (rp == endp || cost_hi < 1)
303:
304: cost_hi = 1;
305: }
306:
307: if (module[0] == '\0')
308:
309: return;
310: if (module[0] == '/')
311: dir_len = 0;
312:
313:
314: need_ext = 0;
315: if (wp - module < (ptrdiff_t) sizeof (gconv_module_ext)
316: || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
317: sizeof (gconv_module_ext)) != 0)
318:
319: need_ext = sizeof (gconv_module_ext) - 1;
320:
321:
322: fake_alias.fromname = strndupa (from, to - from);
323:
324: if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare) != NULL)
325:
326: return;
327:
328: new_module = (struct gconv_module *) calloc (1,
329: sizeof (struct gconv_module)
330: + (wp - from)
331: + dir_len + need_ext);
332: if (new_module != NULL)
333: {
334: char *tmp;
335:
336: new_module->from_string = tmp = (char *) (new_module + 1);
337: tmp = __mempcpy (tmp, from, to - from);
338:
339: new_module->to_string = tmp;
340: tmp = __mempcpy (tmp, to, module - to);
341:
342: new_module->cost_hi = cost_hi;
343: new_module->cost_lo = modcounter;
344:
345: new_module->module_name = tmp;
346:
347: if (dir_len != 0)
348: tmp = __mempcpy (tmp, directory, dir_len);
349:
350: tmp = __mempcpy (tmp, module, wp - module);
351:
352: if (need_ext)
353: memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
354:
355:
356: insert_module (new_module, 1);
357: }
358: }
359:
360:
361:
362: static void
363: internal_function
364: read_conf_file (const char *filename, const char *directory, size_t dir_len,
365: void **modules, size_t *nmodules)
366: {
367:
368:
369: FILE *fp = fopen (filename, "rc");
370: char *line = NULL;
371: size_t line_len = 0;
372: static int modcounter;
373:
374:
375:
376: if (fp == NULL)
377: return;
378:
379:
380: __fsetlocking (fp, FSETLOCKING_BYCALLER);
381:
382:
383:
384: while (!feof_unlocked (fp))
385: {
386: char *rp, *endp, *word;
387: ssize_t n = __getdelim (&line, &line_len, '\n', fp);
388: if (n < 0)
389:
390: break;
391:
392: rp = line;
393:
394:
395: endp = strchr (rp, '#');
396: if (endp != NULL)
397: *endp = '\0';
398: else
399: if (rp[n - 1] == '\n')
400: rp[n - 1] = '\0';
401:
402: while (__isspace_l (*rp, _nl_C_locobj_ptr))
403: ++rp;
404:
405:
406: if (rp == endp)
407: continue;
408:
409: word = rp;
410: while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
411: ++rp;
412:
413: if (rp - word == sizeof ("alias") - 1
414: && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
415: add_alias (rp, *modules);
416: else if (rp - word == sizeof ("module") - 1
417: && memcmp (word, "module", sizeof ("module") - 1) == 0)
418: add_module (rp, directory, dir_len, modules, nmodules, modcounter++);
419:
420:
421: }
422:
423: free (line);
424:
425: fclose (fp);
426: }
427:
428:
429:
430: void
431: internal_function
432: __gconv_get_path (void)
433: {
434: struct path_elem *result;
435: __libc_lock_define_initialized (static, lock);
436:
437: __libc_lock_lock (lock);
438:
439:
440: result = (struct path_elem *) __gconv_path_elem;
441: if (result == NULL)
442: {
443:
444: char *gconv_path;
445: size_t gconv_path_len;
446: char *elem;
447: char *oldp;
448: char *cp;
449: int nelems;
450: char *cwd;
451: size_t cwdlen;
452:
453: if (__gconv_path_envvar == NULL)
454: {
455:
456:
457: gconv_path = strdupa (default_gconv_path);
458: gconv_path_len = sizeof (default_gconv_path);
459: cwd = NULL;
460: cwdlen = 0;
461: }
462: else
463: {
464:
465: size_t user_len = strlen (__gconv_path_envvar);
466:
467: gconv_path_len = user_len + 1 + sizeof (default_gconv_path);
468: gconv_path = alloca (gconv_path_len);
469: __mempcpy (__mempcpy (__mempcpy (gconv_path, __gconv_path_envvar,
470: user_len),
471: ":", 1),
472: default_gconv_path, sizeof (default_gconv_path));
473: cwd = __getcwd (NULL, 0);
474: cwdlen = strlen (cwd);
475: }
476: assert (default_gconv_path[0] == '/');
477:
478:
479: oldp = NULL;
480: cp = strchr (gconv_path, ':');
481: nelems = 1;
482: while (cp != NULL)
483: {
484: if (cp != oldp + 1)
485: ++nelems;
486: oldp = cp;
487: cp = strchr (cp + 1, ':');
488: }
489:
490:
491: result = (struct path_elem *) malloc ((nelems + 1)
492: * sizeof (struct path_elem)
493: + gconv_path_len + nelems
494: + (nelems - 1) * (cwdlen + 1));
495: if (result != NULL)
496: {
497: char *strspace = (char *) &result[nelems + 1];
498: int n = 0;
499:
500:
501: __gconv_max_path_elem_len = 0;
502: elem = __strtok_r (gconv_path, ":", &gconv_path);
503: assert (elem != NULL);
504: do
505: {
506: result[n].name = strspace;
507: if (elem[0] != '/')
508: {
509: assert (cwd != NULL);
510: strspace = __mempcpy (strspace, cwd, cwdlen);
511: *strspace++ = '/';
512: }
513: strspace = __stpcpy (strspace, elem);
514: if (strspace[-1] != '/')
515: *strspace++ = '/';
516:
517: result[n].len = strspace - result[n].name;
518: if (result[n].len > __gconv_max_path_elem_len)
519: __gconv_max_path_elem_len = result[n].len;
520:
521: *strspace++ = '\0';
522: ++n;
523: }
524: while ((elem = __strtok_r (NULL, ":", &gconv_path)) != NULL);
525:
526: result[n].name = NULL;
527: result[n].len = 0;
528: }
529:
530: __gconv_path_elem = result ?: (struct path_elem *) &empty_path_elem;
531:
532: if (cwd != NULL)
533: free (cwd);
534: }
535:
536: __libc_lock_unlock (lock);
537: }
538:
539:
540:
541:
542: void
543: attribute_hidden
544: __gconv_read_conf (void)
545: {
546: void *modules = NULL;
547: size_t nmodules = 0;
548: int save_errno = errno;
549: size_t cnt;
550:
551:
552: if (__gconv_load_cache () == 0)
553: {
554:
555: __set_errno (save_errno);
556: return;
557: }
558:
559: #ifndef STATIC_GCONV
560:
561: if (__gconv_path_elem == NULL)
562: __gconv_get_path ();
563:
564: for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt)
565: {
566: const char *elem = __gconv_path_elem[cnt].name;
567: size_t elem_len = __gconv_path_elem[cnt].len;
568: char *filename;
569:
570:
571:
572: filename = alloca (elem_len + sizeof (gconv_conf_filename));
573: __mempcpy (__mempcpy (filename, elem, elem_len),
574: gconv_conf_filename, sizeof (gconv_conf_filename));
575:
576:
577: read_conf_file (filename, elem, elem_len, &modules, &nmodules);