1 /* $NetBSD: gettext.c,v 1.24 2005/06/01 11:08:57 lukem Exp $ */
4 * Copyright (c) 2000, 2001 Citrus Project,
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
28 * $Citrus: xpg4dl/FreeBSD/lib/libintl/gettext.c,v 1.31 2001/09/27 15:18:45 yamt Exp $
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: gettext.c,v 1.24 2005/06/01 11:08:57 lukem Exp $");
34 #include <sys/param.h>
50 #include "libintl_local.h"
51 #include "plural_parser.h"
52 #include "pathnames.h"
54 static const char *lookup_category(int);
55 static const char *split_locale(const char *);
56 static const char *lookup_mofile(char *, size_t, const char *, const char *,
57 const char *, const char *,
58 struct domainbinding
*);
59 static uint32_t flip(uint32_t, uint32_t);
60 static int validate(void *, struct mohandle
*);
61 static int mapit(const char *, struct domainbinding
*);
62 static int unmapit(struct domainbinding
*);
63 static const char *lookup_hash(const char *, struct domainbinding
*, size_t *);
64 static const char *lookup_bsearch(const char *, struct domainbinding
*,
66 static const char *lookup(const char *, struct domainbinding
*, size_t *);
67 static const char *get_lang_env(const char *);
70 * shortcut functions. the main implementation resides in dcngettext().
73 gettext(const char *msgid
)
76 return dcngettext(NULL
, msgid
, NULL
, 1UL, LC_MESSAGES
);
80 dgettext(const char *domainname
, const char *msgid
)
83 return dcngettext(domainname
, msgid
, NULL
, 1UL, LC_MESSAGES
);
87 dcgettext(const char *domainname
, const char *msgid
, int category
)
90 return dcngettext(domainname
, msgid
, NULL
, 1UL, category
);
94 ngettext(const char *msgid1
, const char *msgid2
, unsigned long int n
)
97 return dcngettext(NULL
, msgid1
, msgid2
, n
, LC_MESSAGES
);
101 dngettext(const char *domainname
, const char *msgid1
, const char *msgid2
,
105 return dcngettext(domainname
, msgid1
, msgid2
, n
, LC_MESSAGES
);
110 * lookup internationalized message on database locale/category/domainname
111 * (like ja_JP.eucJP/LC_MESSAGES/domainname).
112 * if n equals to 1, internationalized message will be looked up for msgid1.
113 * otherwise, message will be looked up for msgid2.
114 * if the lookup fails, the function will return msgid1 or msgid2 as is.
116 * Even though the return type is "char *", caller should not rewrite the
117 * region pointed to by the return value (should be "const char *", but can't
118 * change it for compatibility with other implementations).
120 * by default (if domainname == NULL), domainname is taken from the value set
121 * by textdomain(). usually name of the application (like "ls") is used as
122 * domainname. category is usually LC_MESSAGES.
124 * the code reads in *.mo files generated by GNU gettext. *.mo is a host-
125 * endian encoded file. both endians are supported here, as the files are in
126 * /usr/share/locale! (or we should move those files into /usr/libdata)
130 lookup_category(int category
)
134 case LC_COLLATE
: return "LC_COLLATE";
135 case LC_CTYPE
: return "LC_CTYPE";
136 case LC_MONETARY
: return "LC_MONETARY";
137 case LC_NUMERIC
: return "LC_NUMERIC";
138 case LC_TIME
: return "LC_TIME";
139 case LC_MESSAGES
: return "LC_MESSAGES";
145 * XPG syntax: language[_territory[.codeset]][@modifier]
146 * XXX boundary check on "result" is lacking
149 split_locale(const char *lname
)
151 char buf
[BUFSIZ
], tmp
[BUFSIZ
];
153 static char result
[BUFSIZ
];
155 memset(result
, 0, sizeof(result
));
157 if (strlen(lname
) + 1 > sizeof(buf
)) {
162 strlcpy(buf
, lname
, sizeof(buf
));
163 m
= strrchr(buf
, '@');
166 c
= strrchr(buf
, '.');
169 t
= strrchr(buf
, '_');
181 snprintf(tmp
, sizeof(tmp
), "%s_%s.%s@%s",
183 strlcat(result
, tmp
, sizeof(result
));
184 strlcat(result
, ":", sizeof(result
));
186 snprintf(tmp
, sizeof(tmp
), "%s_%s@%s", l
, t
, m
);
187 strlcat(result
, tmp
, sizeof(result
));
188 strlcat(result
, ":", sizeof(result
));
190 snprintf(tmp
, sizeof(tmp
), "%s@%s", l
, m
);
191 strlcat(result
, tmp
, sizeof(result
));
192 strlcat(result
, ":", sizeof(result
));
196 snprintf(tmp
, sizeof(tmp
), "%s_%s.%s", l
, t
, c
);
197 strlcat(result
, tmp
, sizeof(result
));
198 strlcat(result
, ":", sizeof(result
));
200 snprintf(tmp
, sizeof(tmp
), "%s_%s", l
, t
);
201 strlcat(result
, tmp
, sizeof(result
));
202 strlcat(result
, ":", sizeof(result
));
204 strlcat(result
, l
, sizeof(result
));
210 lookup_mofile(char *buf
, size_t len
, const char *dir
, const char *lpath
,
211 const char *category
, const char *domainname
,
212 struct domainbinding
*db
)
216 char lpath_tmp
[BUFSIZ
];
218 strlcpy(lpath_tmp
, lpath
, sizeof(lpath_tmp
));
228 /* don't mess with default locales */
229 if (strcmp(p
, "C") == 0 || strcmp(p
, "POSIX") == 0)
232 /* validate pathname */
233 if (strchr(p
, '/') || strchr(category
, '/'))
236 if (strchr(domainname
, '/'))
240 snprintf(buf
, len
, "%s/%s/%s/%s.mo", dir
, p
,
241 category
, domainname
);
242 if (stat(buf
, &st
) < 0)
244 if ((st
.st_mode
& S_IFMT
) != S_IFREG
)
247 if (mapit(buf
, db
) == 0)
255 flip(uint32_t v
, uint32_t magic
)
258 if (magic
== MO_MAGIC
)
260 else if (magic
== MO_MAGIC_SWAPPED
) {
261 v
= ((v
>> 24) & 0xff) | ((v
>> 8) & 0xff00) |
262 ((v
<< 8) & 0xff0000) | ((v
<< 24) & 0xff000000);
271 validate(void *arg
, struct mohandle
*mohandle
)
276 if (p
< (char *)mohandle
->addr
||
277 p
> (char *)mohandle
->addr
+ mohandle
->len
)
284 * calculate the step value if the hash value is conflicted.
286 static __inline
uint32_t
287 calc_collision_step(uint32_t hashval
, uint32_t hashsize
)
289 _DIAGASSERT(hashsize
>2);
290 return (hashval
% (hashsize
- 2)) + 1;
294 * calculate the next index while conflicting.
296 static __inline
uint32_t
297 calc_next_index(uint32_t curidx
, uint32_t hashsize
, uint32_t step
)
299 return curidx
+step
- (curidx
>= hashsize
-step
? hashsize
: 0);
303 get_sysdep_string_table(struct mosysdepstr_h
**table_h
, uint32_t *ofstable
,
304 uint32_t nstrings
, uint32_t magic
, char *base
)
309 struct mosysdepstr
*table
;
311 for (i
=0; i
<nstrings
; i
++) {
312 /* get mosysdepstr record */
313 /* LINTED: ignore the alignment problem. */
314 table
= (struct mosysdepstr
*)(base
+ flip(ofstable
[i
], magic
));
315 /* count number of segments */
317 while (flip(table
->segs
[count
++].ref
, magic
) != MO_LASTSEG
)
320 l
= sizeof(struct mosysdepstr_h
) +
321 sizeof(struct mosysdepsegentry_h
) * (count
-1);
322 table_h
[i
] = (struct mosysdepstr_h
*)malloc(l
);
325 memset(table_h
[i
], 0, l
);
326 table_h
[i
]->off
= (const char *)(base
+ flip(table
->off
, magic
));
327 for (j
=0; j
<count
; j
++) {
328 table_h
[i
]->segs
[j
].len
=
329 flip(table
->segs
[j
].len
, magic
);
330 table_h
[i
]->segs
[j
].ref
=
331 flip(table
->segs
[j
].ref
, magic
);
333 /* LINTED: ignore the alignment problem. */
334 table
= (struct mosysdepstr
*)&table
->segs
[count
];
340 expand_sysdep(struct mohandle
*mohandle
, struct mosysdepstr_h
*str
)
346 /* check whether already expanded */
350 /* calc total length */
351 str
->expanded_len
= 1;
352 for (i
=0; /*CONSTCOND*/1; i
++) {
353 str
->expanded_len
+= str
->segs
[i
].len
;
354 if (str
->segs
[i
].ref
== MO_LASTSEG
)
357 mohandle
->mo
.mo_sysdep_segs
[str
->segs
[i
].ref
].len
;
360 str
->expanded
= malloc(str
->expanded_len
);
365 for (i
=0; /*CONSTCOND*/1; i
++) {
366 memcpy(dst
, src
, str
->segs
[i
].len
);
367 src
+= str
->segs
[i
].len
;
368 dst
+= str
->segs
[i
].len
;
369 if (str
->segs
[i
].ref
== MO_LASTSEG
)
371 memcpy(dst
, mohandle
->mo
.mo_sysdep_segs
[str
->segs
[i
].ref
].str
,
372 mohandle
->mo
.mo_sysdep_segs
[str
->segs
[i
].ref
].len
);
373 dst
+= mohandle
->mo
.mo_sysdep_segs
[str
->segs
[i
].ref
].len
;
381 insert_to_hash(uint32_t *htable
, uint32_t hsize
, const char *str
, uint32_t ref
)
383 uint32_t hashval
, idx
, step
;
385 hashval
= __intl_string_hash(str
);
386 step
= calc_collision_step(hashval
, hsize
);
387 idx
= hashval
% hsize
;
390 idx
= calc_next_index(idx
, hsize
, step
);
396 setup_sysdep_stuffs(struct mo
*mo
, struct mohandle
*mohandle
, char *base
)
399 struct moentry
*stable
;
405 magic
= mo
->mo_magic
;
407 mohandle
->mo
.mo_sysdep_nsegs
= flip(mo
->mo_sysdep_nsegs
, magic
);
408 mohandle
->mo
.mo_sysdep_nstring
= flip(mo
->mo_sysdep_nstring
, magic
);
410 if (mohandle
->mo
.mo_sysdep_nstring
== 0)
413 /* check hash size */
414 if (mohandle
->mo
.mo_hsize
<= 2 ||
415 mohandle
->mo
.mo_hsize
<
416 (mohandle
->mo
.mo_nstring
+ mohandle
->mo
.mo_sysdep_nstring
))
419 /* get sysdep segments */
420 l
= sizeof(struct mosysdepsegs_h
) * mohandle
->mo
.mo_sysdep_nsegs
;
421 mohandle
->mo
.mo_sysdep_segs
= (struct mosysdepsegs_h
*)malloc(l
);
422 if (!mohandle
->mo
.mo_sysdep_segs
)
424 /* LINTED: ignore the alignment problem. */
425 stable
= (struct moentry
*)(base
+ flip(mo
->mo_sysdep_segoff
, magic
));
426 for (i
=0; i
<mohandle
->mo
.mo_sysdep_nsegs
; i
++) {
427 v
= base
+ flip(stable
[i
].off
, magic
);
428 mohandle
->mo
.mo_sysdep_segs
[i
].str
=
429 __intl_sysdep_get_string_by_tag(
431 &mohandle
->mo
.mo_sysdep_segs
[i
].len
);
434 /* get sysdep string table */
435 mohandle
->mo
.mo_sysdep_otable
=
436 (struct mosysdepstr_h
**)calloc(mohandle
->mo
.mo_sysdep_nstring
,
437 sizeof(struct mosysdepstr_h
*));
438 if (!mohandle
->mo
.mo_sysdep_otable
)
440 /* LINTED: ignore the alignment problem. */
441 ofstable
= (uint32_t *)(base
+ flip(mo
->mo_sysdep_otable
, magic
));
442 if (get_sysdep_string_table(mohandle
->mo
.mo_sysdep_otable
, ofstable
,
443 mohandle
->mo
.mo_sysdep_nstring
, magic
,
446 mohandle
->mo
.mo_sysdep_ttable
=
447 (struct mosysdepstr_h
**)calloc(mohandle
->mo
.mo_sysdep_nstring
,
448 sizeof(struct mosysdepstr_h
*));
449 if (!mohandle
->mo
.mo_sysdep_ttable
)
451 /* LINTED: ignore the alignment problem. */
452 ofstable
= (uint32_t *)(base
+ flip(mo
->mo_sysdep_ttable
, magic
));
453 if (get_sysdep_string_table(mohandle
->mo
.mo_sysdep_ttable
, ofstable
,
454 mohandle
->mo
.mo_sysdep_nstring
, magic
,
459 for (i
=0; i
<mohandle
->mo
.mo_sysdep_nstring
; i
++) {
460 if (expand_sysdep(mohandle
, mohandle
->mo
.mo_sysdep_otable
[i
]))
462 insert_to_hash(mohandle
->mo
.mo_htable
,
463 mohandle
->mo
.mo_hsize
,
464 mohandle
->mo
.mo_sysdep_otable
[i
]->expanded
,
465 (i
+1) | MO_HASH_SYSDEP_MASK
);
472 mapit(const char *path
, struct domainbinding
*db
)
477 uint32_t magic
, revision
, flags
= 0;
478 struct moentry
*otable
, *ttable
;
479 const uint32_t *htable
;
485 struct mohandle
*mohandle
= &db
->mohandle
;
487 if (mohandle
->addr
&& mohandle
->addr
!= MAP_FAILED
&&
488 mohandle
->mo
.mo_magic
)
489 return 0; /*already opened*/
494 if (secure_path(path
) != 0)
497 if (stat(path
, &st
) < 0)
499 if ((st
.st_mode
& S_IFMT
) != S_IFREG
|| st
.st_size
> GETTEXT_MMAP_MAX
)
501 fd
= open(path
, O_RDONLY
);
504 if (read(fd
, &magic
, sizeof(magic
)) != sizeof(magic
) ||
505 (magic
!= MO_MAGIC
&& magic
!= MO_MAGIC_SWAPPED
)) {
509 if (read(fd
, &revision
, sizeof(revision
)) != sizeof(revision
)) {
513 switch (flip(revision
, magic
)) {
514 case MO_MAKE_REV(0, 0):
516 case MO_MAKE_REV(0, 1):
517 case MO_MAKE_REV(1, 1):
518 flags
|= MO_F_SYSDEP
;
524 mohandle
->addr
= mmap(NULL
, (size_t)st
.st_size
, PROT_READ
,
525 MAP_FILE
| MAP_SHARED
, fd
, (off_t
)0);
526 if (!mohandle
->addr
|| mohandle
->addr
== MAP_FAILED
) {
531 mohandle
->len
= (size_t)st
.st_size
;
533 base
= mohandle
->addr
;
534 mo
= (struct mo
*)mohandle
->addr
;
536 /* flip endian. do not flip magic number! */
537 mohandle
->mo
.mo_magic
= mo
->mo_magic
;
538 mohandle
->mo
.mo_revision
= flip(mo
->mo_revision
, magic
);
539 mohandle
->mo
.mo_nstring
= flip(mo
->mo_nstring
, magic
);
540 mohandle
->mo
.mo_hsize
= flip(mo
->mo_hsize
, magic
);
541 mohandle
->mo
.mo_flags
= flags
;
543 /* validate otable/ttable */
544 /* LINTED: ignore the alignment problem. */
545 otable
= (struct moentry
*)(base
+ flip(mo
->mo_otable
, magic
));
546 /* LINTED: ignore the alignment problem. */
547 ttable
= (struct moentry
*)(base
+ flip(mo
->mo_ttable
, magic
));
548 if (!validate(otable
, mohandle
) ||
549 !validate(&otable
[mohandle
->mo
.mo_nstring
], mohandle
)) {
553 if (!validate(ttable
, mohandle
) ||
554 !validate(&ttable
[mohandle
->mo
.mo_nstring
], mohandle
)) {
559 /* allocate [ot]table, and convert to normal pointer representation. */
560 l
= sizeof(struct moentry_h
) * mohandle
->mo
.mo_nstring
;
561 mohandle
->mo
.mo_otable
= (struct moentry_h
*)malloc(l
);
562 if (!mohandle
->mo
.mo_otable
) {
566 mohandle
->mo
.mo_ttable
= (struct moentry_h
*)malloc(l
);
567 if (!mohandle
->mo
.mo_ttable
) {
571 p
= mohandle
->mo
.mo_otable
;
572 for (i
= 0; i
< mohandle
->mo
.mo_nstring
; i
++) {
573 p
[i
].len
= flip(otable
[i
].len
, magic
);
574 p
[i
].off
= base
+ flip(otable
[i
].off
, magic
);
576 if (!validate(p
[i
].off
, mohandle
) ||
577 !validate(p
[i
].off
+ p
[i
].len
+ 1, mohandle
)) {
582 p
= mohandle
->mo
.mo_ttable
;
583 for (i
= 0; i
< mohandle
->mo
.mo_nstring
; i
++) {
584 p
[i
].len
= flip(ttable
[i
].len
, magic
);
585 p
[i
].off
= base
+ flip(ttable
[i
].off
, magic
);
587 if (!validate(p
[i
].off
, mohandle
) ||
588 !validate(p
[i
].off
+ p
[i
].len
+ 1, mohandle
)) {
593 /* allocate htable, and convert it to the host order. */
594 if (mohandle
->mo
.mo_hsize
> 2) {
595 l
= sizeof(uint32_t) * mohandle
->mo
.mo_hsize
;
596 mohandle
->mo
.mo_htable
= (uint32_t *)malloc(l
);
597 if (!mohandle
->mo
.mo_htable
) {
601 /* LINTED: ignore the alignment problem. */
602 htable
= (const uint32_t *)(base
+flip(mo
->mo_hoffset
, magic
));
603 for (i
=0; i
< mohandle
->mo
.mo_hsize
; i
++) {
604 mohandle
->mo
.mo_htable
[i
] = flip(htable
[i
], magic
);
605 if (mohandle
->mo
.mo_htable
[i
] >=
606 mohandle
->mo
.mo_nstring
+1) {
607 /* illegal string number. */
613 /* grab MIME-header and charset field */
614 mohandle
->mo
.mo_header
= lookup("", db
, &headerlen
);
615 if (mohandle
->mo
.mo_header
)
616 v
= strstr(mohandle
->mo
.mo_header
, "charset=");
620 mohandle
->mo
.mo_charset
= strdup(v
+ 8);
621 if (!mohandle
->mo
.mo_charset
)
623 v
= strchr(mohandle
->mo
.mo_charset
, '\n');
627 if (_gettext_parse_plural(&mohandle
->mo
.mo_plural
,
628 &mohandle
->mo
.mo_nplurals
,
629 mohandle
->mo
.mo_header
, headerlen
))
630 mohandle
->mo
.mo_plural
= NULL
;
633 * XXX check charset, reject it if we are unable to support the charset
634 * with the current locale.
635 * for example, if we are using euc-jp locale and we are looking at
636 * *.mo file encoded by euc-kr (charset=euc-kr), we should reject
637 * the *.mo file as we cannot support it.
640 /* system dependent string support */
641 if ((mohandle
->mo
.mo_flags
& MO_F_SYSDEP
) != 0) {
642 if (setup_sysdep_stuffs(mo
, mohandle
, base
)) {
655 free_sysdep_table(struct mosysdepstr_h
**table
, uint32_t nstring
)
659 for (i
=0; i
<nstring
; i
++) {
661 if (table
[i
]->expanded
)
662 free(table
[i
]->expanded
);
670 unmapit(struct domainbinding
*db
)
672 struct mohandle
*mohandle
= &db
->mohandle
;
674 /* unmap if there's already mapped region */
675 if (mohandle
->addr
&& mohandle
->addr
!= MAP_FAILED
)
676 munmap(mohandle
->addr
, mohandle
->len
);
677 mohandle
->addr
= NULL
;
678 if (mohandle
->mo
.mo_otable
)
679 free(mohandle
->mo
.mo_otable
);
680 if (mohandle
->mo
.mo_ttable
)
681 free(mohandle
->mo
.mo_ttable
);
682 if (mohandle
->mo
.mo_charset
)
683 free(mohandle
->mo
.mo_charset
);
684 if (mohandle
->mo
.mo_htable
)
685 free(mohandle
->mo
.mo_htable
);
686 if (mohandle
->mo
.mo_sysdep_segs
)
687 free(mohandle
->mo
.mo_sysdep_segs
);
688 if (mohandle
->mo
.mo_sysdep_otable
) {
689 free_sysdep_table(mohandle
->mo
.mo_sysdep_otable
,
690 mohandle
->mo
.mo_sysdep_nstring
);
692 if (mohandle
->mo
.mo_sysdep_ttable
) {
693 free_sysdep_table(mohandle
->mo
.mo_sysdep_ttable
,
694 mohandle
->mo
.mo_sysdep_nstring
);
696 if (mohandle
->mo
.mo_plural
)
697 _gettext_free_plural(mohandle
->mo
.mo_plural
);
698 memset(&mohandle
->mo
, 0, sizeof(mohandle
->mo
));
704 lookup_hash(const char *msgid
, struct domainbinding
*db
, size_t *rlen
)
706 struct mohandle
*mohandle
= &db
->mohandle
;
707 uint32_t idx
, hashval
, step
, strno
;
709 struct mosysdepstr_h
*sysdep_otable
, *sysdep_ttable
;
711 if (mohandle
->mo
.mo_hsize
<= 2 || mohandle
->mo
.mo_htable
== NULL
)
714 hashval
= __intl_string_hash(msgid
);
715 step
= calc_collision_step(hashval
, mohandle
->mo
.mo_hsize
);
716 idx
= hashval
% mohandle
->mo
.mo_hsize
;
718 while (/*CONSTCOND*/1) {
719 strno
= mohandle
->mo
.mo_htable
[idx
];
721 /* unexpected miss */
725 if ((strno
& MO_HASH_SYSDEP_MASK
) == 0) {
726 /* system independent strings */
727 if (len
<= mohandle
->mo
.mo_otable
[strno
].len
&&
728 !strcmp(msgid
, mohandle
->mo
.mo_otable
[strno
].off
)) {
732 mohandle
->mo
.mo_ttable
[strno
].len
;
733 return mohandle
->mo
.mo_ttable
[strno
].off
;
736 /* system dependent strings */
737 strno
&= ~MO_HASH_SYSDEP_MASK
;
738 sysdep_otable
= mohandle
->mo
.mo_sysdep_otable
[strno
];
739 sysdep_ttable
= mohandle
->mo
.mo_sysdep_ttable
[strno
];
740 if (len
<= sysdep_otable
->expanded_len
&&
741 !strcmp(msgid
, sysdep_otable
->expanded
)) {
743 if (expand_sysdep(mohandle
, sysdep_ttable
))
744 /* memory exhausted */
747 *rlen
= sysdep_ttable
->expanded_len
;
748 return sysdep_ttable
->expanded
;
751 idx
= calc_next_index(idx
, mohandle
->mo
.mo_hsize
, step
);
757 lookup_bsearch(const char *msgid
, struct domainbinding
*db
, size_t *rlen
)
759 int top
, bottom
, middle
, omiddle
;
761 struct mohandle
*mohandle
= &db
->mohandle
;
764 bottom
= mohandle
->mo
.mo_nstring
;
770 middle
= (top
+ bottom
) / 2;
771 /* avoid possible infinite loop, when the data is not sorted */
772 if (omiddle
== middle
)
774 if (middle
< 0 || middle
>= mohandle
->mo
.mo_nstring
)
777 n
= strcmp(msgid
, mohandle
->mo
.mo_otable
[middle
].off
);
780 *rlen
= mohandle
->mo
.mo_ttable
[middle
].len
;
781 return (const char *)mohandle
->mo
.mo_ttable
[middle
].off
;
794 lookup(const char *msgid
, struct domainbinding
*db
, size_t *rlen
)
798 v
= lookup_hash(msgid
, db
, rlen
);
802 return lookup_bsearch(msgid
, db
, rlen
);
806 get_lang_env(const char *category_name
)
810 /* 1. see LANGUAGE variable first. */
811 lang
= getenv("LANGUAGE");
815 /* 2. if LANGUAGE isn't set, see LC_ALL, LC_xxx, LANG. */
816 lang
= getenv("LC_ALL");
818 lang
= getenv(category_name
);
820 lang
= getenv("LANG");
823 return 0; /* error */
825 return split_locale(lang
);
829 get_indexed_string(const char *str
, size_t len
, unsigned long idx
)
844 #define _NGETTEXT_DEFAULT(msgid1, msgid2, n) \
845 ((char *)__UNCONST((n) == 1 ? (msgid1) : (msgid2)))
848 dcngettext(const char *domainname
, const char *msgid1
, const char *msgid2
,
849 unsigned long int n
, int category
)
854 static char olpath
[PATH_MAX
];
855 const char *cname
= NULL
;
857 static char *ocname
= NULL
;
858 static char *odomainname
= NULL
;
859 struct domainbinding
*db
;
860 unsigned long plural_index
= 0;
864 domainname
= __current_domainname
;
865 cname
= lookup_category(category
);
866 if (!domainname
|| !cname
)
869 lpath
= get_lang_env(cname
);
873 for (db
= __bindings
; db
; db
= db
->next
)
874 if (strcmp(db
->domainname
, domainname
) == 0)
877 if (!bindtextdomain(domainname
, _PATH_TEXTDOMAIN
))
882 /* resolve relative path */
883 /* XXX not necessary? */
884 if (db
->path
[0] != '/') {
887 if (getcwd(buf
, sizeof(buf
)) == 0)
889 if (strlcat(buf
, "/", sizeof(buf
)) >= sizeof(buf
))
891 if (strlcat(buf
, db
->path
, sizeof(buf
)) >= sizeof(buf
))
893 strlcpy(db
->path
, buf
, sizeof(db
->path
));
896 /* don't bother looking it up if the values are the same */
897 if (odomainname
&& strcmp(domainname
, odomainname
) == 0 &&
898 ocname
&& strcmp(cname
, ocname
) == 0 && strcmp(lpath
, olpath
) == 0 &&
899 db
->mohandle
.mo
.mo_magic
)
902 /* try to find appropriate file, from $LANGUAGE */
903 if (lookup_mofile(path
, sizeof(path
), db
->path
, lpath
, cname
,
904 domainname
, db
) == NULL
)
911 odomainname
= strdup(domainname
);
912 ocname
= strdup(cname
);
913 if (!odomainname
|| !ocname
) {
918 odomainname
= ocname
= NULL
;
921 strlcpy(olpath
, lpath
, sizeof(olpath
));
924 if (db
->mohandle
.mo
.mo_plural
) {
926 _gettext_calculate_plural(db
->mohandle
.mo
.mo_plural
, n
);
927 if (plural_index
>= db
->mohandle
.mo
.mo_nplurals
)
931 msgid
= _NGETTEXT_DEFAULT(msgid1
, msgid2
, n
);
936 v
= lookup(msgid
, db
, &len
);
938 if (db
->mohandle
.mo
.mo_plural
)
939 v
= get_indexed_string(v
, len
, plural_index
);
941 * convert the translated message's encoding.
944 * a result of gettext("") shouldn't need any conversion.
947 v
= __gettext_iconv(v
, db
);
950 * Given the amount of printf-format security issues, it may
951 * be a good idea to validate if the original msgid and the
952 * translated message format string carry the same printf-like
953 * format identifiers.
959 return (char *)__UNCONST(msgid
);
962 return _NGETTEXT_DEFAULT(msgid1
, msgid2
, n
);