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