l10nflist.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* Copyright (C) 1995-2026 Free Software Foundation, Inc.
  2. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>. */
  13. /* Tell glibc's <string.h> to provide a prototype for stpcpy().
  14. This must come before <config.h> because <config.h> may include
  15. <features.h>, and once <features.h> has been included, it's too late. */
  16. #ifndef _GNU_SOURCE
  17. # define _GNU_SOURCE 1
  18. #endif
  19. #ifdef HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22. #include <string.h>
  23. #if defined _LIBC || defined HAVE_ARGZ_H
  24. # include <argz.h>
  25. #endif
  26. #include <ctype.h>
  27. #include <sys/types.h>
  28. #include <stdlib.h>
  29. #include "loadinfo.h"
  30. /* On some strange systems still no definition of NULL is found. Sigh! */
  31. #ifndef NULL
  32. # if defined __STDC__ && __STDC__
  33. # define NULL ((void *) 0)
  34. # else
  35. # define NULL 0
  36. # endif
  37. #endif
  38. /* @@ end of prolog @@ */
  39. #ifdef _LIBC
  40. /* Rename the non ANSI C functions. This is required by the standard
  41. because some ANSI C functions will require linking with this object
  42. file and the name space must not be polluted. */
  43. # ifndef stpcpy
  44. # define stpcpy(dest, src) __stpcpy(dest, src)
  45. # endif
  46. #else
  47. # ifndef HAVE_STPCPY
  48. static char *stpcpy (char *dest, const char *src);
  49. # endif
  50. #endif
  51. /* Define function which are usually not available. */
  52. #if defined HAVE_ARGZ_COUNT
  53. # undef __argz_count
  54. # define __argz_count argz_count
  55. #else
  56. /* Returns the number of strings in ARGZ. */
  57. static size_t
  58. argz_count__ (const char *argz, size_t len)
  59. {
  60. size_t count = 0;
  61. while (len > 0)
  62. {
  63. size_t part_len = strlen (argz);
  64. argz += part_len + 1;
  65. len -= part_len + 1;
  66. count++;
  67. }
  68. return count;
  69. }
  70. # undef __argz_count
  71. # define __argz_count(argz, len) argz_count__ (argz, len)
  72. #endif /* !_LIBC && !HAVE_ARGZ_COUNT */
  73. #if defined HAVE_ARGZ_STRINGIFY
  74. # undef __argz_stringify
  75. # define __argz_stringify argz_stringify
  76. #else
  77. /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
  78. except the last into the character SEP. */
  79. static void
  80. argz_stringify__ (char *argz, size_t len, int sep)
  81. {
  82. while (len > 0)
  83. {
  84. size_t part_len = strlen (argz);
  85. argz += part_len;
  86. len -= part_len + 1;
  87. if (len > 0)
  88. *argz++ = sep;
  89. }
  90. }
  91. # undef __argz_stringify
  92. # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
  93. #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
  94. #ifdef _LIBC
  95. #elif defined HAVE_ARGZ_NEXT
  96. # undef __argz_next
  97. # define __argz_next argz_next
  98. #else
  99. static char *
  100. argz_next__ (char *argz, size_t argz_len, const char *entry)
  101. {
  102. if (entry)
  103. {
  104. if (entry < argz + argz_len)
  105. entry = strchr (entry, '\0') + 1;
  106. return entry >= argz + argz_len ? NULL : (char *) entry;
  107. }
  108. else
  109. if (argz_len > 0)
  110. return argz;
  111. else
  112. return 0;
  113. }
  114. # undef __argz_next
  115. # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
  116. #endif /* !_LIBC && !HAVE_ARGZ_NEXT */
  117. /* Return number of bits set in X. */
  118. #ifndef ARCH_POP
  119. static inline int
  120. pop (int x)
  121. {
  122. /* We assume that no more than 16 bits are used. */
  123. x = ((x & ~0x5555) >> 1) + (x & 0x5555);
  124. x = ((x & ~0x3333) >> 2) + (x & 0x3333);
  125. x = ((x >> 4) + x) & 0x0f0f;
  126. x = ((x >> 8) + x) & 0xff;
  127. return x;
  128. }
  129. #endif
  130. struct loaded_l10nfile *
  131. _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
  132. const char *dirlist, size_t dirlist_len,
  133. int mask, const char *language, const char *territory,
  134. const char *codeset, const char *normalized_codeset,
  135. const char *modifier,
  136. const char *filename, int do_allocate)
  137. {
  138. char *abs_filename;
  139. struct loaded_l10nfile *last = NULL;
  140. struct loaded_l10nfile *retval;
  141. char *cp;
  142. size_t entries;
  143. int cnt;
  144. /* Allocate room for the full file name. */
  145. abs_filename = (char *) malloc (dirlist_len
  146. + strlen (language)
  147. + ((mask & XPG_TERRITORY) != 0
  148. ? strlen (territory) + 1 : 0)
  149. + ((mask & XPG_CODESET) != 0
  150. ? strlen (codeset) + 1 : 0)
  151. + ((mask & XPG_NORM_CODESET) != 0
  152. ? strlen (normalized_codeset) + 1 : 0)
  153. + ((mask & XPG_MODIFIER) != 0
  154. ? strlen (modifier) + 1 : 0)
  155. + 1 + strlen (filename) + 1);
  156. if (abs_filename == NULL)
  157. return NULL;
  158. retval = NULL;
  159. last = NULL;
  160. /* Construct file name. */
  161. memcpy (abs_filename, dirlist, dirlist_len);
  162. __argz_stringify (abs_filename, dirlist_len, ':');
  163. cp = abs_filename + (dirlist_len - 1);
  164. *cp++ = '/';
  165. cp = stpcpy (cp, language);
  166. if ((mask & XPG_TERRITORY) != 0)
  167. {
  168. *cp++ = '_';
  169. cp = stpcpy (cp, territory);
  170. }
  171. if ((mask & XPG_CODESET) != 0)
  172. {
  173. *cp++ = '.';
  174. cp = stpcpy (cp, codeset);
  175. }
  176. if ((mask & XPG_NORM_CODESET) != 0)
  177. {
  178. *cp++ = '.';
  179. cp = stpcpy (cp, normalized_codeset);
  180. }
  181. if ((mask & XPG_MODIFIER) != 0)
  182. {
  183. *cp++ = '@';
  184. cp = stpcpy (cp, modifier);
  185. }
  186. *cp++ = '/';
  187. stpcpy (cp, filename);
  188. /* Look in list of already loaded domains whether it is already
  189. available. */
  190. last = NULL;
  191. for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
  192. if (retval->filename != NULL)
  193. {
  194. int compare = strcmp (retval->filename, abs_filename);
  195. if (compare == 0)
  196. /* We found it! */
  197. break;
  198. if (compare < 0)
  199. {
  200. /* It's not in the list. */
  201. retval = NULL;
  202. break;
  203. }
  204. last = retval;
  205. }
  206. if (retval != NULL || do_allocate == 0)
  207. {
  208. free (abs_filename);
  209. return retval;
  210. }
  211. retval = (struct loaded_l10nfile *)
  212. malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
  213. * (1 << pop (mask))
  214. * sizeof (struct loaded_l10nfile *)));
  215. if (retval == NULL)
  216. {
  217. free (abs_filename);
  218. return NULL;
  219. }
  220. retval->filename = abs_filename;
  221. /* If more than one directory is in the list this is a pseudo-entry
  222. which just references others. We do not try to load data for it,
  223. ever. */
  224. retval->decided = (__argz_count (dirlist, dirlist_len) != 1
  225. || ((mask & XPG_CODESET) != 0
  226. && (mask & XPG_NORM_CODESET) != 0));
  227. retval->data = NULL;
  228. if (last == NULL)
  229. {
  230. retval->next = *l10nfile_list;
  231. *l10nfile_list = retval;
  232. }
  233. else
  234. {
  235. retval->next = last->next;
  236. last->next = retval;
  237. }
  238. entries = 0;
  239. /* If the DIRLIST is a real list the RETVAL entry corresponds not to
  240. a real file. So we have to use the DIRLIST separation mechanism
  241. of the inner loop. */
  242. cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
  243. for (; cnt >= 0; --cnt)
  244. if ((cnt & ~mask) == 0)
  245. {
  246. /* Iterate over all elements of the DIRLIST. */
  247. char *dir = NULL;
  248. while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
  249. != NULL)
  250. retval->successor[entries++]
  251. = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
  252. language, territory, codeset,
  253. normalized_codeset, modifier, filename, 1);
  254. }
  255. retval->successor[entries] = NULL;
  256. return retval;
  257. }
  258. /* Normalize codeset name. There is no standard for the codeset
  259. names. Normalization allows the user to use any of the common
  260. names. The return value is dynamically allocated and has to be
  261. freed by the caller. */
  262. const char *
  263. _nl_normalize_codeset (const char *codeset, size_t name_len)
  264. {
  265. size_t len = 0;
  266. int only_digit = 1;
  267. char *retval;
  268. char *wp;
  269. size_t cnt;
  270. #if !IS_IN (libc)
  271. locale_t locale = newlocale (0, "C", NULL);
  272. #else
  273. # define locale _nl_C_locobj_ptr
  274. #endif
  275. for (cnt = 0; cnt < name_len; ++cnt)
  276. if (__isalnum_l ((unsigned char) codeset[cnt], locale))
  277. {
  278. ++len;
  279. if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
  280. only_digit = 0;
  281. }
  282. retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
  283. if (retval != NULL)
  284. {
  285. if (only_digit)
  286. wp = stpcpy (retval, "iso");
  287. else
  288. wp = retval;
  289. for (cnt = 0; cnt < name_len; ++cnt)
  290. if (__isalpha_l ((unsigned char) codeset[cnt], locale))
  291. *wp++ = __tolower_l ((unsigned char) codeset[cnt], locale);
  292. else if (__isdigit_l ((unsigned char) codeset[cnt], locale))
  293. *wp++ = codeset[cnt];
  294. *wp = '\0';
  295. }
  296. return (const char *) retval;
  297. }
  298. /* @@ begin of epilog @@ */
  299. /* We don't want libintl.a to depend on any other library. So we
  300. avoid the non-standard function stpcpy. In GNU C Library this
  301. function is available, though. Also allow the symbol HAVE_STPCPY
  302. to be defined. */
  303. #if !_LIBC && !HAVE_STPCPY
  304. static char *
  305. stpcpy (char *dest, const char *src)
  306. {
  307. while ((*dest++ = *src++) != '\0')
  308. /* Do nothing. */ ;
  309. return dest - 1;
  310. }
  311. #endif