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