10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
56793Smuffin * Common Development and Distribution License (the "License").
66793Smuffin * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
216793Smuffin
220Sstevel@tonic-gate /*
236793Smuffin * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
280Sstevel@tonic-gate
29*6812Sraf #include "lint.h"
300Sstevel@tonic-gate #include "mtlib.h"
310Sstevel@tonic-gate #include <ctype.h>
320Sstevel@tonic-gate #include <stdio.h>
330Sstevel@tonic-gate #include <stdlib.h>
340Sstevel@tonic-gate #include <string.h>
350Sstevel@tonic-gate #include <sys/types.h>
360Sstevel@tonic-gate #include <sys/mman.h>
370Sstevel@tonic-gate #include <sys/param.h>
380Sstevel@tonic-gate #include <sys/stat.h>
390Sstevel@tonic-gate #include <thread.h>
400Sstevel@tonic-gate #include <synch.h>
410Sstevel@tonic-gate #include <unistd.h>
420Sstevel@tonic-gate #include <limits.h>
430Sstevel@tonic-gate #include <errno.h>
446793Smuffin #include <inttypes.h>
450Sstevel@tonic-gate #include "libc.h"
460Sstevel@tonic-gate #include "msgfmt.h"
470Sstevel@tonic-gate #include "nlspath_checks.h"
480Sstevel@tonic-gate #include "gettext.h"
490Sstevel@tonic-gate
500Sstevel@tonic-gate #ifdef DEBUG
510Sstevel@tonic-gate #include <assert.h>
520Sstevel@tonic-gate #endif
530Sstevel@tonic-gate
546793Smuffin /* The following symbols are just for GNU binary compatibility */
556793Smuffin int _nl_msg_cat_cntr;
566793Smuffin int *_nl_domain_bindings;
576793Smuffin
580Sstevel@tonic-gate static const char *nullstr = "";
590Sstevel@tonic-gate
600Sstevel@tonic-gate #define CHARSET_MOD "charset="
610Sstevel@tonic-gate #define CHARSET_LEN (sizeof (CHARSET_MOD) - 1)
620Sstevel@tonic-gate #define NPLURALS_MOD "nplurals="
630Sstevel@tonic-gate #define NPLURALS_LEN (sizeof (NPLURALS_MOD) - 1)
640Sstevel@tonic-gate #define PLURAL_MOD "plural="
650Sstevel@tonic-gate #define PLURAL_LEN (sizeof (PLURAL_MOD) - 1)
660Sstevel@tonic-gate
676793Smuffin static uint32_t get_hash_index(uint32_t *, uint32_t, uint32_t);
686793Smuffin
690Sstevel@tonic-gate /*
700Sstevel@tonic-gate * free_conv_msgstr
710Sstevel@tonic-gate *
720Sstevel@tonic-gate * release the memory allocated for storing code-converted messages
736793Smuffin *
746793Smuffin * f
756793Smuffin * 0: do not free gmnp->conv_msgstr
766793Smuffin * 1: free gmnp->conv_msgstr
770Sstevel@tonic-gate */
780Sstevel@tonic-gate static void
free_conv_msgstr(Msg_g_node * gmnp,int f)796793Smuffin free_conv_msgstr(Msg_g_node *gmnp, int f)
800Sstevel@tonic-gate {
816793Smuffin uint32_t i, num_of_conv;
820Sstevel@tonic-gate
830Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
846793Smuffin gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
856793Smuffin (void *)gmnp, f);
866793Smuffin printgnumsg(gmnp, 1);
870Sstevel@tonic-gate #endif
880Sstevel@tonic-gate
896793Smuffin num_of_conv = gmnp->num_of_str + gmnp->num_of_d_str;
906793Smuffin for (i = 0; i < num_of_conv; i++) {
910Sstevel@tonic-gate if (gmnp->conv_msgstr[i]) {
920Sstevel@tonic-gate free(gmnp->conv_msgstr[i]);
930Sstevel@tonic-gate }
946793Smuffin gmnp->conv_msgstr[i] = NULL;
950Sstevel@tonic-gate }
966793Smuffin if (f) {
976793Smuffin free(gmnp->conv_msgstr);
986793Smuffin gmnp->conv_msgstr = NULL;
996793Smuffin }
1000Sstevel@tonic-gate }
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate /*
1030Sstevel@tonic-gate * dfltmsgstr
1040Sstevel@tonic-gate *
1050Sstevel@tonic-gate * choose an appropriate message by evaluating the plural expression,
1060Sstevel@tonic-gate * and return it.
1070Sstevel@tonic-gate */
1080Sstevel@tonic-gate static char *
dfltmsgstr(Msg_g_node * gmnp,const char * msgstr,uint32_t msgstr_len,struct msg_pack * mp)1096793Smuffin dfltmsgstr(Msg_g_node *gmnp, const char *msgstr, uint32_t msgstr_len,
1106793Smuffin struct msg_pack *mp)
1110Sstevel@tonic-gate {
1120Sstevel@tonic-gate unsigned int pindex;
1130Sstevel@tonic-gate size_t len;
1140Sstevel@tonic-gate const char *p;
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
1176793Smuffin gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
1186793Smuffin (void *)gmnp,
1196793Smuffin msgstr ? msgstr : "(null)", msgstr_len, (void *)mp);
1206793Smuffin printgnumsg(gmnp, 1);
1216793Smuffin printmp(mp, 1);
1220Sstevel@tonic-gate #endif
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate if (mp->plural) {
1250Sstevel@tonic-gate if (gmnp->plural) {
1260Sstevel@tonic-gate pindex = plural_eval(gmnp->plural, mp->n);
1270Sstevel@tonic-gate } else {
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate * This mo does not have plural information.
1300Sstevel@tonic-gate * Using the English form.
1310Sstevel@tonic-gate */
1320Sstevel@tonic-gate if (mp->n == 1)
1330Sstevel@tonic-gate pindex = 0;
1340Sstevel@tonic-gate else
1350Sstevel@tonic-gate pindex = 1;
1360Sstevel@tonic-gate }
1370Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
1386793Smuffin gprintf(0, "plural_eval returned: %u\n", pindex);
1390Sstevel@tonic-gate #endif
1400Sstevel@tonic-gate if (pindex >= gmnp->nplurals) {
1410Sstevel@tonic-gate /* should never happen */
1420Sstevel@tonic-gate pindex = 0;
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate p = msgstr;
1450Sstevel@tonic-gate for (; pindex != 0; pindex--) {
1460Sstevel@tonic-gate len = msgstr_len - (p - msgstr);
1470Sstevel@tonic-gate p = memchr(p, '\0', len);
1486793Smuffin if (p == NULL) {
1490Sstevel@tonic-gate /*
1500Sstevel@tonic-gate * null byte not found
1510Sstevel@tonic-gate * this should never happen
1520Sstevel@tonic-gate */
1530Sstevel@tonic-gate char *result;
1540Sstevel@tonic-gate DFLTMSG(result, mp->msgid1, mp->msgid2,
1556793Smuffin mp->n, mp->plural);
1560Sstevel@tonic-gate return (result);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate p++; /* skip */
1590Sstevel@tonic-gate }
1600Sstevel@tonic-gate return ((char *)p);
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate return ((char *)msgstr);
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate * parse_header
1680Sstevel@tonic-gate *
1690Sstevel@tonic-gate * parse the header entry of the GNU MO file and
1700Sstevel@tonic-gate * extract the src encoding and the plural information of the MO file
1710Sstevel@tonic-gate */
1720Sstevel@tonic-gate static int
parse_header(const char * header,Msg_g_node * gmnp)1730Sstevel@tonic-gate parse_header(const char *header, Msg_g_node *gmnp)
1740Sstevel@tonic-gate {
1750Sstevel@tonic-gate char *charset = NULL;
1760Sstevel@tonic-gate char *charset_str;
1770Sstevel@tonic-gate size_t len;
1780Sstevel@tonic-gate char *nplurals_str, *plural_str;
1790Sstevel@tonic-gate plural_expr_t plural;
1800Sstevel@tonic-gate char *p, *q;
1810Sstevel@tonic-gate unsigned int nplurals;
1820Sstevel@tonic-gate int ret;
1830Sstevel@tonic-gate
1840Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
1856793Smuffin gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
1866793Smuffin header ? header : "(null)", (void *)gmnp);
1876793Smuffin printgnumsg(gmnp, 1);
1880Sstevel@tonic-gate #endif
1890Sstevel@tonic-gate
1906793Smuffin if (header == NULL) {
1910Sstevel@tonic-gate gmnp->src_encoding = (char *)nullstr;
1920Sstevel@tonic-gate gmnp->nplurals = 2;
1930Sstevel@tonic-gate gmnp->plural = NULL;
1940Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
1956793Smuffin gprintf(0, "*************** exiting parse_header\n");
1966793Smuffin gprintf(0, "no header\n");
1970Sstevel@tonic-gate #endif
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate return (0);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate charset_str = strstr(header, CHARSET_MOD);
2036793Smuffin if (charset_str == NULL) {
2040Sstevel@tonic-gate gmnp->src_encoding = (char *)nullstr;
2050Sstevel@tonic-gate } else {
2060Sstevel@tonic-gate p = charset_str + CHARSET_LEN;
2070Sstevel@tonic-gate q = p;
2080Sstevel@tonic-gate while ((*q != ' ') && (*q != '\t') &&
2096793Smuffin (*q != '\n')) {
2100Sstevel@tonic-gate q++;
2110Sstevel@tonic-gate }
2120Sstevel@tonic-gate len = q - p;
2130Sstevel@tonic-gate if (len > 0) {
2146793Smuffin charset = malloc(len + 1);
2156793Smuffin if (charset == NULL) {
2160Sstevel@tonic-gate gmnp->src_encoding = (char *)nullstr;
2170Sstevel@tonic-gate gmnp->nplurals = 2;
2180Sstevel@tonic-gate gmnp->plural = NULL;
2190Sstevel@tonic-gate return (-1);
2200Sstevel@tonic-gate }
2210Sstevel@tonic-gate (void) memcpy(charset, p, len);
2220Sstevel@tonic-gate charset[len] = '\0';
2230Sstevel@tonic-gate gmnp->src_encoding = charset;
2240Sstevel@tonic-gate } else {
2250Sstevel@tonic-gate gmnp->src_encoding = (char *)nullstr;
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate }
2280Sstevel@tonic-gate
2290Sstevel@tonic-gate nplurals_str = strstr(header, NPLURALS_MOD);
2300Sstevel@tonic-gate plural_str = strstr(header, PLURAL_MOD);
2316793Smuffin if (nplurals_str == NULL || plural_str == NULL) {
2320Sstevel@tonic-gate /* no valid plural specification */
2330Sstevel@tonic-gate gmnp->nplurals = 2;
2340Sstevel@tonic-gate gmnp->plural = NULL;
2350Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
2366793Smuffin gprintf(0, "*************** exiting parse_header\n");
2376793Smuffin gprintf(0, "no plural entry\n");
2380Sstevel@tonic-gate #endif
2390Sstevel@tonic-gate return (0);
2400Sstevel@tonic-gate } else {
2410Sstevel@tonic-gate p = nplurals_str + NPLURALS_LEN;
2420Sstevel@tonic-gate while (*p && isspace((unsigned char)*p)) {
2430Sstevel@tonic-gate p++;
2440Sstevel@tonic-gate }
2450Sstevel@tonic-gate nplurals = (unsigned int)strtol(p, &q, 10);
2460Sstevel@tonic-gate if (p != q) {
2470Sstevel@tonic-gate gmnp->nplurals = nplurals;
2480Sstevel@tonic-gate } else {
2490Sstevel@tonic-gate gmnp->nplurals = 2;
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate p = plural_str + PLURAL_LEN;
2530Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
2546793Smuffin gprintf(0, "plural_str: \"%s\"\n", p);
2550Sstevel@tonic-gate #endif
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate ret = plural_expr(&plural, (const char *)p);
2580Sstevel@tonic-gate if (ret == 0) {
2590Sstevel@tonic-gate /* parse succeeded */
2600Sstevel@tonic-gate gmnp->plural = plural;
2610Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
2626793Smuffin gprintf(0, "*************** exiting parse_header\n");
2636793Smuffin gprintf(0, "charset: \"%s\"\n",
2646793Smuffin charset ? charset : "(null)");
2656793Smuffin printexpr(plural, 1);
2660Sstevel@tonic-gate #endif
2670Sstevel@tonic-gate return (0);
2680Sstevel@tonic-gate } else if (ret == 1) {
2690Sstevel@tonic-gate /* parse error */
2700Sstevel@tonic-gate gmnp->nplurals = 2;
2710Sstevel@tonic-gate gmnp->plural = NULL;
2720Sstevel@tonic-gate return (0);
2730Sstevel@tonic-gate } else {
2740Sstevel@tonic-gate /* fatal error */
2750Sstevel@tonic-gate if (charset)
2760Sstevel@tonic-gate free(charset);
2770Sstevel@tonic-gate gmnp->src_encoding = (char *)nullstr;
2780Sstevel@tonic-gate gmnp->nplurals = 2;
2790Sstevel@tonic-gate gmnp->plural = NULL;
2800Sstevel@tonic-gate return (-1);
2810Sstevel@tonic-gate }
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate /* NOTREACHED */
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate /*
2870Sstevel@tonic-gate * handle_lang
2880Sstevel@tonic-gate *
2890Sstevel@tonic-gate * take care of the LANGUAGE specification
2900Sstevel@tonic-gate */
2910Sstevel@tonic-gate char *
handle_lang(struct msg_pack * mp)2926793Smuffin handle_lang(struct msg_pack *mp)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate const char *p, *op, *q;
2956793Smuffin size_t locale_len;
2966793Smuffin char *result;
2976793Smuffin char locale[MAXPATHLEN];
2986793Smuffin
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
3016793Smuffin gprintf(0, "*************** handle_lang(0x%p)\n", (void *)mp);
3026793Smuffin printmp(mp, 1);
3030Sstevel@tonic-gate #endif
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate p = mp->language;
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate while (*p) {
3080Sstevel@tonic-gate op = p;
3090Sstevel@tonic-gate q = strchr(p, ':');
3106793Smuffin if (q == NULL) {
3110Sstevel@tonic-gate locale_len = strlen(p);
3120Sstevel@tonic-gate p += locale_len;
3130Sstevel@tonic-gate } else {
3140Sstevel@tonic-gate locale_len = q - p;
3150Sstevel@tonic-gate p += locale_len + 1;
3160Sstevel@tonic-gate }
3176793Smuffin if (locale_len >= MAXPATHLEN || locale_len == 0) {
3180Sstevel@tonic-gate /* illegal locale name */
3190Sstevel@tonic-gate continue;
3200Sstevel@tonic-gate }
3210Sstevel@tonic-gate (void) memcpy(locale, op, locale_len);
3220Sstevel@tonic-gate locale[locale_len] = '\0';
3230Sstevel@tonic-gate mp->locale = locale;
3246793Smuffin
3250Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
3260Sstevel@tonic-gate *mp->msgfile = '\0';
3270Sstevel@tonic-gate #endif
3280Sstevel@tonic-gate if (mk_msgfile(mp) == NULL) {
3290Sstevel@tonic-gate /* illegal locale name */
3300Sstevel@tonic-gate continue;
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate
3336793Smuffin result = handle_mo(mp);
3346793Smuffin if (mp->status & ST_GNU_MSG_FOUND)
3356793Smuffin return (result);
3360Sstevel@tonic-gate
3376793Smuffin if (mp->status & ST_SUN_MO_FOUND)
3386793Smuffin break;
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate
3416793Smuffin /*
3426793Smuffin * no valid locale found, Sun MO found, or
3436793Smuffin * GNU MO found but no valid msg found there.
3446793Smuffin */
3456793Smuffin
3466793Smuffin if (mp->status & ST_GNU_MO_FOUND) {
3476793Smuffin /*
3486793Smuffin * GNU MO found but no valid msg found there.
3496793Smuffin * returning DFLTMSG.
3506793Smuffin */
3510Sstevel@tonic-gate DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
3520Sstevel@tonic-gate return (result);
3530Sstevel@tonic-gate }
3540Sstevel@tonic-gate return (NULL);
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate * gnu_msgsearch
3590Sstevel@tonic-gate *
3600Sstevel@tonic-gate * Searchs the translation message for the specified msgid1.
3610Sstevel@tonic-gate * Hash algorithm used in this function is Open Addressing
3620Sstevel@tonic-gate * with Double Hashing:
3630Sstevel@tonic-gate * H(k, i) = (H1(k) + i * H2(k)) mod M
3640Sstevel@tonic-gate * H1(k) = hashvalue % M
3650Sstevel@tonic-gate * H2(k) = 1 + (hashvalue % (M - 2))
3660Sstevel@tonic-gate *
3670Sstevel@tonic-gate * Ref: The Art of Computer Programming Volume 3
3680Sstevel@tonic-gate * Sorting and Searching, second edition
3690Sstevel@tonic-gate * Donald E Knuth
3700Sstevel@tonic-gate */
3710Sstevel@tonic-gate static char *
gnu_msgsearch(Msg_g_node * gmnp,const char * msgid1,uint32_t * msgstrlen,uint32_t * midx)3720Sstevel@tonic-gate gnu_msgsearch(Msg_g_node *gmnp, const char *msgid1,
3736793Smuffin uint32_t *msgstrlen, uint32_t *midx)
3740Sstevel@tonic-gate {
3750Sstevel@tonic-gate struct gnu_msg_info *header = gmnp->msg_file_info;
3766793Smuffin struct gnu_msg_ent *msgid_tbl, *msgstr_tbl;
3776793Smuffin uint32_t num_of_str, idx, mlen, msglen;
3786793Smuffin uint32_t hash_size, hash_val, hash_id, hash_inc, hash_idx;
3796793Smuffin uint32_t *hash_table;
3806793Smuffin char *base;
3816793Smuffin char *msg;
3826793Smuffin
3836793Smuffin #ifdef GETTEXT_DEBUG
3846793Smuffin gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
3856793Smuffin "0x%p, 0x%p)\n",
3866793Smuffin (void *)gmnp, msgid1, msgstrlen, midx);
3876793Smuffin printgnumsg(gmnp, 1);
3886793Smuffin #endif
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate base = (char *)header;
3910Sstevel@tonic-gate
3926793Smuffin msgid_tbl = gmnp->msg_tbl[MSGID];
3936793Smuffin msgstr_tbl = gmnp->msg_tbl[MSGSTR];
3940Sstevel@tonic-gate hash_table = gmnp->hash_table;
3956793Smuffin hash_size = gmnp->hash_size;
3966793Smuffin num_of_str = gmnp->num_of_str;
3970Sstevel@tonic-gate
3986793Smuffin if (!(gmnp->flag & ST_REV1) &&
3996793Smuffin (hash_table == NULL || (hash_size <= 2))) {
4000Sstevel@tonic-gate /*
4016793Smuffin * Revision 0 and
4020Sstevel@tonic-gate * No hash table exists or
4036793Smuffin * hash size is enough small.
4040Sstevel@tonic-gate */
4056793Smuffin uint32_t top, bottom;
4060Sstevel@tonic-gate char *msg_id_str;
4070Sstevel@tonic-gate int val;
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate top = 0;
4100Sstevel@tonic-gate bottom = num_of_str;
4110Sstevel@tonic-gate while (top < bottom) {
4120Sstevel@tonic-gate idx = (top + bottom) / 2;
4130Sstevel@tonic-gate msg_id_str = base +
4146793Smuffin SWAP(gmnp, msgid_tbl[idx].offset);
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate val = strcmp(msg_id_str, msgid1);
4170Sstevel@tonic-gate if (val < 0) {
4180Sstevel@tonic-gate top = idx + 1;
4190Sstevel@tonic-gate } else if (val > 0) {
4200Sstevel@tonic-gate bottom = idx;
4210Sstevel@tonic-gate } else {
4226793Smuffin *msgstrlen = (unsigned int)
4236793Smuffin SWAP(gmnp, msgstr_tbl[idx].len) + 1;
4246793Smuffin *midx = idx;
4256793Smuffin return (base +
4266793Smuffin SWAP(gmnp, msgstr_tbl[idx].offset));
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate }
4290Sstevel@tonic-gate /* not found */
4300Sstevel@tonic-gate return ((char *)msgid1);
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate
4330Sstevel@tonic-gate /* use hash table */
4346793Smuffin hash_id = get_hashid(msgid1, &msglen);
4356793Smuffin hash_idx = hash_id % hash_size;
4366793Smuffin hash_inc = 1 + (hash_id % (hash_size - 2));
4370Sstevel@tonic-gate
4380Sstevel@tonic-gate for (;;) {
4396793Smuffin hash_val = HASH_TBL(gmnp, hash_table[hash_idx]);
4400Sstevel@tonic-gate
4416793Smuffin if (hash_val == 0) {
4426793Smuffin /* not found */
4430Sstevel@tonic-gate return ((char *)msgid1);
4440Sstevel@tonic-gate }
4456793Smuffin if (hash_val <= num_of_str) {
4466793Smuffin /* static message */
4476793Smuffin idx = hash_val - 1;
4486793Smuffin mlen = SWAP(gmnp, msgid_tbl[idx].len);
4496793Smuffin msg = base + SWAP(gmnp, msgid_tbl[idx].offset);
4506793Smuffin } else {
4516793Smuffin if (!(gmnp->flag & ST_REV1)) {
4526793Smuffin /* rev 0 does not have dynamic message */
4536793Smuffin return ((char *)msgid1);
4546793Smuffin }
4556793Smuffin /* dynamic message */
4566793Smuffin idx = hash_val - num_of_str - 1;
4576793Smuffin mlen = gmnp->d_msg[MSGID][idx].len;
4586793Smuffin msg = gmnp->mchunk + gmnp->d_msg[MSGID][idx].offset;
4596793Smuffin }
4606793Smuffin if (msglen <= mlen && strcmp(msgid1, msg) == 0) {
4610Sstevel@tonic-gate /* found */
4626793Smuffin break;
4630Sstevel@tonic-gate }
4640Sstevel@tonic-gate hash_idx = (hash_idx + hash_inc) % hash_size;
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate
4676793Smuffin /* msgstrlen should include a null termination */
4686793Smuffin if (hash_val <= num_of_str) {
4690Sstevel@tonic-gate *msgstrlen = SWAP(gmnp, msgstr_tbl[idx].len) + 1;
4706793Smuffin msg = base + SWAP(gmnp, msgstr_tbl[idx].offset);
4710Sstevel@tonic-gate *midx = idx;
4726793Smuffin } else {
4736793Smuffin *msgstrlen = gmnp->d_msg[MSGSTR][idx].len + 1;
4746793Smuffin msg = gmnp->mchunk + gmnp->d_msg[MSGSTR][idx].offset;
4756793Smuffin *midx = idx + num_of_str;
4766793Smuffin }
4776793Smuffin
4786793Smuffin return (msg);
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate * do_conv
4830Sstevel@tonic-gate *
4840Sstevel@tonic-gate * Converts the specified string from the src encoding
4850Sstevel@tonic-gate * to the dst encoding by calling iconv()
4860Sstevel@tonic-gate */
4876793Smuffin static uint32_t *
do_conv(iconv_t fd,const char * src,uint32_t srclen)4886793Smuffin do_conv(iconv_t fd, const char *src, uint32_t srclen)
4890Sstevel@tonic-gate {
4906793Smuffin uint32_t tolen;
4916793Smuffin uint32_t *ptr, *optr;
4926793Smuffin size_t oleft, ileft, bufsize, memincr;
4930Sstevel@tonic-gate char *to, *tptr;
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
4966793Smuffin gprintf(0, "*************** do_conv("
4976793Smuffin "0x%p, \"%s\", %d)\n",
4986793Smuffin (void *)fd, src ? src : "(null)", srclen);
4990Sstevel@tonic-gate #endif
5000Sstevel@tonic-gate
5016793Smuffin memincr = srclen * 2;
5026793Smuffin bufsize = memincr;
5030Sstevel@tonic-gate ileft = srclen;
5040Sstevel@tonic-gate oleft = bufsize;
5056793Smuffin ptr = malloc(bufsize + sizeof (uint32_t));
5066793Smuffin if (ptr == NULL) {
5076793Smuffin return (NULL);
5080Sstevel@tonic-gate }
5096793Smuffin to = (char *)(ptr + 1);
5100Sstevel@tonic-gate
5116793Smuffin for (;;) {
5120Sstevel@tonic-gate tptr = to;
5130Sstevel@tonic-gate errno = 0;
5140Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
5156793Smuffin gprintf(0, "******* calling iconv()\n");
5160Sstevel@tonic-gate #endif
5176793Smuffin if (iconv(fd, &src, &ileft, &tptr, &oleft) == (size_t)-1) {
5180Sstevel@tonic-gate if (errno == E2BIG) {
5196793Smuffin #ifdef GETTEXT_DEBUG
5206793Smuffin gprintf(0, "******* iconv detected E2BIG\n");
5216793Smuffin gprintf(0, "old bufsize: %u\n", bufsize);
5226793Smuffin #endif
5236793Smuffin
5246793Smuffin optr = realloc(ptr,
5256793Smuffin bufsize + memincr + sizeof (uint32_t));
5266793Smuffin if (optr == NULL) {
5276793Smuffin free(ptr);
5286793Smuffin return (NULL);
5290Sstevel@tonic-gate }
5306793Smuffin ptr = optr;
5316793Smuffin to = (char *)(optr + 1);
5326793Smuffin to += bufsize - oleft;
5336793Smuffin oleft += memincr;
5346793Smuffin bufsize += memincr;
5356793Smuffin #ifdef GETTEXT_DEBUG
5366793Smuffin gprintf(0, "new bufsize: %u\n", bufsize);
5376793Smuffin #endif
5380Sstevel@tonic-gate continue;
5390Sstevel@tonic-gate } else {
5406793Smuffin tolen = (uint32_t)(bufsize - oleft);
5410Sstevel@tonic-gate break;
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate }
5446793Smuffin tolen = (uint32_t)(bufsize - oleft);
5450Sstevel@tonic-gate break;
5460Sstevel@tonic-gate }
5476793Smuffin
5486793Smuffin if (tolen < bufsize) {
5496793Smuffin /* shrink the buffer */
5506793Smuffin optr = realloc(ptr, tolen + sizeof (uint32_t));
5516793Smuffin if (optr == NULL) {
5526793Smuffin free(ptr);
5536793Smuffin return (NULL);
5546793Smuffin }
5556793Smuffin ptr = optr;
5566793Smuffin }
5576793Smuffin *ptr = tolen;
5586793Smuffin
5596793Smuffin #ifdef GETTEXT_DEBUG
5606793Smuffin gprintf(0, "******* exiting do_conv()\n");
5616793Smuffin gprintf(0, "tolen: %u\n", *ptr);
5626793Smuffin gprintf(0, "return: 0x%p\n", ptr);
5636793Smuffin #endif
5646793Smuffin return (ptr);
5656793Smuffin }
5666793Smuffin
5676793Smuffin /*
5686793Smuffin * conv_msg
5696793Smuffin */
5706793Smuffin static char *
conv_msg(Msg_g_node * gmnp,char * msgstr,uint32_t msgstr_len,uint32_t midx,struct msg_pack * mp)5716793Smuffin conv_msg(Msg_g_node *gmnp, char *msgstr, uint32_t msgstr_len, uint32_t midx,
5726793Smuffin struct msg_pack *mp)
5736793Smuffin {
5746793Smuffin uint32_t *conv_dst;
5756793Smuffin size_t num_of_conv, conv_msgstr_len;
5766793Smuffin char *conv_msgstr, *result;
5776793Smuffin
5786793Smuffin if (gmnp->conv_msgstr == NULL) {
5796793Smuffin num_of_conv = gmnp->num_of_str + gmnp->num_of_d_str;
5806793Smuffin gmnp->conv_msgstr =
5816793Smuffin calloc((size_t)num_of_conv, sizeof (uint32_t *));
5826793Smuffin if (gmnp->conv_msgstr == NULL) {
5836793Smuffin /* malloc failed */
5846793Smuffin result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
5856793Smuffin return (result);
5866793Smuffin }
5876793Smuffin }
5886793Smuffin
5896793Smuffin conv_dst = do_conv(gmnp->fd, (const char *)msgstr, msgstr_len);
5906793Smuffin
5916793Smuffin if (conv_dst == NULL) {
5926793Smuffin result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
5936793Smuffin return (result);
5946793Smuffin }
5956793Smuffin conv_msgstr_len = *conv_dst;
5966793Smuffin gmnp->conv_msgstr[midx] = conv_dst;
5976793Smuffin conv_msgstr = (char *)(conv_dst + 1);
5986793Smuffin result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp);
5996793Smuffin return (result);
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate /*
6030Sstevel@tonic-gate * gnu_key_2_text
6040Sstevel@tonic-gate *
6050Sstevel@tonic-gate * Extracts msgstr from the GNU MO file
6060Sstevel@tonic-gate */
6070Sstevel@tonic-gate char *
gnu_key_2_text(Msg_g_node * gmnp,const char * codeset,struct msg_pack * mp)6080Sstevel@tonic-gate gnu_key_2_text(Msg_g_node *gmnp, const char *codeset,
6096793Smuffin struct msg_pack *mp)
6100Sstevel@tonic-gate {
6116793Smuffin uint32_t msgstr_len, midx;
6126793Smuffin iconv_t fd;
6130Sstevel@tonic-gate char *result, *msgstr;
6146793Smuffin int ret, conversion, new_encoding;
6150Sstevel@tonic-gate
6160Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
6176793Smuffin gprintf(0, "*************** gnu_key_2_text("
6186793Smuffin "0x%p, \"%s\", 0x%p)\n",
6196793Smuffin (void *)gmnp, codeset ? codeset : "(null)", (void *)mp);
6206793Smuffin printgnumsg(gmnp, 1);
6216793Smuffin printmp(mp, 1);
6220Sstevel@tonic-gate #endif
6230Sstevel@tonic-gate
6240Sstevel@tonic-gate /* first checks if header entry has been processed */
6250Sstevel@tonic-gate if (!(gmnp->flag & ST_CHK)) {
6260Sstevel@tonic-gate char *msg_header;
6270Sstevel@tonic-gate
6286793Smuffin msg_header = gnu_msgsearch(gmnp, "", &msgstr_len, &midx);
6290Sstevel@tonic-gate ret = parse_header((const char *)msg_header, gmnp);
6300Sstevel@tonic-gate if (ret == -1) {
6310Sstevel@tonic-gate /* fatal error */
6320Sstevel@tonic-gate DFLTMSG(result, mp->msgid1, mp->msgid2,
6336793Smuffin mp->n, mp->plural);
6340Sstevel@tonic-gate return (result);
6350Sstevel@tonic-gate }
6360Sstevel@tonic-gate gmnp->flag |= ST_CHK;
6370Sstevel@tonic-gate }
6380Sstevel@tonic-gate msgstr = gnu_msgsearch(gmnp, mp->msgid1, &msgstr_len, &midx);
6390Sstevel@tonic-gate if (msgstr == mp->msgid1) {
6400Sstevel@tonic-gate /* not found */
6410Sstevel@tonic-gate DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
6420Sstevel@tonic-gate return (result);
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate
6450Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
6466793Smuffin printgnumsg(gmnp, 1);
6470Sstevel@tonic-gate #endif
6486793Smuffin if (gmnp->dst_encoding == NULL) {
6490Sstevel@tonic-gate /*
6500Sstevel@tonic-gate * destination encoding has not been set.
6510Sstevel@tonic-gate */
6520Sstevel@tonic-gate char *dupcodeset = strdup(codeset);
6536793Smuffin if (dupcodeset == NULL) {
6540Sstevel@tonic-gate /* strdup failed */
6550Sstevel@tonic-gate result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
6560Sstevel@tonic-gate return (result);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate gmnp->dst_encoding = dupcodeset;
6590Sstevel@tonic-gate
6600Sstevel@tonic-gate if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 0) {
6610Sstevel@tonic-gate /*
6620Sstevel@tonic-gate * target encoding and src encoding
6630Sstevel@tonic-gate * are the same.
6640Sstevel@tonic-gate * No conversion required.
6650Sstevel@tonic-gate */
6660Sstevel@tonic-gate conversion = 0;
6670Sstevel@tonic-gate } else {
6680Sstevel@tonic-gate /*
6690Sstevel@tonic-gate * target encoding is different from
6700Sstevel@tonic-gate * src encoding.
6710Sstevel@tonic-gate * New conversion required.
6720Sstevel@tonic-gate */
6730Sstevel@tonic-gate /* sanity check */
6740Sstevel@tonic-gate if (gmnp->fd && (gmnp->fd != (iconv_t)-1)) {
6750Sstevel@tonic-gate (void) iconv_close(gmnp->fd);
6760Sstevel@tonic-gate gmnp->fd = (iconv_t)-1;
6770Sstevel@tonic-gate }
6780Sstevel@tonic-gate if (gmnp->conv_msgstr)
6796793Smuffin free_conv_msgstr(gmnp, 0);
6800Sstevel@tonic-gate conversion = 1;
6810Sstevel@tonic-gate new_encoding = 1;
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate } else {
6840Sstevel@tonic-gate /*
6850Sstevel@tonic-gate * dst encoding has been already set.
6860Sstevel@tonic-gate */
6870Sstevel@tonic-gate if (strcmp(gmnp->dst_encoding, codeset) == 0) {
6880Sstevel@tonic-gate /*
6890Sstevel@tonic-gate * dst encoding and target encoding are the same.
6900Sstevel@tonic-gate */
6910Sstevel@tonic-gate if (strcmp(gmnp->dst_encoding, gmnp->src_encoding)
6926793Smuffin == 0) {
6930Sstevel@tonic-gate /*
6940Sstevel@tonic-gate * dst encoding and src encoding are the same.
6950Sstevel@tonic-gate * No conversion required.
6960Sstevel@tonic-gate */
6970Sstevel@tonic-gate conversion = 0;
6980Sstevel@tonic-gate } else {
6990Sstevel@tonic-gate /*
7000Sstevel@tonic-gate * dst encoding is different from src encoding.
7010Sstevel@tonic-gate * current conversion is valid.
7020Sstevel@tonic-gate */
7030Sstevel@tonic-gate conversion = 1;
7040Sstevel@tonic-gate new_encoding = 0;
7050Sstevel@tonic-gate /* checks if iconv_open has succeeded before */
7060Sstevel@tonic-gate if (gmnp->fd == (iconv_t)-1) {
7070Sstevel@tonic-gate /*
7080Sstevel@tonic-gate * iconv_open should have failed before
7090Sstevel@tonic-gate * Assume this conversion is invalid
7100Sstevel@tonic-gate */
7110Sstevel@tonic-gate conversion = 0;
7120Sstevel@tonic-gate } else {
7136793Smuffin if (gmnp->conv_msgstr == NULL) {
7140Sstevel@tonic-gate /*
7150Sstevel@tonic-gate * memory allocation for
7160Sstevel@tonic-gate * conv_msgstr should
7170Sstevel@tonic-gate * have failed before.
7180Sstevel@tonic-gate */
7190Sstevel@tonic-gate new_encoding = 1;
7200Sstevel@tonic-gate if (gmnp->fd)
7210Sstevel@tonic-gate (void) iconv_close(
7226793Smuffin gmnp->fd);
7230Sstevel@tonic-gate gmnp->fd = (iconv_t)-1;
7240Sstevel@tonic-gate }
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate }
7270Sstevel@tonic-gate } else {
7280Sstevel@tonic-gate /*
7290Sstevel@tonic-gate * dst encoding is different from target encoding.
7300Sstevel@tonic-gate * It has changed since before.
7310Sstevel@tonic-gate */
7320Sstevel@tonic-gate char *dupcodeset = strdup(codeset);
7336793Smuffin if (dupcodeset == NULL) {
7340Sstevel@tonic-gate result = dfltmsgstr(gmnp, msgstr,
7356793Smuffin msgstr_len, mp);
7360Sstevel@tonic-gate return (result);
7370Sstevel@tonic-gate }
7380Sstevel@tonic-gate free(gmnp->dst_encoding);
7390Sstevel@tonic-gate gmnp->dst_encoding = dupcodeset;
7400Sstevel@tonic-gate if (strcmp(gmnp->dst_encoding, gmnp->src_encoding)
7416793Smuffin == 0) {
7420Sstevel@tonic-gate /*
7430Sstevel@tonic-gate * dst encoding and src encoding are the same.
7440Sstevel@tonic-gate * now, no conversion required.
7450Sstevel@tonic-gate */
7460Sstevel@tonic-gate conversion = 0;
7476793Smuffin if (gmnp->conv_msgstr)
7486793Smuffin free_conv_msgstr(gmnp, 1);
7490Sstevel@tonic-gate } else {
7500Sstevel@tonic-gate /*
7510Sstevel@tonic-gate * dst encoding is different from src encoding.
7520Sstevel@tonic-gate * new conversion required.
7530Sstevel@tonic-gate */
7540Sstevel@tonic-gate conversion = 1;
7550Sstevel@tonic-gate new_encoding = 1;
7566793Smuffin if (gmnp->conv_msgstr)
7576793Smuffin free_conv_msgstr(gmnp, 0);
7580Sstevel@tonic-gate }
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate if (gmnp->fd && (gmnp->fd != (iconv_t)-1)) {
7610Sstevel@tonic-gate (void) iconv_close(gmnp->fd);
7620Sstevel@tonic-gate }
7630Sstevel@tonic-gate if (gmnp->fd != (iconv_t)-1) {
7640Sstevel@tonic-gate gmnp->fd = (iconv_t)-1;
7650Sstevel@tonic-gate }
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate if (conversion == 0) {
7700Sstevel@tonic-gate /* no conversion */
7710Sstevel@tonic-gate result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
7720Sstevel@tonic-gate return (result);
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate /* conversion required */
7750Sstevel@tonic-gate
7760Sstevel@tonic-gate if (new_encoding == 0) {
7770Sstevel@tonic-gate /* dst codeset hasn't been changed since before */
7786793Smuffin uint32_t *cmsg;
7796793Smuffin uint32_t conv_msgstr_len;
7806793Smuffin char *conv_msgstr;
7816793Smuffin
7826793Smuffin if (gmnp->conv_msgstr[midx] == NULL) {
7830Sstevel@tonic-gate /* this msgstr hasn't been converted yet */
7846793Smuffin result = conv_msg(gmnp, msgstr, msgstr_len, midx, mp);
7856793Smuffin return (result);
7860Sstevel@tonic-gate }
7876793Smuffin /* this msgstr is in the conversion cache */
7886793Smuffin cmsg = (uint32_t *)(uintptr_t)gmnp->conv_msgstr[midx];
7896793Smuffin conv_msgstr_len = *cmsg;
7906793Smuffin conv_msgstr = (char *)(cmsg + 1);
7910Sstevel@tonic-gate result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp);
7920Sstevel@tonic-gate return (result);
7930Sstevel@tonic-gate }
7940Sstevel@tonic-gate /* new conversion */
7950Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
7966793Smuffin gprintf(0, "******* calling iconv_open()\n");
7976793Smuffin gprintf(0, " dst: \"%s\", src: \"%s\"\n",
7986793Smuffin gmnp->dst_encoding, gmnp->src_encoding);
7990Sstevel@tonic-gate #endif
8000Sstevel@tonic-gate fd = iconv_open(gmnp->dst_encoding, gmnp->src_encoding);
8010Sstevel@tonic-gate gmnp->fd = fd;
8020Sstevel@tonic-gate if (fd == (iconv_t)-1) {
8030Sstevel@tonic-gate /*
8040Sstevel@tonic-gate * iconv_open() failed.
8050Sstevel@tonic-gate * no conversion
8060Sstevel@tonic-gate */
8070Sstevel@tonic-gate result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
8080Sstevel@tonic-gate return (result);
8090Sstevel@tonic-gate }
8106793Smuffin result = conv_msg(gmnp, msgstr, msgstr_len, midx, mp);
8110Sstevel@tonic-gate return (result);
8120Sstevel@tonic-gate }
8136793Smuffin
8146793Smuffin
8156793Smuffin #define PRI_STR(x, n) PRI##x##n
8166793Smuffin #define PRI_LEN(x, n) (char)(sizeof (PRI_STR(x, n)) - 1)
8176793Smuffin #define PRIS(P, x) {\
8186793Smuffin /* x/N/ */ P(x, 8), P(x, 16), P(x, 32), P(x, 64), \
8196793Smuffin /* xLEAST/N/ */ P(x, LEAST8), P(x, LEAST16), P(x, LEAST32), P(x, LEAST64), \
8206793Smuffin /* xFAST/N/ */ P(x, FAST8), P(x, FAST16), P(x, FAST32), P(x, FAST64), \
8216793Smuffin /* xMAX,PTR */ P(x, MAX), P(x, PTR) \
8226793Smuffin }
8236793Smuffin
8246793Smuffin #define PRI_BIAS_LEAST 4
8256793Smuffin #define PRI_BIAS_FAST 8
8266793Smuffin #define PRI_BIAS_MAX 12
8276793Smuffin #define PRI_BIAS_PTR 13
8286793Smuffin
8296793Smuffin static const char *pri_d[] = PRIS(PRI_STR, d);
8306793Smuffin static const char *pri_i[] = PRIS(PRI_STR, i);
8316793Smuffin static const char *pri_o[] = PRIS(PRI_STR, o);
8326793Smuffin static const char *pri_u[] = PRIS(PRI_STR, u);
8336793Smuffin static const char *pri_x[] = PRIS(PRI_STR, x);
8346793Smuffin static const char *pri_X[] = PRIS(PRI_STR, X);
8356793Smuffin
8366793Smuffin static const char pri_d_len[] = PRIS(PRI_LEN, d);
8376793Smuffin static const char pri_i_len[] = PRIS(PRI_LEN, i);
8386793Smuffin static const char pri_o_len[] = PRIS(PRI_LEN, o);
8396793Smuffin static const char pri_u_len[] = PRIS(PRI_LEN, u);
8406793Smuffin static const char pri_x_len[] = PRIS(PRI_LEN, x);
8416793Smuffin static const char pri_X_len[] = PRIS(PRI_LEN, X);
8426793Smuffin
8436793Smuffin static struct {
8446793Smuffin const char type;
8456793Smuffin const char **str_table;
8466793Smuffin const char *len_table;
8476793Smuffin } pri_table[] = {
8486793Smuffin {'d', pri_d, pri_d_len}, {'i', pri_i, pri_i_len},
8496793Smuffin {'o', pri_o, pri_o_len}, {'u', pri_u, pri_u_len},
8506793Smuffin {'x', pri_x, pri_x_len}, {'X', pri_X, pri_X_len},
8516793Smuffin };
8526793Smuffin
8536793Smuffin static struct {
8546793Smuffin const char *name;
8556793Smuffin const char nlen;
8566793Smuffin const char want_digits;
8576793Smuffin const char bias;
8586793Smuffin } special_table[] = {
8596793Smuffin {"LEAST", 5, 1, PRI_BIAS_LEAST},
8606793Smuffin {"FAST", 4, 1, PRI_BIAS_FAST},
8616793Smuffin {"MAX", 3, 0, PRI_BIAS_MAX},
8626793Smuffin {"PTR", 3, 0, PRI_BIAS_PTR},
8636793Smuffin };
8646793Smuffin
8656793Smuffin /*
8666793Smuffin * conv_macro() returns the conversion specifier corresponding
8676793Smuffin * to the macro name specified in 'name'. 'len' contains the
8686793Smuffin * length of the macro name including the null termination.
8696793Smuffin * '*elen' will be set to the length of the returning conversion
8706793Smuffin * specifier without the null termination.
8716793Smuffin */
8726793Smuffin static const char *
conv_macro(const char * str,uint32_t len,uint32_t * lenp)8736793Smuffin conv_macro(const char *str, uint32_t len, uint32_t *lenp)
8746793Smuffin {
8756793Smuffin const char **tbl;
8766793Smuffin const char *ltbl;
8776793Smuffin char *next;
8786793Smuffin int n, i, num, bias, idx, want_digits;
8796793Smuffin
8806793Smuffin if (len == 2) {
8816793Smuffin if (*str == 'I') {
8826793Smuffin /* Solaris does not support %I */
8836793Smuffin *lenp = 0;
8846793Smuffin return ("");
8856793Smuffin }
8866793Smuffin return (NULL);
8876793Smuffin }
8886793Smuffin
8896793Smuffin if (len <= 4 || strncmp(str, "PRI", 3) != 0)
8906793Smuffin return (NULL);
8916793Smuffin
8926793Smuffin str += 3;
8936793Smuffin
8946793Smuffin n = sizeof (pri_table) / sizeof (pri_table[0]);
8956793Smuffin for (i = 0; i < n; i++) {
8966793Smuffin if (pri_table[i].type == *str)
8976793Smuffin break;
8986793Smuffin }
8996793Smuffin if (i == n)
9006793Smuffin return (NULL);
9016793Smuffin tbl = pri_table[i].str_table;
9026793Smuffin ltbl = pri_table[i].len_table;
9036793Smuffin
9046793Smuffin str++;
9056793Smuffin idx = want_digits = 0;
9066793Smuffin
9076793Smuffin if (isdigit((unsigned char)*str)) {
9086793Smuffin /* PRIx/N/ */
9096793Smuffin bias = 0;
9106793Smuffin want_digits = 1;
9116793Smuffin } else {
9126793Smuffin n = sizeof (special_table) / sizeof (special_table[0]);
9136793Smuffin for (i = 0; i < n; i++) {
9146793Smuffin if (strncmp(special_table[i].name,
9156793Smuffin str, special_table[i].nlen) == 0) {
9166793Smuffin break;
9176793Smuffin }
9186793Smuffin }
9196793Smuffin if (i == n)
9206793Smuffin return (NULL);
9216793Smuffin bias = special_table[i].bias;
9226793Smuffin want_digits = special_table[i].want_digits;
9236793Smuffin str += special_table[i].nlen;
9246793Smuffin }
9256793Smuffin
9266793Smuffin if (want_digits) {
9276793Smuffin if (!isdigit((unsigned char)*str))
9286793Smuffin return (NULL);
9296793Smuffin num = strtol(str, &next, 10);
9306793Smuffin /* see if it is 8/16/32/64 */
9316793Smuffin for (n = 8, idx = 0; idx < 4; idx++, n *= 2) {
9326793Smuffin if (n == num)
9336793Smuffin break;
9346793Smuffin }
9356793Smuffin if (idx == 4)
9366793Smuffin return (NULL);
9376793Smuffin str = next;
9386793Smuffin }
9396793Smuffin if (*str != '\0') {
9406793Smuffin /* unknow format */
9416793Smuffin return (NULL);
9426793Smuffin }
9436793Smuffin
9446793Smuffin *lenp = (uint32_t)ltbl[bias + idx];
9456793Smuffin return (tbl[bias + idx]);
9466793Smuffin }
9476793Smuffin
9486793Smuffin static gnu_d_macro_t *
expand_macros(Msg_g_node * p)9496793Smuffin expand_macros(Msg_g_node *p)
9506793Smuffin {
9516793Smuffin char *base = (char *)p->msg_file_info;
9526793Smuffin struct gnu_msg_rev1_info *rev1_header = p->rev1_header;
9536793Smuffin struct gnu_msg_ent *d_macro_tbl;
9546793Smuffin gnu_d_macro_t *d_macro;
9556793Smuffin uint32_t num_of_d_macro, e_maclen, maclen, i;
9566793Smuffin const char *e_macname;
9576793Smuffin char *macname;
9586793Smuffin
9596793Smuffin /* number of the dynamic macros */
9606793Smuffin num_of_d_macro = SWAP(p, rev1_header->num_of_dynamic_macro);
9616793Smuffin
9626793Smuffin d_macro = malloc((size_t)num_of_d_macro * sizeof (gnu_d_macro_t));
9636793Smuffin if (d_macro == NULL)
9646793Smuffin return (NULL);
9656793Smuffin
9666793Smuffin /* pointer to the dynamic strings table */
9676793Smuffin d_macro_tbl = (struct gnu_msg_ent *)(uintptr_t)
9686793Smuffin (base + SWAP(p, rev1_header->off_dynamic_macro));
9696793Smuffin
9706793Smuffin for (i = 0; i < num_of_d_macro; i++) {
9716793Smuffin macname = base + SWAP(p, d_macro_tbl[i].offset);
9726793Smuffin maclen = SWAP(p, d_macro_tbl[i].len);
9736793Smuffin
9746793Smuffin /*
9756793Smuffin * sanity check
9766793Smuffin * maclen includes a null termination.
9776793Smuffin */
9786793Smuffin if (maclen != strlen(macname) + 1) {
9796793Smuffin free(d_macro);
9806793Smuffin return (NULL);
9816793Smuffin }
9826793Smuffin e_macname = conv_macro(macname, maclen, &e_maclen);
9836793Smuffin if (e_macname == NULL) {
9846793Smuffin free(d_macro);
9856793Smuffin return (NULL);
9866793Smuffin }
9876793Smuffin d_macro[i].len = e_maclen;
9886793Smuffin d_macro[i].ptr = e_macname;
9896793Smuffin }
9906793Smuffin
9916793Smuffin return (d_macro);
9926793Smuffin }
9936793Smuffin
9946793Smuffin static char *
expand_dynamic_message(Msg_g_node * p,struct gnu_msg_ent ** e_msgs)9956793Smuffin expand_dynamic_message(Msg_g_node *p, struct gnu_msg_ent **e_msgs)
9966793Smuffin {
9976793Smuffin
9986793Smuffin char *base = (char *)p->msg_file_info;
9996793Smuffin struct gnu_msg_rev1_info *rev1_header = p->rev1_header;
10006793Smuffin struct gnu_dynamic_tbl *d_info;
10016793Smuffin struct gnu_dynamic_ent *entry;
10026793Smuffin gnu_d_macro_t *d_macro;
10036793Smuffin uint32_t num_of_d_str, mlen, dlen, didx, i, j;
10046793Smuffin uint32_t off_d_tbl;
10056793Smuffin uint32_t *d_msg_off_tbl;
10066793Smuffin size_t mchunk_size, used, need;
10076793Smuffin char *mchunk, *msg;
10086793Smuffin
10096793Smuffin #define MEM_INCR (1024)
10106793Smuffin
10116793Smuffin d_macro = expand_macros(p);
10126793Smuffin if (d_macro == NULL)
10136793Smuffin return (NULL);
10146793Smuffin
10156793Smuffin /* number of dynamic messages */
10166793Smuffin num_of_d_str = p->num_of_d_str;
10176793Smuffin
10186793Smuffin mchunk = NULL;
10196793Smuffin mchunk_size = 0; /* size of the allocated memory in mchunk */
10206793Smuffin used = 0; /* size of the used memory in mchunk */
10216793Smuffin for (i = MSGID; i <= MSGSTR; i++) {
10226793Smuffin /* pointer to the offset table of dynamic msgids/msgstrs */
10236793Smuffin off_d_tbl = SWAP(p,
10246793Smuffin i == MSGID ? rev1_header->off_dynamic_msgid_tbl :
10256793Smuffin rev1_header->off_dynamic_msgstr_tbl);
10266793Smuffin /* pointer to the dynamic msgids/msgstrs */
10276793Smuffin d_msg_off_tbl = (uint32_t *)(uintptr_t)(base + off_d_tbl);
10286793Smuffin for (j = 0; j < num_of_d_str; j++) {
10296793Smuffin e_msgs[i][j].offset = used;
10306793Smuffin d_info = (struct gnu_dynamic_tbl *)(uintptr_t)
10316793Smuffin (base + SWAP(p, d_msg_off_tbl[j]));
10326793Smuffin entry = d_info->entry;
10336793Smuffin msg = base + SWAP(p, d_info->offset);
10346793Smuffin
10356793Smuffin for (;;) {
10366793Smuffin mlen = SWAP(p, entry->len);
10376793Smuffin didx = SWAP(p, entry->idx);
10386793Smuffin dlen = (didx == NOMORE_DYNAMIC_MACRO) ? 0 :
10396793Smuffin d_macro[didx].len;
10406793Smuffin need = used + mlen + dlen;
10416793Smuffin if (need >= mchunk_size) {
10426793Smuffin char *t;
10436793Smuffin size_t n = mchunk_size;
10446793Smuffin do {
10456793Smuffin n += MEM_INCR;
10466793Smuffin } while (n <= need);
10476793Smuffin t = realloc(mchunk, n);
10486793Smuffin if (t == NULL) {
10496793Smuffin free(d_macro);
10506793Smuffin free(mchunk);
10516793Smuffin return (NULL);
10526793Smuffin }
10536793Smuffin mchunk = t;
10546793Smuffin mchunk_size = n;
10556793Smuffin }
10566793Smuffin (void) memcpy(mchunk + used, msg, (size_t)mlen);
10576793Smuffin msg += mlen;
10586793Smuffin used += mlen;
10596793Smuffin
10606793Smuffin if (didx == NOMORE_DYNAMIC_MACRO) {
10616793Smuffin /*
10626793Smuffin * Last segment of a static
10636793Smuffin * msg string contains a null
10646793Smuffin * termination, so an explicit
10656793Smuffin * null termination is not required
10666793Smuffin * here.
10676793Smuffin */
10686793Smuffin break;
10696793Smuffin }
10706793Smuffin (void) memcpy(mchunk + used,
10716793Smuffin d_macro[didx].ptr, (size_t)dlen);
10726793Smuffin used += dlen;
10736793Smuffin entry++; /* to next entry */
10746793Smuffin }
10756793Smuffin /*
10766793Smuffin * e_msgs[][].len does not include a null termination
10776793Smuffin */
10786793Smuffin e_msgs[i][j].len = used - e_msgs[i][j].offset - 1;
10796793Smuffin }
10806793Smuffin }
10816793Smuffin
10826793Smuffin free(d_macro);
10836793Smuffin
10846793Smuffin /* shrink mchunk to 'used' */
10856793Smuffin {
10866793Smuffin char *t;
10876793Smuffin t = realloc(mchunk, used);
10886793Smuffin if (t == NULL) {
10896793Smuffin free(mchunk);
10906793Smuffin return (NULL);
10916793Smuffin }
10926793Smuffin mchunk = t;
10936793Smuffin }
10946793Smuffin
10956793Smuffin return (mchunk);
10966793Smuffin }
10976793Smuffin
10986793Smuffin static int
build_rev1_info(Msg_g_node * p)10996793Smuffin build_rev1_info(Msg_g_node *p)
11006793Smuffin {
11016793Smuffin uint32_t *d_hash;
11026793Smuffin uint32_t num_of_d_str, num_of_str;
11036793Smuffin uint32_t idx, hash_value, hash_size;
11046793Smuffin size_t hash_mem_size;
11056793Smuffin size_t d_msgid_size, d_msgstr_size;
11066793Smuffin char *chunk, *mchunk;
11076793Smuffin int i;
11086793Smuffin
11096793Smuffin #ifdef GETTEXT_DEBUG
11106793Smuffin gprintf(0, "******* entering build_rev1_info(0x%p)\n", p);
11116793Smuffin printgnumsg(p, 1);
11126793Smuffin #endif
11136793Smuffin
11146793Smuffin if (p->hash_table == NULL) {
11156793Smuffin /* Revision 1 always requires the hash table */
11166793Smuffin return (-1);
11176793Smuffin }
11186793Smuffin
11196793Smuffin num_of_str = p->num_of_str;
11206793Smuffin hash_size = p->hash_size;
11216793Smuffin num_of_d_str = p->num_of_d_str;
11226793Smuffin
11236793Smuffin hash_mem_size = hash_size * sizeof (uint32_t);
11246793Smuffin ROUND(hash_mem_size, sizeof (struct gnu_msg_ent));
11256793Smuffin
11266793Smuffin d_msgid_size = num_of_d_str * sizeof (struct gnu_msg_ent);
11276793Smuffin d_msgstr_size = num_of_d_str * sizeof (struct gnu_msg_ent);
11286793Smuffin
11296793Smuffin chunk = malloc(hash_mem_size + d_msgid_size + d_msgstr_size);
11306793Smuffin if (chunk == NULL) {
11316793Smuffin return (-1);
11326793Smuffin }
11336793Smuffin
11346793Smuffin d_hash = (uint32_t *)(uintptr_t)chunk;
11356793Smuffin p->d_msg[MSGID] = (struct gnu_msg_ent *)(uintptr_t)
11366793Smuffin (chunk + hash_mem_size);
11376793Smuffin p->d_msg[MSGSTR] = (struct gnu_msg_ent *)(uintptr_t)
11386793Smuffin (chunk + hash_mem_size + d_msgid_size);
11396793Smuffin
11406793Smuffin if ((mchunk = expand_dynamic_message(p, p->d_msg)) == NULL) {
11416793Smuffin free(chunk);
11426793Smuffin return (-1);
11436793Smuffin }
11446793Smuffin
11456793Smuffin /* copy the original hash table into the dynamic hash table */
11466793Smuffin for (i = 0; i < hash_size; i++) {
11476793Smuffin d_hash[i] = SWAP(p, p->hash_table[i]);
11486793Smuffin }
11496793Smuffin
11506793Smuffin /* fill in the dynamic hash table with dynamic messages */
11516793Smuffin for (i = 0; i < num_of_d_str; i++) {
11526793Smuffin hash_value = get_hashid(mchunk + p->d_msg[MSGID][i].offset,
11536793Smuffin NULL);
11546793Smuffin idx = get_hash_index(d_hash, hash_value, hash_size);
11556793Smuffin d_hash[idx] = num_of_str + i + 1;
11566793Smuffin }
11576793Smuffin
11586793Smuffin p->mchunk = mchunk;
11596793Smuffin p->hash_table = d_hash;
11606793Smuffin
11616793Smuffin #ifdef GETTEXT_DEBUG
11626793Smuffin print_rev1_info(p);
11636793Smuffin gprintf(0, "******* exiting build_rev1_info()\n");
11646793Smuffin printgnumsg(p, 1);
11656793Smuffin #endif
11666793Smuffin
11676793Smuffin return (0);
11686793Smuffin }
11696793Smuffin
11706793Smuffin /*
11716793Smuffin * gnu_setmsg
11726793Smuffin *
11736793Smuffin * INPUT
11746793Smuffin * mnp - message node
11756793Smuffin * addr - address to the mmapped file
11766793Smuffin * size - size of the file
11776793Smuffin *
11786793Smuffin * RETURN
11796793Smuffin * 0 - either T_GNU_MO or T_ILL_MO has been set
11806793Smuffin * -1 - failed
11816793Smuffin */
11826793Smuffin int
gnu_setmsg(Msg_node * mnp,char * addr,size_t size)11836793Smuffin gnu_setmsg(Msg_node *mnp, char *addr, size_t size)
11846793Smuffin {
11856793Smuffin struct gnu_msg_info *gnu_header;
11866793Smuffin Msg_g_node *p;
11876793Smuffin
11886793Smuffin #ifdef GETTEXT_DEBUG
11896793Smuffin gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
11906793Smuffin (void *)mnp, addr, size);
11916793Smuffin printmnp(mnp, 1);
11926793Smuffin #endif
11936793Smuffin
11946793Smuffin /* checks the GNU MAGIC number */
11956793Smuffin if (size < sizeof (struct gnu_msg_info)) {
11966793Smuffin /* invalid mo file */
11976793Smuffin mnp->type = T_ILL_MO;
11986793Smuffin #ifdef GETTEXT_DEBUG
11996793Smuffin gprintf(0, "********* exiting gnu_setmsg\n");
12006793Smuffin printmnp(mnp, 1);
12016793Smuffin #endif
12026793Smuffin return (0);
12036793Smuffin }
12046793Smuffin
12056793Smuffin gnu_header = (struct gnu_msg_info *)(uintptr_t)addr;
12066793Smuffin
12076793Smuffin p = calloc(1, sizeof (Msg_g_node));
12086793Smuffin if (p == NULL) {
12096793Smuffin return (-1);
12106793Smuffin }
12116793Smuffin p->msg_file_info = gnu_header;
12126793Smuffin
12136793Smuffin if (gnu_header->magic == GNU_MAGIC) {
12146793Smuffin switch (gnu_header->revision) {
12156793Smuffin case GNU_REVISION_0_1:
12166793Smuffin case GNU_REVISION_1_1:
12176793Smuffin p->flag |= ST_REV1;
12186793Smuffin break;
12196793Smuffin }
12206793Smuffin } else if (gnu_header->magic == GNU_MAGIC_SWAPPED) {
12216793Smuffin p->flag |= ST_SWP;
12226793Smuffin switch (gnu_header->revision) {
12236793Smuffin case GNU_REVISION_0_1_SWAPPED:
12246793Smuffin case GNU_REVISION_1_1_SWAPPED:
12256793Smuffin p->flag |= ST_REV1;
12266793Smuffin break;
12276793Smuffin }
12286793Smuffin } else {
12296793Smuffin /* invalid mo file */
12306793Smuffin free(p);
12316793Smuffin mnp->type = T_ILL_MO;
12326793Smuffin #ifdef GETTEXT_DEBUG
12336793Smuffin gprintf(0, "********* exiting gnu_setmsg\n");
12346793Smuffin printmnp(mnp, 1);
12356793Smuffin #endif
12366793Smuffin return (0);
12376793Smuffin }
12386793Smuffin
12396793Smuffin p->fsize = size;
12406793Smuffin p->num_of_str = SWAP(p, gnu_header->num_of_str);
12416793Smuffin p->hash_size = SWAP(p, gnu_header->sz_hashtbl);
12426793Smuffin p->hash_table = p->hash_size <= 2 ? NULL :
12436793Smuffin (uint32_t *)(uintptr_t)
12446793Smuffin (addr + SWAP(p, gnu_header->off_hashtbl));
12456793Smuffin
12466793Smuffin p->msg_tbl[MSGID] = (struct gnu_msg_ent *)(uintptr_t)
12476793Smuffin (addr + SWAP(p, gnu_header->off_msgid_tbl));
12486793Smuffin p->msg_tbl[MSGSTR] = (struct gnu_msg_ent *)(uintptr_t)
12496793Smuffin (addr + SWAP(p, gnu_header->off_msgstr_tbl));
12506793Smuffin
12516793Smuffin if (p->flag & ST_REV1) {
12526793Smuffin /* Revision 1 */
12536793Smuffin struct gnu_msg_rev1_info *rev1_header;
12546793Smuffin
12556793Smuffin rev1_header = (struct gnu_msg_rev1_info *)
12566793Smuffin (uintptr_t)(addr + sizeof (struct gnu_msg_info));
12576793Smuffin p->rev1_header = rev1_header;
12586793Smuffin p->num_of_d_str = SWAP(p, rev1_header->num_of_dynamic_str);
12596793Smuffin if (build_rev1_info(p) == -1) {
12606793Smuffin free(p);
12616793Smuffin #ifdef GETTEXT_DEBUG
12626793Smuffin gprintf(0, "******** exiting gnu_setmsg: "
12636793Smuffin "build_rev1_info() failed\n");
12646793Smuffin #endif
12656793Smuffin return (-1);
12666793Smuffin }
12676793Smuffin }
12686793Smuffin
12696793Smuffin mnp->msg.gnumsg = p;
12706793Smuffin mnp->type = T_GNU_MO;
12716793Smuffin
12726793Smuffin #ifdef GETTEXT_DEBUG
12736793Smuffin gprintf(0, "********* exiting gnu_setmsg\n");
12746793Smuffin printmnp(mnp, 1);
12756793Smuffin #endif
12766793Smuffin return (0);
12776793Smuffin }
12786793Smuffin
12796793Smuffin /*
12806793Smuffin * get_hash_index
12816793Smuffin *
12826793Smuffin * Returns the index to an empty slot in the hash table
12836793Smuffin * for the specified hash_value.
12846793Smuffin */
12856793Smuffin static uint32_t
get_hash_index(uint32_t * hash_tbl,uint32_t hash_value,uint32_t hash_size)12866793Smuffin get_hash_index(uint32_t *hash_tbl, uint32_t hash_value, uint32_t hash_size)
12876793Smuffin {
12886793Smuffin uint32_t idx, inc;
12896793Smuffin
12906793Smuffin idx = hash_value % hash_size;
12916793Smuffin inc = 1 + (hash_value % (hash_size - 2));
12926793Smuffin
12936793Smuffin for (;;) {
12946793Smuffin if (hash_tbl[idx] == 0) {
12956793Smuffin /* found an empty slot */
12966793Smuffin return (idx);
12976793Smuffin }
12986793Smuffin idx = (idx + inc) % hash_size;
12996793Smuffin }
13006793Smuffin /* NOTREACHED */
13016793Smuffin }
1302