1/* Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
2   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
3
4   This program is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Library General Public License as published
6   by the Free Software Foundation; either version 2, or (at your option)
7   any later version.
8
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 GNU
12   Library General Public License for more details.
13
14   You should have received a copy of the GNU Library General Public
15   License along with this program; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17   USA.  */
18
19/* Tell glibc's <string.h> to provide a prototype for stpcpy().
20   This must come before <config.h> because <config.h> may include
21   <features.h>, and once <features.h> has been included, it's too late.  */
22#ifndef _GNU_SOURCE
23# define _GNU_SOURCE	1
24#endif
25
26#ifdef HAVE_CONFIG_H
27# include <config.h>
28#endif
29
30#include <string.h>
31
32#if defined _LIBC || defined HAVE_ARGZ_H
33# include <argz.h>
34#endif
35#include <ctype.h>
36#include <sys/types.h>
37#include <stdlib.h>
38
39#include "loadinfo.h"
40
41/* On some strange systems still no definition of NULL is found.  Sigh!  */
42#ifndef NULL
43# if defined __STDC__ && __STDC__
44#  define NULL ((void *) 0)
45# else
46#  define NULL 0
47# endif
48#endif
49
50/* @@ end of prolog @@ */
51
52#ifdef _LIBC
53/* Rename the non ANSI C functions.  This is required by the standard
54   because some ANSI C functions will require linking with this object
55   file and the name space must not be polluted.  */
56# ifndef stpcpy
57#  define stpcpy(dest, src) __stpcpy(dest, src)
58# endif
59#else
60# ifndef HAVE_STPCPY
61#define stpcpy(dest, src) my_stpcpy(dest, src)
62static char *stpcpy (char *dest, const char *src);
63# endif
64#endif
65
66/* Pathname support.
67   ISSLASH(C)           tests whether C is a directory separator character.
68   IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
69                        it may be concatenated to a directory pathname.
70 */
71#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
72  /* Win32, OS/2, DOS */
73# define ISSLASH(C) ((C) == '/' || (C) == '\\')
74# define HAS_DEVICE(P) \
75    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
76     && (P)[1] == ':')
77# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
78#else
79  /* Unix */
80# define ISSLASH(C) ((C) == '/')
81# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
82#endif
83
84/* Define function which are usually not available.  */
85
86#if !defined _LIBC && !defined HAVE___ARGZ_COUNT
87/* Returns the number of strings in ARGZ.  */
88static size_t
89argz_count__ (const char *argz, size_t len)
90{
91  size_t count = 0;
92  while (len > 0)
93    {
94      size_t part_len = strlen (argz);
95      argz += part_len + 1;
96      len -= part_len + 1;
97      count++;
98    }
99  return count;
100}
101# undef __argz_count
102# define __argz_count(argz, len) argz_count__ (argz, len)
103#else
104# ifdef _LIBC
105#  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
106# endif
107#endif	/* !_LIBC && !HAVE___ARGZ_COUNT */
108
109#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
110/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
111   except the last into the character SEP.  */
112static void
113argz_stringify__ (char *argz, size_t len, int sep)
114{
115  while (len > 0)
116    {
117      size_t part_len = strlen (argz);
118      argz += part_len;
119      len -= part_len + 1;
120      if (len > 0)
121	*argz++ = sep;
122    }
123}
124# undef __argz_stringify
125# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
126#else
127# ifdef _LIBC
128#  define __argz_stringify(argz, len, sep) \
129  INTUSE(__argz_stringify) (argz, len, sep)
130# endif
131#endif	/* !_LIBC && !HAVE___ARGZ_STRINGIFY */
132
133#if !defined _LIBC && !defined HAVE___ARGZ_NEXT
134static char *
135argz_next__ (char *argz, size_t argz_len, const char *entry)
136{
137  if (entry)
138    {
139      if (entry < argz + argz_len)
140        entry = strchr (entry, '\0') + 1;
141
142      return entry >= argz + argz_len ? NULL : (char *) entry;
143    }
144  else
145    if (argz_len > 0)
146      return argz;
147    else
148      return 0;
149}
150# undef __argz_next
151# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
152#endif	/* !_LIBC && !HAVE___ARGZ_NEXT */
153
154
155/* Return number of bits set in X.  */
156static inline int
157pop (int x)
158{
159  /* We assume that no more than 16 bits are used.  */
160  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
161  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
162  x = ((x >> 4) + x) & 0x0f0f;
163  x = ((x >> 8) + x) & 0xff;
164
165  return x;
166}
167
168
169struct loaded_l10nfile *
170_nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
171		    const char *dirlist, size_t dirlist_len,
172		    int mask, const char *language, const char *territory,
173		    const char *codeset, const char *normalized_codeset,
174		    const char *modifier, const char *special,
175		    const char *sponsor, const char *revision,
176		    const char *filename, int do_allocate)
177{
178  char *abs_filename;
179  struct loaded_l10nfile **lastp;
180  struct loaded_l10nfile *retval;
181  char *cp;
182  size_t dirlist_count;
183  size_t entries;
184  int cnt;
185
186  /* If LANGUAGE contains an absolute directory specification, we ignore
187     DIRLIST.  */
188  if (IS_ABSOLUTE_PATH (language))
189    dirlist_len = 0;
190
191  /* Allocate room for the full file name.  */
192  abs_filename = (char *) malloc (dirlist_len
193				  + strlen (language)
194				  + ((mask & TERRITORY) != 0
195				     ? strlen (territory) + 1 : 0)
196				  + ((mask & XPG_CODESET) != 0
197				     ? strlen (codeset) + 1 : 0)
198				  + ((mask & XPG_NORM_CODESET) != 0
199				     ? strlen (normalized_codeset) + 1 : 0)
200				  + (((mask & XPG_MODIFIER) != 0
201				      || (mask & CEN_AUDIENCE) != 0)
202				     ? strlen (modifier) + 1 : 0)
203				  + ((mask & CEN_SPECIAL) != 0
204				     ? strlen (special) + 1 : 0)
205				  + (((mask & CEN_SPONSOR) != 0
206				      || (mask & CEN_REVISION) != 0)
207				     ? (1 + ((mask & CEN_SPONSOR) != 0
208					     ? strlen (sponsor) : 0)
209					+ ((mask & CEN_REVISION) != 0
210					   ? strlen (revision) + 1 : 0)) : 0)
211				  + 1 + strlen (filename) + 1);
212
213  if (abs_filename == NULL)
214    return NULL;
215
216  /* Construct file name.  */
217  cp = abs_filename;
218  if (dirlist_len > 0)
219    {
220      memcpy (cp, dirlist, dirlist_len);
221      __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
222      cp += dirlist_len;
223      cp[-1] = '/';
224    }
225
226  cp = stpcpy (cp, language);
227
228  if ((mask & TERRITORY) != 0)
229    {
230      *cp++ = '_';
231      cp = stpcpy (cp, territory);
232    }
233  if ((mask & XPG_CODESET) != 0)
234    {
235      *cp++ = '.';
236      cp = stpcpy (cp, codeset);
237    }
238  if ((mask & XPG_NORM_CODESET) != 0)
239    {
240      *cp++ = '.';
241      cp = stpcpy (cp, normalized_codeset);
242    }
243  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
244    {
245      /* This component can be part of both syntaces but has different
246	 leading characters.  For CEN we use `+', else `@'.  */
247      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
248      cp = stpcpy (cp, modifier);
249    }
250  if ((mask & CEN_SPECIAL) != 0)
251    {
252      *cp++ = '+';
253      cp = stpcpy (cp, special);
254    }
255  if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
256    {
257      *cp++ = ',';
258      if ((mask & CEN_SPONSOR) != 0)
259	cp = stpcpy (cp, sponsor);
260      if ((mask & CEN_REVISION) != 0)
261	{
262	  *cp++ = '_';
263	  cp = stpcpy (cp, revision);
264	}
265    }
266
267  *cp++ = '/';
268  stpcpy (cp, filename);
269
270  /* Look in list of already loaded domains whether it is already
271     available.  */
272  lastp = l10nfile_list;
273  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
274    if (retval->filename != NULL)
275      {
276	int compare = strcmp (retval->filename, abs_filename);
277	if (compare == 0)
278	  /* We found it!  */
279	  break;
280	if (compare < 0)
281	  {
282	    /* It's not in the list.  */
283	    retval = NULL;
284	    break;
285	  }
286
287	lastp = &retval->next;
288      }
289
290  if (retval != NULL || do_allocate == 0)
291    {
292      free (abs_filename);
293      return retval;
294    }
295
296  dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
297
298  /* Allocate a new loaded_l10nfile.  */
299  retval =
300    (struct loaded_l10nfile *)
301    malloc (sizeof (*retval)
302	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
303	       * sizeof (struct loaded_l10nfile *)));
304  if (retval == NULL)
305    return NULL;
306
307  retval->filename = abs_filename;
308
309  /* We set retval->data to NULL here; it is filled in later.
310     Setting retval->decided to 1 here means that retval does not
311     correspond to a real file (dirlist_count > 1) or is not worth
312     looking up (if an unnormalized codeset was specified).  */
313  retval->decided = (dirlist_count > 1
314		     || ((mask & XPG_CODESET) != 0
315			 && (mask & XPG_NORM_CODESET) != 0));
316  retval->data = NULL;
317
318  retval->next = *lastp;
319  *lastp = retval;
320
321  entries = 0;
322  /* Recurse to fill the inheritance list of RETVAL.
323     If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
324     entry does not correspond to a real file; retval->filename contains
325     colons.  In this case we loop across all elements of DIRLIST and
326     across all bit patterns dominated by MASK.
327     If the DIRLIST is a single directory or entirely redundant (i.e.
328     DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
329     MASK, excluding MASK itself.
330     In either case, we loop down from MASK to 0.  This has the effect
331     that the extra bits in the locale name are dropped in this order:
332     first the modifier, then the territory, then the codeset, then the
333     normalized_codeset.  */
334  for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
335    if ((cnt & ~mask) == 0
336	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
337	&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
338      {
339	if (dirlist_count > 1)
340	  {
341	    /* Iterate over all elements of the DIRLIST.  */
342	    char *dir = NULL;
343
344	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
345		   != NULL)
346	      retval->successor[entries++]
347		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
348				      cnt, language, territory, codeset,
349				      normalized_codeset, modifier, special,
350				      sponsor, revision, filename, 1);
351	  }
352	else
353	  retval->successor[entries++]
354	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
355				  cnt, language, territory, codeset,
356				  normalized_codeset, modifier, special,
357				  sponsor, revision, filename, 1);
358      }
359  retval->successor[entries] = NULL;
360
361  return retval;
362}
363
364/* Normalize codeset name.  There is no standard for the codeset
365   names.  Normalization allows the user to use any of the common
366   names.  The return value is dynamically allocated and has to be
367   freed by the caller.  */
368const char *
369_nl_normalize_codeset (const char *codeset, size_t name_len)
370{
371  int len = 0;
372  int only_digit = 1;
373  char *retval;
374  char *wp;
375  size_t cnt;
376
377  for (cnt = 0; cnt < name_len; ++cnt)
378    if (isalnum ((unsigned char) codeset[cnt]))
379      {
380	++len;
381
382	if (isalpha ((unsigned char) codeset[cnt]))
383	  only_digit = 0;
384      }
385
386  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
387
388  if (retval != NULL)
389    {
390      if (only_digit)
391	wp = stpcpy (retval, "iso");
392      else
393	wp = retval;
394
395      for (cnt = 0; cnt < name_len; ++cnt)
396	if (isalpha ((unsigned char) codeset[cnt]))
397	  *wp++ = tolower ((unsigned char) codeset[cnt]);
398	else if (isdigit ((unsigned char) codeset[cnt]))
399	  *wp++ = codeset[cnt];
400
401      *wp = '\0';
402    }
403
404  return (const char *) retval;
405}
406
407
408/* @@ begin of epilog @@ */
409
410/* We don't want libintl.a to depend on any other library.  So we
411   avoid the non-standard function stpcpy.  In GNU C Library this
412   function is available, though.  Also allow the symbol HAVE_STPCPY
413   to be defined.  */
414#if !_LIBC && !HAVE_STPCPY
415static char *
416stpcpy (char *dest, const char *src)
417{
418  while ((*dest++ = *src++) != '\0')
419    /* Do nothing. */ ;
420  return dest - 1;
421}
422#endif
423