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.
33 #include <sys/types.h>
35 #include <sys/param.h>
45 #include "nlspath_checks.h"
52 /* The following symbols are just for GNU binary compatibility */
54 int *_nl_domain_bindings
;
56 static const char *nullstr
= "";
58 #define CHARSET_MOD "charset="
59 #define CHARSET_LEN (sizeof (CHARSET_MOD) - 1)
60 #define NPLURALS_MOD "nplurals="
61 #define NPLURALS_LEN (sizeof (NPLURALS_MOD) - 1)
62 #define PLURAL_MOD "plural="
63 #define PLURAL_LEN (sizeof (PLURAL_MOD) - 1)
65 static uint32_t get_hash_index(uint32_t *, uint32_t, uint32_t);
70 * release the memory allocated for storing code-converted messages
73 * 0: do not free gmnp->conv_msgstr
74 * 1: free gmnp->conv_msgstr
77 free_conv_msgstr(Msg_g_node
*gmnp
, int f
)
79 uint32_t i
, num_of_conv
;
82 gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
87 num_of_conv
= gmnp
->num_of_str
+ gmnp
->num_of_d_str
;
88 for (i
= 0; i
< num_of_conv
; i
++) {
89 if (gmnp
->conv_msgstr
[i
]) {
90 free(gmnp
->conv_msgstr
[i
]);
92 gmnp
->conv_msgstr
[i
] = NULL
;
95 free(gmnp
->conv_msgstr
);
96 gmnp
->conv_msgstr
= NULL
;
103 * choose an appropriate message by evaluating the plural expression,
107 dfltmsgstr(Msg_g_node
*gmnp
, const char *msgstr
, uint32_t msgstr_len
,
115 gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
117 msgstr
? msgstr
: "(null)", msgstr_len
, (void *)mp
);
118 printgnumsg(gmnp
, 1);
124 pindex
= plural_eval(gmnp
->plural
, mp
->n
);
127 * This mo does not have plural information.
128 * Using the English form.
136 gprintf(0, "plural_eval returned: %u\n", pindex
);
138 if (pindex
>= gmnp
->nplurals
) {
139 /* should never happen */
143 for (; pindex
!= 0; pindex
--) {
144 len
= msgstr_len
- (p
- msgstr
);
145 p
= memchr(p
, '\0', len
);
148 * null byte not found
149 * this should never happen
152 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
,
161 return ((char *)msgstr
);
167 * parse the header entry of the GNU MO file and
168 * extract the src encoding and the plural information of the MO file
171 parse_header(const char *header
, Msg_g_node
*gmnp
)
173 char *charset
= NULL
;
176 char *nplurals_str
, *plural_str
;
177 plural_expr_t plural
;
179 unsigned int nplurals
;
183 gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
184 header
? header
: "(null)", (void *)gmnp
);
185 printgnumsg(gmnp
, 1);
188 if (header
== NULL
) {
189 gmnp
->src_encoding
= (char *)nullstr
;
193 gprintf(0, "*************** exiting parse_header\n");
194 gprintf(0, "no header\n");
200 charset_str
= strstr(header
, CHARSET_MOD
);
201 if (charset_str
== NULL
) {
202 gmnp
->src_encoding
= (char *)nullstr
;
204 p
= charset_str
+ CHARSET_LEN
;
206 while ((*q
!= ' ') && (*q
!= '\t') &&
212 charset
= malloc(len
+ 1);
213 if (charset
== NULL
) {
214 gmnp
->src_encoding
= (char *)nullstr
;
219 (void) memcpy(charset
, p
, len
);
221 gmnp
->src_encoding
= charset
;
223 gmnp
->src_encoding
= (char *)nullstr
;
227 nplurals_str
= strstr(header
, NPLURALS_MOD
);
228 plural_str
= strstr(header
, PLURAL_MOD
);
229 if (nplurals_str
== NULL
|| plural_str
== NULL
) {
230 /* no valid plural specification */
234 gprintf(0, "*************** exiting parse_header\n");
235 gprintf(0, "no plural entry\n");
239 p
= nplurals_str
+ NPLURALS_LEN
;
240 while (*p
&& isspace((unsigned char)*p
)) {
243 nplurals
= (unsigned int)strtol(p
, &q
, 10);
245 gmnp
->nplurals
= nplurals
;
250 p
= plural_str
+ PLURAL_LEN
;
252 gprintf(0, "plural_str: \"%s\"\n", p
);
255 ret
= plural_expr(&plural
, (const char *)p
);
257 /* parse succeeded */
258 gmnp
->plural
= plural
;
260 gprintf(0, "*************** exiting parse_header\n");
261 gprintf(0, "charset: \"%s\"\n",
262 charset
? charset
: "(null)");
263 printexpr(plural
, 1);
266 } else if (ret
== 1) {
274 gmnp
->src_encoding
= (char *)nullstr
;
286 * take care of the LANGUAGE specification
289 handle_lang(struct msg_pack
*mp
)
291 const char *p
, *op
, *q
;
294 char locale
[MAXPATHLEN
];
298 gprintf(0, "*************** handle_lang(0x%p)\n", (void *)mp
);
308 locale_len
= strlen(p
);
314 if (locale_len
>= MAXPATHLEN
|| locale_len
== 0) {
315 /* illegal locale name */
318 (void) memcpy(locale
, op
, locale_len
);
319 locale
[locale_len
] = '\0';
325 if (mk_msgfile(mp
) == NULL
) {
326 /* illegal locale name */
330 result
= handle_mo(mp
);
331 if (mp
->status
& ST_GNU_MSG_FOUND
)
334 if (mp
->status
& ST_SUN_MO_FOUND
)
339 * no valid locale found, Sun MO found, or
340 * GNU MO found but no valid msg found there.
343 if (mp
->status
& ST_GNU_MO_FOUND
) {
345 * GNU MO found but no valid msg found there.
348 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
, mp
->n
, mp
->plural
);
357 * Searchs the translation message for the specified msgid1.
358 * Hash algorithm used in this function is Open Addressing
359 * with Double Hashing:
360 * H(k, i) = (H1(k) + i * H2(k)) mod M
361 * H1(k) = hashvalue % M
362 * H2(k) = 1 + (hashvalue % (M - 2))
364 * Ref: The Art of Computer Programming Volume 3
365 * Sorting and Searching, second edition
369 gnu_msgsearch(Msg_g_node
*gmnp
, const char *msgid1
,
370 uint32_t *msgstrlen
, uint32_t *midx
)
372 struct gnu_msg_info
*header
= gmnp
->msg_file_info
;
373 struct gnu_msg_ent
*msgid_tbl
, *msgstr_tbl
;
374 uint32_t num_of_str
, idx
, mlen
, msglen
;
375 uint32_t hash_size
, hash_val
, hash_id
, hash_inc
, hash_idx
;
376 uint32_t *hash_table
;
381 gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
383 (void *)gmnp
, msgid1
, msgstrlen
, midx
);
384 printgnumsg(gmnp
, 1);
387 base
= (char *)header
;
389 msgid_tbl
= gmnp
->msg_tbl
[MSGID
];
390 msgstr_tbl
= gmnp
->msg_tbl
[MSGSTR
];
391 hash_table
= gmnp
->hash_table
;
392 hash_size
= gmnp
->hash_size
;
393 num_of_str
= gmnp
->num_of_str
;
395 if (!(gmnp
->flag
& ST_REV1
) &&
396 (hash_table
== NULL
|| (hash_size
<= 2))) {
399 * No hash table exists or
400 * hash size is enough small.
402 uint32_t top
, bottom
;
408 while (top
< bottom
) {
409 idx
= (top
+ bottom
) / 2;
411 SWAP(gmnp
, msgid_tbl
[idx
].offset
);
413 val
= strcmp(msg_id_str
, msgid1
);
416 } else if (val
> 0) {
419 *msgstrlen
= (unsigned int)
420 SWAP(gmnp
, msgstr_tbl
[idx
].len
) + 1;
423 SWAP(gmnp
, msgstr_tbl
[idx
].offset
));
427 return ((char *)msgid1
);
431 hash_id
= get_hashid(msgid1
, &msglen
);
432 hash_idx
= hash_id
% hash_size
;
433 hash_inc
= 1 + (hash_id
% (hash_size
- 2));
436 hash_val
= HASH_TBL(gmnp
, hash_table
[hash_idx
]);
440 return ((char *)msgid1
);
442 if (hash_val
<= num_of_str
) {
445 mlen
= SWAP(gmnp
, msgid_tbl
[idx
].len
);
446 msg
= base
+ SWAP(gmnp
, msgid_tbl
[idx
].offset
);
448 if (!(gmnp
->flag
& ST_REV1
)) {
449 /* rev 0 does not have dynamic message */
450 return ((char *)msgid1
);
452 /* dynamic message */
453 idx
= hash_val
- num_of_str
- 1;
454 mlen
= gmnp
->d_msg
[MSGID
][idx
].len
;
455 msg
= gmnp
->mchunk
+ gmnp
->d_msg
[MSGID
][idx
].offset
;
457 if (msglen
<= mlen
&& strcmp(msgid1
, msg
) == 0) {
461 hash_idx
= (hash_idx
+ hash_inc
) % hash_size
;
464 /* msgstrlen should include a null termination */
465 if (hash_val
<= num_of_str
) {
466 *msgstrlen
= SWAP(gmnp
, msgstr_tbl
[idx
].len
) + 1;
467 msg
= base
+ SWAP(gmnp
, msgstr_tbl
[idx
].offset
);
470 *msgstrlen
= gmnp
->d_msg
[MSGSTR
][idx
].len
+ 1;
471 msg
= gmnp
->mchunk
+ gmnp
->d_msg
[MSGSTR
][idx
].offset
;
472 *midx
= idx
+ num_of_str
;
481 * Converts the specified string from the src encoding
482 * to the dst encoding by calling iconv()
485 do_conv(iconv_t fd
, const char *src
, uint32_t srclen
)
488 uint32_t *ptr
, *optr
;
489 size_t oleft
, ileft
, bufsize
, memincr
;
493 gprintf(0, "*************** do_conv("
494 "0x%p, \"%s\", %d)\n",
495 (void *)fd
, src
? src
: "(null)", srclen
);
498 memincr
= srclen
* 2;
502 ptr
= malloc(bufsize
+ sizeof (uint32_t));
506 to
= (char *)(ptr
+ 1);
512 gprintf(0, "******* calling iconv()\n");
514 if (iconv(fd
, &src
, &ileft
, &tptr
, &oleft
) == (size_t)-1) {
515 if (errno
== E2BIG
) {
517 gprintf(0, "******* iconv detected E2BIG\n");
518 gprintf(0, "old bufsize: %u\n", bufsize
);
522 bufsize
+ memincr
+ sizeof (uint32_t));
528 to
= (char *)(optr
+ 1);
529 to
+= bufsize
- oleft
;
533 gprintf(0, "new bufsize: %u\n", bufsize
);
537 tolen
= (uint32_t)(bufsize
- oleft
);
541 tolen
= (uint32_t)(bufsize
- oleft
);
545 if (tolen
< bufsize
) {
546 /* shrink the buffer */
547 optr
= realloc(ptr
, tolen
+ sizeof (uint32_t));
557 gprintf(0, "******* exiting do_conv()\n");
558 gprintf(0, "tolen: %u\n", *ptr
);
559 gprintf(0, "return: 0x%p\n", ptr
);
568 conv_msg(Msg_g_node
*gmnp
, char *msgstr
, uint32_t msgstr_len
, uint32_t midx
,
572 size_t num_of_conv
, conv_msgstr_len
;
573 char *conv_msgstr
, *result
;
575 if (gmnp
->conv_msgstr
== NULL
) {
576 num_of_conv
= gmnp
->num_of_str
+ gmnp
->num_of_d_str
;
578 calloc((size_t)num_of_conv
, sizeof (uint32_t *));
579 if (gmnp
->conv_msgstr
== NULL
) {
581 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
586 conv_dst
= do_conv(gmnp
->fd
, (const char *)msgstr
, msgstr_len
);
588 if (conv_dst
== NULL
) {
589 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
592 conv_msgstr_len
= *conv_dst
;
593 gmnp
->conv_msgstr
[midx
] = conv_dst
;
594 conv_msgstr
= (char *)(conv_dst
+ 1);
595 result
= dfltmsgstr(gmnp
, conv_msgstr
, conv_msgstr_len
, mp
);
602 * Extracts msgstr from the GNU MO file
605 gnu_key_2_text(Msg_g_node
*gmnp
, const char *codeset
,
608 uint32_t msgstr_len
, midx
;
610 char *result
, *msgstr
;
611 int ret
, conversion
, new_encoding
;
614 gprintf(0, "*************** gnu_key_2_text("
615 "0x%p, \"%s\", 0x%p)\n",
616 (void *)gmnp
, codeset
? codeset
: "(null)", (void *)mp
);
617 printgnumsg(gmnp
, 1);
621 /* first checks if header entry has been processed */
622 if (!(gmnp
->flag
& ST_CHK
)) {
625 msg_header
= gnu_msgsearch(gmnp
, "", &msgstr_len
, &midx
);
626 ret
= parse_header((const char *)msg_header
, gmnp
);
629 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
,
633 gmnp
->flag
|= ST_CHK
;
635 msgstr
= gnu_msgsearch(gmnp
, mp
->msgid1
, &msgstr_len
, &midx
);
636 if (msgstr
== mp
->msgid1
) {
638 DFLTMSG(result
, mp
->msgid1
, mp
->msgid2
, mp
->n
, mp
->plural
);
643 printgnumsg(gmnp
, 1);
645 if (gmnp
->dst_encoding
== NULL
) {
647 * destination encoding has not been set.
649 char *dupcodeset
= strdup(codeset
);
650 if (dupcodeset
== NULL
) {
652 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
655 gmnp
->dst_encoding
= dupcodeset
;
657 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
) == 0) {
659 * target encoding and src encoding
661 * No conversion required.
666 * target encoding is different from
668 * New conversion required.
671 if (gmnp
->fd
&& (gmnp
->fd
!= (iconv_t
)-1)) {
672 (void) iconv_close(gmnp
->fd
);
673 gmnp
->fd
= (iconv_t
)-1;
675 if (gmnp
->conv_msgstr
)
676 free_conv_msgstr(gmnp
, 0);
682 * dst encoding has been already set.
684 if (strcmp(gmnp
->dst_encoding
, codeset
) == 0) {
686 * dst encoding and target encoding are the same.
688 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
)
691 * dst encoding and src encoding are the same.
692 * No conversion required.
697 * dst encoding is different from src encoding.
698 * current conversion is valid.
702 /* checks if iconv_open has succeeded before */
703 if (gmnp
->fd
== (iconv_t
)-1) {
705 * iconv_open should have failed before
706 * Assume this conversion is invalid
710 if (gmnp
->conv_msgstr
== NULL
) {
712 * memory allocation for
714 * have failed before.
720 gmnp
->fd
= (iconv_t
)-1;
726 * dst encoding is different from target encoding.
727 * It has changed since before.
729 char *dupcodeset
= strdup(codeset
);
730 if (dupcodeset
== NULL
) {
731 result
= dfltmsgstr(gmnp
, msgstr
,
735 free(gmnp
->dst_encoding
);
736 gmnp
->dst_encoding
= dupcodeset
;
737 if (strcmp(gmnp
->dst_encoding
, gmnp
->src_encoding
)
740 * dst encoding and src encoding are the same.
741 * now, no conversion required.
744 if (gmnp
->conv_msgstr
)
745 free_conv_msgstr(gmnp
, 1);
748 * dst encoding is different from src encoding.
749 * new conversion required.
753 if (gmnp
->conv_msgstr
)
754 free_conv_msgstr(gmnp
, 0);
757 if (gmnp
->fd
&& (gmnp
->fd
!= (iconv_t
)-1)) {
758 (void) iconv_close(gmnp
->fd
);
760 if (gmnp
->fd
!= (iconv_t
)-1) {
761 gmnp
->fd
= (iconv_t
)-1;
766 if (conversion
== 0) {
768 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
771 /* conversion required */
773 if (new_encoding
== 0) {
774 /* dst codeset hasn't been changed since before */
776 uint32_t conv_msgstr_len
;
779 if (gmnp
->conv_msgstr
[midx
] == NULL
) {
780 /* this msgstr hasn't been converted yet */
781 result
= conv_msg(gmnp
, msgstr
, msgstr_len
, midx
, mp
);
784 /* this msgstr is in the conversion cache */
785 cmsg
= (uint32_t *)(uintptr_t)gmnp
->conv_msgstr
[midx
];
786 conv_msgstr_len
= *cmsg
;
787 conv_msgstr
= (char *)(cmsg
+ 1);
788 result
= dfltmsgstr(gmnp
, conv_msgstr
, conv_msgstr_len
, mp
);
793 gprintf(0, "******* calling iconv_open()\n");
794 gprintf(0, " dst: \"%s\", src: \"%s\"\n",
795 gmnp
->dst_encoding
, gmnp
->src_encoding
);
797 fd
= iconv_open(gmnp
->dst_encoding
, gmnp
->src_encoding
);
799 if (fd
== (iconv_t
)-1) {
801 * iconv_open() failed.
804 result
= dfltmsgstr(gmnp
, msgstr
, msgstr_len
, mp
);
807 result
= conv_msg(gmnp
, msgstr
, msgstr_len
, midx
, mp
);
812 #define PRI_STR(x, n) PRI##x##n
813 #define PRI_LEN(x, n) (char)(sizeof (PRI_STR(x, n)) - 1)
814 #define PRIS(P, x) {\
815 /* x/N/ */ P(x, 8), P(x, 16), P(x, 32), P(x, 64), \
816 /* xLEAST/N/ */ P(x, LEAST8), P(x, LEAST16), P(x, LEAST32), P(x, LEAST64), \
817 /* xFAST/N/ */ P(x, FAST8), P(x, FAST16), P(x, FAST32), P(x, FAST64), \
818 /* xMAX,PTR */ P(x, MAX), P(x, PTR) \
821 #define PRI_BIAS_LEAST 4
822 #define PRI_BIAS_FAST 8
823 #define PRI_BIAS_MAX 12
824 #define PRI_BIAS_PTR 13
826 static const char *pri_d
[] = PRIS(PRI_STR
, d
);
827 static const char *pri_i
[] = PRIS(PRI_STR
, i
);
828 static const char *pri_o
[] = PRIS(PRI_STR
, o
);
829 static const char *pri_u
[] = PRIS(PRI_STR
, u
);
830 static const char *pri_x
[] = PRIS(PRI_STR
, x
);
831 static const char *pri_X
[] = PRIS(PRI_STR
, X
);
833 static const char pri_d_len
[] = PRIS(PRI_LEN
, d
);
834 static const char pri_i_len
[] = PRIS(PRI_LEN
, i
);
835 static const char pri_o_len
[] = PRIS(PRI_LEN
, o
);
836 static const char pri_u_len
[] = PRIS(PRI_LEN
, u
);
837 static const char pri_x_len
[] = PRIS(PRI_LEN
, x
);
838 static const char pri_X_len
[] = PRIS(PRI_LEN
, X
);
842 const char **str_table
;
843 const char *len_table
;
845 {'d', pri_d
, pri_d_len
}, {'i', pri_i
, pri_i_len
},
846 {'o', pri_o
, pri_o_len
}, {'u', pri_u
, pri_u_len
},
847 {'x', pri_x
, pri_x_len
}, {'X', pri_X
, pri_X_len
},
853 const char want_digits
;
855 } special_table
[] = {
856 {"LEAST", 5, 1, PRI_BIAS_LEAST
},
857 {"FAST", 4, 1, PRI_BIAS_FAST
},
858 {"MAX", 3, 0, PRI_BIAS_MAX
},
859 {"PTR", 3, 0, PRI_BIAS_PTR
},
863 * conv_macro() returns the conversion specifier corresponding
864 * to the macro name specified in 'name'. 'len' contains the
865 * length of the macro name including the null termination.
866 * '*elen' will be set to the length of the returning conversion
867 * specifier without the null termination.
870 conv_macro(const char *str
, uint32_t len
, uint32_t *lenp
)
875 int n
, i
, num
, bias
, idx
, want_digits
;
879 /* Solaris does not support %I */
886 if (len
<= 4 || strncmp(str
, "PRI", 3) != 0)
891 n
= sizeof (pri_table
) / sizeof (pri_table
[0]);
892 for (i
= 0; i
< n
; i
++) {
893 if (pri_table
[i
].type
== *str
)
898 tbl
= pri_table
[i
].str_table
;
899 ltbl
= pri_table
[i
].len_table
;
902 idx
= want_digits
= 0;
904 if (isdigit((unsigned char)*str
)) {
909 n
= sizeof (special_table
) / sizeof (special_table
[0]);
910 for (i
= 0; i
< n
; i
++) {
911 if (strncmp(special_table
[i
].name
,
912 str
, special_table
[i
].nlen
) == 0) {
918 bias
= special_table
[i
].bias
;
919 want_digits
= special_table
[i
].want_digits
;
920 str
+= special_table
[i
].nlen
;
924 if (!isdigit((unsigned char)*str
))
926 num
= strtol(str
, &next
, 10);
927 /* see if it is 8/16/32/64 */
928 for (n
= 8, idx
= 0; idx
< 4; idx
++, n
*= 2) {
941 *lenp
= (uint32_t)ltbl
[bias
+ idx
];
942 return (tbl
[bias
+ idx
]);
945 static gnu_d_macro_t
*
946 expand_macros(Msg_g_node
*p
)
948 char *base
= (char *)p
->msg_file_info
;
949 struct gnu_msg_rev1_info
*rev1_header
= p
->rev1_header
;
950 struct gnu_msg_ent
*d_macro_tbl
;
951 gnu_d_macro_t
*d_macro
;
952 uint32_t num_of_d_macro
, e_maclen
, maclen
, i
;
953 const char *e_macname
;
956 /* number of the dynamic macros */
957 num_of_d_macro
= SWAP(p
, rev1_header
->num_of_dynamic_macro
);
959 d_macro
= malloc((size_t)num_of_d_macro
* sizeof (gnu_d_macro_t
));
963 /* pointer to the dynamic strings table */
964 d_macro_tbl
= (struct gnu_msg_ent
*)(uintptr_t)
965 (base
+ SWAP(p
, rev1_header
->off_dynamic_macro
));
967 for (i
= 0; i
< num_of_d_macro
; i
++) {
968 macname
= base
+ SWAP(p
, d_macro_tbl
[i
].offset
);
969 maclen
= SWAP(p
, d_macro_tbl
[i
].len
);
973 * maclen includes a null termination.
975 if (maclen
!= strlen(macname
) + 1) {
979 e_macname
= conv_macro(macname
, maclen
, &e_maclen
);
980 if (e_macname
== NULL
) {
984 d_macro
[i
].len
= e_maclen
;
985 d_macro
[i
].ptr
= e_macname
;
992 expand_dynamic_message(Msg_g_node
*p
, struct gnu_msg_ent
**e_msgs
)
995 char *base
= (char *)p
->msg_file_info
;
996 struct gnu_msg_rev1_info
*rev1_header
= p
->rev1_header
;
997 struct gnu_dynamic_tbl
*d_info
;
998 struct gnu_dynamic_ent
*entry
;
999 gnu_d_macro_t
*d_macro
;
1000 uint32_t num_of_d_str
, mlen
, dlen
, didx
, i
, j
;
1002 uint32_t *d_msg_off_tbl
;
1003 size_t mchunk_size
, used
, need
;
1006 #define MEM_INCR (1024)
1008 d_macro
= expand_macros(p
);
1009 if (d_macro
== NULL
)
1012 /* number of dynamic messages */
1013 num_of_d_str
= p
->num_of_d_str
;
1016 mchunk_size
= 0; /* size of the allocated memory in mchunk */
1017 used
= 0; /* size of the used memory in mchunk */
1018 for (i
= MSGID
; i
<= MSGSTR
; i
++) {
1019 /* pointer to the offset table of dynamic msgids/msgstrs */
1021 i
== MSGID
? rev1_header
->off_dynamic_msgid_tbl
:
1022 rev1_header
->off_dynamic_msgstr_tbl
);
1023 /* pointer to the dynamic msgids/msgstrs */
1024 d_msg_off_tbl
= (uint32_t *)(uintptr_t)(base
+ off_d_tbl
);
1025 for (j
= 0; j
< num_of_d_str
; j
++) {
1026 e_msgs
[i
][j
].offset
= used
;
1027 d_info
= (struct gnu_dynamic_tbl
*)(uintptr_t)
1028 (base
+ SWAP(p
, d_msg_off_tbl
[j
]));
1029 entry
= d_info
->entry
;
1030 msg
= base
+ SWAP(p
, d_info
->offset
);
1033 mlen
= SWAP(p
, entry
->len
);
1034 didx
= SWAP(p
, entry
->idx
);
1035 dlen
= (didx
== NOMORE_DYNAMIC_MACRO
) ? 0 :
1037 need
= used
+ mlen
+ dlen
;
1038 if (need
>= mchunk_size
) {
1040 size_t n
= mchunk_size
;
1043 } while (n
<= need
);
1044 t
= realloc(mchunk
, n
);
1053 (void) memcpy(mchunk
+ used
, msg
, (size_t)mlen
);
1057 if (didx
== NOMORE_DYNAMIC_MACRO
) {
1059 * Last segment of a static
1060 * msg string contains a null
1061 * termination, so an explicit
1062 * null termination is not required
1067 (void) memcpy(mchunk
+ used
,
1068 d_macro
[didx
].ptr
, (size_t)dlen
);
1070 entry
++; /* to next entry */
1073 * e_msgs[][].len does not include a null termination
1075 e_msgs
[i
][j
].len
= used
- e_msgs
[i
][j
].offset
- 1;
1081 /* shrink mchunk to 'used' */
1084 t
= realloc(mchunk
, used
);
1096 build_rev1_info(Msg_g_node
*p
)
1099 uint32_t num_of_d_str
, num_of_str
;
1100 uint32_t idx
, hash_value
, hash_size
;
1101 size_t hash_mem_size
;
1102 size_t d_msgid_size
, d_msgstr_size
;
1103 char *chunk
, *mchunk
;
1106 #ifdef GETTEXT_DEBUG
1107 gprintf(0, "******* entering build_rev1_info(0x%p)\n", p
);
1111 if (p
->hash_table
== NULL
) {
1112 /* Revision 1 always requires the hash table */
1116 num_of_str
= p
->num_of_str
;
1117 hash_size
= p
->hash_size
;
1118 num_of_d_str
= p
->num_of_d_str
;
1120 hash_mem_size
= hash_size
* sizeof (uint32_t);
1121 ROUND(hash_mem_size
, sizeof (struct gnu_msg_ent
));
1123 d_msgid_size
= num_of_d_str
* sizeof (struct gnu_msg_ent
);
1124 d_msgstr_size
= num_of_d_str
* sizeof (struct gnu_msg_ent
);
1126 chunk
= malloc(hash_mem_size
+ d_msgid_size
+ d_msgstr_size
);
1127 if (chunk
== NULL
) {
1131 d_hash
= (uint32_t *)(uintptr_t)chunk
;
1132 p
->d_msg
[MSGID
] = (struct gnu_msg_ent
*)(uintptr_t)
1133 (chunk
+ hash_mem_size
);
1134 p
->d_msg
[MSGSTR
] = (struct gnu_msg_ent
*)(uintptr_t)
1135 (chunk
+ hash_mem_size
+ d_msgid_size
);
1137 if ((mchunk
= expand_dynamic_message(p
, p
->d_msg
)) == NULL
) {
1142 /* copy the original hash table into the dynamic hash table */
1143 for (i
= 0; i
< hash_size
; i
++) {
1144 d_hash
[i
] = SWAP(p
, p
->hash_table
[i
]);
1147 /* fill in the dynamic hash table with dynamic messages */
1148 for (i
= 0; i
< num_of_d_str
; i
++) {
1149 hash_value
= get_hashid(mchunk
+ p
->d_msg
[MSGID
][i
].offset
,
1151 idx
= get_hash_index(d_hash
, hash_value
, hash_size
);
1152 d_hash
[idx
] = num_of_str
+ i
+ 1;
1156 p
->hash_table
= d_hash
;
1158 #ifdef GETTEXT_DEBUG
1160 gprintf(0, "******* exiting build_rev1_info()\n");
1171 * mnp - message node
1172 * addr - address to the mmapped file
1173 * size - size of the file
1176 * 0 - either T_GNU_MO or T_ILL_MO has been set
1180 gnu_setmsg(Msg_node
*mnp
, char *addr
, size_t size
)
1182 struct gnu_msg_info
*gnu_header
;
1185 #ifdef GETTEXT_DEBUG
1186 gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
1187 (void *)mnp
, addr
, size
);
1191 /* checks the GNU MAGIC number */
1192 if (size
< sizeof (struct gnu_msg_info
)) {
1193 /* invalid mo file */
1194 mnp
->type
= T_ILL_MO
;
1195 #ifdef GETTEXT_DEBUG
1196 gprintf(0, "********* exiting gnu_setmsg\n");
1202 gnu_header
= (struct gnu_msg_info
*)(uintptr_t)addr
;
1204 p
= calloc(1, sizeof (Msg_g_node
));
1208 p
->msg_file_info
= gnu_header
;
1210 if (gnu_header
->magic
== GNU_MAGIC
) {
1211 switch (gnu_header
->revision
) {
1212 case GNU_REVISION_0_1
:
1213 case GNU_REVISION_1_1
:
1217 } else if (gnu_header
->magic
== GNU_MAGIC_SWAPPED
) {
1219 switch (gnu_header
->revision
) {
1220 case GNU_REVISION_0_1_SWAPPED
:
1221 case GNU_REVISION_1_1_SWAPPED
:
1226 /* invalid mo file */
1228 mnp
->type
= T_ILL_MO
;
1229 #ifdef GETTEXT_DEBUG
1230 gprintf(0, "********* exiting gnu_setmsg\n");
1237 p
->num_of_str
= SWAP(p
, gnu_header
->num_of_str
);
1238 p
->hash_size
= SWAP(p
, gnu_header
->sz_hashtbl
);
1239 p
->hash_table
= p
->hash_size
<= 2 ? NULL
:
1240 (uint32_t *)(uintptr_t)
1241 (addr
+ SWAP(p
, gnu_header
->off_hashtbl
));
1243 p
->msg_tbl
[MSGID
] = (struct gnu_msg_ent
*)(uintptr_t)
1244 (addr
+ SWAP(p
, gnu_header
->off_msgid_tbl
));
1245 p
->msg_tbl
[MSGSTR
] = (struct gnu_msg_ent
*)(uintptr_t)
1246 (addr
+ SWAP(p
, gnu_header
->off_msgstr_tbl
));
1248 if (p
->flag
& ST_REV1
) {
1250 struct gnu_msg_rev1_info
*rev1_header
;
1252 rev1_header
= (struct gnu_msg_rev1_info
*)
1253 (uintptr_t)(addr
+ sizeof (struct gnu_msg_info
));
1254 p
->rev1_header
= rev1_header
;
1255 p
->num_of_d_str
= SWAP(p
, rev1_header
->num_of_dynamic_str
);
1256 if (build_rev1_info(p
) == -1) {
1258 #ifdef GETTEXT_DEBUG
1259 gprintf(0, "******** exiting gnu_setmsg: "
1260 "build_rev1_info() failed\n");
1266 mnp
->msg
.gnumsg
= p
;
1267 mnp
->type
= T_GNU_MO
;
1269 #ifdef GETTEXT_DEBUG
1270 gprintf(0, "********* exiting gnu_setmsg\n");
1279 * Returns the index to an empty slot in the hash table
1280 * for the specified hash_value.
1283 get_hash_index(uint32_t *hash_tbl
, uint32_t hash_value
, uint32_t hash_size
)
1287 idx
= hash_value
% hash_size
;
1288 inc
= 1 + (hash_value
% (hash_size
- 2));
1291 if (hash_tbl
[idx
] == 0) {
1292 /* found an empty slot */
1295 idx
= (idx
+ inc
) % hash_size
;