dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libc / port / i18n / gettext_gnu.c
blobc6a8e49c6807c6eb86166d88efb3df2e96c24988
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include "lint.h"
28 #include "mtlib.h"
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <thread.h>
38 #include <synch.h>
39 #include <unistd.h>
40 #include <limits.h>
41 #include <errno.h>
42 #include <inttypes.h>
43 #include "libc.h"
44 #include "msgfmt.h"
45 #include "nlspath_checks.h"
46 #include "gettext.h"
48 #ifdef DEBUG
49 #include <assert.h>
50 #endif
52 /* The following symbols are just for GNU binary compatibility */
53 int _nl_msg_cat_cntr;
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);
68 * free_conv_msgstr
70 * release the memory allocated for storing code-converted messages
72 * f
73 * 0: do not free gmnp->conv_msgstr
74 * 1: free gmnp->conv_msgstr
76 static void
77 free_conv_msgstr(Msg_g_node *gmnp, int f)
79 uint32_t i, num_of_conv;
81 #ifdef GETTEXT_DEBUG
82 gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
83 (void *)gmnp, f);
84 printgnumsg(gmnp, 1);
85 #endif
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;
94 if (f) {
95 free(gmnp->conv_msgstr);
96 gmnp->conv_msgstr = NULL;
101 * dfltmsgstr
103 * choose an appropriate message by evaluating the plural expression,
104 * and return it.
106 static char *
107 dfltmsgstr(Msg_g_node *gmnp, const char *msgstr, uint32_t msgstr_len,
108 struct msg_pack *mp)
110 unsigned int pindex;
111 size_t len;
112 const char *p;
114 #ifdef GETTEXT_DEBUG
115 gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
116 (void *)gmnp,
117 msgstr ? msgstr : "(null)", msgstr_len, (void *)mp);
118 printgnumsg(gmnp, 1);
119 printmp(mp, 1);
120 #endif
122 if (mp->plural) {
123 if (gmnp->plural) {
124 pindex = plural_eval(gmnp->plural, mp->n);
125 } else {
127 * This mo does not have plural information.
128 * Using the English form.
130 if (mp->n == 1)
131 pindex = 0;
132 else
133 pindex = 1;
135 #ifdef GETTEXT_DEBUG
136 gprintf(0, "plural_eval returned: %u\n", pindex);
137 #endif
138 if (pindex >= gmnp->nplurals) {
139 /* should never happen */
140 pindex = 0;
142 p = msgstr;
143 for (; pindex != 0; pindex--) {
144 len = msgstr_len - (p - msgstr);
145 p = memchr(p, '\0', len);
146 if (p == NULL) {
148 * null byte not found
149 * this should never happen
151 char *result;
152 DFLTMSG(result, mp->msgid1, mp->msgid2,
153 mp->n, mp->plural);
154 return (result);
156 p++; /* skip */
158 return ((char *)p);
161 return ((char *)msgstr);
165 * parse_header
167 * parse the header entry of the GNU MO file and
168 * extract the src encoding and the plural information of the MO file
170 static int
171 parse_header(const char *header, Msg_g_node *gmnp)
173 char *charset = NULL;
174 char *charset_str;
175 size_t len;
176 char *nplurals_str, *plural_str;
177 plural_expr_t plural;
178 char *p, *q;
179 unsigned int nplurals;
180 int ret;
182 #ifdef GETTEXT_DEBUG
183 gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
184 header ? header : "(null)", (void *)gmnp);
185 printgnumsg(gmnp, 1);
186 #endif
188 if (header == NULL) {
189 gmnp->src_encoding = (char *)nullstr;
190 gmnp->nplurals = 2;
191 gmnp->plural = NULL;
192 #ifdef GETTEXT_DEBUG
193 gprintf(0, "*************** exiting parse_header\n");
194 gprintf(0, "no header\n");
195 #endif
197 return (0);
200 charset_str = strstr(header, CHARSET_MOD);
201 if (charset_str == NULL) {
202 gmnp->src_encoding = (char *)nullstr;
203 } else {
204 p = charset_str + CHARSET_LEN;
205 q = p;
206 while ((*q != ' ') && (*q != '\t') &&
207 (*q != '\n')) {
208 q++;
210 len = q - p;
211 if (len > 0) {
212 charset = malloc(len + 1);
213 if (charset == NULL) {
214 gmnp->src_encoding = (char *)nullstr;
215 gmnp->nplurals = 2;
216 gmnp->plural = NULL;
217 return (-1);
219 (void) memcpy(charset, p, len);
220 charset[len] = '\0';
221 gmnp->src_encoding = charset;
222 } else {
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 */
231 gmnp->nplurals = 2;
232 gmnp->plural = NULL;
233 #ifdef GETTEXT_DEBUG
234 gprintf(0, "*************** exiting parse_header\n");
235 gprintf(0, "no plural entry\n");
236 #endif
237 return (0);
238 } else {
239 p = nplurals_str + NPLURALS_LEN;
240 while (*p && isspace((unsigned char)*p)) {
241 p++;
243 nplurals = (unsigned int)strtol(p, &q, 10);
244 if (p != q) {
245 gmnp->nplurals = nplurals;
246 } else {
247 gmnp->nplurals = 2;
250 p = plural_str + PLURAL_LEN;
251 #ifdef GETTEXT_DEBUG
252 gprintf(0, "plural_str: \"%s\"\n", p);
253 #endif
255 ret = plural_expr(&plural, (const char *)p);
256 if (ret == 0) {
257 /* parse succeeded */
258 gmnp->plural = plural;
259 #ifdef GETTEXT_DEBUG
260 gprintf(0, "*************** exiting parse_header\n");
261 gprintf(0, "charset: \"%s\"\n",
262 charset ? charset : "(null)");
263 printexpr(plural, 1);
264 #endif
265 return (0);
266 } else if (ret == 1) {
267 /* parse error */
268 gmnp->nplurals = 2;
269 gmnp->plural = NULL;
270 return (0);
271 } else {
272 /* fatal error */
273 free(charset);
274 gmnp->src_encoding = (char *)nullstr;
275 gmnp->nplurals = 2;
276 gmnp->plural = NULL;
277 return (-1);
280 /* NOTREACHED */
284 * handle_lang
286 * take care of the LANGUAGE specification
288 char *
289 handle_lang(struct msg_pack *mp)
291 const char *p, *op, *q;
292 size_t locale_len;
293 char *result;
294 char locale[MAXPATHLEN];
297 #ifdef GETTEXT_DEBUG
298 gprintf(0, "*************** handle_lang(0x%p)\n", (void *)mp);
299 printmp(mp, 1);
300 #endif
302 p = mp->language;
304 while (*p) {
305 op = p;
306 q = strchr(p, ':');
307 if (q == NULL) {
308 locale_len = strlen(p);
309 p += locale_len;
310 } else {
311 locale_len = q - p;
312 p += locale_len + 1;
314 if (locale_len >= MAXPATHLEN || locale_len == 0) {
315 /* illegal locale name */
316 continue;
318 (void) memcpy(locale, op, locale_len);
319 locale[locale_len] = '\0';
320 mp->locale = locale;
322 #ifdef GETTEXT_DEBUG
323 *mp->msgfile = '\0';
324 #endif
325 if (mk_msgfile(mp) == NULL) {
326 /* illegal locale name */
327 continue;
330 result = handle_mo(mp);
331 if (mp->status & ST_GNU_MSG_FOUND)
332 return (result);
334 if (mp->status & ST_SUN_MO_FOUND)
335 break;
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.
346 * returning DFLTMSG.
348 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
349 return (result);
351 return (NULL);
355 * gnu_msgsearch
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
366 * Donald E Knuth
368 static char *
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;
377 char *base;
378 char *msg;
380 #ifdef GETTEXT_DEBUG
381 gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
382 "0x%p, 0x%p)\n",
383 (void *)gmnp, msgid1, msgstrlen, midx);
384 printgnumsg(gmnp, 1);
385 #endif
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))) {
398 * Revision 0 and
399 * No hash table exists or
400 * hash size is enough small.
402 uint32_t top, bottom;
403 char *msg_id_str;
404 int val;
406 top = 0;
407 bottom = num_of_str;
408 while (top < bottom) {
409 idx = (top + bottom) / 2;
410 msg_id_str = base +
411 SWAP(gmnp, msgid_tbl[idx].offset);
413 val = strcmp(msg_id_str, msgid1);
414 if (val < 0) {
415 top = idx + 1;
416 } else if (val > 0) {
417 bottom = idx;
418 } else {
419 *msgstrlen = (unsigned int)
420 SWAP(gmnp, msgstr_tbl[idx].len) + 1;
421 *midx = idx;
422 return (base +
423 SWAP(gmnp, msgstr_tbl[idx].offset));
426 /* not found */
427 return ((char *)msgid1);
430 /* use hash table */
431 hash_id = get_hashid(msgid1, &msglen);
432 hash_idx = hash_id % hash_size;
433 hash_inc = 1 + (hash_id % (hash_size - 2));
435 for (;;) {
436 hash_val = HASH_TBL(gmnp, hash_table[hash_idx]);
438 if (hash_val == 0) {
439 /* not found */
440 return ((char *)msgid1);
442 if (hash_val <= num_of_str) {
443 /* static message */
444 idx = hash_val - 1;
445 mlen = SWAP(gmnp, msgid_tbl[idx].len);
446 msg = base + SWAP(gmnp, msgid_tbl[idx].offset);
447 } else {
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) {
458 /* found */
459 break;
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);
468 *midx = idx;
469 } else {
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;
475 return (msg);
479 * do_conv
481 * Converts the specified string from the src encoding
482 * to the dst encoding by calling iconv()
484 static uint32_t *
485 do_conv(iconv_t fd, const char *src, uint32_t srclen)
487 uint32_t tolen;
488 uint32_t *ptr, *optr;
489 size_t oleft, ileft, bufsize, memincr;
490 char *to, *tptr;
492 #ifdef GETTEXT_DEBUG
493 gprintf(0, "*************** do_conv("
494 "0x%p, \"%s\", %d)\n",
495 (void *)fd, src ? src : "(null)", srclen);
496 #endif
498 memincr = srclen * 2;
499 bufsize = memincr;
500 ileft = srclen;
501 oleft = bufsize;
502 ptr = malloc(bufsize + sizeof (uint32_t));
503 if (ptr == NULL) {
504 return (NULL);
506 to = (char *)(ptr + 1);
508 for (;;) {
509 tptr = to;
510 errno = 0;
511 #ifdef GETTEXT_DEBUG
512 gprintf(0, "******* calling iconv()\n");
513 #endif
514 if (iconv(fd, &src, &ileft, &tptr, &oleft) == (size_t)-1) {
515 if (errno == E2BIG) {
516 #ifdef GETTEXT_DEBUG
517 gprintf(0, "******* iconv detected E2BIG\n");
518 gprintf(0, "old bufsize: %u\n", bufsize);
519 #endif
521 optr = realloc(ptr,
522 bufsize + memincr + sizeof (uint32_t));
523 if (optr == NULL) {
524 free(ptr);
525 return (NULL);
527 ptr = optr;
528 to = (char *)(optr + 1);
529 to += bufsize - oleft;
530 oleft += memincr;
531 bufsize += memincr;
532 #ifdef GETTEXT_DEBUG
533 gprintf(0, "new bufsize: %u\n", bufsize);
534 #endif
535 continue;
536 } else {
537 tolen = (uint32_t)(bufsize - oleft);
538 break;
541 tolen = (uint32_t)(bufsize - oleft);
542 break;
545 if (tolen < bufsize) {
546 /* shrink the buffer */
547 optr = realloc(ptr, tolen + sizeof (uint32_t));
548 if (optr == NULL) {
549 free(ptr);
550 return (NULL);
552 ptr = optr;
554 *ptr = tolen;
556 #ifdef GETTEXT_DEBUG
557 gprintf(0, "******* exiting do_conv()\n");
558 gprintf(0, "tolen: %u\n", *ptr);
559 gprintf(0, "return: 0x%p\n", ptr);
560 #endif
561 return (ptr);
565 * conv_msg
567 static char *
568 conv_msg(Msg_g_node *gmnp, char *msgstr, uint32_t msgstr_len, uint32_t midx,
569 struct msg_pack *mp)
571 uint32_t *conv_dst;
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;
577 gmnp->conv_msgstr =
578 calloc((size_t)num_of_conv, sizeof (uint32_t *));
579 if (gmnp->conv_msgstr == NULL) {
580 /* malloc failed */
581 result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
582 return (result);
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);
590 return (result);
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);
596 return (result);
600 * gnu_key_2_text
602 * Extracts msgstr from the GNU MO file
604 char *
605 gnu_key_2_text(Msg_g_node *gmnp, const char *codeset,
606 struct msg_pack *mp)
608 uint32_t msgstr_len, midx;
609 iconv_t fd;
610 char *result, *msgstr;
611 int ret, conversion, new_encoding;
613 #ifdef GETTEXT_DEBUG
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);
618 printmp(mp, 1);
619 #endif
621 /* first checks if header entry has been processed */
622 if (!(gmnp->flag & ST_CHK)) {
623 char *msg_header;
625 msg_header = gnu_msgsearch(gmnp, "", &msgstr_len, &midx);
626 ret = parse_header((const char *)msg_header, gmnp);
627 if (ret == -1) {
628 /* fatal error */
629 DFLTMSG(result, mp->msgid1, mp->msgid2,
630 mp->n, mp->plural);
631 return (result);
633 gmnp->flag |= ST_CHK;
635 msgstr = gnu_msgsearch(gmnp, mp->msgid1, &msgstr_len, &midx);
636 if (msgstr == mp->msgid1) {
637 /* not found */
638 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
639 return (result);
642 #ifdef GETTEXT_DEBUG
643 printgnumsg(gmnp, 1);
644 #endif
645 if (gmnp->dst_encoding == NULL) {
647 * destination encoding has not been set.
649 char *dupcodeset = strdup(codeset);
650 if (dupcodeset == NULL) {
651 /* strdup failed */
652 result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
653 return (result);
655 gmnp->dst_encoding = dupcodeset;
657 if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 0) {
659 * target encoding and src encoding
660 * are the same.
661 * No conversion required.
663 conversion = 0;
664 } else {
666 * target encoding is different from
667 * src encoding.
668 * New conversion required.
670 /* sanity check */
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);
677 conversion = 1;
678 new_encoding = 1;
680 } else {
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)
689 == 0) {
691 * dst encoding and src encoding are the same.
692 * No conversion required.
694 conversion = 0;
695 } else {
697 * dst encoding is different from src encoding.
698 * current conversion is valid.
700 conversion = 1;
701 new_encoding = 0;
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
708 conversion = 0;
709 } else {
710 if (gmnp->conv_msgstr == NULL) {
712 * memory allocation for
713 * conv_msgstr should
714 * have failed before.
716 new_encoding = 1;
717 if (gmnp->fd)
718 (void) iconv_close(
719 gmnp->fd);
720 gmnp->fd = (iconv_t)-1;
724 } else {
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,
732 msgstr_len, mp);
733 return (result);
735 free(gmnp->dst_encoding);
736 gmnp->dst_encoding = dupcodeset;
737 if (strcmp(gmnp->dst_encoding, gmnp->src_encoding)
738 == 0) {
740 * dst encoding and src encoding are the same.
741 * now, no conversion required.
743 conversion = 0;
744 if (gmnp->conv_msgstr)
745 free_conv_msgstr(gmnp, 1);
746 } else {
748 * dst encoding is different from src encoding.
749 * new conversion required.
751 conversion = 1;
752 new_encoding = 1;
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) {
767 /* no conversion */
768 result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
769 return (result);
771 /* conversion required */
773 if (new_encoding == 0) {
774 /* dst codeset hasn't been changed since before */
775 uint32_t *cmsg;
776 uint32_t conv_msgstr_len;
777 char *conv_msgstr;
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);
782 return (result);
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);
789 return (result);
791 /* new conversion */
792 #ifdef GETTEXT_DEBUG
793 gprintf(0, "******* calling iconv_open()\n");
794 gprintf(0, " dst: \"%s\", src: \"%s\"\n",
795 gmnp->dst_encoding, gmnp->src_encoding);
796 #endif
797 fd = iconv_open(gmnp->dst_encoding, gmnp->src_encoding);
798 gmnp->fd = fd;
799 if (fd == (iconv_t)-1) {
801 * iconv_open() failed.
802 * no conversion
804 result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
805 return (result);
807 result = conv_msg(gmnp, msgstr, msgstr_len, midx, mp);
808 return (result);
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);
840 static struct {
841 const char type;
842 const char **str_table;
843 const char *len_table;
844 } pri_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},
850 static struct {
851 const char *name;
852 const char nlen;
853 const char want_digits;
854 const char bias;
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.
869 static const char *
870 conv_macro(const char *str, uint32_t len, uint32_t *lenp)
872 const char **tbl;
873 const char *ltbl;
874 char *next;
875 int n, i, num, bias, idx, want_digits;
877 if (len == 2) {
878 if (*str == 'I') {
879 /* Solaris does not support %I */
880 *lenp = 0;
881 return ("");
883 return (NULL);
886 if (len <= 4 || strncmp(str, "PRI", 3) != 0)
887 return (NULL);
889 str += 3;
891 n = sizeof (pri_table) / sizeof (pri_table[0]);
892 for (i = 0; i < n; i++) {
893 if (pri_table[i].type == *str)
894 break;
896 if (i == n)
897 return (NULL);
898 tbl = pri_table[i].str_table;
899 ltbl = pri_table[i].len_table;
901 str++;
902 idx = want_digits = 0;
904 if (isdigit((unsigned char)*str)) {
905 /* PRIx/N/ */
906 bias = 0;
907 want_digits = 1;
908 } else {
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) {
913 break;
916 if (i == n)
917 return (NULL);
918 bias = special_table[i].bias;
919 want_digits = special_table[i].want_digits;
920 str += special_table[i].nlen;
923 if (want_digits) {
924 if (!isdigit((unsigned char)*str))
925 return (NULL);
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) {
929 if (n == num)
930 break;
932 if (idx == 4)
933 return (NULL);
934 str = next;
936 if (*str != '\0') {
937 /* unknow format */
938 return (NULL);
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;
954 char *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));
960 if (d_macro == NULL)
961 return (NULL);
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);
972 * sanity check
973 * maclen includes a null termination.
975 if (maclen != strlen(macname) + 1) {
976 free(d_macro);
977 return (NULL);
979 e_macname = conv_macro(macname, maclen, &e_maclen);
980 if (e_macname == NULL) {
981 free(d_macro);
982 return (NULL);
984 d_macro[i].len = e_maclen;
985 d_macro[i].ptr = e_macname;
988 return (d_macro);
991 static char *
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;
1001 uint32_t off_d_tbl;
1002 uint32_t *d_msg_off_tbl;
1003 size_t mchunk_size, used, need;
1004 char *mchunk, *msg;
1006 #define MEM_INCR (1024)
1008 d_macro = expand_macros(p);
1009 if (d_macro == NULL)
1010 return (NULL);
1012 /* number of dynamic messages */
1013 num_of_d_str = p->num_of_d_str;
1015 mchunk = NULL;
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 */
1020 off_d_tbl = SWAP(p,
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);
1032 for (;;) {
1033 mlen = SWAP(p, entry->len);
1034 didx = SWAP(p, entry->idx);
1035 dlen = (didx == NOMORE_DYNAMIC_MACRO) ? 0 :
1036 d_macro[didx].len;
1037 need = used + mlen + dlen;
1038 if (need >= mchunk_size) {
1039 char *t;
1040 size_t n = mchunk_size;
1041 do {
1042 n += MEM_INCR;
1043 } while (n <= need);
1044 t = realloc(mchunk, n);
1045 if (t == NULL) {
1046 free(d_macro);
1047 free(mchunk);
1048 return (NULL);
1050 mchunk = t;
1051 mchunk_size = n;
1053 (void) memcpy(mchunk + used, msg, (size_t)mlen);
1054 msg += mlen;
1055 used += 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
1063 * here.
1065 break;
1067 (void) memcpy(mchunk + used,
1068 d_macro[didx].ptr, (size_t)dlen);
1069 used += 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;
1079 free(d_macro);
1081 /* shrink mchunk to 'used' */
1083 char *t;
1084 t = realloc(mchunk, used);
1085 if (t == NULL) {
1086 free(mchunk);
1087 return (NULL);
1089 mchunk = t;
1092 return (mchunk);
1095 static int
1096 build_rev1_info(Msg_g_node *p)
1098 uint32_t *d_hash;
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;
1104 int i;
1106 #ifdef GETTEXT_DEBUG
1107 gprintf(0, "******* entering build_rev1_info(0x%p)\n", p);
1108 printgnumsg(p, 1);
1109 #endif
1111 if (p->hash_table == NULL) {
1112 /* Revision 1 always requires the hash table */
1113 return (-1);
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) {
1128 return (-1);
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) {
1138 free(chunk);
1139 return (-1);
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,
1150 NULL);
1151 idx = get_hash_index(d_hash, hash_value, hash_size);
1152 d_hash[idx] = num_of_str + i + 1;
1155 p->mchunk = mchunk;
1156 p->hash_table = d_hash;
1158 #ifdef GETTEXT_DEBUG
1159 print_rev1_info(p);
1160 gprintf(0, "******* exiting build_rev1_info()\n");
1161 printgnumsg(p, 1);
1162 #endif
1164 return (0);
1168 * gnu_setmsg
1170 * INPUT
1171 * mnp - message node
1172 * addr - address to the mmapped file
1173 * size - size of the file
1175 * RETURN
1176 * 0 - either T_GNU_MO or T_ILL_MO has been set
1177 * -1 - failed
1180 gnu_setmsg(Msg_node *mnp, char *addr, size_t size)
1182 struct gnu_msg_info *gnu_header;
1183 Msg_g_node *p;
1185 #ifdef GETTEXT_DEBUG
1186 gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
1187 (void *)mnp, addr, size);
1188 printmnp(mnp, 1);
1189 #endif
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");
1197 printmnp(mnp, 1);
1198 #endif
1199 return (0);
1202 gnu_header = (struct gnu_msg_info *)(uintptr_t)addr;
1204 p = calloc(1, sizeof (Msg_g_node));
1205 if (p == NULL) {
1206 return (-1);
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:
1214 p->flag |= ST_REV1;
1215 break;
1217 } else if (gnu_header->magic == GNU_MAGIC_SWAPPED) {
1218 p->flag |= ST_SWP;
1219 switch (gnu_header->revision) {
1220 case GNU_REVISION_0_1_SWAPPED:
1221 case GNU_REVISION_1_1_SWAPPED:
1222 p->flag |= ST_REV1;
1223 break;
1225 } else {
1226 /* invalid mo file */
1227 free(p);
1228 mnp->type = T_ILL_MO;
1229 #ifdef GETTEXT_DEBUG
1230 gprintf(0, "********* exiting gnu_setmsg\n");
1231 printmnp(mnp, 1);
1232 #endif
1233 return (0);
1236 p->fsize = size;
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) {
1249 /* Revision 1 */
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) {
1257 free(p);
1258 #ifdef GETTEXT_DEBUG
1259 gprintf(0, "******** exiting gnu_setmsg: "
1260 "build_rev1_info() failed\n");
1261 #endif
1262 return (-1);
1266 mnp->msg.gnumsg = p;
1267 mnp->type = T_GNU_MO;
1269 #ifdef GETTEXT_DEBUG
1270 gprintf(0, "********* exiting gnu_setmsg\n");
1271 printmnp(mnp, 1);
1272 #endif
1273 return (0);
1277 * get_hash_index
1279 * Returns the index to an empty slot in the hash table
1280 * for the specified hash_value.
1282 static uint32_t
1283 get_hash_index(uint32_t *hash_tbl, uint32_t hash_value, uint32_t hash_size)
1285 uint32_t idx, inc;
1287 idx = hash_value % hash_size;
1288 inc = 1 + (hash_value % (hash_size - 2));
1290 for (;;) {
1291 if (hash_tbl[idx] == 0) {
1292 /* found an empty slot */
1293 return (idx);
1295 idx = (idx + inc) % hash_size;
1297 /* NOTREACHED */