Initial commit of the HEAD branch of the ELinks CVS repository, as of
[elinks/images.git] / src / intl / gettext / l10nflist.c
blob8e00b44b868f03f3de006f1cb6f97b0614978c13
1 /* Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
2 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
19 This must come before <config.h> because <config.h> may include
20 <features.h>, and once <features.h> has been included, it's too late. */
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE 1
23 #endif
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <string.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33 #include <stdlib.h>
35 #include "elinks.h"
37 #include "intl/gettext/loadinfo.h"
38 #include "util/string.h"
40 /* Awful hack to permit compilation under cygwin and its broken configure.
41 * Configure script detects these functions, but compilation clashes on
42 * implicit declarations of them... So we force use of internal ones.
43 * Cygwin argz.h do not contain any declaration for these, nor any other
44 * header while they are available in some linked libs.
45 * Feel free to provide a better fix if any. --Zas */
46 #ifdef HAVE_SYS_CYGWIN_H
47 #undef HAVE___ARGZ_STRINGIFY
48 #undef HAVE___ARGZ_COUNT
49 #undef HAVE___ARGZ_NEXT
50 #else
51 #if defined HAVE_ARGZ_H
52 #include <argz.h>
53 #endif
54 #endif
56 /* On some strange systems still no definition of NULL is found. Sigh! */
57 #ifndef NULL
58 #if defined __STDC__ && __STDC__
59 #define NULL ((void *) 0)
60 #else
61 #define NULL 0
62 #endif
63 #endif
65 /* Define function which are usually not available. */
67 #if !defined HAVE___ARGZ_COUNT
68 /* Returns the number of strings in ARGZ. */
69 static size_t argz_count__(const unsigned char *argz, size_t len);
71 static size_t
72 argz_count__(const unsigned char *argz, size_t len)
74 size_t count = 0;
76 while (len > 0) {
77 size_t part_len = strlen(argz);
79 argz += part_len + 1;
80 len -= part_len + 1;
81 count++;
83 return count;
86 #undef __argz_count
87 #define __argz_count(argz, len) argz_count__ (argz, len)
88 #endif /* !HAVE___ARGZ_COUNT */
90 #if !defined HAVE___ARGZ_STRINGIFY
91 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
92 except the last into the character SEP. */
93 static void argz_stringify__(unsigned char *argz, size_t len, int sep);
95 static void
96 argz_stringify__(unsigned char *argz, size_t len, int sep)
98 while (len > 0) {
99 size_t part_len = strlen(argz);
101 argz += part_len;
102 len -= part_len + 1;
103 if (len > 0)
104 *argz++ = sep;
108 #undef __argz_stringify
109 #define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
110 #endif /* !HAVE___ARGZ_STRINGIFY */
112 #if !defined HAVE___ARGZ_NEXT
113 static unsigned char *argz_next__(unsigned char *argz, size_t argz_len,
114 const unsigned char *entry);
116 static unsigned char *
117 argz_next__(unsigned char *argz, size_t argz_len, const unsigned char *entry)
119 if (entry) {
120 if (entry < argz + argz_len)
121 entry = strchr(entry, '\0') + 1;
123 return entry >= argz + argz_len ? NULL : (unsigned char *) entry;
124 } else if (argz_len > 0)
125 return argz;
126 else
127 return 0;
130 #undef __argz_next
131 #define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
132 #endif /* !HAVE___ARGZ_NEXT */
134 /* Return number of bits set in X. */
135 static inline int
136 pop(int x)
138 /* We assume that no more than 16 bits are used. */
139 x = ((x & ~0x5555) >> 1) + (x & 0x5555);
140 x = ((x & ~0x3333) >> 2) + (x & 0x3333);
141 x = ((x >> 4) + x) & 0x0f0f;
142 x = ((x >> 8) + x) & 0xff;
144 return x;
147 struct loaded_l10nfile *
148 _nl_make_l10nflist(struct loaded_l10nfile **l10nfile_list,
149 const unsigned char *dirlist,
150 size_t dirlist_len,
151 int mask,
152 const unsigned char *language,
153 const unsigned char *territory,
154 const unsigned char *codeset,
155 const unsigned char *normalized_codeset,
156 const unsigned char *modifier,
157 const unsigned char *special,
158 const unsigned char *sponsor,
159 const unsigned char *revision,
160 const unsigned char *filename,
161 int do_allocate)
163 unsigned char *abs_filename, *abs_langdirname;
164 int abs_langdirnamelen;
165 struct loaded_l10nfile *last = NULL;
166 struct loaded_l10nfile *retval;
167 unsigned char *cp;
168 size_t entries;
169 int cnt;
171 /* Allocate room for the full file name. */
172 abs_filename = (unsigned char *) malloc(dirlist_len + strlen(language)
173 + ((mask & TERRITORY) != 0
174 ? strlen(territory) + 1 : 0)
175 + ((mask & XPG_CODESET) != 0
176 ? strlen(codeset) + 1 : 0)
177 + ((mask & XPG_NORM_CODESET) != 0
178 ? strlen(normalized_codeset) + 1 : 0)
179 + (((mask & XPG_MODIFIER) != 0
180 || (mask & CEN_AUDIENCE) != 0)
181 ? strlen(modifier) + 1 : 0)
182 + ((mask & CEN_SPECIAL) != 0
183 ? strlen(special) + 1 : 0)
184 + (((mask & CEN_SPONSOR) != 0
185 || (mask & CEN_REVISION) != 0)
186 ? (1 + ((mask & CEN_SPONSOR) != 0
187 ? strlen(sponsor) + 1 : 0)
188 + ((mask & CEN_REVISION) != 0
189 ? strlen(revision) +
190 1 : 0)) : 0)
191 + 1 + strlen(filename) + 1);
193 if (abs_filename == NULL)
194 return NULL;
196 retval = NULL;
197 last = NULL;
199 /* Construct file name. */
200 memcpy(abs_filename, dirlist, dirlist_len);
201 __argz_stringify(abs_filename, dirlist_len, PATH_SEPARATOR);
202 cp = abs_filename + (dirlist_len - 1);
203 *cp++ = '/';
204 abs_langdirname = cp;
205 cp = stpcpy(cp, language);
207 if ((mask & TERRITORY) != 0) {
208 *cp++ = '_';
209 cp = stpcpy(cp, territory);
211 if ((mask & XPG_CODESET) != 0) {
212 *cp++ = '.';
213 cp = stpcpy(cp, codeset);
215 if ((mask & XPG_NORM_CODESET) != 0) {
216 *cp++ = '.';
217 cp = stpcpy(cp, normalized_codeset);
219 if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0) {
220 /* This component can be part of both syntaces but has different
221 leading characters. For CEN we use `+', else `@'. */
222 *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
223 cp = stpcpy(cp, modifier);
225 if ((mask & CEN_SPECIAL) != 0) {
226 *cp++ = '+';
227 cp = stpcpy(cp, special);
229 if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0) {
230 *cp++ = ',';
231 if ((mask & CEN_SPONSOR) != 0)
232 cp = stpcpy(cp, sponsor);
233 if ((mask & CEN_REVISION) != 0) {
234 *cp++ = '_';
235 cp = stpcpy(cp, revision);
238 abs_langdirnamelen = cp - abs_langdirname;
240 *cp++ = '/';
241 stpcpy(cp, filename);
243 /* Look in list of already loaded domains whether it is already
244 available. */
245 last = NULL;
246 for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
247 if (retval->filename != NULL) {
248 int compare = strcmp(retval->filename, abs_filename);
250 if (compare == 0)
251 /* We found it! */
252 break;
253 if (compare < 0) {
254 /* It's not in the list. */
255 retval = NULL;
256 break;
259 last = retval;
262 if (retval != NULL || do_allocate == 0) {
263 free(abs_filename);
264 return retval;
267 retval = (struct loaded_l10nfile *)
268 malloc(sizeof(*retval) + (__argz_count(dirlist, dirlist_len)
269 * (1 << pop(mask))
270 * sizeof(struct loaded_l10nfile *)));
271 if (retval == NULL)
272 return NULL;
274 retval->filename = abs_filename;
275 retval->langdirname = abs_langdirname;
276 retval->langdirnamelen = abs_langdirnamelen;
277 retval->decided = (__argz_count(dirlist, dirlist_len) != 1
278 || ((mask & XPG_CODESET) != 0
279 && (mask & XPG_NORM_CODESET) != 0));
280 retval->data = NULL;
282 if (last == NULL) {
283 retval->next = *l10nfile_list;
284 *l10nfile_list = retval;
285 } else {
286 retval->next = last->next;
287 last->next = retval;
290 entries = 0;
291 /* If the DIRLIST is a real list the RETVAL entry corresponds not to
292 a real file. So we have to use the DIRLIST separation mechanism
293 of the inner loop. */
294 cnt = __argz_count(dirlist, dirlist_len) == 1 ? mask - 1 : mask;
295 for (; cnt >= 0; --cnt)
296 if ((cnt & ~mask) == 0
297 && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
298 && ((cnt & XPG_CODESET) == 0
299 || (cnt & XPG_NORM_CODESET) == 0)) {
300 /* Iterate over all elements of the DIRLIST. */
301 unsigned char *dir = NULL;
303 while ((dir =
304 __argz_next((unsigned char *) dirlist, dirlist_len, dir))
305 != NULL)
306 retval->successor[entries++]
307 = _nl_make_l10nflist(l10nfile_list, dir,
308 strlen(dir) + 1,
309 cnt, language,
310 territory, codeset,
311 normalized_codeset,
312 modifier, special,
313 sponsor, revision,
314 filename, 1);
316 retval->successor[entries] = NULL;
318 return retval;
321 /* Normalize codeset name. There is no standard for the codeset
322 names. Normalization allows the user to use any of the common
323 names. The return value is dynamically allocated and has to be
324 freed by the caller. */
325 const unsigned char *
326 _nl_normalize_codeset(const unsigned char *codeset, size_t name_len)
328 int len = 0;
329 int only_digit = 1;
330 unsigned char *retval;
331 unsigned char *wp;
332 size_t cnt;
334 for (cnt = 0; cnt < name_len; ++cnt)
335 if (isalnum(codeset[cnt])) {
336 ++len;
338 if (isalpha(codeset[cnt]))
339 only_digit = 0;
342 retval = (unsigned char *) malloc((only_digit ? 3 : 0) + len + 1);
344 if (retval != NULL) {
345 if (only_digit)
346 wp = stpcpy(retval, "iso");
347 else
348 wp = retval;
350 for (cnt = 0; cnt < name_len; ++cnt)
351 if (isalpha(codeset[cnt]))
352 *wp++ = tolower(codeset[cnt]);
353 else if (isdigit(codeset[cnt]))
354 *wp++ = codeset[cnt];
356 *wp = '\0';
359 return (const unsigned char *) retval;