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