Implement POSIX option -E (and make the default behaviour that there
[findutils.git] / intl / l10nflist.c
blob557253eb985164c1b022a9c0c2cb447e368c2ecf
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 stpcpy().
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>
30 #if !HAVE_STRCHR && !defined _LIBC
31 # ifndef strchr
32 # define strchr index
33 # endif
34 #endif
36 #if defined _LIBC || defined HAVE_ARGZ_H
37 # include <argz.h>
38 #endif
39 #include <ctype.h>
40 #include <sys/types.h>
41 #include <stdlib.h>
43 #include "loadinfo.h"
45 /* On some strange systems still no definition of NULL is found. Sigh! */
46 #ifndef NULL
47 # if defined __STDC__ && __STDC__
48 # define NULL ((void *) 0)
49 # else
50 # define NULL 0
51 # endif
52 #endif
54 /* @@ end of prolog @@ */
56 #ifdef _LIBC
57 /* Rename the non ANSI C functions. This is required by the standard
58 because some ANSI C functions will require linking with this object
59 file and the name space must not be polluted. */
60 # ifndef stpcpy
61 # define stpcpy(dest, src) __stpcpy(dest, src)
62 # endif
63 #else
64 # ifndef HAVE_STPCPY
65 static char *stpcpy PARAMS ((char *dest, const char *src));
66 # endif
67 #endif
69 /* Define function which are usually not available. */
71 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
72 /* Returns the number of strings in ARGZ. */
73 static size_t argz_count__ PARAMS ((const char *argz, size_t len));
75 static size_t
76 argz_count__ (argz, len)
77 const char *argz;
78 size_t len;
80 size_t count = 0;
81 while (len > 0)
83 size_t part_len = strlen (argz);
84 argz += part_len + 1;
85 len -= part_len + 1;
86 count++;
88 return count;
90 # undef __argz_count
91 # define __argz_count(argz, len) argz_count__ (argz, len)
92 #endif /* !_LIBC && !HAVE___ARGZ_COUNT */
94 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
95 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
96 except the last into the character SEP. */
97 static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
99 static void
100 argz_stringify__ (argz, len, sep)
101 char *argz;
102 size_t len;
103 int sep;
105 while (len > 0)
107 size_t part_len = strlen (argz);
108 argz += part_len;
109 len -= part_len + 1;
110 if (len > 0)
111 *argz++ = sep;
114 # undef __argz_stringify
115 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
116 #endif /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
118 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
119 static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
120 const char *entry));
122 static char *
123 argz_next__ (argz, argz_len, entry)
124 char *argz;
125 size_t argz_len;
126 const char *entry;
128 if (entry)
130 if (entry < argz + argz_len)
131 entry = strchr (entry, '\0') + 1;
133 return entry >= argz + argz_len ? NULL : (char *) entry;
135 else
136 if (argz_len > 0)
137 return argz;
138 else
139 return 0;
141 # undef __argz_next
142 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
143 #endif /* !_LIBC && !HAVE___ARGZ_NEXT */
146 /* Return number of bits set in X. */
147 static int pop PARAMS ((int x));
149 static inline int
150 pop (x)
151 int x;
153 /* We assume that no more than 16 bits are used. */
154 x = ((x & ~0x5555) >> 1) + (x & 0x5555);
155 x = ((x & ~0x3333) >> 2) + (x & 0x3333);
156 x = ((x >> 4) + x) & 0x0f0f;
157 x = ((x >> 8) + x) & 0xff;
159 return x;
163 struct loaded_l10nfile *
164 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
165 territory, codeset, normalized_codeset, modifier, special,
166 sponsor, revision, filename, do_allocate)
167 struct loaded_l10nfile **l10nfile_list;
168 const char *dirlist;
169 size_t dirlist_len;
170 int mask;
171 const char *language;
172 const char *territory;
173 const char *codeset;
174 const char *normalized_codeset;
175 const char *modifier;
176 const char *special;
177 const char *sponsor;
178 const char *revision;
179 const char *filename;
180 int do_allocate;
182 char *abs_filename;
183 struct loaded_l10nfile *last = NULL;
184 struct loaded_l10nfile *retval;
185 char *cp;
186 size_t entries;
187 int cnt;
189 /* Allocate room for the full file name. */
190 abs_filename = (char *) malloc (dirlist_len
191 + strlen (language)
192 + ((mask & TERRITORY) != 0
193 ? strlen (territory) + 1 : 0)
194 + ((mask & XPG_CODESET) != 0
195 ? strlen (codeset) + 1 : 0)
196 + ((mask & XPG_NORM_CODESET) != 0
197 ? strlen (normalized_codeset) + 1 : 0)
198 + (((mask & XPG_MODIFIER) != 0
199 || (mask & CEN_AUDIENCE) != 0)
200 ? strlen (modifier) + 1 : 0)
201 + ((mask & CEN_SPECIAL) != 0
202 ? strlen (special) + 1 : 0)
203 + (((mask & CEN_SPONSOR) != 0
204 || (mask & CEN_REVISION) != 0)
205 ? (1 + ((mask & CEN_SPONSOR) != 0
206 ? strlen (sponsor) + 1 : 0)
207 + ((mask & CEN_REVISION) != 0
208 ? strlen (revision) + 1 : 0)) : 0)
209 + 1 + strlen (filename) + 1);
211 if (abs_filename == NULL)
212 return NULL;
214 retval = NULL;
215 last = NULL;
217 /* Construct file name. */
218 memcpy (abs_filename, dirlist, dirlist_len);
219 __argz_stringify (abs_filename, dirlist_len, PATH_SEPARATOR);
220 cp = abs_filename + (dirlist_len - 1);
221 *cp++ = '/';
222 cp = stpcpy (cp, language);
224 if ((mask & TERRITORY) != 0)
226 *cp++ = '_';
227 cp = stpcpy (cp, territory);
229 if ((mask & XPG_CODESET) != 0)
231 *cp++ = '.';
232 cp = stpcpy (cp, codeset);
234 if ((mask & XPG_NORM_CODESET) != 0)
236 *cp++ = '.';
237 cp = stpcpy (cp, normalized_codeset);
239 if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
241 /* This component can be part of both syntaces but has different
242 leading characters. For CEN we use `+', else `@'. */
243 *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
244 cp = stpcpy (cp, modifier);
246 if ((mask & CEN_SPECIAL) != 0)
248 *cp++ = '+';
249 cp = stpcpy (cp, special);
251 if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
253 *cp++ = ',';
254 if ((mask & CEN_SPONSOR) != 0)
255 cp = stpcpy (cp, sponsor);
256 if ((mask & CEN_REVISION) != 0)
258 *cp++ = '_';
259 cp = stpcpy (cp, revision);
263 *cp++ = '/';
264 stpcpy (cp, filename);
266 /* Look in list of already loaded domains whether it is already
267 available. */
268 last = NULL;
269 for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
270 if (retval->filename != NULL)
272 int compare = strcmp (retval->filename, abs_filename);
273 if (compare == 0)
274 /* We found it! */
275 break;
276 if (compare < 0)
278 /* It's not in the list. */
279 retval = NULL;
280 break;
283 last = retval;
286 if (retval != NULL || do_allocate == 0)
288 free (abs_filename);
289 return retval;
292 retval = (struct loaded_l10nfile *)
293 malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
294 * (1 << pop (mask))
295 * sizeof (struct loaded_l10nfile *)));
296 if (retval == NULL)
297 return NULL;
299 retval->filename = abs_filename;
300 retval->decided = (__argz_count (dirlist, dirlist_len) != 1
301 || ((mask & XPG_CODESET) != 0
302 && (mask & XPG_NORM_CODESET) != 0));
303 retval->data = NULL;
305 if (last == NULL)
307 retval->next = *l10nfile_list;
308 *l10nfile_list = retval;
310 else
312 retval->next = last->next;
313 last->next = retval;
316 entries = 0;
317 /* If the DIRLIST is a real list the RETVAL entry corresponds not to
318 a real file. So we have to use the DIRLIST separation mechanism
319 of the inner loop. */
320 cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
321 for (; cnt >= 0; --cnt)
322 if ((cnt & ~mask) == 0
323 && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
324 && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
326 /* Iterate over all elements of the DIRLIST. */
327 char *dir = NULL;
329 while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
330 != NULL)
331 retval->successor[entries++]
332 = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
333 language, territory, codeset,
334 normalized_codeset, modifier, special,
335 sponsor, revision, filename, 1);
337 retval->successor[entries] = NULL;
339 return retval;
342 /* Normalize codeset name. There is no standard for the codeset
343 names. Normalization allows the user to use any of the common
344 names. The return value is dynamically allocated and has to be
345 freed by the caller. */
346 const char *
347 _nl_normalize_codeset (codeset, name_len)
348 const char *codeset;
349 size_t name_len;
351 int len = 0;
352 int only_digit = 1;
353 char *retval;
354 char *wp;
355 size_t cnt;
357 for (cnt = 0; cnt < name_len; ++cnt)
358 if (isalnum (codeset[cnt]))
360 ++len;
362 if (isalpha (codeset[cnt]))
363 only_digit = 0;
366 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
368 if (retval != NULL)
370 if (only_digit)
371 wp = stpcpy (retval, "iso");
372 else
373 wp = retval;
375 for (cnt = 0; cnt < name_len; ++cnt)
376 if (isalpha (codeset[cnt]))
377 *wp++ = tolower (codeset[cnt]);
378 else if (isdigit (codeset[cnt]))
379 *wp++ = codeset[cnt];
381 *wp = '\0';
384 return (const char *) retval;
388 /* @@ begin of epilog @@ */
390 /* We don't want libintl.a to depend on any other library. So we
391 avoid the non-standard function stpcpy. In GNU C Library this
392 function is available, though. Also allow the symbol HAVE_STPCPY
393 to be defined. */
394 #if !_LIBC && !HAVE_STPCPY
395 static char *
396 stpcpy (dest, src)
397 char *dest;
398 const char *src;
400 while ((*dest++ = *src++) != '\0')
401 /* Do nothing. */ ;
402 return dest - 1;
404 #endif