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