1*9b2f4799Schristos /* $NetBSD: gettext.c,v 1.33 2024/08/18 17:46:24 christos Exp $ */ 2bb42e65bSitojun 3bb42e65bSitojun /*- 43bad25dcSminoura * Copyright (c) 2000, 2001 Citrus Project, 5bb42e65bSitojun * All rights reserved. 6bb42e65bSitojun * 7bb42e65bSitojun * Redistribution and use in source and binary forms, with or without 8bb42e65bSitojun * modification, are permitted provided that the following conditions 9bb42e65bSitojun * are met: 10bb42e65bSitojun * 1. Redistributions of source code must retain the above copyright 11bb42e65bSitojun * notice, this list of conditions and the following disclaimer. 12bb42e65bSitojun * 2. Redistributions in binary form must reproduce the above copyright 13bb42e65bSitojun * notice, this list of conditions and the following disclaimer in the 14bb42e65bSitojun * documentation and/or other materials provided with the distribution. 15bb42e65bSitojun * 16bb42e65bSitojun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17bb42e65bSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18bb42e65bSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19bb42e65bSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20bb42e65bSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21bb42e65bSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22bb42e65bSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23bb42e65bSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24bb42e65bSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25bb42e65bSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26bb42e65bSitojun * SUCH DAMAGE. 27782b5c88Syamt * 28782b5c88Syamt * $Citrus: xpg4dl/FreeBSD/lib/libintl/gettext.c,v 1.31 2001/09/27 15:18:45 yamt Exp $ 29bb42e65bSitojun */ 30bb42e65bSitojun 31bb42e65bSitojun #include <sys/cdefs.h> 32*9b2f4799Schristos __RCSID("$NetBSD: gettext.c,v 1.33 2024/08/18 17:46:24 christos Exp $"); 33bb42e65bSitojun 34bb42e65bSitojun #include <sys/param.h> 35bb42e65bSitojun #include <sys/stat.h> 36bb42e65bSitojun #include <sys/mman.h> 37bb42e65bSitojun #include <sys/uio.h> 38bb42e65bSitojun 3929be49b4Stshiozak #include <assert.h> 40bb42e65bSitojun #include <fcntl.h> 41bb42e65bSitojun #include <stdio.h> 42bb42e65bSitojun #include <stdlib.h> 43bb42e65bSitojun #include <unistd.h> 44bb42e65bSitojun #include <string.h> 45bb42e65bSitojun #if 0 46bb42e65bSitojun #include <util.h> 47bb42e65bSitojun #endif 48bb42e65bSitojun #include <libintl.h> 49bb42e65bSitojun #include <locale.h> 50bb42e65bSitojun #include "libintl_local.h" 51dd416aa8Stshiozak #include "plural_parser.h" 52bb42e65bSitojun #include "pathnames.h" 53bb42e65bSitojun 545297022aSchristos /* GNU gettext added a hack to add some context to messages. If a message is 555297022aSchristos * used in multiple locations, it needs some amount of context to make the 565297022aSchristos * translation clear to translators. GNU gettext, rather than modifying the 575297022aSchristos * message format, concatenates the context, \004 and the message id. 585297022aSchristos */ 595297022aSchristos #define MSGCTXT_ID_SEPARATOR '\004' 605297022aSchristos 615297022aSchristos static const char *pgettext_impl(const char *, const char *, const char *, 625297022aSchristos const char *, unsigned long int, int); 635297022aSchristos static char *concatenate_ctxt_id(const char *, const char *); 644271bf2eSjunyoung static const char *lookup_category(int); 654271bf2eSjunyoung static const char *split_locale(const char *); 664271bf2eSjunyoung static const char *lookup_mofile(char *, size_t, const char *, const char *, 674271bf2eSjunyoung const char *, const char *, 684271bf2eSjunyoung struct domainbinding *); 694271bf2eSjunyoung static uint32_t flip(uint32_t, uint32_t); 704271bf2eSjunyoung static int validate(void *, struct mohandle *); 714271bf2eSjunyoung static int mapit(const char *, struct domainbinding *); 724271bf2eSjunyoung static int unmapit(struct domainbinding *); 734271bf2eSjunyoung static const char *lookup_hash(const char *, struct domainbinding *, size_t *); 744271bf2eSjunyoung static const char *lookup_bsearch(const char *, struct domainbinding *, 754271bf2eSjunyoung size_t *); 764271bf2eSjunyoung static const char *lookup(const char *, struct domainbinding *, size_t *); 774271bf2eSjunyoung static const char *get_lang_env(const char *); 78bb42e65bSitojun 79bb42e65bSitojun /* 80bb42e65bSitojun * shortcut functions. the main implementation resides in dcngettext(). 81bb42e65bSitojun */ 82bb42e65bSitojun char * 834271bf2eSjunyoung gettext(const char *msgid) 84bb42e65bSitojun { 85bb42e65bSitojun 86bb42e65bSitojun return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES); 87bb42e65bSitojun } 88bb42e65bSitojun 89bb42e65bSitojun char * 904271bf2eSjunyoung dgettext(const char *domainname, const char *msgid) 91bb42e65bSitojun { 92bb42e65bSitojun 93bb42e65bSitojun return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES); 94bb42e65bSitojun } 95bb42e65bSitojun 96bb42e65bSitojun char * 974271bf2eSjunyoung dcgettext(const char *domainname, const char *msgid, int category) 98bb42e65bSitojun { 99bb42e65bSitojun 100bb42e65bSitojun return dcngettext(domainname, msgid, NULL, 1UL, category); 101bb42e65bSitojun } 102bb42e65bSitojun 103bb42e65bSitojun char * 1044271bf2eSjunyoung ngettext(const char *msgid1, const char *msgid2, unsigned long int n) 105bb42e65bSitojun { 106bb42e65bSitojun 107bb42e65bSitojun return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES); 108bb42e65bSitojun } 109bb42e65bSitojun 110bb42e65bSitojun char * 1114271bf2eSjunyoung dngettext(const char *domainname, const char *msgid1, const char *msgid2, 1124271bf2eSjunyoung unsigned long int n) 113bb42e65bSitojun { 114bb42e65bSitojun 115bb42e65bSitojun return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); 116bb42e65bSitojun } 117bb42e65bSitojun 1185297022aSchristos const char * 1195297022aSchristos pgettext(const char *msgctxt, const char *msgid) 1205297022aSchristos { 1215297022aSchristos 1225297022aSchristos return pgettext_impl(NULL, msgctxt, msgid, NULL, 1UL, LC_MESSAGES); 1235297022aSchristos } 1245297022aSchristos 1255297022aSchristos const char * 1265297022aSchristos dpgettext(const char *domainname, const char *msgctxt, const char *msgid) 1275297022aSchristos { 1285297022aSchristos 1295297022aSchristos return pgettext_impl(domainname, msgctxt, msgid, NULL, 1UL, LC_MESSAGES); 1305297022aSchristos } 1315297022aSchristos 1325297022aSchristos const char * 1335297022aSchristos dcpgettext(const char *domainname, const char *msgctxt, const char *msgid, 1345297022aSchristos int category) 1355297022aSchristos { 1365297022aSchristos 1375297022aSchristos return pgettext_impl(domainname, msgctxt, msgid, NULL, 1UL, category); 1385297022aSchristos } 1395297022aSchristos 1405297022aSchristos const char * 1415297022aSchristos npgettext(const char *msgctxt, const char *msgid1, const char *msgid2, 1425297022aSchristos unsigned long int n) 1435297022aSchristos { 1445297022aSchristos 1455297022aSchristos return pgettext_impl(NULL, msgctxt, msgid1, msgid2, n, LC_MESSAGES); 1465297022aSchristos } 1475297022aSchristos 1485297022aSchristos const char * 1495297022aSchristos dnpgettext(const char *domainname, const char *msgctxt, const char *msgid1, 1505297022aSchristos const char *msgid2, unsigned long int n) 1515297022aSchristos { 1525297022aSchristos 1535297022aSchristos return pgettext_impl(domainname, msgctxt, msgid1, msgid2, n, LC_MESSAGES); 1545297022aSchristos } 1555297022aSchristos 1565297022aSchristos const char * 1575297022aSchristos dcnpgettext(const char *domainname, const char *msgctxt, const char *msgid1, 1585297022aSchristos const char *msgid2, unsigned long int n, int category) 1595297022aSchristos { 1605297022aSchristos 1615297022aSchristos return pgettext_impl(domainname, msgctxt, msgid1, msgid2, n, category); 1625297022aSchristos } 1635297022aSchristos 1645297022aSchristos static const char * 1655297022aSchristos pgettext_impl(const char *domainname, const char *msgctxt, const char *msgid1, 1665297022aSchristos const char *msgid2, unsigned long int n, int category) 1675297022aSchristos { 1685297022aSchristos char *msgctxt_id; 1695297022aSchristos char *translation; 1705297022aSchristos char *p; 1715297022aSchristos 1725297022aSchristos if ((msgctxt_id = concatenate_ctxt_id(msgctxt, msgid1)) == NULL) 1735297022aSchristos return msgid1; 1745297022aSchristos 1755297022aSchristos translation = dcngettext(domainname, msgctxt_id, 1765297022aSchristos msgid2, n, category); 177*9b2f4799Schristos 178*9b2f4799Schristos if (translation == msgctxt_id) { 1795297022aSchristos free(msgctxt_id); 18028614955Schristos return msgid1; 181*9b2f4799Schristos } 18228614955Schristos 183*9b2f4799Schristos free(msgctxt_id); 1845297022aSchristos p = strchr(translation, '\004'); 1855297022aSchristos if (p) 1865297022aSchristos return p + 1; 1875297022aSchristos return translation; 1885297022aSchristos } 1895297022aSchristos 190bb42e65bSitojun /* 191bb42e65bSitojun * dcngettext() - 192bb42e65bSitojun * lookup internationalized message on database locale/category/domainname 193bb42e65bSitojun * (like ja_JP.eucJP/LC_MESSAGES/domainname). 194bb42e65bSitojun * if n equals to 1, internationalized message will be looked up for msgid1. 195bb42e65bSitojun * otherwise, message will be looked up for msgid2. 196bb42e65bSitojun * if the lookup fails, the function will return msgid1 or msgid2 as is. 197bb42e65bSitojun * 198bb42e65bSitojun * Even though the return type is "char *", caller should not rewrite the 199bb42e65bSitojun * region pointed to by the return value (should be "const char *", but can't 200bb42e65bSitojun * change it for compatibility with other implementations). 201bb42e65bSitojun * 202bb42e65bSitojun * by default (if domainname == NULL), domainname is taken from the value set 203bb42e65bSitojun * by textdomain(). usually name of the application (like "ls") is used as 204bb42e65bSitojun * domainname. category is usually LC_MESSAGES. 205bb42e65bSitojun * 206bb42e65bSitojun * the code reads in *.mo files generated by GNU gettext. *.mo is a host- 207bb42e65bSitojun * endian encoded file. both endians are supported here, as the files are in 208bb42e65bSitojun * /usr/share/locale! (or we should move those files into /usr/libdata) 209bb42e65bSitojun */ 210bb42e65bSitojun 2115297022aSchristos static char * 2125297022aSchristos concatenate_ctxt_id(const char *msgctxt, const char *msgid) 2135297022aSchristos { 2145297022aSchristos char *ret; 2155297022aSchristos 2165297022aSchristos if (asprintf(&ret, "%s%c%s", msgctxt, MSGCTXT_ID_SEPARATOR, msgid) == -1) 2175297022aSchristos return NULL; 2185297022aSchristos 2195297022aSchristos return ret; 2205297022aSchristos } 2215297022aSchristos 222bb42e65bSitojun static const char * 2234271bf2eSjunyoung lookup_category(int category) 224bb42e65bSitojun { 225bb42e65bSitojun 226bb42e65bSitojun switch (category) { 227bb42e65bSitojun case LC_COLLATE: return "LC_COLLATE"; 228bb42e65bSitojun case LC_CTYPE: return "LC_CTYPE"; 229bb42e65bSitojun case LC_MONETARY: return "LC_MONETARY"; 230bb42e65bSitojun case LC_NUMERIC: return "LC_NUMERIC"; 231bb42e65bSitojun case LC_TIME: return "LC_TIME"; 232bb42e65bSitojun case LC_MESSAGES: return "LC_MESSAGES"; 233bb42e65bSitojun } 234bb42e65bSitojun return NULL; 235bb42e65bSitojun } 236bb42e65bSitojun 237b490ea0cSchristos #define MAXBUFLEN 1024 238bb42e65bSitojun /* 239bb42e65bSitojun * XPG syntax: language[_territory[.codeset]][@modifier] 240bb42e65bSitojun * XXX boundary check on "result" is lacking 241bb42e65bSitojun */ 242bb42e65bSitojun static const char * 2434271bf2eSjunyoung split_locale(const char *lname) 244bb42e65bSitojun { 245b490ea0cSchristos char buf[MAXBUFLEN], tmp[2 * MAXBUFLEN]; 246bb42e65bSitojun char *l, *t, *c, *m; 247b490ea0cSchristos static char result[4 * MAXBUFLEN]; 248bb42e65bSitojun 249bb42e65bSitojun memset(result, 0, sizeof(result)); 250bb42e65bSitojun 251bb42e65bSitojun if (strlen(lname) + 1 > sizeof(buf)) { 252bb42e65bSitojun fail: 253bb42e65bSitojun return lname; 254bb42e65bSitojun } 255bb42e65bSitojun 256bb42e65bSitojun strlcpy(buf, lname, sizeof(buf)); 257bb42e65bSitojun m = strrchr(buf, '@'); 258bb42e65bSitojun if (m) 259bb42e65bSitojun *m++ = '\0'; 260bb42e65bSitojun c = strrchr(buf, '.'); 261bb42e65bSitojun if (c) 262bb42e65bSitojun *c++ = '\0'; 263bb42e65bSitojun t = strrchr(buf, '_'); 264bb42e65bSitojun if (t) 265bb42e65bSitojun *t++ = '\0'; 266bb42e65bSitojun l = buf; 267bb42e65bSitojun if (strlen(l) == 0) 268bb42e65bSitojun goto fail; 269bb42e65bSitojun if (c && !t) 270bb42e65bSitojun goto fail; 271bb42e65bSitojun 272bb42e65bSitojun if (m) { 273bb42e65bSitojun if (t) { 274bb42e65bSitojun if (c) { 275bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s", 276bb42e65bSitojun l, t, c, m); 277bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 278bb42e65bSitojun strlcat(result, ":", sizeof(result)); 279bb42e65bSitojun } 280bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m); 281bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 282bb42e65bSitojun strlcat(result, ":", sizeof(result)); 283bb42e65bSitojun } 284bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s@%s", l, m); 285bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 286bb42e65bSitojun strlcat(result, ":", sizeof(result)); 287bb42e65bSitojun } 288bb42e65bSitojun if (t) { 289bb42e65bSitojun if (c) { 290bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c); 291bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 292bb42e65bSitojun strlcat(result, ":", sizeof(result)); 293bb42e65bSitojun } 2947cd60a32Syamt snprintf(tmp, sizeof(tmp), "%s_%s", l, t); 295bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 296bb42e65bSitojun strlcat(result, ":", sizeof(result)); 297bb42e65bSitojun } 298bb42e65bSitojun strlcat(result, l, sizeof(result)); 299bb42e65bSitojun 300bb42e65bSitojun return result; 301bb42e65bSitojun } 302bb42e65bSitojun 303bb42e65bSitojun static const char * 3044271bf2eSjunyoung lookup_mofile(char *buf, size_t len, const char *dir, const char *lpath, 3054271bf2eSjunyoung const char *category, const char *domainname, 3064271bf2eSjunyoung struct domainbinding *db) 307bb42e65bSitojun { 308bb42e65bSitojun struct stat st; 309bb42e65bSitojun char *p, *q; 310782b5c88Syamt char lpath_tmp[BUFSIZ]; 311bb42e65bSitojun 3125c3967c0Syamt /* 3135c3967c0Syamt * LANGUAGE is a colon separated list of locale names. 3145c3967c0Syamt */ 3155c3967c0Syamt 316782b5c88Syamt strlcpy(lpath_tmp, lpath, sizeof(lpath_tmp)); 317782b5c88Syamt q = lpath_tmp; 3183bad25dcSminoura /* CONSTCOND */ 319bb42e65bSitojun while (1) { 320bb42e65bSitojun p = strsep(&q, ":"); 321bb42e65bSitojun if (!p) 322bb42e65bSitojun break; 323bb42e65bSitojun if (!*p) 324bb42e65bSitojun continue; 325bb42e65bSitojun 326bb42e65bSitojun /* don't mess with default locales */ 327bb42e65bSitojun if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0) 328bb42e65bSitojun return NULL; 329bb42e65bSitojun 330bb42e65bSitojun /* validate pathname */ 331bb42e65bSitojun if (strchr(p, '/') || strchr(category, '/')) 332bb42e65bSitojun continue; 333bb42e65bSitojun #if 1 /*?*/ 334bb42e65bSitojun if (strchr(domainname, '/')) 335bb42e65bSitojun continue; 336bb42e65bSitojun #endif 337bb42e65bSitojun 33882974f22Smrg int rv = snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p, 339bb42e65bSitojun category, domainname); 34082974f22Smrg if (rv > (int)len) 34182974f22Smrg return NULL; 342bb42e65bSitojun if (stat(buf, &st) < 0) 343bb42e65bSitojun continue; 344bb42e65bSitojun if ((st.st_mode & S_IFMT) != S_IFREG) 345bb42e65bSitojun continue; 346bb42e65bSitojun 3473bad25dcSminoura if (mapit(buf, db) == 0) 348bb42e65bSitojun return buf; 349bb42e65bSitojun } 350bb42e65bSitojun 351bb42e65bSitojun return NULL; 352bb42e65bSitojun } 353bb42e65bSitojun 3544271bf2eSjunyoung static uint32_t 3554271bf2eSjunyoung flip(uint32_t v, uint32_t magic) 356bb42e65bSitojun { 357bb42e65bSitojun 358bb42e65bSitojun if (magic == MO_MAGIC) 359bb42e65bSitojun return v; 360bb42e65bSitojun else if (magic == MO_MAGIC_SWAPPED) { 361bb42e65bSitojun v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) | 362bb42e65bSitojun ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000); 363bb42e65bSitojun return v; 364bb42e65bSitojun } else { 365bb42e65bSitojun abort(); 366bb42e65bSitojun /*NOTREACHED*/ 367bb42e65bSitojun } 368bb42e65bSitojun } 369bb42e65bSitojun 370bb42e65bSitojun static int 3714271bf2eSjunyoung validate(void *arg, struct mohandle *mohandle) 372bb42e65bSitojun { 373bb42e65bSitojun char *p; 374bb42e65bSitojun 375bb42e65bSitojun p = (char *)arg; 3763bad25dcSminoura if (p < (char *)mohandle->addr || 3773bad25dcSminoura p > (char *)mohandle->addr + mohandle->len) 378bb42e65bSitojun return 0; 379bb42e65bSitojun else 380bb42e65bSitojun return 1; 381bb42e65bSitojun } 382bb42e65bSitojun 383922c0d00Stshiozak /* 384922c0d00Stshiozak * calculate the step value if the hash value is conflicted. 385922c0d00Stshiozak */ 3864271bf2eSjunyoung static __inline uint32_t 3874271bf2eSjunyoung calc_collision_step(uint32_t hashval, uint32_t hashsize) 388922c0d00Stshiozak { 389922c0d00Stshiozak _DIAGASSERT(hashsize>2); 390922c0d00Stshiozak return (hashval % (hashsize - 2)) + 1; 391922c0d00Stshiozak } 392922c0d00Stshiozak 393922c0d00Stshiozak /* 394922c0d00Stshiozak * calculate the next index while conflicting. 395922c0d00Stshiozak */ 3964271bf2eSjunyoung static __inline uint32_t 3974271bf2eSjunyoung calc_next_index(uint32_t curidx, uint32_t hashsize, uint32_t step) 398922c0d00Stshiozak { 399922c0d00Stshiozak return curidx+step - (curidx >= hashsize-step ? hashsize : 0); 400922c0d00Stshiozak } 401922c0d00Stshiozak 402922c0d00Stshiozak static int 4034271bf2eSjunyoung get_sysdep_string_table(struct mosysdepstr_h **table_h, uint32_t *ofstable, 4044271bf2eSjunyoung uint32_t nstrings, uint32_t magic, char *base) 405922c0d00Stshiozak { 406e1a2f47fSmatt unsigned int i; 407e1a2f47fSmatt int j, count; 408922c0d00Stshiozak size_t l; 409922c0d00Stshiozak struct mosysdepstr *table; 410922c0d00Stshiozak 411922c0d00Stshiozak for (i=0; i<nstrings; i++) { 412922c0d00Stshiozak /* get mosysdepstr record */ 413922c0d00Stshiozak /* LINTED: ignore the alignment problem. */ 414922c0d00Stshiozak table = (struct mosysdepstr *)(base + flip(ofstable[i], magic)); 415922c0d00Stshiozak /* count number of segments */ 416922c0d00Stshiozak count = 0; 417922c0d00Stshiozak while (flip(table->segs[count++].ref, magic) != MO_LASTSEG) 418922c0d00Stshiozak ; 419922c0d00Stshiozak /* get table */ 420922c0d00Stshiozak l = sizeof(struct mosysdepstr_h) + 421922c0d00Stshiozak sizeof(struct mosysdepsegentry_h) * (count-1); 422922c0d00Stshiozak table_h[i] = (struct mosysdepstr_h *)malloc(l); 423922c0d00Stshiozak if (!table_h[i]) 424922c0d00Stshiozak return -1; 425922c0d00Stshiozak memset(table_h[i], 0, l); 426922c0d00Stshiozak table_h[i]->off = (const char *)(base + flip(table->off, magic)); 427922c0d00Stshiozak for (j=0; j<count; j++) { 428922c0d00Stshiozak table_h[i]->segs[j].len = 429922c0d00Stshiozak flip(table->segs[j].len, magic); 430922c0d00Stshiozak table_h[i]->segs[j].ref = 431922c0d00Stshiozak flip(table->segs[j].ref, magic); 432922c0d00Stshiozak } 433922c0d00Stshiozak /* LINTED: ignore the alignment problem. */ 434922c0d00Stshiozak table = (struct mosysdepstr *)&table->segs[count]; 435922c0d00Stshiozak } 436922c0d00Stshiozak return 0; 437922c0d00Stshiozak } 438922c0d00Stshiozak 439922c0d00Stshiozak static int 440922c0d00Stshiozak expand_sysdep(struct mohandle *mohandle, struct mosysdepstr_h *str) 441922c0d00Stshiozak { 442922c0d00Stshiozak int i; 443922c0d00Stshiozak const char *src; 444922c0d00Stshiozak char *dst; 445922c0d00Stshiozak 446922c0d00Stshiozak /* check whether already expanded */ 447922c0d00Stshiozak if (str->expanded) 448922c0d00Stshiozak return 0; 449922c0d00Stshiozak 450922c0d00Stshiozak /* calc total length */ 451922c0d00Stshiozak str->expanded_len = 1; 452922c0d00Stshiozak for (i=0; /*CONSTCOND*/1; i++) { 453922c0d00Stshiozak str->expanded_len += str->segs[i].len; 454922c0d00Stshiozak if (str->segs[i].ref == MO_LASTSEG) 455922c0d00Stshiozak break; 456922c0d00Stshiozak str->expanded_len += 457922c0d00Stshiozak mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len; 458922c0d00Stshiozak } 459922c0d00Stshiozak /* expand */ 460922c0d00Stshiozak str->expanded = malloc(str->expanded_len); 461922c0d00Stshiozak if (!str->expanded) 462922c0d00Stshiozak return -1; 463922c0d00Stshiozak src = str->off; 464922c0d00Stshiozak dst = str->expanded; 465922c0d00Stshiozak for (i=0; /*CONSTCOND*/1; i++) { 466922c0d00Stshiozak memcpy(dst, src, str->segs[i].len); 467922c0d00Stshiozak src += str->segs[i].len; 468922c0d00Stshiozak dst += str->segs[i].len; 469922c0d00Stshiozak if (str->segs[i].ref == MO_LASTSEG) 470922c0d00Stshiozak break; 471922c0d00Stshiozak memcpy(dst, mohandle->mo.mo_sysdep_segs[str->segs[i].ref].str, 472922c0d00Stshiozak mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len); 473922c0d00Stshiozak dst += mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len; 474922c0d00Stshiozak } 475922c0d00Stshiozak *dst = '\0'; 476922c0d00Stshiozak 477922c0d00Stshiozak return 0; 478922c0d00Stshiozak } 479922c0d00Stshiozak 480922c0d00Stshiozak static void 4814271bf2eSjunyoung insert_to_hash(uint32_t *htable, uint32_t hsize, const char *str, uint32_t ref) 482922c0d00Stshiozak { 4834271bf2eSjunyoung uint32_t hashval, idx, step; 484922c0d00Stshiozak 485922c0d00Stshiozak hashval = __intl_string_hash(str); 486922c0d00Stshiozak step = calc_collision_step(hashval, hsize); 487922c0d00Stshiozak idx = hashval % hsize; 488922c0d00Stshiozak 489922c0d00Stshiozak while (htable[idx]) 490922c0d00Stshiozak idx = calc_next_index(idx, hsize, step); 491922c0d00Stshiozak 492922c0d00Stshiozak htable[idx] = ref; 493922c0d00Stshiozak } 494922c0d00Stshiozak 495922c0d00Stshiozak static int 496922c0d00Stshiozak setup_sysdep_stuffs(struct mo *mo, struct mohandle *mohandle, char *base) 497922c0d00Stshiozak { 4984271bf2eSjunyoung uint32_t magic; 499922c0d00Stshiozak struct moentry *stable; 500922c0d00Stshiozak size_t l; 501e1a2f47fSmatt unsigned int i; 502922c0d00Stshiozak char *v; 5034271bf2eSjunyoung uint32_t *ofstable; 504922c0d00Stshiozak 505922c0d00Stshiozak magic = mo->mo_magic; 506922c0d00Stshiozak 507922c0d00Stshiozak mohandle->mo.mo_sysdep_nsegs = flip(mo->mo_sysdep_nsegs, magic); 508922c0d00Stshiozak mohandle->mo.mo_sysdep_nstring = flip(mo->mo_sysdep_nstring, magic); 509922c0d00Stshiozak 510922c0d00Stshiozak if (mohandle->mo.mo_sysdep_nstring == 0) 511922c0d00Stshiozak return 0; 512922c0d00Stshiozak 513922c0d00Stshiozak /* check hash size */ 514922c0d00Stshiozak if (mohandle->mo.mo_hsize <= 2 || 515922c0d00Stshiozak mohandle->mo.mo_hsize < 516922c0d00Stshiozak (mohandle->mo.mo_nstring + mohandle->mo.mo_sysdep_nstring)) 517922c0d00Stshiozak return -1; 518922c0d00Stshiozak 519922c0d00Stshiozak /* get sysdep segments */ 5204882631eSyamt l = sizeof(struct mosysdepsegs_h) * mohandle->mo.mo_sysdep_nsegs; 521922c0d00Stshiozak mohandle->mo.mo_sysdep_segs = (struct mosysdepsegs_h *)malloc(l); 522922c0d00Stshiozak if (!mohandle->mo.mo_sysdep_segs) 523922c0d00Stshiozak return -1; 524922c0d00Stshiozak /* LINTED: ignore the alignment problem. */ 525922c0d00Stshiozak stable = (struct moentry *)(base + flip(mo->mo_sysdep_segoff, magic)); 526922c0d00Stshiozak for (i=0; i<mohandle->mo.mo_sysdep_nsegs; i++) { 527922c0d00Stshiozak v = base + flip(stable[i].off, magic); 528922c0d00Stshiozak mohandle->mo.mo_sysdep_segs[i].str = 529922c0d00Stshiozak __intl_sysdep_get_string_by_tag( 530922c0d00Stshiozak v, 531922c0d00Stshiozak &mohandle->mo.mo_sysdep_segs[i].len); 532922c0d00Stshiozak } 533922c0d00Stshiozak 534922c0d00Stshiozak /* get sysdep string table */ 535922c0d00Stshiozak mohandle->mo.mo_sysdep_otable = 536922c0d00Stshiozak (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring, 537922c0d00Stshiozak sizeof(struct mosysdepstr_h *)); 538922c0d00Stshiozak if (!mohandle->mo.mo_sysdep_otable) 539922c0d00Stshiozak return -1; 540922c0d00Stshiozak /* LINTED: ignore the alignment problem. */ 5414271bf2eSjunyoung ofstable = (uint32_t *)(base + flip(mo->mo_sysdep_otable, magic)); 542922c0d00Stshiozak if (get_sysdep_string_table(mohandle->mo.mo_sysdep_otable, ofstable, 543922c0d00Stshiozak mohandle->mo.mo_sysdep_nstring, magic, 544922c0d00Stshiozak base)) 545922c0d00Stshiozak return -1; 546922c0d00Stshiozak mohandle->mo.mo_sysdep_ttable = 547922c0d00Stshiozak (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring, 548922c0d00Stshiozak sizeof(struct mosysdepstr_h *)); 549922c0d00Stshiozak if (!mohandle->mo.mo_sysdep_ttable) 550922c0d00Stshiozak return -1; 551922c0d00Stshiozak /* LINTED: ignore the alignment problem. */ 5524271bf2eSjunyoung ofstable = (uint32_t *)(base + flip(mo->mo_sysdep_ttable, magic)); 553922c0d00Stshiozak if (get_sysdep_string_table(mohandle->mo.mo_sysdep_ttable, ofstable, 554922c0d00Stshiozak mohandle->mo.mo_sysdep_nstring, magic, 555922c0d00Stshiozak base)) 556922c0d00Stshiozak return -1; 557922c0d00Stshiozak 558922c0d00Stshiozak /* update hash */ 559922c0d00Stshiozak for (i=0; i<mohandle->mo.mo_sysdep_nstring; i++) { 560922c0d00Stshiozak if (expand_sysdep(mohandle, mohandle->mo.mo_sysdep_otable[i])) 561922c0d00Stshiozak return -1; 562922c0d00Stshiozak insert_to_hash(mohandle->mo.mo_htable, 563922c0d00Stshiozak mohandle->mo.mo_hsize, 564922c0d00Stshiozak mohandle->mo.mo_sysdep_otable[i]->expanded, 565922c0d00Stshiozak (i+1) | MO_HASH_SYSDEP_MASK); 566922c0d00Stshiozak } 567922c0d00Stshiozak 568922c0d00Stshiozak return 0; 569922c0d00Stshiozak } 570922c0d00Stshiozak 571bb42e65bSitojun int 5724271bf2eSjunyoung mapit(const char *path, struct domainbinding *db) 573bb42e65bSitojun { 574bb42e65bSitojun int fd; 575bb42e65bSitojun struct stat st; 576bb42e65bSitojun char *base; 5774271bf2eSjunyoung uint32_t magic, revision, flags = 0; 578bb42e65bSitojun struct moentry *otable, *ttable; 5794271bf2eSjunyoung const uint32_t *htable; 580bb42e65bSitojun struct moentry_h *p; 581bb42e65bSitojun struct mo *mo; 582dd416aa8Stshiozak size_t l, headerlen; 583e1a2f47fSmatt unsigned int i; 584bb42e65bSitojun char *v; 5853bad25dcSminoura struct mohandle *mohandle = &db->mohandle; 586bb42e65bSitojun 5873bad25dcSminoura if (mohandle->addr && mohandle->addr != MAP_FAILED && 5883bad25dcSminoura mohandle->mo.mo_magic) 589bb42e65bSitojun return 0; /*already opened*/ 590bb42e65bSitojun 5913bad25dcSminoura unmapit(db); 592bb42e65bSitojun 593bb42e65bSitojun #if 0 594bb42e65bSitojun if (secure_path(path) != 0) 595bb42e65bSitojun goto fail; 596bb42e65bSitojun #endif 597bb42e65bSitojun if (stat(path, &st) < 0) 598bb42e65bSitojun goto fail; 599bb42e65bSitojun if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX) 600bb42e65bSitojun goto fail; 601bb42e65bSitojun fd = open(path, O_RDONLY); 602bb42e65bSitojun if (fd < 0) 603bb42e65bSitojun goto fail; 604435b1e30Sitojun if (read(fd, &magic, sizeof(magic)) != sizeof(magic) || 605bb42e65bSitojun (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) { 606bb42e65bSitojun close(fd); 607bb42e65bSitojun goto fail; 608bb42e65bSitojun } 60929be49b4Stshiozak if (read(fd, &revision, sizeof(revision)) != sizeof(revision)) { 61029be49b4Stshiozak close(fd); 61129be49b4Stshiozak goto fail; 61229be49b4Stshiozak } 61329be49b4Stshiozak switch (flip(revision, magic)) { 61429be49b4Stshiozak case MO_MAKE_REV(0, 0): 615922c0d00Stshiozak break; 61629be49b4Stshiozak case MO_MAKE_REV(0, 1): 61729be49b4Stshiozak case MO_MAKE_REV(1, 1): 618922c0d00Stshiozak flags |= MO_F_SYSDEP; 61929be49b4Stshiozak break; 62029be49b4Stshiozak default: 621bb42e65bSitojun close(fd); 622bb42e65bSitojun goto fail; 623bb42e65bSitojun } 6243bad25dcSminoura mohandle->addr = mmap(NULL, (size_t)st.st_size, PROT_READ, 625099fdc2aSitojun MAP_FILE | MAP_SHARED, fd, (off_t)0); 6263bad25dcSminoura if (!mohandle->addr || mohandle->addr == MAP_FAILED) { 627bb42e65bSitojun close(fd); 628bb42e65bSitojun goto fail; 629bb42e65bSitojun } 630bb42e65bSitojun close(fd); 6313bad25dcSminoura mohandle->len = (size_t)st.st_size; 632bb42e65bSitojun 6333bad25dcSminoura base = mohandle->addr; 6343bad25dcSminoura mo = (struct mo *)mohandle->addr; 635bb42e65bSitojun 636bb42e65bSitojun /* flip endian. do not flip magic number! */ 6373bad25dcSminoura mohandle->mo.mo_magic = mo->mo_magic; 6383bad25dcSminoura mohandle->mo.mo_revision = flip(mo->mo_revision, magic); 6393bad25dcSminoura mohandle->mo.mo_nstring = flip(mo->mo_nstring, magic); 64029be49b4Stshiozak mohandle->mo.mo_hsize = flip(mo->mo_hsize, magic); 641922c0d00Stshiozak mohandle->mo.mo_flags = flags; 642bb42e65bSitojun 643bb42e65bSitojun /* validate otable/ttable */ 64429be49b4Stshiozak /* LINTED: ignore the alignment problem. */ 645bb42e65bSitojun otable = (struct moentry *)(base + flip(mo->mo_otable, magic)); 64629be49b4Stshiozak /* LINTED: ignore the alignment problem. */ 647bb42e65bSitojun ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic)); 6483bad25dcSminoura if (!validate(otable, mohandle) || 6493bad25dcSminoura !validate(&otable[mohandle->mo.mo_nstring], mohandle)) { 6503bad25dcSminoura unmapit(db); 651bb42e65bSitojun goto fail; 652bb42e65bSitojun } 6533bad25dcSminoura if (!validate(ttable, mohandle) || 6543bad25dcSminoura !validate(&ttable[mohandle->mo.mo_nstring], mohandle)) { 6553bad25dcSminoura unmapit(db); 656bb42e65bSitojun goto fail; 657bb42e65bSitojun } 658bb42e65bSitojun 659bb42e65bSitojun /* allocate [ot]table, and convert to normal pointer representation. */ 6603bad25dcSminoura l = sizeof(struct moentry_h) * mohandle->mo.mo_nstring; 6613bad25dcSminoura mohandle->mo.mo_otable = (struct moentry_h *)malloc(l); 6623bad25dcSminoura if (!mohandle->mo.mo_otable) { 6633bad25dcSminoura unmapit(db); 664bb42e65bSitojun goto fail; 665bb42e65bSitojun } 6663bad25dcSminoura mohandle->mo.mo_ttable = (struct moentry_h *)malloc(l); 6673bad25dcSminoura if (!mohandle->mo.mo_ttable) { 6683bad25dcSminoura unmapit(db); 669bb42e65bSitojun goto fail; 670bb42e65bSitojun } 6713bad25dcSminoura p = mohandle->mo.mo_otable; 6723bad25dcSminoura for (i = 0; i < mohandle->mo.mo_nstring; i++) { 673bb42e65bSitojun p[i].len = flip(otable[i].len, magic); 674bb42e65bSitojun p[i].off = base + flip(otable[i].off, magic); 675bb42e65bSitojun 6763bad25dcSminoura if (!validate(p[i].off, mohandle) || 6773bad25dcSminoura !validate(p[i].off + p[i].len + 1, mohandle)) { 6783bad25dcSminoura unmapit(db); 679bb42e65bSitojun goto fail; 680bb42e65bSitojun } 681bb42e65bSitojun } 6823bad25dcSminoura p = mohandle->mo.mo_ttable; 6833bad25dcSminoura for (i = 0; i < mohandle->mo.mo_nstring; i++) { 684bb42e65bSitojun p[i].len = flip(ttable[i].len, magic); 685bb42e65bSitojun p[i].off = base + flip(ttable[i].off, magic); 686bb42e65bSitojun 6873bad25dcSminoura if (!validate(p[i].off, mohandle) || 6883bad25dcSminoura !validate(p[i].off + p[i].len + 1, mohandle)) { 6893bad25dcSminoura unmapit(db); 690bb42e65bSitojun goto fail; 691bb42e65bSitojun } 692bb42e65bSitojun } 69329be49b4Stshiozak /* allocate htable, and convert it to the host order. */ 69429be49b4Stshiozak if (mohandle->mo.mo_hsize > 2) { 6954271bf2eSjunyoung l = sizeof(uint32_t) * mohandle->mo.mo_hsize; 6964271bf2eSjunyoung mohandle->mo.mo_htable = (uint32_t *)malloc(l); 69729be49b4Stshiozak if (!mohandle->mo.mo_htable) { 69829be49b4Stshiozak unmapit(db); 69929be49b4Stshiozak goto fail; 70029be49b4Stshiozak } 70129be49b4Stshiozak /* LINTED: ignore the alignment problem. */ 7024271bf2eSjunyoung htable = (const uint32_t *)(base+flip(mo->mo_hoffset, magic)); 70329be49b4Stshiozak for (i=0; i < mohandle->mo.mo_hsize; i++) { 70429be49b4Stshiozak mohandle->mo.mo_htable[i] = flip(htable[i], magic); 70529be49b4Stshiozak if (mohandle->mo.mo_htable[i] >= 70629be49b4Stshiozak mohandle->mo.mo_nstring+1) { 70729be49b4Stshiozak /* illegal string number. */ 70829be49b4Stshiozak unmapit(db); 70929be49b4Stshiozak goto fail; 71029be49b4Stshiozak } 71129be49b4Stshiozak } 71229be49b4Stshiozak } 713bb42e65bSitojun /* grab MIME-header and charset field */ 714dd416aa8Stshiozak mohandle->mo.mo_header = lookup("", db, &headerlen); 7153bad25dcSminoura if (mohandle->mo.mo_header) 7163bad25dcSminoura v = strstr(mohandle->mo.mo_header, "charset="); 717bb42e65bSitojun else 718bb42e65bSitojun v = NULL; 719bb42e65bSitojun if (v) { 7203bad25dcSminoura mohandle->mo.mo_charset = strdup(v + 8); 7213bad25dcSminoura if (!mohandle->mo.mo_charset) 7223280f611Sitojun goto fail; 7233bad25dcSminoura v = strchr(mohandle->mo.mo_charset, '\n'); 724bb42e65bSitojun if (v) 725bb42e65bSitojun *v = '\0'; 726bb42e65bSitojun } 727dc7498dfStnozaki if (!mohandle->mo.mo_header || 728dc7498dfStnozaki _gettext_parse_plural(&mohandle->mo.mo_plural, 729dd416aa8Stshiozak &mohandle->mo.mo_nplurals, 730dd416aa8Stshiozak mohandle->mo.mo_header, headerlen)) 731dd416aa8Stshiozak mohandle->mo.mo_plural = NULL; 732bb42e65bSitojun 733bb42e65bSitojun /* 734bb42e65bSitojun * XXX check charset, reject it if we are unable to support the charset 735bb42e65bSitojun * with the current locale. 736bb42e65bSitojun * for example, if we are using euc-jp locale and we are looking at 737bb42e65bSitojun * *.mo file encoded by euc-kr (charset=euc-kr), we should reject 738bb42e65bSitojun * the *.mo file as we cannot support it. 739bb42e65bSitojun */ 740bb42e65bSitojun 741922c0d00Stshiozak /* system dependent string support */ 742922c0d00Stshiozak if ((mohandle->mo.mo_flags & MO_F_SYSDEP) != 0) { 743922c0d00Stshiozak if (setup_sysdep_stuffs(mo, mohandle, base)) { 744922c0d00Stshiozak unmapit(db); 745922c0d00Stshiozak goto fail; 746922c0d00Stshiozak } 747922c0d00Stshiozak } 748922c0d00Stshiozak 749bb42e65bSitojun return 0; 750bb42e65bSitojun 751bb42e65bSitojun fail: 752bb42e65bSitojun return -1; 753bb42e65bSitojun } 754bb42e65bSitojun 755922c0d00Stshiozak static void 7564271bf2eSjunyoung free_sysdep_table(struct mosysdepstr_h **table, uint32_t nstring) 757922c0d00Stshiozak { 758922c0d00Stshiozak 7595297022aSchristos if (! table) 7605297022aSchristos return; 7615297022aSchristos 7625297022aSchristos for (uint32_t i = 0; i < nstring; i++) { 763922c0d00Stshiozak if (table[i]) { 764922c0d00Stshiozak free(table[i]->expanded); 765922c0d00Stshiozak free(table[i]); 766922c0d00Stshiozak } 767922c0d00Stshiozak } 768922c0d00Stshiozak free(table); 769922c0d00Stshiozak } 770922c0d00Stshiozak 771bb42e65bSitojun static int 7724271bf2eSjunyoung unmapit(struct domainbinding *db) 773bb42e65bSitojun { 7743bad25dcSminoura struct mohandle *mohandle = &db->mohandle; 775bb42e65bSitojun 776bb42e65bSitojun /* unmap if there's already mapped region */ 7773bad25dcSminoura if (mohandle->addr && mohandle->addr != MAP_FAILED) 7783bad25dcSminoura munmap(mohandle->addr, mohandle->len); 7793bad25dcSminoura mohandle->addr = NULL; 7803bad25dcSminoura free(mohandle->mo.mo_otable); 7813bad25dcSminoura free(mohandle->mo.mo_ttable); 7823bad25dcSminoura free(mohandle->mo.mo_charset); 78329be49b4Stshiozak free(mohandle->mo.mo_htable); 784922c0d00Stshiozak free(mohandle->mo.mo_sysdep_segs); 785922c0d00Stshiozak free_sysdep_table(mohandle->mo.mo_sysdep_otable, 786922c0d00Stshiozak mohandle->mo.mo_sysdep_nstring); 787922c0d00Stshiozak free_sysdep_table(mohandle->mo.mo_sysdep_ttable, 788922c0d00Stshiozak mohandle->mo.mo_sysdep_nstring); 789dd416aa8Stshiozak _gettext_free_plural(mohandle->mo.mo_plural); 7903bad25dcSminoura memset(&mohandle->mo, 0, sizeof(mohandle->mo)); 791bb42e65bSitojun return 0; 792bb42e65bSitojun } 793bb42e65bSitojun 7943bad25dcSminoura /* ARGSUSED */ 795bb42e65bSitojun static const char * 7964271bf2eSjunyoung lookup_hash(const char *msgid, struct domainbinding *db, size_t *rlen) 797bb42e65bSitojun { 79829be49b4Stshiozak struct mohandle *mohandle = &db->mohandle; 7994271bf2eSjunyoung uint32_t idx, hashval, step, strno; 80029be49b4Stshiozak size_t len; 801922c0d00Stshiozak struct mosysdepstr_h *sysdep_otable, *sysdep_ttable; 802bb42e65bSitojun 80329be49b4Stshiozak if (mohandle->mo.mo_hsize <= 2 || mohandle->mo.mo_htable == NULL) 804bb42e65bSitojun return NULL; 80529be49b4Stshiozak 80629be49b4Stshiozak hashval = __intl_string_hash(msgid); 80729be49b4Stshiozak step = calc_collision_step(hashval, mohandle->mo.mo_hsize); 80829be49b4Stshiozak idx = hashval % mohandle->mo.mo_hsize; 80929be49b4Stshiozak len = strlen(msgid); 81029be49b4Stshiozak while (/*CONSTCOND*/1) { 81129be49b4Stshiozak strno = mohandle->mo.mo_htable[idx]; 81229be49b4Stshiozak if (strno == 0) { 81329be49b4Stshiozak /* unexpected miss */ 81429be49b4Stshiozak return NULL; 81529be49b4Stshiozak } 81629be49b4Stshiozak strno--; 817922c0d00Stshiozak if ((strno & MO_HASH_SYSDEP_MASK) == 0) { 818922c0d00Stshiozak /* system independent strings */ 81929be49b4Stshiozak if (len <= mohandle->mo.mo_otable[strno].len && 82029be49b4Stshiozak !strcmp(msgid, mohandle->mo.mo_otable[strno].off)) { 82129be49b4Stshiozak /* hit */ 822dd416aa8Stshiozak if (rlen) 823dd416aa8Stshiozak *rlen = 824dd416aa8Stshiozak mohandle->mo.mo_ttable[strno].len; 82529be49b4Stshiozak return mohandle->mo.mo_ttable[strno].off; 82629be49b4Stshiozak } 827922c0d00Stshiozak } else { 828922c0d00Stshiozak /* system dependent strings */ 829922c0d00Stshiozak strno &= ~MO_HASH_SYSDEP_MASK; 830922c0d00Stshiozak sysdep_otable = mohandle->mo.mo_sysdep_otable[strno]; 831922c0d00Stshiozak sysdep_ttable = mohandle->mo.mo_sysdep_ttable[strno]; 832922c0d00Stshiozak if (len <= sysdep_otable->expanded_len && 833922c0d00Stshiozak !strcmp(msgid, sysdep_otable->expanded)) { 834922c0d00Stshiozak /* hit */ 835922c0d00Stshiozak if (expand_sysdep(mohandle, sysdep_ttable)) 836922c0d00Stshiozak /* memory exhausted */ 837922c0d00Stshiozak return NULL; 838dd416aa8Stshiozak if (rlen) 839dd416aa8Stshiozak *rlen = sysdep_ttable->expanded_len; 840922c0d00Stshiozak return sysdep_ttable->expanded; 841922c0d00Stshiozak } 842922c0d00Stshiozak } 84329be49b4Stshiozak idx = calc_next_index(idx, mohandle->mo.mo_hsize, step); 84429be49b4Stshiozak } 84529be49b4Stshiozak /*NOTREACHED*/ 846bb42e65bSitojun } 847bb42e65bSitojun 848bb42e65bSitojun static const char * 8494271bf2eSjunyoung lookup_bsearch(const char *msgid, struct domainbinding *db, size_t *rlen) 850bb42e65bSitojun { 851bb42e65bSitojun int top, bottom, middle, omiddle; 852bb42e65bSitojun int n; 8533bad25dcSminoura struct mohandle *mohandle = &db->mohandle; 854bb42e65bSitojun 855bb42e65bSitojun top = 0; 8563bad25dcSminoura bottom = mohandle->mo.mo_nstring; 857bb42e65bSitojun omiddle = -1; 8583bad25dcSminoura /* CONSTCOND */ 859bb42e65bSitojun while (1) { 860bb42e65bSitojun if (top > bottom) 861099fdc2aSitojun break; 862bb42e65bSitojun middle = (top + bottom) / 2; 863bb42e65bSitojun /* avoid possible infinite loop, when the data is not sorted */ 864bb42e65bSitojun if (omiddle == middle) 865099fdc2aSitojun break; 866e1a2f47fSmatt if ((size_t)middle >= mohandle->mo.mo_nstring) 867099fdc2aSitojun break; 868bb42e65bSitojun 8693bad25dcSminoura n = strcmp(msgid, mohandle->mo.mo_otable[middle].off); 870dd416aa8Stshiozak if (n == 0) { 871dd416aa8Stshiozak if (rlen) 872dd416aa8Stshiozak *rlen = mohandle->mo.mo_ttable[middle].len; 8733bad25dcSminoura return (const char *)mohandle->mo.mo_ttable[middle].off; 874dd416aa8Stshiozak } 875bb42e65bSitojun else if (n < 0) 876bb42e65bSitojun bottom = middle; 877bb42e65bSitojun else 878bb42e65bSitojun top = middle; 879bb42e65bSitojun omiddle = middle; 880bb42e65bSitojun } 881bb42e65bSitojun 882bb42e65bSitojun return NULL; 883bb42e65bSitojun } 884bb42e65bSitojun 885bb42e65bSitojun static const char * 8864271bf2eSjunyoung lookup(const char *msgid, struct domainbinding *db, size_t *rlen) 887bb42e65bSitojun { 888bb42e65bSitojun const char *v; 889bb42e65bSitojun 890dd416aa8Stshiozak v = lookup_hash(msgid, db, rlen); 891bb42e65bSitojun if (v) 892bb42e65bSitojun return v; 893bb42e65bSitojun 894dd416aa8Stshiozak return lookup_bsearch(msgid, db, rlen); 895bb42e65bSitojun } 896bb42e65bSitojun 8970169ab0aSitojun static const char * 8980169ab0aSitojun get_lang_env(const char *category_name) 899782b5c88Syamt { 900782b5c88Syamt const char *lang; 901782b5c88Syamt 9025c3967c0Syamt /* 9035c3967c0Syamt * 1. see LANGUAGE variable first. 9045c3967c0Syamt * 9055c3967c0Syamt * LANGUAGE is a GNU extension. 9065c3967c0Syamt * It's a colon separated list of locale names. 9075c3967c0Syamt */ 908782b5c88Syamt lang = getenv("LANGUAGE"); 909782b5c88Syamt if (lang) 910782b5c88Syamt return lang; 911782b5c88Syamt 9125c3967c0Syamt /* 9135c3967c0Syamt * 2. if LANGUAGE isn't set, see LC_ALL, LC_xxx, LANG. 9145c3967c0Syamt * 9155c3967c0Syamt * It's essentially setlocale(LC_xxx, NULL). 9165c3967c0Syamt */ 917782b5c88Syamt lang = getenv("LC_ALL"); 918782b5c88Syamt if (!lang) 9196c208635Syamt lang = getenv(category_name); 9206c208635Syamt if (!lang) 921782b5c88Syamt lang = getenv("LANG"); 922782b5c88Syamt 923782b5c88Syamt if (!lang) 924782b5c88Syamt return 0; /* error */ 925782b5c88Syamt 926782b5c88Syamt return split_locale(lang); 927782b5c88Syamt } 928782b5c88Syamt 929dd416aa8Stshiozak static const char * 930dd416aa8Stshiozak get_indexed_string(const char *str, size_t len, unsigned long idx) 931dd416aa8Stshiozak { 932dd416aa8Stshiozak while (idx > 0) { 933dd416aa8Stshiozak if (len <= 1) 934dd416aa8Stshiozak return str; 935dd416aa8Stshiozak if (*str == '\0') 936dd416aa8Stshiozak idx--; 937dd416aa8Stshiozak if (len > 0) { 938dd416aa8Stshiozak str++; 939dd416aa8Stshiozak len--; 940dd416aa8Stshiozak } 941dd416aa8Stshiozak } 942dd416aa8Stshiozak return str; 943dd416aa8Stshiozak } 944dd416aa8Stshiozak 94562de7e0cSyamt #define _NGETTEXT_DEFAULT(msgid1, msgid2, n) \ 94662de7e0cSyamt ((char *)__UNCONST((n) == 1 ? (msgid1) : (msgid2))) 94762de7e0cSyamt 948bb42e65bSitojun char * 9494271bf2eSjunyoung dcngettext(const char *domainname, const char *msgid1, const char *msgid2, 9504271bf2eSjunyoung unsigned long int n, int category) 951bb42e65bSitojun { 952bb42e65bSitojun const char *msgid; 95382974f22Smrg char path[PATH_MAX+1]; 954782b5c88Syamt const char *lpath; 955bb42e65bSitojun static char olpath[PATH_MAX]; 9563280f611Sitojun const char *cname = NULL; 957bb42e65bSitojun const char *v; 9583280f611Sitojun static char *ocname = NULL; 9593280f611Sitojun static char *odomainname = NULL; 960a09524d4Sitojun struct domainbinding *db; 9619f3f3adfSlukem unsigned long plural_index = 0; 962dd416aa8Stshiozak size_t len; 963bb42e65bSitojun 964bb42e65bSitojun if (!domainname) 9653bad25dcSminoura domainname = __current_domainname; 966bb42e65bSitojun cname = lookup_category(category); 967bb42e65bSitojun if (!domainname || !cname) 968bb42e65bSitojun goto fail; 969bb42e65bSitojun 970782b5c88Syamt lpath = get_lang_env(cname); 971782b5c88Syamt if (!lpath) 972bb42e65bSitojun goto fail; 973bb42e65bSitojun 9743bad25dcSminoura for (db = __bindings; db; db = db->next) 975a09524d4Sitojun if (strcmp(db->domainname, domainname) == 0) 976a09524d4Sitojun break; 9773bad25dcSminoura if (!db) { 9783bad25dcSminoura if (!bindtextdomain(domainname, _PATH_TEXTDOMAIN)) 9793bad25dcSminoura goto fail; 9803bad25dcSminoura db = __bindings; 9813bad25dcSminoura } 982a09524d4Sitojun 9830c36fb14Syamt /* resolve relative path */ 9840c36fb14Syamt /* XXX not necessary? */ 9850c36fb14Syamt if (db->path[0] != '/') { 9860c36fb14Syamt char buf[PATH_MAX]; 9870c36fb14Syamt 9880c36fb14Syamt if (getcwd(buf, sizeof(buf)) == 0) 9890c36fb14Syamt goto fail; 9900c36fb14Syamt if (strlcat(buf, "/", sizeof(buf)) >= sizeof(buf)) 9910c36fb14Syamt goto fail; 9920c36fb14Syamt if (strlcat(buf, db->path, sizeof(buf)) >= sizeof(buf)) 9930c36fb14Syamt goto fail; 994edffd458Sitojun strlcpy(db->path, buf, sizeof(db->path)); 9950c36fb14Syamt } 9960c36fb14Syamt 997bb42e65bSitojun /* don't bother looking it up if the values are the same */ 998a09524d4Sitojun if (odomainname && strcmp(domainname, odomainname) == 0 && 9993bad25dcSminoura ocname && strcmp(cname, ocname) == 0 && strcmp(lpath, olpath) == 0 && 10003bad25dcSminoura db->mohandle.mo.mo_magic) 1001bb42e65bSitojun goto found; 1002bb42e65bSitojun 1003bb42e65bSitojun /* try to find appropriate file, from $LANGUAGE */ 1004a09524d4Sitojun if (lookup_mofile(path, sizeof(path), db->path, lpath, cname, 10053bad25dcSminoura domainname, db) == NULL) 1006bb42e65bSitojun goto fail; 1007bb42e65bSitojun 1008a09524d4Sitojun free(odomainname); 1009a09524d4Sitojun free(ocname); 10105297022aSchristos 10113280f611Sitojun odomainname = strdup(domainname); 1012a09524d4Sitojun ocname = strdup(cname); 10133280f611Sitojun if (!odomainname || !ocname) { 10143280f611Sitojun free(odomainname); 10153280f611Sitojun free(ocname); 10165297022aSchristos 10173280f611Sitojun odomainname = ocname = NULL; 10183280f611Sitojun } 1019782b5c88Syamt else 1020a09524d4Sitojun strlcpy(olpath, lpath, sizeof(olpath)); 1021a09524d4Sitojun 1022bb42e65bSitojun found: 1023dd416aa8Stshiozak if (db->mohandle.mo.mo_plural) { 1024dd416aa8Stshiozak plural_index = 1025dd416aa8Stshiozak _gettext_calculate_plural(db->mohandle.mo.mo_plural, n); 1026dd416aa8Stshiozak if (plural_index >= db->mohandle.mo.mo_nplurals) 1027dd416aa8Stshiozak plural_index = 0; 1028dd416aa8Stshiozak msgid = msgid1; 1029dd416aa8Stshiozak } else 103062de7e0cSyamt msgid = _NGETTEXT_DEFAULT(msgid1, msgid2, n); 1031dd416aa8Stshiozak 1032dd416aa8Stshiozak if (msgid == NULL) 1033dd416aa8Stshiozak return NULL; 1034dd416aa8Stshiozak 1035dd416aa8Stshiozak v = lookup(msgid, db, &len); 1036bb42e65bSitojun if (v) { 1037dd416aa8Stshiozak if (db->mohandle.mo.mo_plural) 1038dd416aa8Stshiozak v = get_indexed_string(v, len, plural_index); 1039bb42e65bSitojun /* 1040f3ec2d57Syamt * convert the translated message's encoding. 1041f3ec2d57Syamt * 1042f3ec2d57Syamt * special case: 1043f3ec2d57Syamt * a result of gettext("") shouldn't need any conversion. 1044bb42e65bSitojun */ 1045f3ec2d57Syamt if (msgid[0]) 1046f3ec2d57Syamt v = __gettext_iconv(v, db); 1047bb42e65bSitojun 1048bb42e65bSitojun /* 1049bb42e65bSitojun * Given the amount of printf-format security issues, it may 1050bb42e65bSitojun * be a good idea to validate if the original msgid and the 1051bb42e65bSitojun * translated message format string carry the same printf-like 1052bb42e65bSitojun * format identifiers. 1053bb42e65bSitojun */ 1054bb42e65bSitojun 1055bb42e65bSitojun msgid = v; 1056bb42e65bSitojun } 1057bb42e65bSitojun 105829be49b4Stshiozak return (char *)__UNCONST(msgid); 105962de7e0cSyamt 106062de7e0cSyamt fail: 106162de7e0cSyamt return _NGETTEXT_DEFAULT(msgid1, msgid2, n); 1062bb42e65bSitojun } 1063