libintl.cpp
00001 /* libintl.cpp -- gettext related functions from glibc-2.0.5 00002 Copyright (C) 1995 Software Foundation, Inc. 00003 00004 This file is part of the KDE libraries, but it's derived work out 00005 of glibc. The master sources can be found in 00006 00007 bindtextdom.c 00008 dcgettext.c 00009 dgettext.c 00010 explodename.c 00011 finddomain.c 00012 gettext.c 00013 gettext.h 00014 gettextP.h 00015 hash-string.h 00016 l10nflist.c 00017 libintl.h 00018 loadinfo.h 00019 loadmsgcat.c 00020 localealias.c 00021 textdomain.c 00022 00023 which are part of glibc. The license is the same as in GLIBC, which 00024 is the GNU Library General Public License. See COPYING.LIB for more 00025 details. 00026 00027 */ 00028 00029 /* gettext.c -- implementation of gettext(3) function 00030 Copyright (C) 1995 Software Foundation, Inc. 00031 00032 This file is part of the GNU C Library. Its master source is NOT part of 00033 the C library, however. The master source lives in /gd/gnu/lib. 00034 00035 The GNU C Library is free software; you can redistribute it and/or 00036 modify it under the terms of the GNU Library General Public License as 00037 published by the Free Software Foundation; either version 2 of the 00038 License, or (at your option) any later version. 00039 00040 The GNU C Library is distributed in the hope that it will be useful, 00041 but WITHOUT ANY WARRANTY; without even the implied warranty of 00042 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00043 Library General Public License for more details. 00044 00045 You should have received a copy of the GNU Library General Public 00046 License along with the GNU C Library; see the file COPYING.LIB. If 00047 not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00048 Boston, MA 02110-1301, USA. */ 00049 00050 #include "tdelibs_export.h" 00051 #include "kde_file.h" 00052 #include <config.h> 00053 00054 #include <tqglobal.h> 00055 00056 #include <stdlib.h> 00057 00058 #if defined HAVE_STRING_H 00059 # include <string.h> 00060 #else 00061 # include <strings.h> 00062 #endif 00063 00064 #include <sys/types.h> 00065 #include <fcntl.h> 00066 #include <sys/stat.h> 00067 00068 #if defined HAVE_UNISTD_H 00069 # include <unistd.h> 00070 #endif 00071 00072 #if (defined HAVE_MMAP && defined HAVE_MUNMAP) 00073 # include <sys/mman.h> 00074 #endif 00075 00076 #ifndef W 00077 # define W(flag, data) ((flag) ? SWAP (data) : (data)) 00078 #endif 00079 00080 typedef TQ_UINT32 nls_uint32; 00081 00082 struct loaded_domain 00083 { 00084 const char *data; 00085 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00086 int use_mmap; 00087 size_t mmap_size; 00088 #endif 00089 int must_swap; 00090 nls_uint32 nstrings; 00091 struct string_desc *orig_tab; 00092 struct string_desc *trans_tab; 00093 nls_uint32 hash_size; 00094 nls_uint32 *hash_tab; 00095 }; 00096 00097 struct kde_loaded_l10nfile 00098 { 00099 const char *filename; 00100 int decided; 00101 00102 const void *data; 00103 00104 kde_loaded_l10nfile() : filename(0), decided(0), data(0) {} 00105 }; 00106 00107 void k_nl_load_domain(struct kde_loaded_l10nfile *__domain); 00108 00109 static inline nls_uint32 00110 SWAP (nls_uint32 i) 00111 { 00112 return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); 00113 } 00114 00115 /* @@ end of prolog @@ */ 00116 00117 /* The magic number of the GNU message catalog format. */ 00118 #define _MAGIC 0x950412de 00119 #define _MAGIC_SWAPPED 0xde120495 00120 00121 /* Revision number of the currently used .mo (binary) file format. */ 00122 #define MO_REVISION_NUMBER 0 00123 00124 00125 /* Defines the so called `hashpjw' function by P.J. Weinberger 00126 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 00127 1986, 1987 Bell Telephone Laboratories, Inc.] */ 00128 static inline unsigned long hash_string (const char *__str_param); 00129 00130 /* @@ end of prolog @@ */ 00131 00132 /* Header for binary .mo file format. */ 00133 struct mo_file_header 00134 { 00135 /* The magic number. */ 00136 nls_uint32 magic; 00137 /* The revision number of the file format. */ 00138 nls_uint32 revision; 00139 /* The number of strings pairs. */ 00140 nls_uint32 nstrings; 00141 /* Offset of table with start offsets of original strings. */ 00142 nls_uint32 orig_tab_offset; 00143 /* Offset of table with start offsets of translation strings. */ 00144 nls_uint32 trans_tab_offset; 00145 /* Size of hashing table. */ 00146 nls_uint32 hash_tab_size; 00147 /* Offset of first hashing entry. */ 00148 nls_uint32 hash_tab_offset; 00149 }; 00150 00151 struct string_desc 00152 { 00153 /* Length of addressed string. */ 00154 nls_uint32 length; 00155 /* Offset of string in file. */ 00156 nls_uint32 offset; 00157 }; 00158 00159 /* Prototypes for local functions. */ 00160 char *k_nl_find_msg (struct kde_loaded_l10nfile *domain_file, 00161 const char *msgid); 00162 00163 char * 00164 k_nl_find_msg (struct kde_loaded_l10nfile *domain_file, const char *msgid) 00165 { 00166 size_t top, act, bottom; 00167 struct loaded_domain *domain; 00168 00169 if (domain_file->decided == 0) 00170 k_nl_load_domain (domain_file); 00171 00172 if (domain_file->data == NULL) 00173 return NULL; 00174 00175 domain = (struct loaded_domain *) domain_file->data; 00176 00177 /* Locate the MSGID and its translation. */ 00178 if (domain->hash_size > 2 && domain->hash_tab != NULL) 00179 { 00180 /* Use the hashing table. */ 00181 nls_uint32 len = strlen (msgid); 00182 nls_uint32 hash_val = hash_string (msgid); 00183 nls_uint32 idx = hash_val % domain->hash_size; 00184 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); 00185 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00186 00187 if (nstr == 0) 00188 /* Hash table entry is empty. */ 00189 return NULL; 00190 00191 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00192 && strcmp (msgid, 00193 domain->data + W (domain->must_swap, 00194 domain->orig_tab[nstr - 1].offset)) == 0) 00195 return (char *) domain->data + W (domain->must_swap, 00196 domain->trans_tab[nstr - 1].offset); 00197 00198 while (1) 00199 { 00200 if (idx >= domain->hash_size - incr) 00201 idx -= domain->hash_size - incr; 00202 else 00203 idx += incr; 00204 00205 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00206 if (nstr == 0) 00207 /* Hash table entry is empty. */ 00208 return NULL; 00209 00210 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00211 && strcmp (msgid, 00212 domain->data + W (domain->must_swap, 00213 domain->orig_tab[nstr - 1].offset)) 00214 == 0) 00215 return (char *) domain->data 00216 + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); 00217 } 00218 /* NOTREACHED */ 00219 } 00220 00221 /* Now we try the default method: binary search in the sorted 00222 array of messages. */ 00223 bottom = 0; 00224 top = domain->nstrings; 00225 act = top; 00226 while (bottom < top) 00227 { 00228 int cmp_val; 00229 00230 act = (bottom + top) / 2; 00231 cmp_val = strcmp (msgid, domain->data 00232 + W (domain->must_swap, 00233 domain->orig_tab[act].offset)); 00234 if (cmp_val < 0) 00235 top = act; 00236 else if (cmp_val > 0) 00237 bottom = act + 1; 00238 else 00239 break; 00240 } 00241 00242 /* If an translation is found return this. */ 00243 return bottom >= top ? NULL : (char *) domain->data 00244 + W (domain->must_swap, 00245 domain->trans_tab[act].offset); 00246 } 00247 00248 /* @@ begin of epilog @@ */ 00249 /* We assume to have `unsigned long int' value with at least 32 bits. */ 00250 #define HASHWORDBITS 32 00251 00252 static inline unsigned long 00253 hash_string (const char *str_param) 00254 { 00255 unsigned long int hval, g; 00256 const char *str = str_param; 00257 00258 /* Compute the hash value for the given string. */ 00259 hval = 0; 00260 while (*str != '\0') 00261 { 00262 hval <<= 4; 00263 hval += (unsigned long) *str++; 00264 g = hval & ((unsigned long) 0xf << (HASHWORDBITS - 4)); 00265 if (g != 0) 00266 { 00267 hval ^= g >> (HASHWORDBITS - 8); 00268 hval ^= g; 00269 } 00270 } 00271 return hval; 00272 } 00273 00274 /* Load the message catalogs specified by FILENAME. If it is no valid 00275 message catalog do nothing. */ 00276 void 00277 k_nl_load_domain (struct kde_loaded_l10nfile *domain_file) 00278 { 00279 int fd; 00280 struct stat st; 00281 struct mo_file_header *data = (struct mo_file_header *) -1; 00282 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00283 int use_mmap = 0; 00284 #endif 00285 struct loaded_domain *domain; 00286 00287 domain_file->decided = 1; 00288 domain_file->data = NULL; 00289 00290 /* If the record does not represent a valid locale the FILENAME 00291 might be NULL. This can happen when according to the given 00292 specification the locale file name is different for XPG and CEN 00293 syntax. */ 00294 if (domain_file->filename == NULL) 00295 return; 00296 00297 /* Try to open the addressed file. */ 00298 fd = KDE_open (domain_file->filename, O_RDONLY); 00299 if (fd == -1) 00300 return; 00301 00302 /* We must know about the size of the file. */ 00303 if (fstat (fd, &st) != 0 00304 || st.st_size < (off_t) sizeof (struct mo_file_header)) 00305 { 00306 /* Something went wrong. */ 00307 close (fd); 00308 return; 00309 } 00310 00311 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00312 /* Now we are ready to load the file. If mmap() is available we try 00313 this first. If not available or it failed we try to load it. */ 00314 data = (struct mo_file_header *) mmap (NULL, st.st_size, PROT_READ, 00315 MAP_PRIVATE, fd, 0); 00316 00317 if (data != (struct mo_file_header *) -1) 00318 { 00319 /* mmap() call was successful. */ 00320 close (fd); 00321 use_mmap = 1; 00322 } 00323 #endif 00324 00325 /* If the data is not yet available (i.e. mmap'ed) we try to load 00326 it manually. */ 00327 if (data == (struct mo_file_header *) -1) 00328 { 00329 off_t to_read; 00330 char *read_ptr; 00331 00332 data = (struct mo_file_header *) malloc (st.st_size); 00333 if (data == NULL) 00334 return; 00335 00336 to_read = st.st_size; 00337 read_ptr = (char *) data; 00338 do 00339 { 00340 long int nb = (long int) read (fd, read_ptr, to_read); 00341 if (nb == -1) 00342 { 00343 close (fd); 00344 return; 00345 } 00346 00347 read_ptr += nb; 00348 to_read -= nb; 00349 } 00350 while (to_read > 0); 00351 00352 close (fd); 00353 } 00354 00355 /* Using the magic number we can test whether it really is a message 00356 catalog file. */ 00357 if (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED) 00358 { 00359 /* The magic number is wrong: not a message catalog file. */ 00360 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00361 if (use_mmap) 00362 munmap ((char *) data, st.st_size); 00363 else 00364 #endif 00365 free (data); 00366 return; 00367 } 00368 00369 domain_file->data 00370 = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); 00371 if (domain_file->data == NULL) 00372 return; 00373 00374 domain = (struct loaded_domain *) domain_file->data; 00375 domain->data = (char *) data; 00376 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00377 domain->use_mmap = use_mmap; 00378 domain->mmap_size = st.st_size; 00379 #endif 00380 domain->must_swap = data->magic != _MAGIC; 00381 00382 /* Fill in the information about the available tables. */ 00383 switch (W (domain->must_swap, data->revision)) 00384 { 00385 case 0: 00386 domain->nstrings = W (domain->must_swap, data->nstrings); 00387 domain->orig_tab = (struct string_desc *) 00388 ((char *) data + W (domain->must_swap, data->orig_tab_offset)); 00389 domain->trans_tab = (struct string_desc *) 00390 ((char *) data + W (domain->must_swap, data->trans_tab_offset)); 00391 domain->hash_size = W (domain->must_swap, data->hash_tab_size); 00392 domain->hash_tab = (nls_uint32 *) 00393 ((char *) data + W (domain->must_swap, data->hash_tab_offset)); 00394 break; 00395 default: 00396 /* This is an illegal revision. */ 00397 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00398 if (use_mmap) 00399 munmap ((char *) data, st.st_size); 00400 else 00401 #endif 00402 free (data); 00403 free (domain); 00404 domain_file->data = NULL; 00405 return; 00406 } 00407 } 00408 00409 void 00410 k_nl_unload_domain (struct loaded_domain *domain) 00411 { 00412 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) 00413 if (domain->use_mmap) 00414 munmap ((caddr_t) domain->data, domain->mmap_size); 00415 else 00416 # endif 00417 free ((void *) domain->data); 00418 00419 free (domain); 00420 }