1*bb42e65bSitojun /* $NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */ 2*bb42e65bSitojun 3*bb42e65bSitojun /*- 4*bb42e65bSitojun * Copyright (c) 2000 Citrus Project, 5*bb42e65bSitojun * All rights reserved. 6*bb42e65bSitojun * 7*bb42e65bSitojun * Redistribution and use in source and binary forms, with or without 8*bb42e65bSitojun * modification, are permitted provided that the following conditions 9*bb42e65bSitojun * are met: 10*bb42e65bSitojun * 1. Redistributions of source code must retain the above copyright 11*bb42e65bSitojun * notice, this list of conditions and the following disclaimer. 12*bb42e65bSitojun * 2. Redistributions in binary form must reproduce the above copyright 13*bb42e65bSitojun * notice, this list of conditions and the following disclaimer in the 14*bb42e65bSitojun * documentation and/or other materials provided with the distribution. 15*bb42e65bSitojun * 16*bb42e65bSitojun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17*bb42e65bSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*bb42e65bSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*bb42e65bSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20*bb42e65bSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*bb42e65bSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22*bb42e65bSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23*bb42e65bSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24*bb42e65bSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25*bb42e65bSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26*bb42e65bSitojun * SUCH DAMAGE. 27*bb42e65bSitojun */ 28*bb42e65bSitojun 29*bb42e65bSitojun #include <sys/cdefs.h> 30*bb42e65bSitojun #if defined(LIBC_SCCS) && !defined(lint) 31*bb42e65bSitojun __RCSID("$NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $"); 32*bb42e65bSitojun #endif /* LIBC_SCCS and not lint */ 33*bb42e65bSitojun 34*bb42e65bSitojun #include <sys/types.h> 35*bb42e65bSitojun #include <sys/param.h> 36*bb42e65bSitojun #include <sys/stat.h> 37*bb42e65bSitojun #include <sys/mman.h> 38*bb42e65bSitojun #include <sys/uio.h> 39*bb42e65bSitojun 40*bb42e65bSitojun #include <fcntl.h> 41*bb42e65bSitojun #include <stdio.h> 42*bb42e65bSitojun #include <stdlib.h> 43*bb42e65bSitojun #include <unistd.h> 44*bb42e65bSitojun #include <string.h> 45*bb42e65bSitojun #if 0 46*bb42e65bSitojun #include <util.h> 47*bb42e65bSitojun #endif 48*bb42e65bSitojun #include <libintl.h> 49*bb42e65bSitojun #include <locale.h> 50*bb42e65bSitojun #include "libintl_local.h" 51*bb42e65bSitojun #include "pathnames.h" 52*bb42e65bSitojun 53*bb42e65bSitojun static struct mohandle mohandle; 54*bb42e65bSitojun 55*bb42e65bSitojun static const char *lookup_category __P((int)); 56*bb42e65bSitojun static const char *split_locale __P((const char *)); 57*bb42e65bSitojun static const char *lookup_mofile __P((char *, size_t, const char *, 58*bb42e65bSitojun char *, const char *, const char *)); 59*bb42e65bSitojun static u_int32_t flip __P((u_int32_t, u_int32_t)); 60*bb42e65bSitojun static int validate __P((void *)); 61*bb42e65bSitojun static int mapit __P((const char *)); 62*bb42e65bSitojun static int unmapit __P((void)); 63*bb42e65bSitojun static const char *lookup_hash __P((const char *)); 64*bb42e65bSitojun static const char *lookup_bsearch __P((const char *)); 65*bb42e65bSitojun static const char *lookup __P((const char *)); 66*bb42e65bSitojun 67*bb42e65bSitojun /* 68*bb42e65bSitojun * shortcut functions. the main implementation resides in dcngettext(). 69*bb42e65bSitojun */ 70*bb42e65bSitojun char * 71*bb42e65bSitojun gettext(msgid) 72*bb42e65bSitojun const char *msgid; 73*bb42e65bSitojun { 74*bb42e65bSitojun 75*bb42e65bSitojun return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES); 76*bb42e65bSitojun } 77*bb42e65bSitojun 78*bb42e65bSitojun char * 79*bb42e65bSitojun dgettext(domainname, msgid) 80*bb42e65bSitojun const char *domainname; 81*bb42e65bSitojun const char *msgid; 82*bb42e65bSitojun { 83*bb42e65bSitojun 84*bb42e65bSitojun return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES); 85*bb42e65bSitojun } 86*bb42e65bSitojun 87*bb42e65bSitojun char * 88*bb42e65bSitojun dcgettext(domainname, msgid, category) 89*bb42e65bSitojun const char *domainname; 90*bb42e65bSitojun const char *msgid; 91*bb42e65bSitojun int category; 92*bb42e65bSitojun { 93*bb42e65bSitojun 94*bb42e65bSitojun return dcngettext(domainname, msgid, NULL, 1UL, category); 95*bb42e65bSitojun } 96*bb42e65bSitojun 97*bb42e65bSitojun char * 98*bb42e65bSitojun ngettext(msgid1, msgid2, n) 99*bb42e65bSitojun const char *msgid1; 100*bb42e65bSitojun const char *msgid2; 101*bb42e65bSitojun unsigned long int n; 102*bb42e65bSitojun { 103*bb42e65bSitojun 104*bb42e65bSitojun return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES); 105*bb42e65bSitojun } 106*bb42e65bSitojun 107*bb42e65bSitojun char * 108*bb42e65bSitojun dngettext(domainname, msgid1, msgid2, n) 109*bb42e65bSitojun const char *domainname; 110*bb42e65bSitojun const char *msgid1; 111*bb42e65bSitojun const char *msgid2; 112*bb42e65bSitojun unsigned long int n; 113*bb42e65bSitojun { 114*bb42e65bSitojun 115*bb42e65bSitojun return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); 116*bb42e65bSitojun } 117*bb42e65bSitojun 118*bb42e65bSitojun /* 119*bb42e65bSitojun * dcngettext() - 120*bb42e65bSitojun * lookup internationalized message on database locale/category/domainname 121*bb42e65bSitojun * (like ja_JP.eucJP/LC_MESSAGES/domainname). 122*bb42e65bSitojun * if n equals to 1, internationalized message will be looked up for msgid1. 123*bb42e65bSitojun * otherwise, message will be looked up for msgid2. 124*bb42e65bSitojun * if the lookup fails, the function will return msgid1 or msgid2 as is. 125*bb42e65bSitojun * 126*bb42e65bSitojun * Even though the return type is "char *", caller should not rewrite the 127*bb42e65bSitojun * region pointed to by the return value (should be "const char *", but can't 128*bb42e65bSitojun * change it for compatibility with other implementations). 129*bb42e65bSitojun * 130*bb42e65bSitojun * by default (if domainname == NULL), domainname is taken from the value set 131*bb42e65bSitojun * by textdomain(). usually name of the application (like "ls") is used as 132*bb42e65bSitojun * domainname. category is usually LC_MESSAGES. 133*bb42e65bSitojun * 134*bb42e65bSitojun * the code reads in *.mo files generated by GNU gettext. *.mo is a host- 135*bb42e65bSitojun * endian encoded file. both endians are supported here, as the files are in 136*bb42e65bSitojun * /usr/share/locale! (or we should move those files into /usr/libdata) 137*bb42e65bSitojun */ 138*bb42e65bSitojun 139*bb42e65bSitojun static const char * 140*bb42e65bSitojun lookup_category(category) 141*bb42e65bSitojun int category; 142*bb42e65bSitojun { 143*bb42e65bSitojun 144*bb42e65bSitojun switch (category) { 145*bb42e65bSitojun case LC_COLLATE: return "LC_COLLATE"; 146*bb42e65bSitojun case LC_CTYPE: return "LC_CTYPE"; 147*bb42e65bSitojun case LC_MONETARY: return "LC_MONETARY"; 148*bb42e65bSitojun case LC_NUMERIC: return "LC_NUMERIC"; 149*bb42e65bSitojun case LC_TIME: return "LC_TIME"; 150*bb42e65bSitojun case LC_MESSAGES: return "LC_MESSAGES"; 151*bb42e65bSitojun } 152*bb42e65bSitojun return NULL; 153*bb42e65bSitojun } 154*bb42e65bSitojun 155*bb42e65bSitojun /* 156*bb42e65bSitojun * XPG syntax: language[_territory[.codeset]][@modifier] 157*bb42e65bSitojun * XXX boundary check on "result" is lacking 158*bb42e65bSitojun */ 159*bb42e65bSitojun static const char * 160*bb42e65bSitojun split_locale(lname) 161*bb42e65bSitojun const char *lname; 162*bb42e65bSitojun { 163*bb42e65bSitojun char buf[BUFSIZ], tmp[BUFSIZ]; 164*bb42e65bSitojun char *l, *t, *c, *m; 165*bb42e65bSitojun static char result[BUFSIZ]; 166*bb42e65bSitojun 167*bb42e65bSitojun memset(result, 0, sizeof(result)); 168*bb42e65bSitojun 169*bb42e65bSitojun if (strlen(lname) + 1 > sizeof(buf)) { 170*bb42e65bSitojun fail: 171*bb42e65bSitojun return lname; 172*bb42e65bSitojun } 173*bb42e65bSitojun 174*bb42e65bSitojun strlcpy(buf, lname, sizeof(buf)); 175*bb42e65bSitojun m = strrchr(buf, '@'); 176*bb42e65bSitojun if (m) 177*bb42e65bSitojun *m++ = '\0'; 178*bb42e65bSitojun c = strrchr(buf, '.'); 179*bb42e65bSitojun if (c) 180*bb42e65bSitojun *c++ = '\0'; 181*bb42e65bSitojun t = strrchr(buf, '_'); 182*bb42e65bSitojun if (t) 183*bb42e65bSitojun *t++ = '\0'; 184*bb42e65bSitojun l = buf; 185*bb42e65bSitojun if (strlen(l) == 0) 186*bb42e65bSitojun goto fail; 187*bb42e65bSitojun if (c && !t) 188*bb42e65bSitojun goto fail; 189*bb42e65bSitojun 190*bb42e65bSitojun if (m) { 191*bb42e65bSitojun if (t) { 192*bb42e65bSitojun if (c) { 193*bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s", 194*bb42e65bSitojun l, t, c, m); 195*bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 196*bb42e65bSitojun strlcat(result, ":", sizeof(result)); 197*bb42e65bSitojun } 198*bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m); 199*bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 200*bb42e65bSitojun strlcat(result, ":", sizeof(result)); 201*bb42e65bSitojun } 202*bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s@%s", l, m); 203*bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 204*bb42e65bSitojun strlcat(result, ":", sizeof(result)); 205*bb42e65bSitojun } 206*bb42e65bSitojun if (t) { 207*bb42e65bSitojun if (c) { 208*bb42e65bSitojun snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c); 209*bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 210*bb42e65bSitojun strlcat(result, ":", sizeof(result)); 211*bb42e65bSitojun } 212*bb42e65bSitojun strlcat(result, tmp, sizeof(result)); 213*bb42e65bSitojun strlcat(result, ":", sizeof(result)); 214*bb42e65bSitojun } 215*bb42e65bSitojun strlcat(result, l, sizeof(result)); 216*bb42e65bSitojun 217*bb42e65bSitojun return result; 218*bb42e65bSitojun } 219*bb42e65bSitojun 220*bb42e65bSitojun static const char * 221*bb42e65bSitojun lookup_mofile(buf, len, dir, lpath, category, domainname) 222*bb42e65bSitojun char *buf; 223*bb42e65bSitojun size_t len; 224*bb42e65bSitojun const char *dir; 225*bb42e65bSitojun char *lpath; /* list of locales to be tried */ 226*bb42e65bSitojun const char *category; 227*bb42e65bSitojun const char *domainname; 228*bb42e65bSitojun { 229*bb42e65bSitojun struct stat st; 230*bb42e65bSitojun char *p, *q; 231*bb42e65bSitojun 232*bb42e65bSitojun q = lpath; 233*bb42e65bSitojun while (1) { 234*bb42e65bSitojun p = strsep(&q, ":"); 235*bb42e65bSitojun if (!p) 236*bb42e65bSitojun break; 237*bb42e65bSitojun if (!*p) 238*bb42e65bSitojun continue; 239*bb42e65bSitojun 240*bb42e65bSitojun /* don't mess with default locales */ 241*bb42e65bSitojun if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0) 242*bb42e65bSitojun return NULL; 243*bb42e65bSitojun 244*bb42e65bSitojun /* validate pathname */ 245*bb42e65bSitojun if (strchr(p, '/') || strchr(category, '/')) 246*bb42e65bSitojun continue; 247*bb42e65bSitojun #if 1 /*?*/ 248*bb42e65bSitojun if (strchr(domainname, '/')) 249*bb42e65bSitojun continue; 250*bb42e65bSitojun #endif 251*bb42e65bSitojun 252*bb42e65bSitojun snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p, 253*bb42e65bSitojun category, domainname); 254*bb42e65bSitojun if (stat(buf, &st) < 0) 255*bb42e65bSitojun continue; 256*bb42e65bSitojun if ((st.st_mode & S_IFMT) != S_IFREG) 257*bb42e65bSitojun continue; 258*bb42e65bSitojun 259*bb42e65bSitojun if (mapit(buf) == 0) 260*bb42e65bSitojun return buf; 261*bb42e65bSitojun } 262*bb42e65bSitojun 263*bb42e65bSitojun return NULL; 264*bb42e65bSitojun } 265*bb42e65bSitojun 266*bb42e65bSitojun static u_int32_t 267*bb42e65bSitojun flip(v, magic) 268*bb42e65bSitojun u_int32_t v; 269*bb42e65bSitojun u_int32_t magic; 270*bb42e65bSitojun { 271*bb42e65bSitojun 272*bb42e65bSitojun if (magic == MO_MAGIC) 273*bb42e65bSitojun return v; 274*bb42e65bSitojun else if (magic == MO_MAGIC_SWAPPED) { 275*bb42e65bSitojun v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) | 276*bb42e65bSitojun ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000); 277*bb42e65bSitojun return v; 278*bb42e65bSitojun } else { 279*bb42e65bSitojun abort(); 280*bb42e65bSitojun /*NOTREACHED*/ 281*bb42e65bSitojun } 282*bb42e65bSitojun } 283*bb42e65bSitojun 284*bb42e65bSitojun static int 285*bb42e65bSitojun validate(arg) 286*bb42e65bSitojun void *arg; 287*bb42e65bSitojun { 288*bb42e65bSitojun char *p; 289*bb42e65bSitojun 290*bb42e65bSitojun p = (char *)arg; 291*bb42e65bSitojun if (p < (char *)mohandle.addr || 292*bb42e65bSitojun p > (char *)mohandle.addr + mohandle.len) 293*bb42e65bSitojun return 0; 294*bb42e65bSitojun else 295*bb42e65bSitojun return 1; 296*bb42e65bSitojun } 297*bb42e65bSitojun 298*bb42e65bSitojun int 299*bb42e65bSitojun mapit(path) 300*bb42e65bSitojun const char *path; 301*bb42e65bSitojun { 302*bb42e65bSitojun int fd; 303*bb42e65bSitojun struct stat st; 304*bb42e65bSitojun char *base; 305*bb42e65bSitojun u_int32_t magic, revision; 306*bb42e65bSitojun struct moentry *otable, *ttable; 307*bb42e65bSitojun struct moentry_h *p; 308*bb42e65bSitojun struct mo *mo; 309*bb42e65bSitojun size_t l; 310*bb42e65bSitojun int i; 311*bb42e65bSitojun char *v; 312*bb42e65bSitojun 313*bb42e65bSitojun if (mohandle.addr && strcmp(path, mohandle.path) == 0) 314*bb42e65bSitojun return 0; /*already opened*/ 315*bb42e65bSitojun 316*bb42e65bSitojun unmapit(); 317*bb42e65bSitojun 318*bb42e65bSitojun #if 0 319*bb42e65bSitojun if (secure_path(path) != 0) 320*bb42e65bSitojun goto fail; 321*bb42e65bSitojun #endif 322*bb42e65bSitojun if (stat(path, &st) < 0) 323*bb42e65bSitojun goto fail; 324*bb42e65bSitojun if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX) 325*bb42e65bSitojun goto fail; 326*bb42e65bSitojun fd = open(path, O_RDONLY); 327*bb42e65bSitojun if (fd < 0) 328*bb42e65bSitojun goto fail; 329*bb42e65bSitojun if (read(fd, &magic, sizeof(magic)) < 0 || 330*bb42e65bSitojun (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) { 331*bb42e65bSitojun close(fd); 332*bb42e65bSitojun goto fail; 333*bb42e65bSitojun } 334*bb42e65bSitojun if (read(fd, &revision, sizeof(revision)) < 0 || 335*bb42e65bSitojun flip(revision, magic) != MO_REVISION) { 336*bb42e65bSitojun close(fd); 337*bb42e65bSitojun goto fail; 338*bb42e65bSitojun } 339*bb42e65bSitojun mohandle.addr = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, 340*bb42e65bSitojun fd, (off_t)0); 341*bb42e65bSitojun if (!mohandle.addr) { 342*bb42e65bSitojun close(fd); 343*bb42e65bSitojun goto fail; 344*bb42e65bSitojun } 345*bb42e65bSitojun close(fd); 346*bb42e65bSitojun mohandle.len = st.st_size; 347*bb42e65bSitojun strlcpy(mohandle.path, path, sizeof(mohandle.path)); 348*bb42e65bSitojun 349*bb42e65bSitojun base = mohandle.addr; 350*bb42e65bSitojun mo = (struct mo *)mohandle.addr; 351*bb42e65bSitojun 352*bb42e65bSitojun /* flip endian. do not flip magic number! */ 353*bb42e65bSitojun mohandle.mo.mo_magic = mo->mo_magic; 354*bb42e65bSitojun mohandle.mo.mo_revision = flip(mo->mo_revision, magic); 355*bb42e65bSitojun mohandle.mo.mo_nstring = flip(mo->mo_nstring, magic); 356*bb42e65bSitojun 357*bb42e65bSitojun /* validate otable/ttable */ 358*bb42e65bSitojun otable = (struct moentry *)(base + flip(mo->mo_otable, magic)); 359*bb42e65bSitojun ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic)); 360*bb42e65bSitojun if (!validate(otable) || !validate(&otable[mohandle.mo.mo_nstring])) { 361*bb42e65bSitojun unmapit(); 362*bb42e65bSitojun goto fail; 363*bb42e65bSitojun } 364*bb42e65bSitojun if (!validate(ttable) || !validate(&ttable[mohandle.mo.mo_nstring])) { 365*bb42e65bSitojun unmapit(); 366*bb42e65bSitojun goto fail; 367*bb42e65bSitojun } 368*bb42e65bSitojun 369*bb42e65bSitojun /* allocate [ot]table, and convert to normal pointer representation. */ 370*bb42e65bSitojun l = sizeof(struct moentry_h) * mohandle.mo.mo_nstring; 371*bb42e65bSitojun mohandle.mo.mo_otable = (struct moentry_h *)malloc(l); 372*bb42e65bSitojun if (!mohandle.mo.mo_otable) { 373*bb42e65bSitojun unmapit(); 374*bb42e65bSitojun goto fail; 375*bb42e65bSitojun } 376*bb42e65bSitojun mohandle.mo.mo_ttable = (struct moentry_h *)malloc(l); 377*bb42e65bSitojun if (!mohandle.mo.mo_ttable) { 378*bb42e65bSitojun unmapit(); 379*bb42e65bSitojun goto fail; 380*bb42e65bSitojun } 381*bb42e65bSitojun p = mohandle.mo.mo_otable; 382*bb42e65bSitojun for (i = 0; i < mohandle.mo.mo_nstring; i++) { 383*bb42e65bSitojun p[i].len = flip(otable[i].len, magic); 384*bb42e65bSitojun p[i].off = base + flip(otable[i].off, magic); 385*bb42e65bSitojun 386*bb42e65bSitojun if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) { 387*bb42e65bSitojun unmapit(); 388*bb42e65bSitojun goto fail; 389*bb42e65bSitojun } 390*bb42e65bSitojun } 391*bb42e65bSitojun p = mohandle.mo.mo_ttable; 392*bb42e65bSitojun for (i = 0; i < mohandle.mo.mo_nstring; i++) { 393*bb42e65bSitojun p[i].len = flip(ttable[i].len, magic); 394*bb42e65bSitojun p[i].off = base + flip(ttable[i].off, magic); 395*bb42e65bSitojun 396*bb42e65bSitojun if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) { 397*bb42e65bSitojun unmapit(); 398*bb42e65bSitojun goto fail; 399*bb42e65bSitojun } 400*bb42e65bSitojun } 401*bb42e65bSitojun 402*bb42e65bSitojun /* grab MIME-header and charset field */ 403*bb42e65bSitojun mohandle.mo.mo_header = lookup(""); 404*bb42e65bSitojun if (mohandle.mo.mo_header) 405*bb42e65bSitojun v = strstr(mohandle.mo.mo_header, "charset="); 406*bb42e65bSitojun else 407*bb42e65bSitojun v = NULL; 408*bb42e65bSitojun if (v) { 409*bb42e65bSitojun mohandle.mo.mo_charset = strdup(v + 8); 410*bb42e65bSitojun v = strchr(mohandle.mo.mo_charset, '\n'); 411*bb42e65bSitojun if (v) 412*bb42e65bSitojun *v = '\0'; 413*bb42e65bSitojun } 414*bb42e65bSitojun 415*bb42e65bSitojun /* 416*bb42e65bSitojun * XXX check charset, reject it if we are unable to support the charset 417*bb42e65bSitojun * with the current locale. 418*bb42e65bSitojun * for example, if we are using euc-jp locale and we are looking at 419*bb42e65bSitojun * *.mo file encoded by euc-kr (charset=euc-kr), we should reject 420*bb42e65bSitojun * the *.mo file as we cannot support it. 421*bb42e65bSitojun */ 422*bb42e65bSitojun 423*bb42e65bSitojun return 0; 424*bb42e65bSitojun 425*bb42e65bSitojun fail: 426*bb42e65bSitojun return -1; 427*bb42e65bSitojun } 428*bb42e65bSitojun 429*bb42e65bSitojun static int 430*bb42e65bSitojun unmapit() 431*bb42e65bSitojun { 432*bb42e65bSitojun 433*bb42e65bSitojun /* unmap if there's already mapped region */ 434*bb42e65bSitojun if (mohandle.addr) 435*bb42e65bSitojun munmap(mohandle.addr, mohandle.len); 436*bb42e65bSitojun mohandle.addr = NULL; 437*bb42e65bSitojun mohandle.path[0] = '\0'; 438*bb42e65bSitojun if (mohandle.mo.mo_otable) 439*bb42e65bSitojun free(mohandle.mo.mo_otable); 440*bb42e65bSitojun if (mohandle.mo.mo_ttable) 441*bb42e65bSitojun free(mohandle.mo.mo_ttable); 442*bb42e65bSitojun if (mohandle.mo.mo_charset) 443*bb42e65bSitojun free(mohandle.mo.mo_charset); 444*bb42e65bSitojun memset(&mohandle.mo, 0, sizeof(mohandle.mo)); 445*bb42e65bSitojun return 0; 446*bb42e65bSitojun } 447*bb42e65bSitojun 448*bb42e65bSitojun static const char * 449*bb42e65bSitojun lookup_hash(msgid) 450*bb42e65bSitojun const char *msgid; 451*bb42e65bSitojun { 452*bb42e65bSitojun 453*bb42e65bSitojun /* 454*bb42e65bSitojun * XXX should try a hashed lookup here, but to do so, we need to 455*bb42e65bSitojun * look inside the GPL'ed *.c and re-implement... 456*bb42e65bSitojun */ 457*bb42e65bSitojun return NULL; 458*bb42e65bSitojun } 459*bb42e65bSitojun 460*bb42e65bSitojun static const char * 461*bb42e65bSitojun lookup_bsearch(msgid) 462*bb42e65bSitojun const char *msgid; 463*bb42e65bSitojun { 464*bb42e65bSitojun size_t l; 465*bb42e65bSitojun int top, bottom, middle, omiddle; 466*bb42e65bSitojun int n; 467*bb42e65bSitojun 468*bb42e65bSitojun l = strlen(msgid); 469*bb42e65bSitojun 470*bb42e65bSitojun top = 0; 471*bb42e65bSitojun bottom = mohandle.mo.mo_nstring; 472*bb42e65bSitojun omiddle = -1; 473*bb42e65bSitojun while (1) { 474*bb42e65bSitojun if (top > bottom) 475*bb42e65bSitojun return NULL; 476*bb42e65bSitojun middle = (top + bottom) / 2; 477*bb42e65bSitojun /* avoid possible infinite loop, when the data is not sorted */ 478*bb42e65bSitojun if (omiddle == middle) 479*bb42e65bSitojun return NULL; 480*bb42e65bSitojun if (middle < 0 || middle >= mohandle.mo.mo_nstring) 481*bb42e65bSitojun return NULL; 482*bb42e65bSitojun 483*bb42e65bSitojun n = strcmp(msgid, mohandle.mo.mo_otable[middle].off); 484*bb42e65bSitojun if (n == 0) 485*bb42e65bSitojun return (const char *)mohandle.mo.mo_ttable[middle].off; 486*bb42e65bSitojun else if (n < 0) 487*bb42e65bSitojun bottom = middle; 488*bb42e65bSitojun else 489*bb42e65bSitojun top = middle; 490*bb42e65bSitojun omiddle = middle; 491*bb42e65bSitojun } 492*bb42e65bSitojun 493*bb42e65bSitojun return NULL; 494*bb42e65bSitojun } 495*bb42e65bSitojun 496*bb42e65bSitojun static const char * 497*bb42e65bSitojun lookup(msgid) 498*bb42e65bSitojun const char *msgid; 499*bb42e65bSitojun { 500*bb42e65bSitojun const char *v; 501*bb42e65bSitojun 502*bb42e65bSitojun v = lookup_hash(msgid); 503*bb42e65bSitojun if (v) 504*bb42e65bSitojun return v; 505*bb42e65bSitojun 506*bb42e65bSitojun return lookup_bsearch(msgid); 507*bb42e65bSitojun } 508*bb42e65bSitojun 509*bb42e65bSitojun char * 510*bb42e65bSitojun dcngettext(domainname, msgid1, msgid2, n, category) 511*bb42e65bSitojun const char *domainname; 512*bb42e65bSitojun const char *msgid1; 513*bb42e65bSitojun const char *msgid2; 514*bb42e65bSitojun unsigned long int n; 515*bb42e65bSitojun int category; 516*bb42e65bSitojun { 517*bb42e65bSitojun const char *msgid; 518*bb42e65bSitojun char path[PATH_MAX]; 519*bb42e65bSitojun static char lpath[PATH_MAX]; 520*bb42e65bSitojun static char olpath[PATH_MAX]; 521*bb42e65bSitojun const char *locale; 522*bb42e65bSitojun const char *language; 523*bb42e65bSitojun const char *cname; 524*bb42e65bSitojun const char *v; 525*bb42e65bSitojun 526*bb42e65bSitojun msgid = (n == 1) ? msgid1 : msgid2; 527*bb42e65bSitojun 528*bb42e65bSitojun if (!domainname) 529*bb42e65bSitojun domainname = __domainname; 530*bb42e65bSitojun cname = lookup_category(category); 531*bb42e65bSitojun if (!domainname || !cname) 532*bb42e65bSitojun goto fail; 533*bb42e65bSitojun 534*bb42e65bSitojun language = getenv("LANGUAGE"); 535*bb42e65bSitojun locale = setlocale(LC_MESSAGES, NULL); /*XXX*/ 536*bb42e65bSitojun if (locale) 537*bb42e65bSitojun locale = split_locale(locale); 538*bb42e65bSitojun if (language && locale) { 539*bb42e65bSitojun if (strlen(language) + strlen(locale) + 2 > sizeof(lpath)) 540*bb42e65bSitojun goto fail; 541*bb42e65bSitojun snprintf(lpath, sizeof(lpath), "%s:%s", language, locale); 542*bb42e65bSitojun } else if (language) { 543*bb42e65bSitojun if (strlen(language) + 1 > sizeof(lpath)) 544*bb42e65bSitojun goto fail; 545*bb42e65bSitojun strlcpy(lpath, language, sizeof(lpath)); 546*bb42e65bSitojun } else if (locale) { 547*bb42e65bSitojun if (strlen(locale) + 1 > sizeof(lpath)) 548*bb42e65bSitojun goto fail; 549*bb42e65bSitojun strlcpy(lpath, locale, sizeof(lpath)); 550*bb42e65bSitojun } else 551*bb42e65bSitojun goto fail; 552*bb42e65bSitojun 553*bb42e65bSitojun /* don't bother looking it up if the values are the same */ 554*bb42e65bSitojun if (strcmp(lpath, olpath) == 0) 555*bb42e65bSitojun goto found; 556*bb42e65bSitojun 557*bb42e65bSitojun strlcpy(olpath, lpath, sizeof(olpath)); 558*bb42e65bSitojun 559*bb42e65bSitojun /* try to find appropriate file, from $LANGUAGE */ 560*bb42e65bSitojun if (lookup_mofile(path, sizeof(path), __domainpath, lpath, cname, 561*bb42e65bSitojun domainname) != NULL) 562*bb42e65bSitojun goto found; 563*bb42e65bSitojun 564*bb42e65bSitojun goto fail; 565*bb42e65bSitojun 566*bb42e65bSitojun found: 567*bb42e65bSitojun v = lookup(msgid); 568*bb42e65bSitojun if (v) { 569*bb42e65bSitojun /* 570*bb42e65bSitojun * XXX call iconv() here, if translated text is encoded 571*bb42e65bSitojun * differently from currently-selected encoding (locale). 572*bb42e65bSitojun * look at Content-type header in *.mo file, in string obtained 573*bb42e65bSitojun * by gettext(""). 574*bb42e65bSitojun */ 575*bb42e65bSitojun 576*bb42e65bSitojun /* 577*bb42e65bSitojun * Given the amount of printf-format security issues, it may 578*bb42e65bSitojun * be a good idea to validate if the original msgid and the 579*bb42e65bSitojun * translated message format string carry the same printf-like 580*bb42e65bSitojun * format identifiers. 581*bb42e65bSitojun */ 582*bb42e65bSitojun 583*bb42e65bSitojun msgid = v; 584*bb42e65bSitojun } 585*bb42e65bSitojun 586*bb42e65bSitojun fail: 587*bb42e65bSitojun /* LINTED const cast */ 588*bb42e65bSitojun return (char *)msgid; 589*bb42e65bSitojun } 590