Annotation of embedaddon/lrzsz/intl/dcgettext.c, revision 1.1
1.1 ! misho 1: /* Implementation of the dcgettext(3) function
! 2: Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
! 3:
! 4: This program is free software; you can redistribute it and/or modify
! 5: it under the terms of the GNU General Public License as published by
! 6: 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
! 12: GNU General Public License for more details.
! 13:
! 14: You should have received a copy of the GNU General Public License
! 15: along with this program; if not, write to the Free Software Foundation,
! 16: Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
! 17:
! 18: #ifdef HAVE_CONFIG_H
! 19: # include <config.h>
! 20: #endif
! 21:
! 22: #include <sys/types.h>
! 23:
! 24: #ifdef __GNUC__
! 25: # define alloca __builtin_alloca
! 26: # define HAVE_ALLOCA 1
! 27: #else
! 28: # if defined HAVE_ALLOCA_H || defined _LIBC
! 29: # include <alloca.h>
! 30: # else
! 31: # ifdef _AIX
! 32: #pragma alloca
! 33: # else
! 34: # ifndef alloca
! 35: char *alloca ();
! 36: # endif
! 37: # endif
! 38: # endif
! 39: #endif
! 40:
! 41: #include <errno.h>
! 42: #ifndef errno
! 43: extern int errno;
! 44: #endif
! 45: #ifndef __set_errno
! 46: # define __set_errno(val) errno = (val)
! 47: #endif
! 48:
! 49: #if defined STDC_HEADERS || defined _LIBC
! 50: # include <stdlib.h>
! 51: #else
! 52: char *getenv ();
! 53: # ifdef HAVE_MALLOC_H
! 54: # include <malloc.h>
! 55: # else
! 56: void free ();
! 57: # endif
! 58: #endif
! 59:
! 60: #if defined HAVE_STRING_H || defined _LIBC
! 61: # ifndef _GNU_SOURCE
! 62: # define _GNU_SOURCE 1
! 63: # endif
! 64: # include <string.h>
! 65: #else
! 66: # include <strings.h>
! 67: #endif
! 68: #if !HAVE_STRCHR && !defined _LIBC
! 69: # ifndef strchr
! 70: # define strchr index
! 71: # endif
! 72: #endif
! 73:
! 74: #if defined HAVE_UNISTD_H || defined _LIBC
! 75: # include <unistd.h>
! 76: #endif
! 77:
! 78: #include "gettext.h"
! 79: #include "gettextP.h"
! 80: #ifdef _LIBC
! 81: # include <libintl.h>
! 82: #else
! 83: # include "libgettext.h"
! 84: #endif
! 85: #include "hash-string.h"
! 86:
! 87: /* @@ end of prolog @@ */
! 88:
! 89: #ifdef _LIBC
! 90: /* Rename the non ANSI C functions. This is required by the standard
! 91: because some ANSI C functions will require linking with this object
! 92: file and the name space must not be polluted. */
! 93: # define getcwd __getcwd
! 94: # define stpcpy __stpcpy
! 95: #else
! 96: # if !defined HAVE_GETCWD
! 97: char *getwd ();
! 98: # define getcwd(buf, max) getwd (buf)
! 99: # else
! 100: char *getcwd ();
! 101: # endif
! 102: # ifndef HAVE_STPCPY
! 103: static char *stpcpy PARAMS ((char *dest, const char *src));
! 104: # endif
! 105: #endif
! 106:
! 107: /* Amount to increase buffer size by in each try. */
! 108: #define PATH_INCR 32
! 109:
! 110: /* The following is from pathmax.h. */
! 111: /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
! 112: PATH_MAX but might cause redefinition warnings when sys/param.h is
! 113: later included (as on MORE/BSD 4.3). */
! 114: #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
! 115: # include <limits.h>
! 116: #endif
! 117:
! 118: #ifndef _POSIX_PATH_MAX
! 119: # define _POSIX_PATH_MAX 255
! 120: #endif
! 121:
! 122: #if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
! 123: # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
! 124: #endif
! 125:
! 126: /* Don't include sys/param.h if it already has been. */
! 127: #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
! 128: # include <sys/param.h>
! 129: #endif
! 130:
! 131: #if !defined(PATH_MAX) && defined(MAXPATHLEN)
! 132: # define PATH_MAX MAXPATHLEN
! 133: #endif
! 134:
! 135: #ifndef PATH_MAX
! 136: # define PATH_MAX _POSIX_PATH_MAX
! 137: #endif
! 138:
! 139: /* XPG3 defines the result of `setlocale (category, NULL)' as:
! 140: ``Directs `setlocale()' to query `category' and return the current
! 141: setting of `local'.''
! 142: However it does not specify the exact format. And even worse: POSIX
! 143: defines this not at all. So we can use this feature only on selected
! 144: system (e.g. those using GNU C Library). */
! 145: #ifdef _LIBC
! 146: # define HAVE_LOCALE_NULL
! 147: #endif
! 148:
! 149: /* Name of the default domain used for gettext(3) prior any call to
! 150: textdomain(3). The default value for this is "messages". */
! 151: const char _nl_default_default_domain[] = "messages";
! 152:
! 153: /* Value used as the default domain for gettext(3). */
! 154: const char *_nl_current_default_domain = _nl_default_default_domain;
! 155:
! 156: /* Contains the default location of the message catalogs. */
! 157: const char _nl_default_dirname[] = GNULOCALEDIR;
! 158:
! 159: /* List with bindings of specific domains created by bindtextdomain()
! 160: calls. */
! 161: struct binding *_nl_domain_bindings;
! 162:
! 163: /* Prototypes for local functions. */
! 164: static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file,
! 165: const char *msgid));
! 166: static const char *category_to_name PARAMS ((int category));
! 167: static const char *guess_category_value PARAMS ((int category,
! 168: const char *categoryname));
! 169:
! 170:
! 171: /* For those loosing systems which don't have `alloca' we have to add
! 172: some additional code emulating it. */
! 173: #ifdef HAVE_ALLOCA
! 174: /* Nothing has to be done. */
! 175: # define ADD_BLOCK(list, address) /* nothing */
! 176: # define FREE_BLOCKS(list) /* nothing */
! 177: #else
! 178: struct block_list
! 179: {
! 180: void *address;
! 181: struct block_list *next;
! 182: };
! 183: # define ADD_BLOCK(list, addr) \
! 184: do { \
! 185: struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
! 186: /* If we cannot get a free block we cannot add the new element to \
! 187: the list. */ \
! 188: if (newp != NULL) { \
! 189: newp->address = (addr); \
! 190: newp->next = (list); \
! 191: (list) = newp; \
! 192: } \
! 193: } while (0)
! 194: # define FREE_BLOCKS(list) \
! 195: do { \
! 196: while (list != NULL) { \
! 197: struct block_list *old = list; \
! 198: list = list->next; \
! 199: free (old); \
! 200: } \
! 201: } while (0)
! 202: # undef alloca
! 203: # define alloca(size) (malloc (size))
! 204: #endif /* have alloca */
! 205:
! 206:
! 207: /* Names for the libintl functions are a problem. They must not clash
! 208: with existing names and they should follow ANSI C. But this source
! 209: code is also used in GNU C Library where the names have a __
! 210: prefix. So we have to make a difference here. */
! 211: #ifdef _LIBC
! 212: # define DCGETTEXT __dcgettext
! 213: #else
! 214: # define DCGETTEXT dcgettext__
! 215: #endif
! 216:
! 217: /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
! 218: locale. */
! 219: char *
! 220: DCGETTEXT (domainname, msgid, category)
! 221: const char *domainname;
! 222: const char *msgid;
! 223: int category;
! 224: {
! 225: #ifndef HAVE_ALLOCA
! 226: struct block_list *block_list = NULL;
! 227: #endif
! 228: struct loaded_l10nfile *domain;
! 229: struct binding *binding;
! 230: const char *categoryname;
! 231: const char *categoryvalue;
! 232: char *dirname, *xdomainname;
! 233: char *single_locale;
! 234: char *retval;
! 235: int saved_errno = errno;
! 236:
! 237: /* If no real MSGID is given return NULL. */
! 238: if (msgid == NULL)
! 239: return NULL;
! 240:
! 241: /* If DOMAINNAME is NULL, we are interested in the default domain. If
! 242: CATEGORY is not LC_MESSAGES this might not make much sense but the
! 243: defintion left this undefined. */
! 244: if (domainname == NULL)
! 245: domainname = _nl_current_default_domain;
! 246:
! 247: /* First find matching binding. */
! 248: for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
! 249: {
! 250: int compare = strcmp (domainname, binding->domainname);
! 251: if (compare == 0)
! 252: /* We found it! */
! 253: break;
! 254: if (compare < 0)
! 255: {
! 256: /* It is not in the list. */
! 257: binding = NULL;
! 258: break;
! 259: }
! 260: }
! 261:
! 262: if (binding == NULL)
! 263: dirname = (char *) _nl_default_dirname;
! 264: else if (binding->dirname[0] == '/')
! 265: dirname = binding->dirname;
! 266: else
! 267: {
! 268: /* We have a relative path. Make it absolute now. */
! 269: size_t dirname_len = strlen (binding->dirname) + 1;
! 270: size_t path_max;
! 271: char *ret;
! 272:
! 273: path_max = (unsigned) PATH_MAX;
! 274: path_max += 2; /* The getcwd docs say to do this. */
! 275:
! 276: dirname = (char *) alloca (path_max + dirname_len);
! 277: ADD_BLOCK (block_list, dirname);
! 278:
! 279: __set_errno (0);
! 280: while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
! 281: {
! 282: path_max += PATH_INCR;
! 283: dirname = (char *) alloca (path_max + dirname_len);
! 284: ADD_BLOCK (block_list, dirname);
! 285: __set_errno (0);
! 286: }
! 287:
! 288: if (ret == NULL)
! 289: {
! 290: /* We cannot get the current working directory. Don't signal an
! 291: error but simply return the default string. */
! 292: FREE_BLOCKS (block_list);
! 293: __set_errno (saved_errno);
! 294: return (char *) msgid;
! 295: }
! 296:
! 297: stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
! 298: }
! 299:
! 300: /* Now determine the symbolic name of CATEGORY and its value. */
! 301: categoryname = category_to_name (category);
! 302: categoryvalue = guess_category_value (category, categoryname);
! 303:
! 304: xdomainname = (char *) alloca (strlen (categoryname)
! 305: + strlen (domainname) + 5);
! 306: ADD_BLOCK (block_list, xdomainname);
! 307:
! 308: stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
! 309: domainname),
! 310: ".mo");
! 311:
! 312: /* Creating working area. */
! 313: single_locale = (char *) alloca (strlen (categoryvalue) + 1);
! 314: ADD_BLOCK (block_list, single_locale);
! 315:
! 316:
! 317: /* Search for the given string. This is a loop because we perhaps
! 318: got an ordered list of languages to consider for th translation. */
! 319: while (1)
! 320: {
! 321: /* Make CATEGORYVALUE point to the next element of the list. */
! 322: while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
! 323: ++categoryvalue;
! 324: if (categoryvalue[0] == '\0')
! 325: {
! 326: /* The whole contents of CATEGORYVALUE has been searched but
! 327: no valid entry has been found. We solve this situation
! 328: by implicitly appending a "C" entry, i.e. no translation
! 329: will take place. */
! 330: single_locale[0] = 'C';
! 331: single_locale[1] = '\0';
! 332: }
! 333: else
! 334: {
! 335: char *cp = single_locale;
! 336: while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
! 337: *cp++ = *categoryvalue++;
! 338: *cp = '\0';
! 339: }
! 340:
! 341: /* If the current locale value is C (or POSIX) we don't load a
! 342: domain. Return the MSGID. */
! 343: if (strcmp (single_locale, "C") == 0
! 344: || strcmp (single_locale, "POSIX") == 0)
! 345: {
! 346: FREE_BLOCKS (block_list);
! 347: __set_errno (saved_errno);
! 348: return (char *) msgid;
! 349: }
! 350:
! 351:
! 352: /* Find structure describing the message catalog matching the
! 353: DOMAINNAME and CATEGORY. */
! 354: domain = _nl_find_domain (dirname, single_locale, xdomainname);
! 355:
! 356: if (domain != NULL)
! 357: {
! 358: retval = find_msg (domain, msgid);
! 359:
! 360: if (retval == NULL)
! 361: {
! 362: int cnt;
! 363:
! 364: for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
! 365: {
! 366: retval = find_msg (domain->successor[cnt], msgid);
! 367:
! 368: if (retval != NULL)
! 369: break;
! 370: }
! 371: }
! 372:
! 373: if (retval != NULL)
! 374: {
! 375: FREE_BLOCKS (block_list);
! 376: __set_errno (saved_errno);
! 377: return retval;
! 378: }
! 379: }
! 380: }
! 381: /* NOTREACHED */
! 382: }
! 383:
! 384: #ifdef _LIBC
! 385: /* Alias for function name in GNU C Library. */
! 386: weak_alias (__dcgettext, dcgettext);
! 387: #endif
! 388:
! 389:
! 390: static char *
! 391: find_msg (domain_file, msgid)
! 392: struct loaded_l10nfile *domain_file;
! 393: const char *msgid;
! 394: {
! 395: size_t top, act, bottom;
! 396: struct loaded_domain *domain;
! 397:
! 398: if (domain_file->decided == 0)
! 399: _nl_load_domain (domain_file);
! 400:
! 401: if (domain_file->data == NULL)
! 402: return NULL;
! 403:
! 404: domain = (struct loaded_domain *) domain_file->data;
! 405:
! 406: /* Locate the MSGID and its translation. */
! 407: if (domain->hash_size > 2 && domain->hash_tab != NULL)
! 408: {
! 409: /* Use the hashing table. */
! 410: nls_uint32 len = strlen (msgid);
! 411: nls_uint32 hash_val = hash_string (msgid);
! 412: nls_uint32 idx = hash_val % domain->hash_size;
! 413: nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
! 414: nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
! 415:
! 416: if (nstr == 0)
! 417: /* Hash table entry is empty. */
! 418: return NULL;
! 419:
! 420: if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
! 421: && strcmp (msgid,
! 422: domain->data + W (domain->must_swap,
! 423: domain->orig_tab[nstr - 1].offset)) == 0)
! 424: return (char *) domain->data + W (domain->must_swap,
! 425: domain->trans_tab[nstr - 1].offset);
! 426:
! 427: while (1)
! 428: {
! 429: if (idx >= domain->hash_size - incr)
! 430: idx -= domain->hash_size - incr;
! 431: else
! 432: idx += incr;
! 433:
! 434: nstr = W (domain->must_swap, domain->hash_tab[idx]);
! 435: if (nstr == 0)
! 436: /* Hash table entry is empty. */
! 437: return NULL;
! 438:
! 439: if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
! 440: && strcmp (msgid,
! 441: domain->data + W (domain->must_swap,
! 442: domain->orig_tab[nstr - 1].offset))
! 443: == 0)
! 444: return (char *) domain->data
! 445: + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
! 446: }
! 447: /* NOTREACHED */
! 448: }
! 449:
! 450: /* Now we try the default method: binary search in the sorted
! 451: array of messages. */
! 452: bottom = 0;
! 453: top = domain->nstrings;
! 454: while (bottom < top)
! 455: {
! 456: int cmp_val;
! 457:
! 458: act = (bottom + top) / 2;
! 459: cmp_val = strcmp (msgid, domain->data
! 460: + W (domain->must_swap,
! 461: domain->orig_tab[act].offset));
! 462: if (cmp_val < 0)
! 463: top = act;
! 464: else if (cmp_val > 0)
! 465: bottom = act + 1;
! 466: else
! 467: break;
! 468: }
! 469:
! 470: /* If an translation is found return this. */
! 471: return bottom >= top ? NULL : (char *) domain->data
! 472: + W (domain->must_swap,
! 473: domain->trans_tab[act].offset);
! 474: }
! 475:
! 476:
! 477: /* Return string representation of locale CATEGORY. */
! 478: static const char *
! 479: category_to_name (category)
! 480: int category;
! 481: {
! 482: const char *retval;
! 483:
! 484: switch (category)
! 485: {
! 486: #ifdef LC_COLLATE
! 487: case LC_COLLATE:
! 488: retval = "LC_COLLATE";
! 489: break;
! 490: #endif
! 491: #ifdef LC_CTYPE
! 492: case LC_CTYPE:
! 493: retval = "LC_CTYPE";
! 494: break;
! 495: #endif
! 496: #ifdef LC_MONETARY
! 497: case LC_MONETARY:
! 498: retval = "LC_MONETARY";
! 499: break;
! 500: #endif
! 501: #ifdef LC_NUMERIC
! 502: case LC_NUMERIC:
! 503: retval = "LC_NUMERIC";
! 504: break;
! 505: #endif
! 506: #ifdef LC_TIME
! 507: case LC_TIME:
! 508: retval = "LC_TIME";
! 509: break;
! 510: #endif
! 511: #ifdef LC_MESSAGES
! 512: case LC_MESSAGES:
! 513: retval = "LC_MESSAGES";
! 514: break;
! 515: #endif
! 516: #ifdef LC_RESPONSE
! 517: case LC_RESPONSE:
! 518: retval = "LC_RESPONSE";
! 519: break;
! 520: #endif
! 521: #ifdef LC_ALL
! 522: case LC_ALL:
! 523: /* This might not make sense but is perhaps better than any other
! 524: value. */
! 525: retval = "LC_ALL";
! 526: break;
! 527: #endif
! 528: default:
! 529: /* If you have a better idea for a default value let me know. */
! 530: retval = "LC_XXX";
! 531: }
! 532:
! 533: return retval;
! 534: }
! 535:
! 536: /* Guess value of current locale from value of the environment variables. */
! 537: static const char *
! 538: guess_category_value (category, categoryname)
! 539: int category;
! 540: const char *categoryname;
! 541: {
! 542: const char *retval;
! 543:
! 544: /* The highest priority value is the `LANGUAGE' environment
! 545: variable. This is a GNU extension. */
! 546: retval = getenv ("LANGUAGE");
! 547: if (retval != NULL && retval[0] != '\0')
! 548: return retval;
! 549:
! 550: /* `LANGUAGE' is not set. So we have to proceed with the POSIX
! 551: methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
! 552: systems this can be done by the `setlocale' function itself. */
! 553: #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
! 554: return setlocale (category, NULL);
! 555: #else
! 556: /* Setting of LC_ALL overwrites all other. */
! 557: retval = getenv ("LC_ALL");
! 558: if (retval != NULL && retval[0] != '\0')
! 559: return retval;
! 560:
! 561: /* Next comes the name of the desired category. */
! 562: retval = getenv (categoryname);
! 563: if (retval != NULL && retval[0] != '\0')
! 564: return retval;
! 565:
! 566: /* Last possibility is the LANG environment variable. */
! 567: retval = getenv ("LANG");
! 568: if (retval != NULL && retval[0] != '\0')
! 569: return retval;
! 570:
! 571: /* We use C as the default domain. POSIX says this is implementation
! 572: defined. */
! 573: return "C";
! 574: #endif
! 575: }
! 576:
! 577: /* @@ begin of epilog @@ */
! 578:
! 579: /* We don't want libintl.a to depend on any other library. So we
! 580: avoid the non-standard function stpcpy. In GNU C Library this
! 581: function is available, though. Also allow the symbol HAVE_STPCPY
! 582: to be defined. */
! 583: #if !_LIBC && !HAVE_STPCPY
! 584: static char *
! 585: stpcpy (dest, src)
! 586: char *dest;
! 587: const char *src;
! 588: {
! 589: while ((*dest++ = *src++) != '\0')
! 590: /* Do nothing. */ ;
! 591: return dest - 1;
! 592: }
! 593: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>