1/* Copyright (C) 1995-1999, 2000, 2001, 2002 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
61static char *stpcpy PARAMS ((char *dest, const char *src));
62# endif
63#endif
64
65/* Pathname support.
66   ISSLASH(C)           tests whether C is a directory separator character.
67   IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
68                        it may be concatenated to a directory pathname.
69 */
70#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
71  /* Win32, OS/2, DOS */
72# define ISSLASH(C) ((C) == '/' || (C) == '\\')
73# define HAS_DEVICE(P) \
74    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
75     && (P)[1] == ':')
76# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
77#else
78  /* Unix */
79# define ISSLASH(C) ((C) == '/')
80# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
81#endif
82
83/* Define function which are usually not available.  */
84
85#if !defined _LIBC && !defined HAVE___ARGZ_COUNT
86/* Returns the number of strings in ARGZ.  */
87static size_t argz_count__ PARAMS ((const char *argz, size_t len));
88
89static size_t
90argz_count__ (argz, len)
91     const char *argz;
92     size_t len;
93{
94  size_t count = 0;
95  while (len > 0)
96    {
97      size_t part_len = strlen (argz);
98      argz += part_len + 1;
99      len -= part_len + 1;
100      count++;
101    }
102  return count;
103}
104# undef __argz_count
105# define __argz_count(argz, len) argz_count__ (argz, len)
106#else
107# ifdef _LIBC
108#  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
109# endif
110#endif	/* !_LIBC && !HAVE___ARGZ_COUNT */
111
112#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
113/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
114   except the last into the character SEP.  */
115static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
116
117static void
118argz_stringify__ (argz, len, sep)
119     char *argz;
120     size_t len;
121     int sep;
122{
123  while (len > 0)
124    {
125      size_t part_len = strlen (argz);
126      argz += part_len;
127      len -= part_len + 1;
128      if (len > 0)
129	*argz++ = sep;
130    }
131}
132# undef __argz_stringify
133# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
134#else
135# ifdef _LIBC
136#  define __argz_stringify(argz, len, sep) \
137  INTUSE(__argz_stringify) (argz, len, sep)
138# endif
139#endif	/* !_LIBC && !HAVE___ARGZ_STRINGIFY */
140
141#if !defined _LIBC && !defined HAVE___ARGZ_NEXT
142static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
143				  const char *entry));
144
145static char *
146argz_next__ (argz, argz_len, entry)
147     char *argz;
148     size_t argz_len;
149     const char *entry;
150{
151  if (entry)
152    {
153      if (entry < argz + argz_len)
154        entry = strchr (entry, '\0') + 1;
155
156      return entry >= argz + argz_len ? NULL : (char *) entry;
157    }
158  else
159    if (argz_len > 0)
160      return argz;
161    else
162      return 0;
163}
164# undef __argz_next
165# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
166#endif	/* !_LIBC && !HAVE___ARGZ_NEXT */
167
168
169/* Return number of bits set in X.  */
170static int pop PARAMS ((int x));
171
172static inline int
173pop (x)
174     int x;
175{
176  /* We assume that no more than 16 bits are used.  */
177  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
178  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
179  x = ((x >> 4) + x) & 0x0f0f;
180  x = ((x >> 8) + x) & 0xff;
181
182  return x;
183}
184
185
186struct loaded_l10nfile *
187_nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
188		    territory, codeset, normalized_codeset, modifier, special,
189		    sponsor, revision, filename, do_allocate)
190     struct loaded_l10nfile **l10nfile_list;
191     const char *dirlist;
192     size_t dirlist_len;
193     int mask;
194     const char *language;
195     const char *territory;
196     const char *codeset;
197     const char *normalized_codeset;
198     const char *modifier;
199     const char *special;
200     const char *sponsor;
201     const char *revision;
202     const char *filename;
203     int do_allocate;
204{
205  char *abs_filename;
206  struct loaded_l10nfile **lastp;
207  struct loaded_l10nfile *retval;
208  char *cp;
209  size_t dirlist_count;
210  size_t entries;
211  int cnt;
212
213  /* If LANGUAGE contains an absolute directory specification, we ignore
214     DIRLIST.  */
215  if (IS_ABSOLUTE_PATH (language))
216    dirlist_len = 0;
217
218  /* Allocate room for the full file name.  */
219  abs_filename = (char *) malloc (dirlist_len
220				  + strlen (language)
221				  + ((mask & TERRITORY) != 0
222				     ? strlen (territory) + 1 : 0)
223				  + ((mask & XPG_CODESET) != 0
224				     ? strlen (codeset) + 1 : 0)
225				  + ((mask & XPG_NORM_CODESET) != 0
226				     ? strlen (normalized_codeset) + 1 : 0)
227				  + (((mask & XPG_MODIFIER) != 0
228				      || (mask & CEN_AUDIENCE) != 0)
229				     ? strlen (modifier) + 1 : 0)
230				  + ((mask & CEN_SPECIAL) != 0
231				     ? strlen (special) + 1 : 0)
232				  + (((mask & CEN_SPONSOR) != 0
233				      || (mask & CEN_REVISION) != 0)
234				     ? (1 + ((mask & CEN_SPONSOR) != 0
235					     ? strlen (sponsor) : 0)
236					+ ((mask & CEN_REVISION) != 0
237					   ? strlen (revision) + 1 : 0)) : 0)
238				  + 1 + strlen (filename) + 1);
239
240  if (abs_filename == NULL)
241    return NULL;
242
243  /* Construct file name.  */
244  cp = abs_filename;
245  if (dirlist_len > 0)
246    {
247      memcpy (cp, dirlist, dirlist_len);
248      __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
249      cp += dirlist_len;
250      cp[-1] = '/';
251    }
252
253  cp = stpcpy (cp, language);
254
255  if ((mask & TERRITORY) != 0)
256    {
257      *cp++ = '_';
258      cp = stpcpy (cp, territory);
259    }
260  if ((mask & XPG_CODESET) != 0)
261    {
262      *cp++ = '.';
263      cp = stpcpy (cp, codeset);
264    }
265  if ((mask & XPG_NORM_CODESET) != 0)
266    {
267      *cp++ = '.';
268      cp = stpcpy (cp, normalized_codeset);
269    }
270  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
271    {
272      /* This component can be part of both syntaces but has different
273	 leading characters.  For CEN we use `+', else `@'.  */
274      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
275      cp = stpcpy (cp, modifier);
276    }
277  if ((mask & CEN_SPECIAL) != 0)
278    {
279      *cp++ = '+';
280      cp = stpcpy (cp, special);
281    }
282  if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
283    {
284      *cp++ = ',';
285      if ((mask & CEN_SPONSOR) != 0)
286	cp = stpcpy (cp, sponsor);
287      if ((mask & CEN_REVISION) != 0)
288	{
289	  *cp++ = '_';
290	  cp = stpcpy (cp, revision);
291	}
292    }
293
294  *cp++ = '/';
295  stpcpy (cp, filename);
296
297  /* Look in list of already loaded domains whether it is already
298     available.  */
299  lastp = l10nfile_list;
300  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
301    if (retval->filename != NULL)
302      {
303	int compare = strcmp (retval->filename, abs_filename);
304	if (compare == 0)
305	  /* We found it!  */
306	  break;
307	if (compare < 0)
308	  {
309	    /* It's not in the list.  */
310	    retval = NULL;
311	    break;
312	  }
313
314	lastp = &retval->next;
315      }
316
317  if (retval != NULL || do_allocate == 0)
318    {
319      free (abs_filename);
320      return retval;
321    }
322
323  dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
324
325  /* Allocate a new loaded_l10nfile.  */
326  retval =
327    (struct loaded_l10nfile *)
328    malloc (sizeof (*retval)
329	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
330	       * sizeof (struct loaded_l10nfile *)));
331  if (retval == NULL)
332    return NULL;
333
334  retval->filename = abs_filename;
335
336  /* We set retval->data to NULL here; it is filled in later.
337     Setting retval->decided to 1 here means that retval does not
338     correspond to a real file (dirlist_count > 1) or is not worth
339     looking up (if an unnormalized codeset was specified).  */
340  retval->decided = (dirlist_count > 1
341		     || ((mask & XPG_CODESET) != 0
342			 && (mask & XPG_NORM_CODESET) != 0));
343  retval->data = NULL;
344
345  retval->next = *lastp;
346  *lastp = retval;
347
348  entries = 0;
349  /* Recurse to fill the inheritance list of RETVAL.
350     If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
351     entry does not correspond to a real file; retval->filename contains
352     colons.  In this case we loop across all elements of DIRLIST and
353     across all bit patterns dominated by MASK.
354     If the DIRLIST is a single directory or entirely redundant (i.e.
355     DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
356     MASK, excluding MASK itself.
357     In either case, we loop down from MASK to 0.  This has the effect
358     that the extra bits in the locale name are dropped in this order:
359     first the modifier, then the territory, then the codeset, then the
360     normalized_codeset.  */
361  for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
362    if ((cnt & ~mask) == 0
363	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
364	&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
365      {
366	if (dirlist_count > 1)
367	  {
368	    /* Iterate over all elements of the DIRLIST.  */
369	    char *dir = NULL;
370
371	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
372		   != NULL)
373	      retval->successor[entries++]
374		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
375				      cnt, language, territory, codeset,
376				      normalized_codeset, modifier, special,
377				      sponsor, revision, filename, 1);
378	  }
379	else
380	  retval->successor[entries++]
381	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
382				  cnt, language, territory, codeset,
383				  normalized_codeset, modifier, special,
384				  sponsor, revision, filename, 1);
385      }
386  retval->successor[entries] = NULL;
387
388  return retval;
389}
390
391/* Normalize codeset name.  There is no standard for the codeset
392   names.  Normalization allows the user to use any of the common
393   names.  The return value is dynamically allocated and has to be
394   freed by the caller.  */
395const char *
396_nl_normalize_codeset (codeset, name_len)
397     const char *codeset;
398     size_t name_len;
399{
400  int len = 0;
401  int only_digit = 1;
402  char *retval;
403  char *wp;
404  size_t cnt;
405
406  for (cnt = 0; cnt < name_len; ++cnt)
407    if (isalnum ((unsigned char) codeset[cnt]))
408      {
409	++len;
410
411	if (isalpha ((unsigned char) codeset[cnt]))
412	  only_digit = 0;
413      }
414
415  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
416
417  if (retval != NULL)
418    {
419      if (only_digit)
420	wp = stpcpy (retval, "iso");
421      else
422	wp = retval;
423
424      for (cnt = 0; cnt < name_len; ++cnt)
425	if (isalpha ((unsigned char) codeset[cnt]))
426	  *wp++ = tolower ((unsigned char) codeset[cnt]);
427	else if (isdigit ((unsigned char) codeset[cnt]))
428	  *wp++ = codeset[cnt];
429
430      *wp = '\0';
431    }
432
433  return (const char *) retval;
434}
435
436
437/* @@ begin of epilog @@ */
438
439/* We don't want libintl.a to depend on any other library.  So we
440   avoid the non-standard function stpcpy.  In GNU C Library this
441   function is available, though.  Also allow the symbol HAVE_STPCPY
442   to be defined.  */
443#if !_LIBC && !HAVE_STPCPY
444static char *
445stpcpy (dest, src)
446     char *dest;
447     const char *src;
448{
449  while ((*dest++ = *src++) != '\0')
450    /* Do nothing. */ ;
451  return dest - 1;
452}
453#endif
454