4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
35 #include <sys/types.h>
37 #include <sys/param.h>
47 #include "nlspath_checks.h"
54 /* The following symbols are just for GNU binary compatibility */
56 int *_nl_domain_bindings
;
58 static const char *nullstr
= "";
60 #define CHARSET_MOD "charset="
61 #define CHARSET_LEN (sizeof (CHARSET_MOD) - 1)
62 #define NPLURALS_MOD "nplurals="
63 #define NPLURALS_LEN (sizeof (NPLURALS_MOD) - 1)
64 #define PLURAL_MOD "plural="
65 #define PLURAL_LEN (sizeof (PLURAL_MOD) - 1)
67 static uint32_t get_hash_index(uint32_t *, uint32_t, uint32_t);
72 * release the memory allocated for storing code-converted messages
75 * 0: do not free gmnp->conv_msgstr
76 * 1: free gmnp->conv_msgstr
79 free_conv_msgstr(Msg_g_node
*gmnp
, int f
)
81 uint32_t i
, num_of_conv
;
84 gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
89 num_of_conv
= gmnp
->num_of_str
+ gmnp
->num_of_d_str
;
90 for (i
= 0; i
< num_of_conv
; i
++) {
91 if (gmnp
->conv_msgstr
[i
]) {
92 free(gmnp
->conv_msgstr
[i
]);
94 gmnp
->conv_msgstr
[i
] = NULL
;
97 free(gmnp
->conv_msgstr
);
98 gmnp
->conv_msgstr
= NULL
;
105 * choose an appropriate message by evaluating the plural expression,
109 dfltmsgstr(Msg_g_node
*gmnp
, const char *msgstr
, uint32_t msgstr_len
,
117 gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
119 msgstr
? msgstr
: "(null)", msgstr_len
, (void *)mp
);
120 printgnumsg(gmnp
, 1);
126 pindex
= plural_eval(gmnp
->plural
, mp
->n
);
129 * This mo does not have plural information.
130 * Using the English form.
138 gprintf(0, "plural_eval returned: %u\n", pindex
);
140 if (pindex
>= gmnp
->nplurals
) {
141 /* should never happen */
145 for (; pindex
!= 0; pindex
--) {
146 len
= msgstr_len
- (p
- msgstr
);
147 p
= memchr(p
, '\0', len
);
150 * null byte not found
151 * this should never happen
154 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
,
163 return ((char *)msgstr
);
169 * parse the header entry of the GNU MO file and
170 * extract the src encoding and the plural information of the MO file
173 parse_header(const char *header
, Msg_g_node
*gmnp
)
175 char *charset
= NULL
;
178 char *nplurals_str
, *plural_str
;
179 plural_expr_t plural
;
181 unsigned int nplurals
;
185 gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
186 header
? header
: "(null)", (void *)gmnp
);
187 printgnumsg(gmnp
, 1);
190 if (header
== NULL
) {
191 gmnp
->src_encoding
= (char *)nullstr
;
195 gprintf(0, "*************** exiting parse_header\n");
196 gprintf(0, "no header\n");
202 charset_str
= strstr(header
, CHARSET_MOD
);
203 if (charset_str
== NULL
) {
204 gmnp
->src_encoding
= (char *)nullstr
;
206 p
= charset_str
+ CHARSET_LEN
;
208 while ((*q
!= ' ') && (*q
!= '\t') &&
214 charset
= malloc(len
+ 1);
215 if (charset
== NULL
) {
216 gmnp
->src_encoding
= (char *)nullstr
;
221 (void) memcpy(charset
, p
, len
);
223 gmnp
->src_encoding
= charset
;
225 gmnp
->src_encoding
= (char *)nullstr
;
229 nplurals_str
= strstr(header
, NPLURALS_MOD
);
230 plural_str
= strstr(header
, PLURAL_MOD
);
231 if (nplurals_str
== NULL
|| plural_str
== NULL
) {
232 /* no valid plural specification */
236 gprintf(0, "*************** exiting parse_header\n");
237 gprintf(0, "no plural entry\n");
241 p
= nplurals_str
+ NPLURALS_LEN
;
242 while (*p
&& isspace((unsigned char)*p
)) {
245 nplurals
= (unsigned int)strtol(p
, &q
, 10);
247 gmnp
->nplurals
= nplurals
;
252 p
= plural_str
+ PLURAL_LEN
;
254 gprintf(0, "plural_str: \"%s\"\n", p
);
257 ret
= plural_expr(&plural
, (const char *)p
);
259 /* parse succeeded */
260 gmnp
->plural
= plural
;
262 gprintf(0, "*************** exiting parse_header\n");
263 gprintf(0, "charset: \"%s\"\n",
264 charset
? charset
: "(null)");
265 printexpr(plural
, 1);
268 } else if (ret
== 1) {
277 gmnp
->src_encoding
= (char *)nullstr
;
289 * take care of the LANGUAGE specification
292 handle_lang(struct msg_pack
*mp
)
294 const char *p
, *op
, *q
;
297 char locale
[MAXPATHLEN
];
301 gprintf(0, "*************** handle_lang(0x%p)\n", (void *)mp
);
311 locale_len
= strlen(p
);
317 if (locale_len
>= MAXPATHLEN
|| locale_len
== 0) {
318 /* illegal locale name */
321 (void) memcpy(locale
, op
, locale_len
);
322 locale
[locale_len
] = '\0';
328 if (mk_msgfile(mp
) == NULL
) {
329 /* illegal locale name */
333 result
= handle_mo(mp
);
334 if (mp
->status
& ST_GNU_MSG_FOUND
)
337 if (mp
->status
& ST_SUN_MO_FOUND
)
342 * no valid locale found, Sun MO found, or
343 * GNU MO found but no valid msg found there.
346 if (mp
->status
& ST_GNU_MO_FOUND
) {
348 * GNU MO found but no valid msg found there.
351 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
, mp
->n
, mp
->plural
);
360 * Searchs the translation message for the specified msgid1.
361 * Hash algorithm used in this function is Open Addressing
362 * with Double Hashing:
363 * H(k, i) = (H1(k) + i * H2(k)) mod M
364 * H1(k) = hashvalue % M
365 * H2(k) = 1 + (hashvalue % (M - 2))
367 * Ref: The Art of Computer Programming Volume 3
368 * Sorting and Searching, second edition
372 gnu_msgsearch(Msg_g_node
*gmnp
, const char *msgid1
,
373 uint32_t *msgstrlen
, uint32_t *midx
)
375 struct gnu_msg_info
*header
= gmnp
->msg_file_info
;
376 struct gnu_msg_ent
*msgid_tbl
, *msgstr_tbl
;
377 uint32_t num_of_str
, idx
, mlen
, msglen
;
378 uint32_t hash_size
, hash_val
, hash_id
, hash_inc
, hash_idx
;
379 uint32_t *hash_table
;
384 gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
386 (void *)gmnp
, msgid1
, msgstrlen
, midx
);
387 printgnumsg(gmnp
, 1);
390 base
= (char *)header
;
392 msgid_tbl
= gmnp
->msg_tbl
[MSGID
];
393 msgstr_tbl
= gmnp
->msg_tbl
[MSGSTR
];
394 hash_table
= gmnp
->hash_table
;
395 hash_size
= gmnp
->hash_size
;
396 num_of_str
= gmnp
->num_of_str
;
398 if (!(gmnp
->flag
& ST_REV1
) &&
399 (hash_table
== NULL
|| (hash_size
<= 2))) {
402 * No hash table exists or
403 * hash size is enough small.
405 uint32_t top
, bottom
;
411 while (top
< bottom
) {
412 idx
= (top
+ bottom
) / 2;
414 SWAP(gmnp
, msgid_tbl
[idx
].offset
);
416 val
= strcmp(msg_id_str
, msgid1
);
419 } else if (val
> 0) {
422 *msgstrlen
= (unsigned int)
423 SWAP(gmnp
, msgstr_tbl
[idx
].len
) + 1;
426 SWAP(gmnp
, msgstr_tbl
[idx
].offset
));
430 return ((char *)msgid1
);
434 hash_id
= get_hashid(msgid1
, &msglen
);
435 hash_idx
= hash_id
% hash_size
;
436 hash_inc
= 1 + (hash_id
% (hash_size
- 2));
439 hash_val
= HASH_TBL(gmnp
, hash_table
[hash_idx
]);
443 return ((char *)msgid1
);
445 if (hash_val
<= num_of_str
) {
448 mlen
= SWAP(gmnp
, msgid_tbl
[idx
].len
);
449 msg
= base
+ SWAP(gmnp
, msgid_tbl
[idx
].offset
);
451 if (!(gmnp
->flag
& ST_REV1
)) {
452 /* rev 0 does not have dynamic message */
453 return ((char *)msgid1
);
455 /* dynamic message */
456 idx
= hash_val
- num_of_str
- 1;
457 mlen
= gmnp
->d_msg
[MSGID
][idx
].len
;
458 msg
= gmnp
->mchunk
+ gmnp
->d_msg
[MSGID
][idx
].offset
;
460 if (msglen
<= mlen
&& strcmp(msgid1
, msg
) == 0) {
464 hash_idx
= (hash_idx
+ hash_inc
) % hash_size
;
467 /* msgstrlen should include a null termination */
468 if (hash_val
<= num_of_str
) {
469 *msgstrlen
= SWAP(gmnp
, msgstr_tbl
[idx
].len
) + 1;
470 msg
= base
+ SWAP(gmnp
, msgstr_tbl
[idx
].offset
);
473 *msgstrlen
= gmnp
->d_msg
[MSGSTR
][idx
].len
+ 1;
474 msg
= gmnp
->mchunk
+ gmnp
->d_msg
[MSGSTR
][idx
].offset
;
475 *midx
= idx
+ num_of_str
;
484 * Converts the specified string from the src encoding
485 * to the dst encoding by calling iconv()
488 do_conv(iconv_t fd
, const char *src
, uint32_t srclen
)
491 uint32_t *ptr
, *optr
;
492 size_t oleft
, ileft
, bufsize
, memincr
;
496 gprintf(0, "*************** do_conv("
497 "0x%p, \"%s\", %d)\n",
498 (void *)fd
, src
? src
: "(null)", srclen
);
501 memincr
= srclen
* 2;
505 ptr
= malloc(bufsize
+ sizeof (uint32_t));
509 to
= (char *)(ptr
+ 1);
515 gprintf(0, "******* calling iconv()\n");
517 if (iconv(fd
, &src
, &ileft
, &tptr
, &oleft
) == (size_t)-1) {
518 if (errno
== E2BIG
) {
520 gprintf(0, "******* iconv detected E2BIG\n");
521 gprintf(0, "old bufsize: %u\n", bufsize
);
525 bufsize
+ memincr
+ sizeof (uint32_t));
531 to
= (char *)(optr
+ 1);
532 to
+= bufsize
- oleft
;
536 gprintf(0, "new bufsize: %u\n", bufsize
);
540 tolen
= (uint32_t)(bufsize
- oleft
);
544 tolen
= (uint32_t)(bufsize
- oleft
);
548 if (tolen
< bufsize
) {
549 /* shrink the buffer */
550 optr
= realloc(ptr
, tolen
+ sizeof (uint32_t));
560 gprintf(0, "******* exiting do_conv()\n");
561 gprintf(0, "tolen: %u\n", *ptr
);
562 gprintf(0, "return: 0x%p\n", ptr
);
571 conv_msg(Msg_g_node
*gmnp
, char *msgstr
, uint32_t msgstr_len
, uint32_t midx
,
575 size_t num_of_conv
, conv_msgstr_len
;
576 char *conv_msgstr
, *result
;
578 if (gmnp
->conv_msgstr
== NULL
) {
579 num_of_conv
= gmnp
->num_of_str
+ gmnp
->num_of_d_str
;
581 calloc((size_t)num_of_conv
, sizeof (uint32_t *));
582 if (gmnp
->conv_msgstr
== NULL
) {
584 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
589 conv_dst
= do_conv(gmnp
->fd
, (const char *)msgstr
, msgstr_len
);
591 if (conv_dst
== NULL
) {
592 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
595 conv_msgstr_len
= *conv_dst
;
596 gmnp
->conv_msgstr
[midx
] = conv_dst
;
597 conv_msgstr
= (char *)(conv_dst
+ 1);
598 result
= dfltmsgstr(gmnp
, conv_msgstr
, conv_msgstr_len
, mp
);
605 * Extracts msgstr from the GNU MO file
608 gnu_key_2_text(Msg_g_node
*gmnp
, const char *codeset
,
611 uint32_t msgstr_len
, midx
;
613 char *result
, *msgstr
;
614 int ret
, conversion
, new_encoding
;
617 gprintf(0, "*************** gnu_key_2_text("
618 "0x%p, \"%s\", 0x%p)\n",
619 (void *)gmnp
, codeset
? codeset
: "(null)", (void *)mp
);
620 printgnumsg(gmnp
, 1);
624 /* first checks if header entry has been processed */
625 if (!(gmnp
->flag
& ST_CHK
)) {
628 msg_header
= gnu_msgsearch(gmnp
, "", &msgstr_len
, &midx
);
629 ret
= parse_header((const char *)msg_header
, gmnp
);
632 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
,
636 gmnp
->flag
|= ST_CHK
;
638 msgstr
= gnu_msgsearch(gmnp
, mp
->msgid1
, &msgstr_len
, &midx
);
639 if (msgstr
== mp
->msgid1
) {
641 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
, mp
->n
, mp
->plural
);
646 printgnumsg(gmnp
, 1);
648 if (gmnp
->dst_encoding
== NULL
) {
650 * destination encoding has not been set.
652 char *dupcodeset
= strdup(codeset
);
653 if (dupcodeset
== NULL
) {
655 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
658 gmnp
->dst_encoding
= dupcodeset
;
660 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
) == 0) {
662 * target encoding and src encoding
664 * No conversion required.
669 * target encoding is different from
671 * New conversion required.
674 if (gmnp
->fd
&& (gmnp
->fd
!= (iconv_t
)-1)) {
675 (void) iconv_close(gmnp
->fd
);
676 gmnp
->fd
= (iconv_t
)-1;
678 if (gmnp
->conv_msgstr
)
679 free_conv_msgstr(gmnp
, 0);
685 * dst encoding has been already set.
687 if (strcmp(gmnp
->dst_encoding
, codeset
) == 0) {
689 * dst encoding and target encoding are the same.
691 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
)
694 * dst encoding and src encoding are the same.
695 * No conversion required.
700 * dst encoding is different from src encoding.
701 * current conversion is valid.
705 /* checks if iconv_open has succeeded before */
706 if (gmnp
->fd
== (iconv_t
)-1) {
708 * iconv_open should have failed before
709 * Assume this conversion is invalid
713 if (gmnp
->conv_msgstr
== NULL
) {
715 * memory allocation for
717 * have failed before.
723 gmnp
->fd
= (iconv_t
)-1;
729 * dst encoding is different from target encoding.
730 * It has changed since before.
732 char *dupcodeset
= strdup(codeset
);
733 if (dupcodeset
== NULL
) {
734 result
= dfltmsgstr(gmnp
, msgstr
,
738 free(gmnp
->dst_encoding
);
739 gmnp
->dst_encoding
= dupcodeset
;
740 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
)
743 * dst encoding and src encoding are the same.
744 * now, no conversion required.
747 if (gmnp
->conv_msgstr
)
748 free_conv_msgstr(gmnp
, 1);
751 * dst encoding is different from src encoding.
752 * new conversion required.
756 if (gmnp
->conv_msgstr
)
757 free_conv_msgstr(gmnp
, 0);
760 if (gmnp
->fd
&& (gmnp
->fd
!= (iconv_t
)-1)) {
761 (void) iconv_close(gmnp
->fd
);
763 if (gmnp
->fd
!= (iconv_t
)-1) {
764 gmnp
->fd
= (iconv_t
)-1;
769 if (conversion
== 0) {
771 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
774 /* conversion required */
776 if (new_encoding
== 0) {
777 /* dst codeset hasn't been changed since before */
779 uint32_t conv_msgstr_len
;
782 if (gmnp
->conv_msgstr
[midx
] == NULL
) {
783 /* this msgstr hasn't been converted yet */
784 result
= conv_msg(gmnp
, msgstr
, msgstr_len
, midx
, mp
);
787 /* this msgstr is in the conversion cache */
788 cmsg
= (uint32_t *)(uintptr_t)gmnp
->conv_msgstr
[midx
];
789 conv_msgstr_len
= *cmsg
;
790 conv_msgstr
= (char *)(cmsg
+ 1);
791 result
= dfltmsgstr(gmnp
, conv_msgstr
, conv_msgstr_len
, mp
);
796 gprintf(0, "******* calling iconv_open()\n");
797 gprintf(0, " dst: \"%s\", src: \"%s\"\n",
798 gmnp
->dst_encoding
, gmnp
->src_encoding
);
800 fd
= iconv_open(gmnp
->dst_encoding
, gmnp
->src_encoding
);
802 if (fd
== (iconv_t
)-1) {
804 * iconv_open() failed.
807 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
810 result
= conv_msg(gmnp
, msgstr
, msgstr_len
, midx
, mp
);
815 #define PRI_STR(x, n) PRI##x##n
816 #define PRI_LEN(x, n) (char)(sizeof (PRI_STR(x, n)) - 1)
817 #define PRIS(P, x) {\
818 /* x/N/ */ P(x, 8), P(x, 16), P(x, 32), P(x, 64), \
819 /* xLEAST/N/ */ P(x, LEAST8), P(x, LEAST16), P(x, LEAST32), P(x, LEAST64), \
820 /* xFAST/N/ */ P(x, FAST8), P(x, FAST16), P(x, FAST32), P(x, FAST64), \
821 /* xMAX,PTR */ P(x, MAX), P(x, PTR) \
824 #define PRI_BIAS_LEAST 4
825 #define PRI_BIAS_FAST 8
826 #define PRI_BIAS_MAX 12
827 #define PRI_BIAS_PTR 13
829 static const char *pri_d
[] = PRIS(PRI_STR
, d
);
830 static const char *pri_i
[] = PRIS(PRI_STR
, i
);
831 static const char *pri_o
[] = PRIS(PRI_STR
, o
);
832 static const char *pri_u
[] = PRIS(PRI_STR
, u
);
833 static const char *pri_x
[] = PRIS(PRI_STR
, x
);
834 static const char *pri_X
[] = PRIS(PRI_STR
, X
);
836 static const char pri_d_len
[] = PRIS(PRI_LEN
, d
);
837 static const char pri_i_len
[] = PRIS(PRI_LEN
, i
);
838 static const char pri_o_len
[] = PRIS(PRI_LEN
, o
);
839 static const char pri_u_len
[] = PRIS(PRI_LEN
, u
);
840 static const char pri_x_len
[] = PRIS(PRI_LEN
, x
);
841 static const char pri_X_len
[] = PRIS(PRI_LEN
, X
);
845 const char **str_table
;
846 const char *len_table
;
848 {'d', pri_d
, pri_d_len
}, {'i', pri_i
, pri_i_len
},
849 {'o', pri_o
, pri_o_len
}, {'u', pri_u
, pri_u_len
},
850 {'x', pri_x
, pri_x_len
}, {'X', pri_X
, pri_X_len
},
856 const char want_digits
;
858 } special_table
[] = {
859 {"LEAST", 5, 1, PRI_BIAS_LEAST
},
860 {"FAST", 4, 1, PRI_BIAS_FAST
},
861 {"MAX", 3, 0, PRI_BIAS_MAX
},
862 {"PTR", 3, 0, PRI_BIAS_PTR
},
866 * conv_macro() returns the conversion specifier corresponding
867 * to the macro name specified in 'name'. 'len' contains the
868 * length of the macro name including the null termination.
869 * '*elen' will be set to the length of the returning conversion
870 * specifier without the null termination.
873 conv_macro(const char *str
, uint32_t len
, uint32_t *lenp
)
878 int n
, i
, num
, bias
, idx
, want_digits
;
882 /* Solaris does not support %I */
889 if (len
<= 4 || strncmp(str
, "PRI", 3) != 0)
894 n
= sizeof (pri_table
) / sizeof (pri_table
[0]);
895 for (i
= 0; i
< n
; i
++) {
896 if (pri_table
[i
].type
== *str
)
901 tbl
= pri_table
[i
].str_table
;
902 ltbl
= pri_table
[i
].len_table
;
905 idx
= want_digits
= 0;
907 if (isdigit((unsigned char)*str
)) {
912 n
= sizeof (special_table
) / sizeof (special_table
[0]);
913 for (i
= 0; i
< n
; i
++) {
914 if (strncmp(special_table
[i
].name
,
915 str
, special_table
[i
].nlen
) == 0) {
921 bias
= special_table
[i
].bias
;
922 want_digits
= special_table
[i
].want_digits
;
923 str
+= special_table
[i
].nlen
;
927 if (!isdigit((unsigned char)*str
))
929 num
= strtol(str
, &next
, 10);
930 /* see if it is 8/16/32/64 */
931 for (n
= 8, idx
= 0; idx
< 4; idx
++, n
*= 2) {
944 *lenp
= (uint32_t)ltbl
[bias
+ idx
];
945 return (tbl
[bias
+ idx
]);
948 static gnu_d_macro_t
*
949 expand_macros(Msg_g_node
*p
)
951 char *base
= (char *)p
->msg_file_info
;
952 struct gnu_msg_rev1_info
*rev1_header
= p
->rev1_header
;
953 struct gnu_msg_ent
*d_macro_tbl
;
954 gnu_d_macro_t
*d_macro
;
955 uint32_t num_of_d_macro
, e_maclen
, maclen
, i
;
956 const char *e_macname
;
959 /* number of the dynamic macros */
960 num_of_d_macro
= SWAP(p
, rev1_header
->num_of_dynamic_macro
);
962 d_macro
= malloc((size_t)num_of_d_macro
* sizeof (gnu_d_macro_t
));
966 /* pointer to the dynamic strings table */
967 d_macro_tbl
= (struct gnu_msg_ent
*)(uintptr_t)
968 (base
+ SWAP(p
, rev1_header
->off_dynamic_macro
));
970 for (i
= 0; i
< num_of_d_macro
; i
++) {
971 macname
= base
+ SWAP(p
, d_macro_tbl
[i
].offset
);
972 maclen
= SWAP(p
, d_macro_tbl
[i
].len
);
976 * maclen includes a null termination.
978 if (maclen
!= strlen(macname
) + 1) {
982 e_macname
= conv_macro(macname
, maclen
, &e_maclen
);
983 if (e_macname
== NULL
) {
987 d_macro
[i
].len
= e_maclen
;
988 d_macro
[i
].ptr
= e_macname
;
995 expand_dynamic_message(Msg_g_node
*p
, struct gnu_msg_ent
**e_msgs
)
998 char *base
= (char *)p
->msg_file_info
;
999 struct gnu_msg_rev1_info
*rev1_header
= p
->rev1_header
;
1000 struct gnu_dynamic_tbl
*d_info
;
1001 struct gnu_dynamic_ent
*entry
;
1002 gnu_d_macro_t
*d_macro
;
1003 uint32_t num_of_d_str
, mlen
, dlen
, didx
, i
, j
;
1005 uint32_t *d_msg_off_tbl
;
1006 size_t mchunk_size
, used
, need
;
1009 #define MEM_INCR (1024)
1011 d_macro
= expand_macros(p
);
1012 if (d_macro
== NULL
)
1015 /* number of dynamic messages */
1016 num_of_d_str
= p
->num_of_d_str
;
1019 mchunk_size
= 0; /* size of the allocated memory in mchunk */
1020 used
= 0; /* size of the used memory in mchunk */
1021 for (i
= MSGID
; i
<= MSGSTR
; i
++) {
1022 /* pointer to the offset table of dynamic msgids/msgstrs */
1024 i
== MSGID
? rev1_header
->off_dynamic_msgid_tbl
:
1025 rev1_header
->off_dynamic_msgstr_tbl
);
1026 /* pointer to the dynamic msgids/msgstrs */
1027 d_msg_off_tbl
= (uint32_t *)(uintptr_t)(base
+ off_d_tbl
);
1028 for (j
= 0; j
< num_of_d_str
; j
++) {
1029 e_msgs
[i
][j
].offset
= used
;
1030 d_info
= (struct gnu_dynamic_tbl
*)(uintptr_t)
1031 (base
+ SWAP(p
, d_msg_off_tbl
[j
]));
1032 entry
= d_info
->entry
;
1033 msg
= base
+ SWAP(p
, d_info
->offset
);
1036 mlen
= SWAP(p
, entry
->len
);
1037 didx
= SWAP(p
, entry
->idx
);
1038 dlen
= (didx
== NOMORE_DYNAMIC_MACRO
) ? 0 :
1040 need
= used
+ mlen
+ dlen
;
1041 if (need
>= mchunk_size
) {
1043 size_t n
= mchunk_size
;
1046 } while (n
<= need
);
1047 t
= realloc(mchunk
, n
);
1056 (void) memcpy(mchunk
+ used
, msg
, (size_t)mlen
);
1060 if (didx
== NOMORE_DYNAMIC_MACRO
) {
1062 * Last segment of a static
1063 * msg string contains a null
1064 * termination, so an explicit
1065 * null termination is not required
1070 (void) memcpy(mchunk
+ used
,
1071 d_macro
[didx
].ptr
, (size_t)dlen
);
1073 entry
++; /* to next entry */
1076 * e_msgs[][].len does not include a null termination
1078 e_msgs
[i
][j
].len
= used
- e_msgs
[i
][j
].offset
- 1;
1084 /* shrink mchunk to 'used' */
1087 t
= realloc(mchunk
, used
);
1099 build_rev1_info(Msg_g_node
*p
)
1102 uint32_t num_of_d_str
, num_of_str
;
1103 uint32_t idx
, hash_value
, hash_size
;
1104 size_t hash_mem_size
;
1105 size_t d_msgid_size
, d_msgstr_size
;
1106 char *chunk
, *mchunk
;
1109 #ifdef GETTEXT_DEBUG
1110 gprintf(0, "******* entering build_rev1_info(0x%p)\n", p
);
1114 if (p
->hash_table
== NULL
) {
1115 /* Revision 1 always requires the hash table */
1119 num_of_str
= p
->num_of_str
;
1120 hash_size
= p
->hash_size
;
1121 num_of_d_str
= p
->num_of_d_str
;
1123 hash_mem_size
= hash_size
* sizeof (uint32_t);
1124 ROUND(hash_mem_size
, sizeof (struct gnu_msg_ent
));
1126 d_msgid_size
= num_of_d_str
* sizeof (struct gnu_msg_ent
);
1127 d_msgstr_size
= num_of_d_str
* sizeof (struct gnu_msg_ent
);
1129 chunk
= malloc(hash_mem_size
+ d_msgid_size
+ d_msgstr_size
);
1130 if (chunk
== NULL
) {
1134 d_hash
= (uint32_t *)(uintptr_t)chunk
;
1135 p
->d_msg
[MSGID
] = (struct gnu_msg_ent
*)(uintptr_t)
1136 (chunk
+ hash_mem_size
);
1137 p
->d_msg
[MSGSTR
] = (struct gnu_msg_ent
*)(uintptr_t)
1138 (chunk
+ hash_mem_size
+ d_msgid_size
);
1140 if ((mchunk
= expand_dynamic_message(p
, p
->d_msg
)) == NULL
) {
1145 /* copy the original hash table into the dynamic hash table */
1146 for (i
= 0; i
< hash_size
; i
++) {
1147 d_hash
[i
] = SWAP(p
, p
->hash_table
[i
]);
1150 /* fill in the dynamic hash table with dynamic messages */
1151 for (i
= 0; i
< num_of_d_str
; i
++) {
1152 hash_value
= get_hashid(mchunk
+ p
->d_msg
[MSGID
][i
].offset
,
1154 idx
= get_hash_index(d_hash
, hash_value
, hash_size
);
1155 d_hash
[idx
] = num_of_str
+ i
+ 1;
1159 p
->hash_table
= d_hash
;
1161 #ifdef GETTEXT_DEBUG
1163 gprintf(0, "******* exiting build_rev1_info()\n");
1174 * mnp - message node
1175 * addr - address to the mmapped file
1176 * size - size of the file
1179 * 0 - either T_GNU_MO or T_ILL_MO has been set
1183 gnu_setmsg(Msg_node
*mnp
, char *addr
, size_t size
)
1185 struct gnu_msg_info
*gnu_header
;
1188 #ifdef GETTEXT_DEBUG
1189 gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
1190 (void *)mnp
, addr
, size
);
1194 /* checks the GNU MAGIC number */
1195 if (size
< sizeof (struct gnu_msg_info
)) {
1196 /* invalid mo file */
1197 mnp
->type
= T_ILL_MO
;
1198 #ifdef GETTEXT_DEBUG
1199 gprintf(0, "********* exiting gnu_setmsg\n");
1205 gnu_header
= (struct gnu_msg_info
*)(uintptr_t)addr
;
1207 p
= calloc(1, sizeof (Msg_g_node
));
1211 p
->msg_file_info
= gnu_header
;
1213 if (gnu_header
->magic
== GNU_MAGIC
) {
1214 switch (gnu_header
->revision
) {
1215 case GNU_REVISION_0_1
:
1216 case GNU_REVISION_1_1
:
1220 } else if (gnu_header
->magic
== GNU_MAGIC_SWAPPED
) {
1222 switch (gnu_header
->revision
) {
1223 case GNU_REVISION_0_1_SWAPPED
:
1224 case GNU_REVISION_1_1_SWAPPED
:
1229 /* invalid mo file */
1231 mnp
->type
= T_ILL_MO
;
1232 #ifdef GETTEXT_DEBUG
1233 gprintf(0, "********* exiting gnu_setmsg\n");
1240 p
->num_of_str
= SWAP(p
, gnu_header
->num_of_str
);
1241 p
->hash_size
= SWAP(p
, gnu_header
->sz_hashtbl
);
1242 p
->hash_table
= p
->hash_size
<= 2 ? NULL
:
1243 (uint32_t *)(uintptr_t)
1244 (addr
+ SWAP(p
, gnu_header
->off_hashtbl
));
1246 p
->msg_tbl
[MSGID
] = (struct gnu_msg_ent
*)(uintptr_t)
1247 (addr
+ SWAP(p
, gnu_header
->off_msgid_tbl
));
1248 p
->msg_tbl
[MSGSTR
] = (struct gnu_msg_ent
*)(uintptr_t)
1249 (addr
+ SWAP(p
, gnu_header
->off_msgstr_tbl
));
1251 if (p
->flag
& ST_REV1
) {
1253 struct gnu_msg_rev1_info
*rev1_header
;
1255 rev1_header
= (struct gnu_msg_rev1_info
*)
1256 (uintptr_t)(addr
+ sizeof (struct gnu_msg_info
));
1257 p
->rev1_header
= rev1_header
;
1258 p
->num_of_d_str
= SWAP(p
, rev1_header
->num_of_dynamic_str
);
1259 if (build_rev1_info(p
) == -1) {
1261 #ifdef GETTEXT_DEBUG
1262 gprintf(0, "******** exiting gnu_setmsg: "
1263 "build_rev1_info() failed\n");
1269 mnp
->msg
.gnumsg
= p
;
1270 mnp
->type
= T_GNU_MO
;
1272 #ifdef GETTEXT_DEBUG
1273 gprintf(0, "********* exiting gnu_setmsg\n");
1282 * Returns the index to an empty slot in the hash table
1283 * for the specified hash_value.
1286 get_hash_index(uint32_t *hash_tbl
, uint32_t hash_value
, uint32_t hash_size
)
1290 idx
= hash_value
% hash_size
;
1291 inc
= 1 + (hash_value
% (hash_size
- 2));
1294 if (hash_tbl
[idx
] == 0) {
1295 /* found an empty slot */
1298 idx
= (idx
+ inc
) % hash_size
;