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