1 /* $NetBSD: gettext.c,v 1.20 2004/09/23 21:35:27 tshiozak Exp $ */ 2 3 /*- 4 * Copyright (c) 2000, 2001 Citrus Project, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $Citrus: xpg4dl/FreeBSD/lib/libintl/gettext.c,v 1.31 2001/09/27 15:18:45 yamt Exp $ 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: gettext.c,v 1.20 2004/09/23 21:35:27 tshiozak Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/stat.h> 36 #include <sys/mman.h> 37 #include <sys/uio.h> 38 39 #include <assert.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <string.h> 45 #if 0 46 #include <util.h> 47 #endif 48 #include <libintl.h> 49 #include <locale.h> 50 #include "libintl_local.h" 51 #include "pathnames.h" 52 53 static const char *lookup_category __P((int)); 54 static const char *split_locale __P((const char *)); 55 static const char *lookup_mofile __P((char *, size_t, const char *, 56 const char *, const char *, const char *, struct domainbinding *)); 57 static u_int32_t flip __P((u_int32_t, u_int32_t)); 58 static int validate __P((void *, struct mohandle *)); 59 static int mapit __P((const char *, struct domainbinding *)); 60 static int unmapit __P((struct domainbinding *)); 61 static const char *lookup_hash __P((const char *, struct domainbinding *)); 62 static const char *lookup_bsearch __P((const char *, struct domainbinding *)); 63 static const char *lookup __P((const char *, struct domainbinding *)); 64 static const char *get_lang_env __P((const char *)); 65 66 /* 67 * shortcut functions. the main implementation resides in dcngettext(). 68 */ 69 char * 70 gettext(msgid) 71 const char *msgid; 72 { 73 74 return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES); 75 } 76 77 char * 78 dgettext(domainname, msgid) 79 const char *domainname; 80 const char *msgid; 81 { 82 83 return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES); 84 } 85 86 char * 87 dcgettext(domainname, msgid, category) 88 const char *domainname; 89 const char *msgid; 90 int category; 91 { 92 93 return dcngettext(domainname, msgid, NULL, 1UL, category); 94 } 95 96 char * 97 ngettext(msgid1, msgid2, n) 98 const char *msgid1; 99 const char *msgid2; 100 unsigned long int n; 101 { 102 103 return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES); 104 } 105 106 char * 107 dngettext(domainname, msgid1, msgid2, n) 108 const char *domainname; 109 const char *msgid1; 110 const char *msgid2; 111 unsigned long int n; 112 { 113 114 return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); 115 } 116 117 /* 118 * dcngettext() - 119 * lookup internationalized message on database locale/category/domainname 120 * (like ja_JP.eucJP/LC_MESSAGES/domainname). 121 * if n equals to 1, internationalized message will be looked up for msgid1. 122 * otherwise, message will be looked up for msgid2. 123 * if the lookup fails, the function will return msgid1 or msgid2 as is. 124 * 125 * Even though the return type is "char *", caller should not rewrite the 126 * region pointed to by the return value (should be "const char *", but can't 127 * change it for compatibility with other implementations). 128 * 129 * by default (if domainname == NULL), domainname is taken from the value set 130 * by textdomain(). usually name of the application (like "ls") is used as 131 * domainname. category is usually LC_MESSAGES. 132 * 133 * the code reads in *.mo files generated by GNU gettext. *.mo is a host- 134 * endian encoded file. both endians are supported here, as the files are in 135 * /usr/share/locale! (or we should move those files into /usr/libdata) 136 */ 137 138 static const char * 139 lookup_category(category) 140 int category; 141 { 142 143 switch (category) { 144 case LC_COLLATE: return "LC_COLLATE"; 145 case LC_CTYPE: return "LC_CTYPE"; 146 case LC_MONETARY: return "LC_MONETARY"; 147 case LC_NUMERIC: return "LC_NUMERIC"; 148 case LC_TIME: return "LC_TIME"; 149 case LC_MESSAGES: return "LC_MESSAGES"; 150 } 151 return NULL; 152 } 153 154 /* 155 * XPG syntax: language[_territory[.codeset]][@modifier] 156 * XXX boundary check on "result" is lacking 157 */ 158 static const char * 159 split_locale(lname) 160 const char *lname; 161 { 162 char buf[BUFSIZ], tmp[BUFSIZ]; 163 char *l, *t, *c, *m; 164 static char result[BUFSIZ]; 165 166 memset(result, 0, sizeof(result)); 167 168 if (strlen(lname) + 1 > sizeof(buf)) { 169 fail: 170 return lname; 171 } 172 173 strlcpy(buf, lname, sizeof(buf)); 174 m = strrchr(buf, '@'); 175 if (m) 176 *m++ = '\0'; 177 c = strrchr(buf, '.'); 178 if (c) 179 *c++ = '\0'; 180 t = strrchr(buf, '_'); 181 if (t) 182 *t++ = '\0'; 183 l = buf; 184 if (strlen(l) == 0) 185 goto fail; 186 if (c && !t) 187 goto fail; 188 189 if (m) { 190 if (t) { 191 if (c) { 192 snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s", 193 l, t, c, m); 194 strlcat(result, tmp, sizeof(result)); 195 strlcat(result, ":", sizeof(result)); 196 } 197 snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m); 198 strlcat(result, tmp, sizeof(result)); 199 strlcat(result, ":", sizeof(result)); 200 } 201 snprintf(tmp, sizeof(tmp), "%s@%s", l, m); 202 strlcat(result, tmp, sizeof(result)); 203 strlcat(result, ":", sizeof(result)); 204 } 205 if (t) { 206 if (c) { 207 snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c); 208 strlcat(result, tmp, sizeof(result)); 209 strlcat(result, ":", sizeof(result)); 210 } 211 snprintf(tmp, sizeof(tmp), "%s_%s", l, t); 212 strlcat(result, tmp, sizeof(result)); 213 strlcat(result, ":", sizeof(result)); 214 } 215 strlcat(result, l, sizeof(result)); 216 217 return result; 218 } 219 220 static const char * 221 lookup_mofile(buf, len, dir, lpath, category, domainname, db) 222 char *buf; 223 size_t len; 224 const char *dir; 225 const char *lpath; /* list of locales to be tried */ 226 const char *category; 227 const char *domainname; 228 struct domainbinding *db; 229 { 230 struct stat st; 231 char *p, *q; 232 char lpath_tmp[BUFSIZ]; 233 234 strlcpy(lpath_tmp, lpath, sizeof(lpath_tmp)); 235 q = lpath_tmp; 236 /* CONSTCOND */ 237 while (1) { 238 p = strsep(&q, ":"); 239 if (!p) 240 break; 241 if (!*p) 242 continue; 243 244 /* don't mess with default locales */ 245 if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0) 246 return NULL; 247 248 /* validate pathname */ 249 if (strchr(p, '/') || strchr(category, '/')) 250 continue; 251 #if 1 /*?*/ 252 if (strchr(domainname, '/')) 253 continue; 254 #endif 255 256 snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p, 257 category, domainname); 258 if (stat(buf, &st) < 0) 259 continue; 260 if ((st.st_mode & S_IFMT) != S_IFREG) 261 continue; 262 263 if (mapit(buf, db) == 0) 264 return buf; 265 } 266 267 return NULL; 268 } 269 270 static u_int32_t 271 flip(v, magic) 272 u_int32_t v; 273 u_int32_t magic; 274 { 275 276 if (magic == MO_MAGIC) 277 return v; 278 else if (magic == MO_MAGIC_SWAPPED) { 279 v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) | 280 ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000); 281 return v; 282 } else { 283 abort(); 284 /*NOTREACHED*/ 285 } 286 } 287 288 static int 289 validate(arg, mohandle) 290 void *arg; 291 struct mohandle *mohandle; 292 { 293 char *p; 294 295 p = (char *)arg; 296 if (p < (char *)mohandle->addr || 297 p > (char *)mohandle->addr + mohandle->len) 298 return 0; 299 else 300 return 1; 301 } 302 303 /* 304 * calculate the step value if the hash value is conflicted. 305 */ 306 static __inline u_int32_t 307 calc_collision_step(u_int32_t hashval, u_int32_t hashsize) 308 { 309 _DIAGASSERT(hashsize>2); 310 return (hashval % (hashsize - 2)) + 1; 311 } 312 313 /* 314 * calculate the next index while conflicting. 315 */ 316 static __inline u_int32_t 317 calc_next_index(u_int32_t curidx, u_int32_t hashsize, u_int32_t step) 318 { 319 return curidx+step - (curidx >= hashsize-step ? hashsize : 0); 320 } 321 322 static int 323 get_sysdep_string_table(struct mosysdepstr_h **table_h, 324 u_int32_t *ofstable, uint32_t nstrings, 325 u_int32_t magic, char *base) 326 { 327 int i, j; 328 int count; 329 size_t l; 330 struct mosysdepstr *table; 331 332 for (i=0; i<nstrings; i++) { 333 /* get mosysdepstr record */ 334 /* LINTED: ignore the alignment problem. */ 335 table = (struct mosysdepstr *)(base + flip(ofstable[i], magic)); 336 /* count number of segments */ 337 count = 0; 338 while (flip(table->segs[count++].ref, magic) != MO_LASTSEG) 339 ; 340 /* get table */ 341 l = sizeof(struct mosysdepstr_h) + 342 sizeof(struct mosysdepsegentry_h) * (count-1); 343 table_h[i] = (struct mosysdepstr_h *)malloc(l); 344 if (!table_h[i]) 345 return -1; 346 memset(table_h[i], 0, l); 347 table_h[i]->off = (const char *)(base + flip(table->off, magic)); 348 for (j=0; j<count; j++) { 349 table_h[i]->segs[j].len = 350 flip(table->segs[j].len, magic); 351 table_h[i]->segs[j].ref = 352 flip(table->segs[j].ref, magic); 353 } 354 /* LINTED: ignore the alignment problem. */ 355 table = (struct mosysdepstr *)&table->segs[count]; 356 } 357 return 0; 358 } 359 360 static int 361 expand_sysdep(struct mohandle *mohandle, struct mosysdepstr_h *str) 362 { 363 int i; 364 const char *src; 365 char *dst; 366 367 /* check whether already expanded */ 368 if (str->expanded) 369 return 0; 370 371 /* calc total length */ 372 str->expanded_len = 1; 373 for (i=0; /*CONSTCOND*/1; i++) { 374 str->expanded_len += str->segs[i].len; 375 if (str->segs[i].ref == MO_LASTSEG) 376 break; 377 str->expanded_len += 378 mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len; 379 } 380 /* expand */ 381 str->expanded = malloc(str->expanded_len); 382 if (!str->expanded) 383 return -1; 384 src = str->off; 385 dst = str->expanded; 386 for (i=0; /*CONSTCOND*/1; i++) { 387 memcpy(dst, src, str->segs[i].len); 388 src += str->segs[i].len; 389 dst += str->segs[i].len; 390 if (str->segs[i].ref == MO_LASTSEG) 391 break; 392 memcpy(dst, mohandle->mo.mo_sysdep_segs[str->segs[i].ref].str, 393 mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len); 394 dst += mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len; 395 } 396 *dst = '\0'; 397 398 return 0; 399 } 400 401 static void 402 insert_to_hash(u_int32_t *htable, u_int32_t hsize, const char *str, 403 u_int32_t ref) 404 { 405 u_int32_t hashval, idx, step; 406 407 hashval = __intl_string_hash(str); 408 step = calc_collision_step(hashval, hsize); 409 idx = hashval % hsize; 410 411 while (htable[idx]) 412 idx = calc_next_index(idx, hsize, step); 413 414 htable[idx] = ref; 415 } 416 417 static int 418 setup_sysdep_stuffs(struct mo *mo, struct mohandle *mohandle, char *base) 419 { 420 u_int32_t magic; 421 struct moentry *stable; 422 size_t l; 423 int i; 424 char *v; 425 u_int32_t *ofstable; 426 427 magic = mo->mo_magic; 428 429 mohandle->mo.mo_sysdep_nsegs = flip(mo->mo_sysdep_nsegs, magic); 430 mohandle->mo.mo_sysdep_nstring = flip(mo->mo_sysdep_nstring, magic); 431 432 if (mohandle->mo.mo_sysdep_nstring == 0) 433 return 0; 434 435 /* check hash size */ 436 if (mohandle->mo.mo_hsize <= 2 || 437 mohandle->mo.mo_hsize < 438 (mohandle->mo.mo_nstring + mohandle->mo.mo_sysdep_nstring)) 439 return -1; 440 441 /* get sysdep segments */ 442 l = sizeof(struct mosysdepsegs_h *) * mohandle->mo.mo_sysdep_nsegs; 443 mohandle->mo.mo_sysdep_segs = (struct mosysdepsegs_h *)malloc(l); 444 if (!mohandle->mo.mo_sysdep_segs) 445 return -1; 446 /* LINTED: ignore the alignment problem. */ 447 stable = (struct moentry *)(base + flip(mo->mo_sysdep_segoff, magic)); 448 for (i=0; i<mohandle->mo.mo_sysdep_nsegs; i++) { 449 v = base + flip(stable[i].off, magic); 450 mohandle->mo.mo_sysdep_segs[i].str = 451 __intl_sysdep_get_string_by_tag( 452 v, 453 &mohandle->mo.mo_sysdep_segs[i].len); 454 } 455 456 /* get sysdep string table */ 457 mohandle->mo.mo_sysdep_otable = 458 (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring, 459 sizeof(struct mosysdepstr_h *)); 460 if (!mohandle->mo.mo_sysdep_otable) 461 return -1; 462 /* LINTED: ignore the alignment problem. */ 463 ofstable = (u_int32_t *)(base + flip(mo->mo_sysdep_otable, magic)); 464 if (get_sysdep_string_table(mohandle->mo.mo_sysdep_otable, ofstable, 465 mohandle->mo.mo_sysdep_nstring, magic, 466 base)) 467 return -1; 468 mohandle->mo.mo_sysdep_ttable = 469 (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring, 470 sizeof(struct mosysdepstr_h *)); 471 if (!mohandle->mo.mo_sysdep_ttable) 472 return -1; 473 /* LINTED: ignore the alignment problem. */ 474 ofstable = (u_int32_t *)(base + flip(mo->mo_sysdep_ttable, magic)); 475 if (get_sysdep_string_table(mohandle->mo.mo_sysdep_ttable, ofstable, 476 mohandle->mo.mo_sysdep_nstring, magic, 477 base)) 478 return -1; 479 480 /* update hash */ 481 for (i=0; i<mohandle->mo.mo_sysdep_nstring; i++) { 482 if (expand_sysdep(mohandle, mohandle->mo.mo_sysdep_otable[i])) 483 return -1; 484 insert_to_hash(mohandle->mo.mo_htable, 485 mohandle->mo.mo_hsize, 486 mohandle->mo.mo_sysdep_otable[i]->expanded, 487 (i+1) | MO_HASH_SYSDEP_MASK); 488 } 489 490 return 0; 491 } 492 493 int 494 mapit(path, db) 495 const char *path; 496 struct domainbinding *db; 497 { 498 int fd; 499 struct stat st; 500 char *base; 501 u_int32_t magic, revision, flags = 0; 502 struct moentry *otable, *ttable; 503 const u_int32_t *htable; 504 struct moentry_h *p; 505 struct mo *mo; 506 size_t l; 507 int i; 508 char *v; 509 struct mohandle *mohandle = &db->mohandle; 510 511 if (mohandle->addr && mohandle->addr != MAP_FAILED && 512 mohandle->mo.mo_magic) 513 return 0; /*already opened*/ 514 515 unmapit(db); 516 517 #if 0 518 if (secure_path(path) != 0) 519 goto fail; 520 #endif 521 if (stat(path, &st) < 0) 522 goto fail; 523 if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX) 524 goto fail; 525 fd = open(path, O_RDONLY); 526 if (fd < 0) 527 goto fail; 528 if (read(fd, &magic, sizeof(magic)) != sizeof(magic) || 529 (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) { 530 close(fd); 531 goto fail; 532 } 533 if (read(fd, &revision, sizeof(revision)) != sizeof(revision)) { 534 close(fd); 535 goto fail; 536 } 537 switch (flip(revision, magic)) { 538 case MO_MAKE_REV(0, 0): 539 break; 540 case MO_MAKE_REV(0, 1): 541 case MO_MAKE_REV(1, 1): 542 flags |= MO_F_SYSDEP; 543 break; 544 default: 545 close(fd); 546 goto fail; 547 } 548 mohandle->addr = mmap(NULL, (size_t)st.st_size, PROT_READ, 549 MAP_FILE | MAP_SHARED, fd, (off_t)0); 550 if (!mohandle->addr || mohandle->addr == MAP_FAILED) { 551 close(fd); 552 goto fail; 553 } 554 close(fd); 555 mohandle->len = (size_t)st.st_size; 556 557 base = mohandle->addr; 558 mo = (struct mo *)mohandle->addr; 559 560 /* flip endian. do not flip magic number! */ 561 mohandle->mo.mo_magic = mo->mo_magic; 562 mohandle->mo.mo_revision = flip(mo->mo_revision, magic); 563 mohandle->mo.mo_nstring = flip(mo->mo_nstring, magic); 564 mohandle->mo.mo_hsize = flip(mo->mo_hsize, magic); 565 mohandle->mo.mo_flags = flags; 566 567 /* validate otable/ttable */ 568 /* LINTED: ignore the alignment problem. */ 569 otable = (struct moentry *)(base + flip(mo->mo_otable, magic)); 570 /* LINTED: ignore the alignment problem. */ 571 ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic)); 572 if (!validate(otable, mohandle) || 573 !validate(&otable[mohandle->mo.mo_nstring], mohandle)) { 574 unmapit(db); 575 goto fail; 576 } 577 if (!validate(ttable, mohandle) || 578 !validate(&ttable[mohandle->mo.mo_nstring], mohandle)) { 579 unmapit(db); 580 goto fail; 581 } 582 583 /* allocate [ot]table, and convert to normal pointer representation. */ 584 l = sizeof(struct moentry_h) * mohandle->mo.mo_nstring; 585 mohandle->mo.mo_otable = (struct moentry_h *)malloc(l); 586 if (!mohandle->mo.mo_otable) { 587 unmapit(db); 588 goto fail; 589 } 590 mohandle->mo.mo_ttable = (struct moentry_h *)malloc(l); 591 if (!mohandle->mo.mo_ttable) { 592 unmapit(db); 593 goto fail; 594 } 595 p = mohandle->mo.mo_otable; 596 for (i = 0; i < mohandle->mo.mo_nstring; i++) { 597 p[i].len = flip(otable[i].len, magic); 598 p[i].off = base + flip(otable[i].off, magic); 599 600 if (!validate(p[i].off, mohandle) || 601 !validate(p[i].off + p[i].len + 1, mohandle)) { 602 unmapit(db); 603 goto fail; 604 } 605 } 606 p = mohandle->mo.mo_ttable; 607 for (i = 0; i < mohandle->mo.mo_nstring; i++) { 608 p[i].len = flip(ttable[i].len, magic); 609 p[i].off = base + flip(ttable[i].off, magic); 610 611 if (!validate(p[i].off, mohandle) || 612 !validate(p[i].off + p[i].len + 1, mohandle)) { 613 unmapit(db); 614 goto fail; 615 } 616 } 617 /* allocate htable, and convert it to the host order. */ 618 if (mohandle->mo.mo_hsize > 2) { 619 l = sizeof(u_int32_t) * mohandle->mo.mo_hsize; 620 mohandle->mo.mo_htable = (u_int32_t *)malloc(l); 621 if (!mohandle->mo.mo_htable) { 622 unmapit(db); 623 goto fail; 624 } 625 /* LINTED: ignore the alignment problem. */ 626 htable = (const u_int32_t *)(base+flip(mo->mo_hoffset, magic)); 627 for (i=0; i < mohandle->mo.mo_hsize; i++) { 628 mohandle->mo.mo_htable[i] = flip(htable[i], magic); 629 if (mohandle->mo.mo_htable[i] >= 630 mohandle->mo.mo_nstring+1) { 631 /* illegal string number. */ 632 unmapit(db); 633 goto fail; 634 } 635 } 636 } 637 /* grab MIME-header and charset field */ 638 mohandle->mo.mo_header = lookup("", db); 639 if (mohandle->mo.mo_header) 640 v = strstr(mohandle->mo.mo_header, "charset="); 641 else 642 v = NULL; 643 if (v) { 644 mohandle->mo.mo_charset = strdup(v + 8); 645 if (!mohandle->mo.mo_charset) 646 goto fail; 647 v = strchr(mohandle->mo.mo_charset, '\n'); 648 if (v) 649 *v = '\0'; 650 } 651 652 /* 653 * XXX check charset, reject it if we are unable to support the charset 654 * with the current locale. 655 * for example, if we are using euc-jp locale and we are looking at 656 * *.mo file encoded by euc-kr (charset=euc-kr), we should reject 657 * the *.mo file as we cannot support it. 658 */ 659 660 /* system dependent string support */ 661 if ((mohandle->mo.mo_flags & MO_F_SYSDEP) != 0) { 662 if (setup_sysdep_stuffs(mo, mohandle, base)) { 663 unmapit(db); 664 goto fail; 665 } 666 } 667 668 return 0; 669 670 fail: 671 return -1; 672 } 673 674 static void 675 free_sysdep_table(struct mosysdepstr_h **table, u_int32_t nstring) 676 { 677 u_int32_t i; 678 679 for (i=0; i<nstring; i++) { 680 if (table[i]) { 681 if (table[i]->expanded) 682 free(table[i]->expanded); 683 free(table[i]); 684 } 685 } 686 free(table); 687 } 688 689 static int 690 unmapit(db) 691 struct domainbinding *db; 692 { 693 struct mohandle *mohandle = &db->mohandle; 694 695 /* unmap if there's already mapped region */ 696 if (mohandle->addr && mohandle->addr != MAP_FAILED) 697 munmap(mohandle->addr, mohandle->len); 698 mohandle->addr = NULL; 699 if (mohandle->mo.mo_otable) 700 free(mohandle->mo.mo_otable); 701 if (mohandle->mo.mo_ttable) 702 free(mohandle->mo.mo_ttable); 703 if (mohandle->mo.mo_charset) 704 free(mohandle->mo.mo_charset); 705 if (mohandle->mo.mo_htable) 706 free(mohandle->mo.mo_htable); 707 if (mohandle->mo.mo_sysdep_segs) 708 free(mohandle->mo.mo_sysdep_segs); 709 if (mohandle->mo.mo_sysdep_otable) { 710 free_sysdep_table(mohandle->mo.mo_sysdep_otable, 711 mohandle->mo.mo_sysdep_nstring); 712 } 713 if (mohandle->mo.mo_sysdep_ttable) { 714 free_sysdep_table(mohandle->mo.mo_sysdep_ttable, 715 mohandle->mo.mo_sysdep_nstring); 716 } 717 memset(&mohandle->mo, 0, sizeof(mohandle->mo)); 718 return 0; 719 } 720 721 /* ARGSUSED */ 722 static const char * 723 lookup_hash(msgid, db) 724 const char *msgid; 725 struct domainbinding *db; 726 { 727 struct mohandle *mohandle = &db->mohandle; 728 u_int32_t idx, hashval, step, strno; 729 size_t len; 730 struct mosysdepstr_h *sysdep_otable, *sysdep_ttable; 731 732 if (mohandle->mo.mo_hsize <= 2 || mohandle->mo.mo_htable == NULL) 733 return NULL; 734 735 hashval = __intl_string_hash(msgid); 736 step = calc_collision_step(hashval, mohandle->mo.mo_hsize); 737 idx = hashval % mohandle->mo.mo_hsize; 738 len = strlen(msgid); 739 while (/*CONSTCOND*/1) { 740 strno = mohandle->mo.mo_htable[idx]; 741 if (strno == 0) { 742 /* unexpected miss */ 743 return NULL; 744 } 745 strno--; 746 if ((strno & MO_HASH_SYSDEP_MASK) == 0) { 747 /* system independent strings */ 748 if (len <= mohandle->mo.mo_otable[strno].len && 749 !strcmp(msgid, mohandle->mo.mo_otable[strno].off)) { 750 /* hit */ 751 return mohandle->mo.mo_ttable[strno].off; 752 } 753 } else { 754 /* system dependent strings */ 755 strno &= ~MO_HASH_SYSDEP_MASK; 756 sysdep_otable = mohandle->mo.mo_sysdep_otable[strno]; 757 sysdep_ttable = mohandle->mo.mo_sysdep_ttable[strno]; 758 if (len <= sysdep_otable->expanded_len && 759 !strcmp(msgid, sysdep_otable->expanded)) { 760 /* hit */ 761 if (expand_sysdep(mohandle, sysdep_ttable)) 762 /* memory exhausted */ 763 return NULL; 764 return sysdep_ttable->expanded; 765 } 766 } 767 idx = calc_next_index(idx, mohandle->mo.mo_hsize, step); 768 } 769 /*NOTREACHED*/ 770 } 771 772 static const char * 773 lookup_bsearch(msgid, db) 774 const char *msgid; 775 struct domainbinding *db; 776 { 777 int top, bottom, middle, omiddle; 778 int n; 779 struct mohandle *mohandle = &db->mohandle; 780 781 top = 0; 782 bottom = mohandle->mo.mo_nstring; 783 omiddle = -1; 784 /* CONSTCOND */ 785 while (1) { 786 if (top > bottom) 787 break; 788 middle = (top + bottom) / 2; 789 /* avoid possible infinite loop, when the data is not sorted */ 790 if (omiddle == middle) 791 break; 792 if (middle < 0 || middle >= mohandle->mo.mo_nstring) 793 break; 794 795 n = strcmp(msgid, mohandle->mo.mo_otable[middle].off); 796 if (n == 0) 797 return (const char *)mohandle->mo.mo_ttable[middle].off; 798 else if (n < 0) 799 bottom = middle; 800 else 801 top = middle; 802 omiddle = middle; 803 } 804 805 return NULL; 806 } 807 808 static const char * 809 lookup(msgid, db) 810 const char *msgid; 811 struct domainbinding *db; 812 { 813 const char *v; 814 815 v = lookup_hash(msgid, db); 816 if (v) 817 return v; 818 819 return lookup_bsearch(msgid, db); 820 } 821 822 static const char * 823 get_lang_env(const char *category_name) 824 { 825 const char *lang; 826 827 /* 1. see LANGUAGE variable first. */ 828 lang = getenv("LANGUAGE"); 829 if (lang) 830 return lang; 831 832 /* 2. if LANGUAGE isn't set, see LC_ALL, LC_xxx, LANG. */ 833 lang = getenv("LC_ALL"); 834 if (!lang) 835 lang = getenv(category_name); 836 if (!lang) 837 lang = getenv("LANG"); 838 839 if (!lang) 840 return 0; /* error */ 841 842 return split_locale(lang); 843 } 844 845 char * 846 dcngettext(domainname, msgid1, msgid2, n, category) 847 const char *domainname; 848 const char *msgid1; 849 const char *msgid2; 850 unsigned long int n; 851 int category; 852 { 853 const char *msgid; 854 char path[PATH_MAX]; 855 const char *lpath; 856 static char olpath[PATH_MAX]; 857 const char *cname = NULL; 858 const char *v; 859 static char *ocname = NULL; 860 static char *odomainname = NULL; 861 struct domainbinding *db; 862 863 msgid = (n == 1) ? msgid1 : msgid2; 864 if (msgid == NULL) 865 return NULL; 866 867 if (!domainname) 868 domainname = __current_domainname; 869 cname = lookup_category(category); 870 if (!domainname || !cname) 871 goto fail; 872 873 lpath = get_lang_env(cname); 874 if (!lpath) 875 goto fail; 876 877 for (db = __bindings; db; db = db->next) 878 if (strcmp(db->domainname, domainname) == 0) 879 break; 880 if (!db) { 881 if (!bindtextdomain(domainname, _PATH_TEXTDOMAIN)) 882 goto fail; 883 db = __bindings; 884 } 885 886 /* resolve relative path */ 887 /* XXX not necessary? */ 888 if (db->path[0] != '/') { 889 char buf[PATH_MAX]; 890 891 if (getcwd(buf, sizeof(buf)) == 0) 892 goto fail; 893 if (strlcat(buf, "/", sizeof(buf)) >= sizeof(buf)) 894 goto fail; 895 if (strlcat(buf, db->path, sizeof(buf)) >= sizeof(buf)) 896 goto fail; 897 strlcpy(db->path, buf, sizeof(db->path)); 898 } 899 900 /* don't bother looking it up if the values are the same */ 901 if (odomainname && strcmp(domainname, odomainname) == 0 && 902 ocname && strcmp(cname, ocname) == 0 && strcmp(lpath, olpath) == 0 && 903 db->mohandle.mo.mo_magic) 904 goto found; 905 906 /* try to find appropriate file, from $LANGUAGE */ 907 if (lookup_mofile(path, sizeof(path), db->path, lpath, cname, 908 domainname, db) == NULL) 909 goto fail; 910 911 if (odomainname) 912 free(odomainname); 913 if (ocname) 914 free(ocname); 915 odomainname = strdup(domainname); 916 ocname = strdup(cname); 917 if (!odomainname || !ocname) { 918 if (odomainname) 919 free(odomainname); 920 if (ocname) 921 free(ocname); 922 odomainname = ocname = NULL; 923 } 924 else 925 strlcpy(olpath, lpath, sizeof(olpath)); 926 927 found: 928 v = lookup(msgid, db); 929 if (v) { 930 /* 931 * convert the translated message's encoding. 932 * 933 * special case: 934 * a result of gettext("") shouldn't need any conversion. 935 */ 936 if (msgid[0]) 937 v = __gettext_iconv(v, db); 938 939 /* 940 * Given the amount of printf-format security issues, it may 941 * be a good idea to validate if the original msgid and the 942 * translated message format string carry the same printf-like 943 * format identifiers. 944 */ 945 946 msgid = v; 947 } 948 949 fail: 950 return (char *)__UNCONST(msgid); 951 } 952