17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5f13ac639Smuffin  * Common Development and Distribution License (the "License").
6f13ac639Smuffin  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21f13ac639Smuffin 
227c478bd9Sstevel@tonic-gate /*
23f13ac639Smuffin  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27*7257d1b4Sraf #include "lint.h"
287c478bd9Sstevel@tonic-gate #include "mtlib.h"
297c478bd9Sstevel@tonic-gate #include <ctype.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/mman.h>
357c478bd9Sstevel@tonic-gate #include <sys/param.h>
367c478bd9Sstevel@tonic-gate #include <sys/stat.h>
377c478bd9Sstevel@tonic-gate #include <thread.h>
387c478bd9Sstevel@tonic-gate #include <synch.h>
397c478bd9Sstevel@tonic-gate #include <unistd.h>
407c478bd9Sstevel@tonic-gate #include <limits.h>
417c478bd9Sstevel@tonic-gate #include <errno.h>
42f13ac639Smuffin #include <inttypes.h>
437c478bd9Sstevel@tonic-gate #include "libc.h"
447c478bd9Sstevel@tonic-gate #include "msgfmt.h"
457c478bd9Sstevel@tonic-gate #include "nlspath_checks.h"
467c478bd9Sstevel@tonic-gate #include "gettext.h"
477c478bd9Sstevel@tonic-gate 
48f13ac639Smuffin /* The following symbols are just for GNU binary compatibility */
49f13ac639Smuffin int	_nl_msg_cat_cntr;
50f13ac639Smuffin int	*_nl_domain_bindings;
51f13ac639Smuffin 
527c478bd9Sstevel@tonic-gate static const char	*nullstr = "";
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #define	CHARSET_MOD	"charset="
557c478bd9Sstevel@tonic-gate #define	CHARSET_LEN	(sizeof (CHARSET_MOD) - 1)
567c478bd9Sstevel@tonic-gate #define	NPLURALS_MOD	"nplurals="
577c478bd9Sstevel@tonic-gate #define	NPLURALS_LEN	(sizeof (NPLURALS_MOD) - 1)
587c478bd9Sstevel@tonic-gate #define	PLURAL_MOD	"plural="
597c478bd9Sstevel@tonic-gate #define	PLURAL_LEN	(sizeof (PLURAL_MOD) - 1)
607c478bd9Sstevel@tonic-gate 
61f13ac639Smuffin static uint32_t	get_hash_index(uint32_t *, uint32_t, uint32_t);
62f13ac639Smuffin 
637c478bd9Sstevel@tonic-gate /*
647c478bd9Sstevel@tonic-gate  * free_conv_msgstr
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  * release the memory allocated for storing code-converted messages
67f13ac639Smuffin  *
68f13ac639Smuffin  * f
69f13ac639Smuffin  *	0:	do not free gmnp->conv_msgstr
70f13ac639Smuffin  *	1:	free gmnp->conv_msgstr
717c478bd9Sstevel@tonic-gate  */
727c478bd9Sstevel@tonic-gate static void
free_conv_msgstr(Msg_g_node * gmnp,int f)73f13ac639Smuffin free_conv_msgstr(Msg_g_node *gmnp, int f)
747c478bd9Sstevel@tonic-gate {
75f13ac639Smuffin 	uint32_t	i, num_of_conv;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
78f13ac639Smuffin 	gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
79f13ac639Smuffin 	    (void *)gmnp, f);
80f13ac639Smuffin 	printgnumsg(gmnp, 1);
817c478bd9Sstevel@tonic-gate #endif
827c478bd9Sstevel@tonic-gate 
83f13ac639Smuffin 	num_of_conv = gmnp->num_of_str + gmnp->num_of_d_str;
84f13ac639Smuffin 	for (i = 0; i < num_of_conv; i++) {
857c478bd9Sstevel@tonic-gate 		if (gmnp->conv_msgstr[i]) {
867c478bd9Sstevel@tonic-gate 			free(gmnp->conv_msgstr[i]);
877c478bd9Sstevel@tonic-gate 		}
88f13ac639Smuffin 		gmnp->conv_msgstr[i] = NULL;
89f13ac639Smuffin 	}
90f13ac639Smuffin 	if (f) {
91f13ac639Smuffin 		free(gmnp->conv_msgstr);
92f13ac639Smuffin 		gmnp->conv_msgstr = NULL;
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * dfltmsgstr
987c478bd9Sstevel@tonic-gate  *
997c478bd9Sstevel@tonic-gate  * choose an appropriate message by evaluating the plural expression,
1007c478bd9Sstevel@tonic-gate  * and return it.
1017c478bd9Sstevel@tonic-gate  */
1027c478bd9Sstevel@tonic-gate static char *
dfltmsgstr(Msg_g_node * gmnp,const char * msgstr,uint32_t msgstr_len,struct msg_pack * mp)103f13ac639Smuffin dfltmsgstr(Msg_g_node *gmnp, const char *msgstr, uint32_t msgstr_len,
104f13ac639Smuffin     struct msg_pack *mp)
1057c478bd9Sstevel@tonic-gate {
1067c478bd9Sstevel@tonic-gate 	unsigned int	pindex;
1077c478bd9Sstevel@tonic-gate 	size_t	len;
1087c478bd9Sstevel@tonic-gate 	const char	*p;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
111f13ac639Smuffin 	gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
112f13ac639Smuffin 	    (void *)gmnp,
113f13ac639Smuffin 	    msgstr ? msgstr : "(null)", msgstr_len, (void *)mp);
114f13ac639Smuffin 	printgnumsg(gmnp, 1);
115f13ac639Smuffin 	printmp(mp, 1);
1167c478bd9Sstevel@tonic-gate #endif
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	if (mp->plural) {
1197c478bd9Sstevel@tonic-gate 		if (gmnp->plural) {
1207c478bd9Sstevel@tonic-gate 			pindex = plural_eval(gmnp->plural, mp->n);
1217c478bd9Sstevel@tonic-gate 		} else {
1227c478bd9Sstevel@tonic-gate 			/*
1237c478bd9Sstevel@tonic-gate 			 * This mo does not have plural information.
1247c478bd9Sstevel@tonic-gate 			 * Using the English form.
1257c478bd9Sstevel@tonic-gate 			 */
1267c478bd9Sstevel@tonic-gate 			if (mp->n == 1)
1277c478bd9Sstevel@tonic-gate 				pindex = 0;
1287c478bd9Sstevel@tonic-gate 			else
1297c478bd9Sstevel@tonic-gate 				pindex = 1;
1307c478bd9Sstevel@tonic-gate 		}
1317c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
132f13ac639Smuffin 		gprintf(0, "plural_eval returned: %u\n", pindex);
1337c478bd9Sstevel@tonic-gate #endif
1347c478bd9Sstevel@tonic-gate 		if (pindex >= gmnp->nplurals) {
1357c478bd9Sstevel@tonic-gate 			/* should never happen */
1367c478bd9Sstevel@tonic-gate 			pindex = 0;
1377c478bd9Sstevel@tonic-gate 		}
1387c478bd9Sstevel@tonic-gate 		p = msgstr;
1397c478bd9Sstevel@tonic-gate 		for (; pindex != 0; pindex--) {
1407c478bd9Sstevel@tonic-gate 			len = msgstr_len - (p - msgstr);
1417c478bd9Sstevel@tonic-gate 			p = memchr(p, '\0', len);
142f13ac639Smuffin 			if (p == NULL) {
1437c478bd9Sstevel@tonic-gate 				/*
1447c478bd9Sstevel@tonic-gate 				 * null byte not found
1457c478bd9Sstevel@tonic-gate 				 * this should never happen
1467c478bd9Sstevel@tonic-gate 				 */
1477c478bd9Sstevel@tonic-gate 				char	*result;
1487c478bd9Sstevel@tonic-gate 				DFLTMSG(result, mp->msgid1, mp->msgid2,
149f13ac639Smuffin 				    mp->n, mp->plural);
1507c478bd9Sstevel@tonic-gate 				return (result);
1517c478bd9Sstevel@tonic-gate 			}
1527c478bd9Sstevel@tonic-gate 			p++;		/* skip */
1537c478bd9Sstevel@tonic-gate 		}
1547c478bd9Sstevel@tonic-gate 		return ((char *)p);
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	return ((char *)msgstr);
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate /*
1617c478bd9Sstevel@tonic-gate  * parse_header
1627c478bd9Sstevel@tonic-gate  *
1637c478bd9Sstevel@tonic-gate  * parse the header entry of the GNU MO file and
1647c478bd9Sstevel@tonic-gate  * extract the src encoding and the plural information of the MO file
1657c478bd9Sstevel@tonic-gate  */
1667c478bd9Sstevel@tonic-gate static int
parse_header(const char * header,Msg_g_node * gmnp)1677c478bd9Sstevel@tonic-gate parse_header(const char *header, Msg_g_node *gmnp)
1687c478bd9Sstevel@tonic-gate {
1697c478bd9Sstevel@tonic-gate 	char	*charset = NULL;
1707c478bd9Sstevel@tonic-gate 	char	*charset_str;
1717c478bd9Sstevel@tonic-gate 	size_t	len;
1727c478bd9Sstevel@tonic-gate 	char	*nplurals_str, *plural_str;
1737c478bd9Sstevel@tonic-gate 	plural_expr_t	plural;
1747c478bd9Sstevel@tonic-gate 	char	*p, *q;
1757c478bd9Sstevel@tonic-gate 	unsigned int	nplurals;
1767c478bd9Sstevel@tonic-gate 	int	ret;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
179f13ac639Smuffin 	gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
180f13ac639Smuffin 	    header ? header : "(null)", (void *)gmnp);
181f13ac639Smuffin 	printgnumsg(gmnp, 1);
1827c478bd9Sstevel@tonic-gate #endif
1837c478bd9Sstevel@tonic-gate 
184f13ac639Smuffin 	if (header == NULL) {
1857c478bd9Sstevel@tonic-gate 		gmnp->src_encoding = (char *)nullstr;
1867c478bd9Sstevel@tonic-gate 		gmnp->nplurals = 2;
1877c478bd9Sstevel@tonic-gate 		gmnp->plural = NULL;
1887c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
189f13ac639Smuffin 		gprintf(0, "*************** exiting parse_header\n");
190f13ac639Smuffin 		gprintf(0, "no header\n");
1917c478bd9Sstevel@tonic-gate #endif
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 		return (0);
1947c478bd9Sstevel@tonic-gate 	}
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	charset_str = strstr(header, CHARSET_MOD);
197f13ac639Smuffin 	if (charset_str == NULL) {
1987c478bd9Sstevel@tonic-gate 		gmnp->src_encoding = (char *)nullstr;
1997c478bd9Sstevel@tonic-gate 	} else {
2007c478bd9Sstevel@tonic-gate 		p = charset_str + CHARSET_LEN;
2017c478bd9Sstevel@tonic-gate 		q = p;
2027c478bd9Sstevel@tonic-gate 		while ((*q != ' ') && (*q != '\t') &&
203f13ac639Smuffin 		    (*q != '\n')) {
2047c478bd9Sstevel@tonic-gate 			q++;
2057c478bd9Sstevel@tonic-gate 		}
2067c478bd9Sstevel@tonic-gate 		len = q - p;
2077c478bd9Sstevel@tonic-gate 		if (len > 0) {
208f13ac639Smuffin 			charset = malloc(len + 1);
209f13ac639Smuffin 			if (charset == NULL) {
2107c478bd9Sstevel@tonic-gate 				gmnp->src_encoding = (char *)nullstr;
2117c478bd9Sstevel@tonic-gate 				gmnp->nplurals = 2;
2127c478bd9Sstevel@tonic-gate 				gmnp->plural = NULL;
2137c478bd9Sstevel@tonic-gate 				return (-1);
2147c478bd9Sstevel@tonic-gate 			}
2157c478bd9Sstevel@tonic-gate 			(void) memcpy(charset, p, len);
2167c478bd9Sstevel@tonic-gate 			charset[len] = '\0';
2177c478bd9Sstevel@tonic-gate 			gmnp->src_encoding = charset;
2187c478bd9Sstevel@tonic-gate 		} else {
2197c478bd9Sstevel@tonic-gate 			gmnp->src_encoding = (char *)nullstr;
2207c478bd9Sstevel@tonic-gate 		}
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	nplurals_str = strstr(header, NPLURALS_MOD);
2247c478bd9Sstevel@tonic-gate 	plural_str = strstr(header, PLURAL_MOD);
225f13ac639Smuffin 	if (nplurals_str == NULL || plural_str == NULL) {
2267c478bd9Sstevel@tonic-gate 		/* no valid plural specification */
2277c478bd9Sstevel@tonic-gate 		gmnp->nplurals = 2;
2287c478bd9Sstevel@tonic-gate 		gmnp->plural = NULL;
2297c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
230f13ac639Smuffin 		gprintf(0, "*************** exiting parse_header\n");
231f13ac639Smuffin 		gprintf(0, "no plural entry\n");
2327c478bd9Sstevel@tonic-gate #endif
2337c478bd9Sstevel@tonic-gate 		return (0);
2347c478bd9Sstevel@tonic-gate 	} else {
2357c478bd9Sstevel@tonic-gate 		p = nplurals_str + NPLURALS_LEN;
2367c478bd9Sstevel@tonic-gate 		while (*p && isspace((unsigned char)*p)) {
2377c478bd9Sstevel@tonic-gate 			p++;
2387c478bd9Sstevel@tonic-gate 		}
2397c478bd9Sstevel@tonic-gate 		nplurals = (unsigned int)strtol(p, &q, 10);
2407c478bd9Sstevel@tonic-gate 		if (p != q) {
2417c478bd9Sstevel@tonic-gate 			gmnp->nplurals = nplurals;
2427c478bd9Sstevel@tonic-gate 		} else {
2437c478bd9Sstevel@tonic-gate 			gmnp->nplurals = 2;
2447c478bd9Sstevel@tonic-gate 		}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		p = plural_str + PLURAL_LEN;
2477c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
248f13ac639Smuffin 		gprintf(0, "plural_str: \"%s\"\n", p);
2497c478bd9Sstevel@tonic-gate #endif
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 		ret = plural_expr(&plural, (const char *)p);
2527c478bd9Sstevel@tonic-gate 		if (ret == 0) {
2537c478bd9Sstevel@tonic-gate 			/* parse succeeded */
2547c478bd9Sstevel@tonic-gate 			gmnp->plural = plural;
2557c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
256f13ac639Smuffin 		gprintf(0, "*************** exiting parse_header\n");
257f13ac639Smuffin 		gprintf(0, "charset: \"%s\"\n",
258f13ac639Smuffin 		    charset ? charset : "(null)");
259f13ac639Smuffin 		printexpr(plural, 1);
2607c478bd9Sstevel@tonic-gate #endif
2617c478bd9Sstevel@tonic-gate 			return (0);
2627c478bd9Sstevel@tonic-gate 		} else if (ret == 1) {
2637c478bd9Sstevel@tonic-gate 			/* parse error */
2647c478bd9Sstevel@tonic-gate 			gmnp->nplurals = 2;
2657c478bd9Sstevel@tonic-gate 			gmnp->plural = NULL;
2667c478bd9Sstevel@tonic-gate 			return (0);
2677c478bd9Sstevel@tonic-gate 		} else {
2687c478bd9Sstevel@tonic-gate 			/* fatal error */
2697c478bd9Sstevel@tonic-gate 			if (charset)
2707c478bd9Sstevel@tonic-gate 				free(charset);
2717c478bd9Sstevel@tonic-gate 			gmnp->src_encoding = (char *)nullstr;
2727c478bd9Sstevel@tonic-gate 			gmnp->nplurals = 2;
2737c478bd9Sstevel@tonic-gate 			gmnp->plural = NULL;
2747c478bd9Sstevel@tonic-gate 			return (-1);
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate /*
2817c478bd9Sstevel@tonic-gate  * handle_lang
2827c478bd9Sstevel@tonic-gate  *
2837c478bd9Sstevel@tonic-gate  * take care of the LANGUAGE specification
2847c478bd9Sstevel@tonic-gate  */
2857c478bd9Sstevel@tonic-gate char *
handle_lang(struct msg_pack * mp)286f13ac639Smuffin handle_lang(struct msg_pack *mp)
2877c478bd9Sstevel@tonic-gate {
2887c478bd9Sstevel@tonic-gate 	const char	*p, *op, *q;
289f13ac639Smuffin 	size_t	locale_len;
290f13ac639Smuffin 	char	*result;
291f13ac639Smuffin 	char	locale[MAXPATHLEN];
292f13ac639Smuffin 
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
295f13ac639Smuffin 	gprintf(0, "*************** handle_lang(0x%p)\n", (void *)mp);
296f13ac639Smuffin 	printmp(mp, 1);
2977c478bd9Sstevel@tonic-gate #endif
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	p = mp->language;
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	while (*p) {
3027c478bd9Sstevel@tonic-gate 		op = p;
3037c478bd9Sstevel@tonic-gate 		q = strchr(p, ':');
304f13ac639Smuffin 		if (q == NULL) {
3057c478bd9Sstevel@tonic-gate 			locale_len = strlen(p);
3067c478bd9Sstevel@tonic-gate 			p += locale_len;
3077c478bd9Sstevel@tonic-gate 		} else {
3087c478bd9Sstevel@tonic-gate 			locale_len = q - p;
3097c478bd9Sstevel@tonic-gate 			p += locale_len + 1;
3107c478bd9Sstevel@tonic-gate 		}
311f13ac639Smuffin 		if (locale_len >= MAXPATHLEN || locale_len == 0) {
3127c478bd9Sstevel@tonic-gate 			/* illegal locale name */
3137c478bd9Sstevel@tonic-gate 			continue;
3147c478bd9Sstevel@tonic-gate 		}
3157c478bd9Sstevel@tonic-gate 		(void) memcpy(locale, op, locale_len);
3167c478bd9Sstevel@tonic-gate 		locale[locale_len] = '\0';
3177c478bd9Sstevel@tonic-gate 		mp->locale = locale;
318f13ac639Smuffin 
3197c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
3207c478bd9Sstevel@tonic-gate 		*mp->msgfile = '\0';
3217c478bd9Sstevel@tonic-gate #endif
3227c478bd9Sstevel@tonic-gate 		if (mk_msgfile(mp) == NULL) {
3237c478bd9Sstevel@tonic-gate 			/* illegal locale name */
3247c478bd9Sstevel@tonic-gate 			continue;
3257c478bd9Sstevel@tonic-gate 		}
3267c478bd9Sstevel@tonic-gate 
327f13ac639Smuffin 		result = handle_mo(mp);
328f13ac639Smuffin 		if (mp->status & ST_GNU_MSG_FOUND)
3297c478bd9Sstevel@tonic-gate 			return (result);
3307c478bd9Sstevel@tonic-gate 
331f13ac639Smuffin 		if (mp->status & ST_SUN_MO_FOUND)
332f13ac639Smuffin 			break;
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
335f13ac639Smuffin 	/*
336f13ac639Smuffin 	 * no valid locale found, Sun MO found, or
337f13ac639Smuffin 	 * GNU MO found but no valid msg found there.
338f13ac639Smuffin 	 */
339f13ac639Smuffin 
340f13ac639Smuffin 	if (mp->status & ST_GNU_MO_FOUND) {
341f13ac639Smuffin 		/*
342f13ac639Smuffin 		 * GNU MO found but no valid msg found there.
343f13ac639Smuffin 		 * returning DFLTMSG.
344f13ac639Smuffin 		 */
3457c478bd9Sstevel@tonic-gate 		DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
3467c478bd9Sstevel@tonic-gate 		return (result);
3477c478bd9Sstevel@tonic-gate 	}
3487c478bd9Sstevel@tonic-gate 	return (NULL);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate /*
3527c478bd9Sstevel@tonic-gate  * gnu_msgsearch
3537c478bd9Sstevel@tonic-gate  *
3547c478bd9Sstevel@tonic-gate  * Searchs the translation message for the specified msgid1.
3557c478bd9Sstevel@tonic-gate  * Hash algorithm used in this function is Open Addressing
3567c478bd9Sstevel@tonic-gate  * with Double Hashing:
3577c478bd9Sstevel@tonic-gate  * H(k, i) = (H1(k) + i * H2(k)) mod M
3587c478bd9Sstevel@tonic-gate  * H1(k) = hashvalue % M
3597c478bd9Sstevel@tonic-gate  * H2(k) = 1 + (hashvalue % (M - 2))
3607c478bd9Sstevel@tonic-gate  *
3617c478bd9Sstevel@tonic-gate  * Ref: The Art of Computer Programming Volume 3
3627c478bd9Sstevel@tonic-gate  * Sorting and Searching, second edition
3637c478bd9Sstevel@tonic-gate  * Donald E Knuth
3647c478bd9Sstevel@tonic-gate  */
3657c478bd9Sstevel@tonic-gate static char *
gnu_msgsearch(Msg_g_node * gmnp,const char * msgid1,uint32_t * msgstrlen,uint32_t * midx)3667c478bd9Sstevel@tonic-gate gnu_msgsearch(Msg_g_node *gmnp, const char *msgid1,
367f13ac639Smuffin     uint32_t *msgstrlen, uint32_t *midx)
3687c478bd9Sstevel@tonic-gate {
369f13ac639Smuffin 	struct gnu_msg_info	*header = gmnp->msg_file_info;
3707c478bd9Sstevel@tonic-gate 	struct gnu_msg_ent	*msgid_tbl, *msgstr_tbl;
371f13ac639Smuffin 	uint32_t	num_of_str, idx, mlen, msglen;
372f13ac639Smuffin 	uint32_t	hash_size, hash_val, hash_id, hash_inc, hash_idx;
373f13ac639Smuffin 	uint32_t	*hash_table;
3747c478bd9Sstevel@tonic-gate 	char	*base;
375f13ac639Smuffin 	char	*msg;
376f13ac639Smuffin 
377f13ac639Smuffin #ifdef GETTEXT_DEBUG
378f13ac639Smuffin 	gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
379f13ac639Smuffin 	    "0x%p, 0x%p)\n",
380f13ac639Smuffin 	    (void *)gmnp, msgid1, msgstrlen, midx);
381f13ac639Smuffin 	printgnumsg(gmnp, 1);
382f13ac639Smuffin #endif
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	base = (char *)header;
3857c478bd9Sstevel@tonic-gate 
386f13ac639Smuffin 	msgid_tbl = gmnp->msg_tbl[MSGID];
387f13ac639Smuffin 	msgstr_tbl = gmnp->msg_tbl[MSGSTR];
3887c478bd9Sstevel@tonic-gate 	hash_table = gmnp->hash_table;
389f13ac639Smuffin 	hash_size = gmnp->hash_size;
390f13ac639Smuffin 	num_of_str = gmnp->num_of_str;
3917c478bd9Sstevel@tonic-gate 
392f13ac639Smuffin 	if (!(gmnp->flag & ST_REV1) &&
393f13ac639Smuffin 	    (hash_table == NULL || (hash_size <= 2))) {
3947c478bd9Sstevel@tonic-gate 		/*
395f13ac639Smuffin 		 * Revision 0 and
3967c478bd9Sstevel@tonic-gate 		 * No hash table exists or
397f13ac639Smuffin 		 * hash size is enough small.
3987c478bd9Sstevel@tonic-gate 		 */
399f13ac639Smuffin 		uint32_t	top, bottom;
4007c478bd9Sstevel@tonic-gate 		char	*msg_id_str;
4017c478bd9Sstevel@tonic-gate 		int	val;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 		top = 0;
4047c478bd9Sstevel@tonic-gate 		bottom = num_of_str;
4057c478bd9Sstevel@tonic-gate 		while (top < bottom) {
4067c478bd9Sstevel@tonic-gate 			idx = (top + bottom) / 2;
4077c478bd9Sstevel@tonic-gate 			msg_id_str = base +
408f13ac639Smuffin 			    SWAP(gmnp, msgid_tbl[idx].offset);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 			val = strcmp(msg_id_str, msgid1);
4117c478bd9Sstevel@tonic-gate 			if (val < 0) {
4127c478bd9Sstevel@tonic-gate 				top = idx + 1;
4137c478bd9Sstevel@tonic-gate 			} else if (val > 0) {
4147c478bd9Sstevel@tonic-gate 				bottom = idx;
4157c478bd9Sstevel@tonic-gate 			} else {
416f13ac639Smuffin 				*msgstrlen = (unsigned int)
417f13ac639Smuffin 				    SWAP(gmnp, msgstr_tbl[idx].len) + 1;
418f13ac639Smuffin 				*midx = idx;
419f13ac639Smuffin 				return (base +
420f13ac639Smuffin 				    SWAP(gmnp, msgstr_tbl[idx].offset));
4217c478bd9Sstevel@tonic-gate 			}
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 		/* not found */
4247c478bd9Sstevel@tonic-gate 		return ((char *)msgid1);
4257c478bd9Sstevel@tonic-gate 	}
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	/* use hash table */
428f13ac639Smuffin 	hash_id = get_hashid(msgid1, &msglen);
429f13ac639Smuffin 	hash_idx = hash_id % hash_size;
430f13ac639Smuffin 	hash_inc = 1 + (hash_id % (hash_size - 2));
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	for (;;) {
433f13ac639Smuffin 		hash_val = HASH_TBL(gmnp, hash_table[hash_idx]);
4347c478bd9Sstevel@tonic-gate 
435f13ac639Smuffin 		if (hash_val == 0) {
436f13ac639Smuffin 			/* not found */
4377c478bd9Sstevel@tonic-gate 			return ((char *)msgid1);
4387c478bd9Sstevel@tonic-gate 		}
439f13ac639Smuffin 		if (hash_val <= num_of_str) {
440f13ac639Smuffin 			/* static message */
441f13ac639Smuffin 			idx = hash_val - 1;
442f13ac639Smuffin 			mlen = SWAP(gmnp, msgid_tbl[idx].len);
443f13ac639Smuffin 			msg = base + SWAP(gmnp, msgid_tbl[idx].offset);
444f13ac639Smuffin 		} else {
445f13ac639Smuffin 			if (!(gmnp->flag & ST_REV1)) {
446f13ac639Smuffin 				/* rev 0 does not have dynamic message */
447f13ac639Smuffin 				return ((char *)msgid1);
448f13ac639Smuffin 			}
449f13ac639Smuffin 			/* dynamic message */
450f13ac639Smuffin 			idx = hash_val - num_of_str - 1;
451f13ac639Smuffin 			mlen = gmnp->d_msg[MSGID][idx].len;
452f13ac639Smuffin 			msg = gmnp->mchunk + gmnp->d_msg[MSGID][idx].offset;
453f13ac639Smuffin 		}
454f13ac639Smuffin 		if (msglen <= mlen && strcmp(msgid1, msg) == 0) {
4557c478bd9Sstevel@tonic-gate 			/* found */
456f13ac639Smuffin 			break;
4577c478bd9Sstevel@tonic-gate 		}
4587c478bd9Sstevel@tonic-gate 		hash_idx = (hash_idx + hash_inc) % hash_size;
4597c478bd9Sstevel@tonic-gate 	}
4607c478bd9Sstevel@tonic-gate 
461f13ac639Smuffin 	/* msgstrlen should include a null termination */
462f13ac639Smuffin 	if (hash_val <= num_of_str) {
4637c478bd9Sstevel@tonic-gate 		*msgstrlen = SWAP(gmnp, msgstr_tbl[idx].len) + 1;
464f13ac639Smuffin 		msg = base + SWAP(gmnp, msgstr_tbl[idx].offset);
4657c478bd9Sstevel@tonic-gate 		*midx = idx;
466f13ac639Smuffin 	} else {
467f13ac639Smuffin 		*msgstrlen = gmnp->d_msg[MSGSTR][idx].len + 1;
468f13ac639Smuffin 		msg = gmnp->mchunk + gmnp->d_msg[MSGSTR][idx].offset;
469f13ac639Smuffin 		*midx = idx + num_of_str;
470f13ac639Smuffin 	}
471f13ac639Smuffin 
472f13ac639Smuffin 	return (msg);
4737c478bd9Sstevel@tonic-gate }
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate /*
4767c478bd9Sstevel@tonic-gate  * do_conv
4777c478bd9Sstevel@tonic-gate  *
4787c478bd9Sstevel@tonic-gate  * Converts the specified string from the src encoding
4797c478bd9Sstevel@tonic-gate  * to the dst encoding by calling iconv()
4807c478bd9Sstevel@tonic-gate  */
481f13ac639Smuffin static uint32_t *
do_conv(iconv_t fd,const char * src,uint32_t srclen)482f13ac639Smuffin do_conv(iconv_t fd, const char *src, uint32_t srclen)
4837c478bd9Sstevel@tonic-gate {
484f13ac639Smuffin 	uint32_t	tolen;
485f13ac639Smuffin 	uint32_t	*ptr, *optr;
486f13ac639Smuffin 	size_t	oleft, ileft, bufsize, memincr;
4877c478bd9Sstevel@tonic-gate 	char	*to, *tptr;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
490f13ac639Smuffin 	gprintf(0, "*************** do_conv("
491f13ac639Smuffin 	    "0x%p, \"%s\", %d)\n",
492f13ac639Smuffin 	    (void *)fd, src ? src : "(null)", srclen);
4937c478bd9Sstevel@tonic-gate #endif
4947c478bd9Sstevel@tonic-gate 
495f13ac639Smuffin 	memincr = srclen * 2;
496f13ac639Smuffin 	bufsize = memincr;
4977c478bd9Sstevel@tonic-gate 	ileft = srclen;
4987c478bd9Sstevel@tonic-gate 	oleft = bufsize;
499f13ac639Smuffin 	ptr = malloc(bufsize + sizeof (uint32_t));
500f13ac639Smuffin 	if (ptr == NULL) {
501f13ac639Smuffin 		return (NULL);
5027c478bd9Sstevel@tonic-gate 	}
503f13ac639Smuffin 	to = (char *)(ptr + 1);
5047c478bd9Sstevel@tonic-gate 
505f13ac639Smuffin 	for (;;) {
5067c478bd9Sstevel@tonic-gate 		tptr = to;
5077c478bd9Sstevel@tonic-gate 		errno = 0;
5087c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
509f13ac639Smuffin 		gprintf(0, "******* calling iconv()\n");
5107c478bd9Sstevel@tonic-gate #endif
511f13ac639Smuffin 		if (iconv(fd, &src, &ileft, &tptr, &oleft) == (size_t)-1) {
5127c478bd9Sstevel@tonic-gate 			if (errno == E2BIG) {
513f13ac639Smuffin #ifdef GETTEXT_DEBUG
514f13ac639Smuffin 				gprintf(0, "******* iconv detected E2BIG\n");
515f13ac639Smuffin 				gprintf(0, "old bufsize: %u\n", bufsize);
516f13ac639Smuffin #endif
517f13ac639Smuffin 
518f13ac639Smuffin 				optr = realloc(ptr,
519f13ac639Smuffin 				    bufsize + memincr + sizeof (uint32_t));
520f13ac639Smuffin 				if (optr == NULL) {
521f13ac639Smuffin 					free(ptr);
522f13ac639Smuffin 					return (NULL);
5237c478bd9Sstevel@tonic-gate 				}
524f13ac639Smuffin 				ptr = optr;
525f13ac639Smuffin 				to = (char *)(optr + 1);
526f13ac639Smuffin 				to += bufsize - oleft;
527f13ac639Smuffin 				oleft += memincr;
528f13ac639Smuffin 				bufsize += memincr;
529f13ac639Smuffin #ifdef GETTEXT_DEBUG
530f13ac639Smuffin 				gprintf(0, "new bufsize: %u\n", bufsize);
531f13ac639Smuffin #endif
5327c478bd9Sstevel@tonic-gate 				continue;
5337c478bd9Sstevel@tonic-gate 			} else {
534f13ac639Smuffin 				tolen = (uint32_t)(bufsize - oleft);
5357c478bd9Sstevel@tonic-gate 				break;
5367c478bd9Sstevel@tonic-gate 			}
5377c478bd9Sstevel@tonic-gate 		}
538f13ac639Smuffin 		tolen = (uint32_t)(bufsize - oleft);
5397c478bd9Sstevel@tonic-gate 		break;
5407c478bd9Sstevel@tonic-gate 	}
541f13ac639Smuffin 
542f13ac639Smuffin 	if (tolen < bufsize) {
543f13ac639Smuffin 		/* shrink the buffer */
544f13ac639Smuffin 		optr = realloc(ptr, tolen + sizeof (uint32_t));
545f13ac639Smuffin 		if (optr == NULL) {
546f13ac639Smuffin 			free(ptr);
547f13ac639Smuffin 			return (NULL);
548f13ac639Smuffin 		}
549f13ac639Smuffin 		ptr = optr;
550f13ac639Smuffin 	}
551f13ac639Smuffin 	*ptr = tolen;
552f13ac639Smuffin 
553f13ac639Smuffin #ifdef GETTEXT_DEBUG
554f13ac639Smuffin 	gprintf(0, "******* exiting do_conv()\n");
555f13ac639Smuffin 	gprintf(0, "tolen: %u\n", *ptr);
556f13ac639Smuffin 	gprintf(0, "return: 0x%p\n", ptr);
557f13ac639Smuffin #endif
558f13ac639Smuffin 	return (ptr);
559f13ac639Smuffin }
560f13ac639Smuffin 
561f13ac639Smuffin /*
562f13ac639Smuffin  * conv_msg
563f13ac639Smuffin  */
564f13ac639Smuffin static char *
conv_msg(Msg_g_node * gmnp,char * msgstr,uint32_t msgstr_len,uint32_t midx,struct msg_pack * mp)565f13ac639Smuffin conv_msg(Msg_g_node *gmnp, char *msgstr, uint32_t msgstr_len, uint32_t midx,
566f13ac639Smuffin     struct msg_pack *mp)
567f13ac639Smuffin {
568f13ac639Smuffin 	uint32_t	*conv_dst;
569f13ac639Smuffin 	size_t	num_of_conv, conv_msgstr_len;
570f13ac639Smuffin 	char	*conv_msgstr, *result;
571f13ac639Smuffin 
572f13ac639Smuffin 	if (gmnp->conv_msgstr == NULL) {
573f13ac639Smuffin 		num_of_conv = gmnp->num_of_str + gmnp->num_of_d_str;
574f13ac639Smuffin 		gmnp->conv_msgstr =
575f13ac639Smuffin 		    calloc((size_t)num_of_conv, sizeof (uint32_t *));
576f13ac639Smuffin 		if (gmnp->conv_msgstr == NULL) {
577f13ac639Smuffin 			/* malloc failed */
578f13ac639Smuffin 			result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
579f13ac639Smuffin 			return (result);
580f13ac639Smuffin 		}
581f13ac639Smuffin 	}
582f13ac639Smuffin 
583f13ac639Smuffin 	conv_dst = do_conv(gmnp->fd, (const char *)msgstr, msgstr_len);
584f13ac639Smuffin 
585f13ac639Smuffin 	if (conv_dst == NULL) {
586f13ac639Smuffin 		result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
587f13ac639Smuffin 		return (result);
588f13ac639Smuffin 	}
589f13ac639Smuffin 	conv_msgstr_len = *conv_dst;
590f13ac639Smuffin 	gmnp->conv_msgstr[midx] = conv_dst;
591f13ac639Smuffin 	conv_msgstr = (char *)(conv_dst + 1);
592f13ac639Smuffin 	result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp);
593f13ac639Smuffin 	return (result);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate  * gnu_key_2_text
5987c478bd9Sstevel@tonic-gate  *
5997c478bd9Sstevel@tonic-gate  * Extracts msgstr from the GNU MO file
6007c478bd9Sstevel@tonic-gate  */
6017c478bd9Sstevel@tonic-gate char *
gnu_key_2_text(Msg_g_node * gmnp,const char * codeset,struct msg_pack * mp)6027c478bd9Sstevel@tonic-gate gnu_key_2_text(Msg_g_node *gmnp, const char *codeset,
603f13ac639Smuffin     struct msg_pack *mp)
6047c478bd9Sstevel@tonic-gate {
605f13ac639Smuffin 	uint32_t	msgstr_len, midx;
6067c478bd9Sstevel@tonic-gate 	iconv_t	fd;
607f13ac639Smuffin 	char	*result, *msgstr;
608f13ac639Smuffin 	int	ret, conversion, new_encoding;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
611f13ac639Smuffin 	gprintf(0, "*************** gnu_key_2_text("
612f13ac639Smuffin 	    "0x%p, \"%s\", 0x%p)\n",
613f13ac639Smuffin 	    (void *)gmnp, codeset ? codeset : "(null)", (void *)mp);
614f13ac639Smuffin 	printgnumsg(gmnp, 1);
615f13ac639Smuffin 	printmp(mp, 1);
6167c478bd9Sstevel@tonic-gate #endif
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	/* first checks if header entry has been processed */
6197c478bd9Sstevel@tonic-gate 	if (!(gmnp->flag & ST_CHK)) {
6207c478bd9Sstevel@tonic-gate 		char	*msg_header;
6217c478bd9Sstevel@tonic-gate 
622f13ac639Smuffin 		msg_header = gnu_msgsearch(gmnp, "", &msgstr_len, &midx);
6237c478bd9Sstevel@tonic-gate 		ret = parse_header((const char *)msg_header, gmnp);
6247c478bd9Sstevel@tonic-gate 		if (ret == -1) {
6257c478bd9Sstevel@tonic-gate 			/* fatal error */
6267c478bd9Sstevel@tonic-gate 			DFLTMSG(result, mp->msgid1, mp->msgid2,
627f13ac639Smuffin 			    mp->n, mp->plural);
6287c478bd9Sstevel@tonic-gate 			return (result);
6297c478bd9Sstevel@tonic-gate 		}
6307c478bd9Sstevel@tonic-gate 		gmnp->flag |= ST_CHK;
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 	msgstr = gnu_msgsearch(gmnp, mp->msgid1, &msgstr_len, &midx);
6337c478bd9Sstevel@tonic-gate 	if (msgstr == mp->msgid1) {
6347c478bd9Sstevel@tonic-gate 		/* not found */
6357c478bd9Sstevel@tonic-gate 		DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
6367c478bd9Sstevel@tonic-gate 		return (result);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
640f13ac639Smuffin 	printgnumsg(gmnp, 1);
6417c478bd9Sstevel@tonic-gate #endif
642f13ac639Smuffin 	if (gmnp->dst_encoding == NULL) {
6437c478bd9Sstevel@tonic-gate 		/*
6447c478bd9Sstevel@tonic-gate 		 * destination encoding has not been set.
6457c478bd9Sstevel@tonic-gate 		 */
6467c478bd9Sstevel@tonic-gate 		char	*dupcodeset = strdup(codeset);
647f13ac639Smuffin 		if (dupcodeset == NULL) {
6487c478bd9Sstevel@tonic-gate 			/* strdup failed */
6497c478bd9Sstevel@tonic-gate 			result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
6507c478bd9Sstevel@tonic-gate 			return (result);
6517c478bd9Sstevel@tonic-gate 		}
6527c478bd9Sstevel@tonic-gate 		gmnp->dst_encoding = dupcodeset;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 0) {
6557c478bd9Sstevel@tonic-gate 			/*
6567c478bd9Sstevel@tonic-gate 			 * target encoding and src encoding
6577c478bd9Sstevel@tonic-gate 			 * are the same.
6587c478bd9Sstevel@tonic-gate 			 * No conversion required.
6597c478bd9Sstevel@tonic-gate 			 */
6607c478bd9Sstevel@tonic-gate 			conversion = 0;
6617c478bd9Sstevel@tonic-gate 		} else {
6627c478bd9Sstevel@tonic-gate 			/*
6637c478bd9Sstevel@tonic-gate 			 * target encoding is different from
6647c478bd9Sstevel@tonic-gate 			 * src encoding.
6657c478bd9Sstevel@tonic-gate 			 * New conversion required.
6667c478bd9Sstevel@tonic-gate 			 */
6677c478bd9Sstevel@tonic-gate 			/* sanity check */
6687c478bd9Sstevel@tonic-gate 			if (gmnp->fd && (gmnp->fd != (iconv_t)-1)) {
6697c478bd9Sstevel@tonic-gate 				(void) iconv_close(gmnp->fd);
6707c478bd9Sstevel@tonic-gate 				gmnp->fd = (iconv_t)-1;
6717c478bd9Sstevel@tonic-gate 			}
6727c478bd9Sstevel@tonic-gate 			if (gmnp->conv_msgstr)
673f13ac639Smuffin 				free_conv_msgstr(gmnp, 0);
6747c478bd9Sstevel@tonic-gate 			conversion = 1;
6757c478bd9Sstevel@tonic-gate 			new_encoding = 1;
6767c478bd9Sstevel@tonic-gate 		}
6777c478bd9Sstevel@tonic-gate 	} else {
6787c478bd9Sstevel@tonic-gate 		/*
6797c478bd9Sstevel@tonic-gate 		 * dst encoding has been already set.
6807c478bd9Sstevel@tonic-gate 		 */
6817c478bd9Sstevel@tonic-gate 		if (strcmp(gmnp->dst_encoding, codeset) == 0) {
6827c478bd9Sstevel@tonic-gate 			/*
6837c478bd9Sstevel@tonic-gate 			 * dst encoding and target encoding are the same.
6847c478bd9Sstevel@tonic-gate 			 */
6857c478bd9Sstevel@tonic-gate 			if (strcmp(gmnp->dst_encoding, gmnp->src_encoding)
686f13ac639Smuffin 			    == 0) {
6877c478bd9Sstevel@tonic-gate 				/*
6887c478bd9Sstevel@tonic-gate 				 * dst encoding and src encoding are the same.
6897c478bd9Sstevel@tonic-gate 				 * No conversion required.
6907c478bd9Sstevel@tonic-gate 				 */
6917c478bd9Sstevel@tonic-gate 				conversion = 0;
6927c478bd9Sstevel@tonic-gate 			} else {
6937c478bd9Sstevel@tonic-gate 				/*
6947c478bd9Sstevel@tonic-gate 				 * dst encoding is different from src encoding.
6957c478bd9Sstevel@tonic-gate 				 * current conversion is valid.
6967c478bd9Sstevel@tonic-gate 				 */
6977c478bd9Sstevel@tonic-gate 				conversion = 1;
6987c478bd9Sstevel@tonic-gate 				new_encoding = 0;
6997c478bd9Sstevel@tonic-gate 				/* checks if iconv_open has succeeded before */
7007c478bd9Sstevel@tonic-gate 				if (gmnp->fd == (iconv_t)-1) {
7017c478bd9Sstevel@tonic-gate 					/*
7027c478bd9Sstevel@tonic-gate 					 * iconv_open should have failed before
7037c478bd9Sstevel@tonic-gate 					 * Assume this conversion is invalid
7047c478bd9Sstevel@tonic-gate 					 */
7057c478bd9Sstevel@tonic-gate 					conversion = 0;
7067c478bd9Sstevel@tonic-gate 				} else {
707f13ac639Smuffin 					if (gmnp->conv_msgstr == NULL) {
7087c478bd9Sstevel@tonic-gate 						/*
7097c478bd9Sstevel@tonic-gate 						 * memory allocation for
7107c478bd9Sstevel@tonic-gate 						 * conv_msgstr should
7117c478bd9Sstevel@tonic-gate 						 * have failed before.
7127c478bd9Sstevel@tonic-gate 						 */
7137c478bd9Sstevel@tonic-gate 						new_encoding = 1;
7147c478bd9Sstevel@tonic-gate 						if (gmnp->fd)
7157c478bd9Sstevel@tonic-gate 							(void) iconv_close(
716f13ac639Smuffin 							    gmnp->fd);
7177c478bd9Sstevel@tonic-gate 						gmnp->fd = (iconv_t)-1;
7187c478bd9Sstevel@tonic-gate 					}
7197c478bd9Sstevel@tonic-gate 				}
7207c478bd9Sstevel@tonic-gate 			}
7217c478bd9Sstevel@tonic-gate 		} else {
7227c478bd9Sstevel@tonic-gate 			/*
7237c478bd9Sstevel@tonic-gate 			 * dst encoding is different from target encoding.
7247c478bd9Sstevel@tonic-gate 			 * It has changed since before.
7257c478bd9Sstevel@tonic-gate 			 */
7267c478bd9Sstevel@tonic-gate 			char	*dupcodeset = strdup(codeset);
727f13ac639Smuffin 			if (dupcodeset == NULL) {
7287c478bd9Sstevel@tonic-gate 				result = dfltmsgstr(gmnp, msgstr,
729f13ac639Smuffin 				    msgstr_len, mp);
7307c478bd9Sstevel@tonic-gate 				return (result);
7317c478bd9Sstevel@tonic-gate 			}
7327c478bd9Sstevel@tonic-gate 			free(gmnp->dst_encoding);
7337c478bd9Sstevel@tonic-gate 			gmnp->dst_encoding = dupcodeset;
7347c478bd9Sstevel@tonic-gate 			if (strcmp(gmnp->dst_encoding, gmnp->src_encoding)
735f13ac639Smuffin 			    == 0) {
7367c478bd9Sstevel@tonic-gate 				/*
7377c478bd9Sstevel@tonic-gate 				 * dst encoding and src encoding are the same.
7387c478bd9Sstevel@tonic-gate 				 * now, no conversion required.
7397c478bd9Sstevel@tonic-gate 				 */
7407c478bd9Sstevel@tonic-gate 				conversion = 0;
741f13ac639Smuffin 				if (gmnp->conv_msgstr)
742f13ac639Smuffin 					free_conv_msgstr(gmnp, 1);
7437c478bd9Sstevel@tonic-gate 			} else {
7447c478bd9Sstevel@tonic-gate 				/*
7457c478bd9Sstevel@tonic-gate 				 * dst encoding is different from src encoding.
7467c478bd9Sstevel@tonic-gate 				 * new conversion required.
7477c478bd9Sstevel@tonic-gate 				 */
7487c478bd9Sstevel@tonic-gate 				conversion = 1;
7497c478bd9Sstevel@tonic-gate 				new_encoding = 1;
750f13ac639Smuffin 				if (gmnp->conv_msgstr)
751f13ac639Smuffin 					free_conv_msgstr(gmnp, 0);
7527c478bd9Sstevel@tonic-gate 			}
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 			if (gmnp->fd && (gmnp->fd != (iconv_t)-1)) {
7557c478bd9Sstevel@tonic-gate 				(void) iconv_close(gmnp->fd);
7567c478bd9Sstevel@tonic-gate 			}
7577c478bd9Sstevel@tonic-gate 			if (gmnp->fd != (iconv_t)-1) {
7587c478bd9Sstevel@tonic-gate 				gmnp->fd = (iconv_t)-1;
7597c478bd9Sstevel@tonic-gate 			}
7607c478bd9Sstevel@tonic-gate 		}
7617c478bd9Sstevel@tonic-gate 	}
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	if (conversion == 0) {
7647c478bd9Sstevel@tonic-gate 		/* no conversion */
7657c478bd9Sstevel@tonic-gate 		result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
7667c478bd9Sstevel@tonic-gate 		return (result);
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 	/* conversion required */
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	if (new_encoding == 0) {
7717c478bd9Sstevel@tonic-gate 		/* dst codeset hasn't been changed since before */
772f13ac639Smuffin 		uint32_t	*cmsg;
773f13ac639Smuffin 		uint32_t	conv_msgstr_len;
774f13ac639Smuffin 		char	*conv_msgstr;
775f13ac639Smuffin 
776f13ac639Smuffin 		if (gmnp->conv_msgstr[midx] == NULL) {
7777c478bd9Sstevel@tonic-gate 			/* this msgstr hasn't been converted yet */
778f13ac639Smuffin 			result = conv_msg(gmnp, msgstr, msgstr_len, midx, mp);
779f13ac639Smuffin 			return (result);
7807c478bd9Sstevel@tonic-gate 		}
781f13ac639Smuffin 		/* this msgstr is in the conversion cache */
782f13ac639Smuffin 		cmsg = (uint32_t *)(uintptr_t)gmnp->conv_msgstr[midx];
783f13ac639Smuffin 		conv_msgstr_len = *cmsg;
784f13ac639Smuffin 		conv_msgstr = (char *)(cmsg + 1);
7857c478bd9Sstevel@tonic-gate 		result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp);
7867c478bd9Sstevel@tonic-gate 		return (result);
7877c478bd9Sstevel@tonic-gate 	}
7887c478bd9Sstevel@tonic-gate 	/* new conversion */
7897c478bd9Sstevel@tonic-gate #ifdef GETTEXT_DEBUG
790f13ac639Smuffin 	gprintf(0, "******* calling iconv_open()\n");
791f13ac639Smuffin 	gprintf(0, "      dst: \"%s\", src: \"%s\"\n",
792f13ac639Smuffin 	    gmnp->dst_encoding, gmnp->src_encoding);
7937c478bd9Sstevel@tonic-gate #endif
7947c478bd9Sstevel@tonic-gate 	fd = iconv_open(gmnp->dst_encoding, gmnp->src_encoding);
7957c478bd9Sstevel@tonic-gate 	gmnp->fd = fd;
7967c478bd9Sstevel@tonic-gate 	if (fd == (iconv_t)-1) {
7977c478bd9Sstevel@tonic-gate 		/*
7987c478bd9Sstevel@tonic-gate 		 * iconv_open() failed.
7997c478bd9Sstevel@tonic-gate 		 * no conversion
8007c478bd9Sstevel@tonic-gate 		 */
8017c478bd9Sstevel@tonic-gate 		result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp);
8027c478bd9Sstevel@tonic-gate 		return (result);
8037c478bd9Sstevel@tonic-gate 	}
804f13ac639Smuffin 	result = conv_msg(gmnp, msgstr, msgstr_len, midx, mp);
805f13ac639Smuffin 	return (result);
806f13ac639Smuffin }
807f13ac639Smuffin 
808f13ac639Smuffin 
809f13ac639Smuffin #define	PRI_STR(x, n)	PRI##x##n
810f13ac639Smuffin #define	PRI_LEN(x, n)	(char)(sizeof (PRI_STR(x, n)) - 1)
811f13ac639Smuffin #define	PRIS(P, x)	{\
812f13ac639Smuffin /* x/N/ */	P(x, 8), P(x, 16), P(x, 32), P(x, 64), \
813f13ac639Smuffin /* xLEAST/N/ */	P(x, LEAST8), P(x, LEAST16), P(x, LEAST32), P(x, LEAST64), \
814f13ac639Smuffin /* xFAST/N/ */	P(x, FAST8), P(x, FAST16), P(x, FAST32), P(x, FAST64), \
815f13ac639Smuffin /* xMAX,PTR */	P(x, MAX), P(x, PTR) \
816f13ac639Smuffin }
817f13ac639Smuffin 
818f13ac639Smuffin #define	PRI_BIAS_LEAST	4
819f13ac639Smuffin #define	PRI_BIAS_FAST	8
820f13ac639Smuffin #define	PRI_BIAS_MAX	12
821f13ac639Smuffin #define	PRI_BIAS_PTR	13
822f13ac639Smuffin 
823f13ac639Smuffin static const char	*pri_d[] = PRIS(PRI_STR, d);
824f13ac639Smuffin static const char	*pri_i[] = PRIS(PRI_STR, i);
825f13ac639Smuffin static const char	*pri_o[] = PRIS(PRI_STR, o);
826f13ac639Smuffin static const char	*pri_u[] = PRIS(PRI_STR, u);
827f13ac639Smuffin static const char	*pri_x[] = PRIS(PRI_STR, x);
828f13ac639Smuffin static const char	*pri_X[] = PRIS(PRI_STR, X);
829f13ac639Smuffin 
830f13ac639Smuffin static const char	pri_d_len[] = PRIS(PRI_LEN, d);
831f13ac639Smuffin static const char	pri_i_len[] = PRIS(PRI_LEN, i);
832f13ac639Smuffin static const char	pri_o_len[] = PRIS(PRI_LEN, o);
833f13ac639Smuffin static const char	pri_u_len[] = PRIS(PRI_LEN, u);
834f13ac639Smuffin static const char	pri_x_len[] = PRIS(PRI_LEN, x);
835f13ac639Smuffin static const char	pri_X_len[] = PRIS(PRI_LEN, X);
836f13ac639Smuffin 
837f13ac639Smuffin static struct {
838f13ac639Smuffin 	const char	type;
839f13ac639Smuffin 	const char	**str_table;
840f13ac639Smuffin 	const char	*len_table;
841f13ac639Smuffin } pri_table[] = {
842f13ac639Smuffin 	{'d', pri_d, pri_d_len}, {'i', pri_i, pri_i_len},
843f13ac639Smuffin 	{'o', pri_o, pri_o_len}, {'u', pri_u, pri_u_len},
844f13ac639Smuffin 	{'x', pri_x, pri_x_len}, {'X', pri_X, pri_X_len},
845f13ac639Smuffin };
846f13ac639Smuffin 
847f13ac639Smuffin static struct {
848f13ac639Smuffin 	const char	*name;
849f13ac639Smuffin 	const char	nlen;
850f13ac639Smuffin 	const char	want_digits;
851f13ac639Smuffin 	const char	bias;
852f13ac639Smuffin } special_table[] = {
853f13ac639Smuffin 	{"LEAST",	5, 1, PRI_BIAS_LEAST},
854f13ac639Smuffin 	{"FAST",	4, 1, PRI_BIAS_FAST},
855f13ac639Smuffin 	{"MAX",		3, 0, PRI_BIAS_MAX},
856f13ac639Smuffin 	{"PTR",		3, 0, PRI_BIAS_PTR},
857f13ac639Smuffin };
858f13ac639Smuffin 
859f13ac639Smuffin /*
860f13ac639Smuffin  * conv_macro() returns the conversion specifier corresponding
861f13ac639Smuffin  * to the macro name specified in 'name'.  'len' contains the
862f13ac639Smuffin  * length of the macro name including the null termination.
863f13ac639Smuffin  * '*elen' will be set to the length of the returning conversion
864f13ac639Smuffin  * specifier without the null termination.
865f13ac639Smuffin  */
866f13ac639Smuffin static const char *
conv_macro(const char * str,uint32_t len,uint32_t * lenp)867f13ac639Smuffin conv_macro(const char *str, uint32_t len, uint32_t *lenp)
868f13ac639Smuffin {
869f13ac639Smuffin 	const char	**tbl;
870f13ac639Smuffin 	const char	*ltbl;
871f13ac639Smuffin 	char	*next;
872f13ac639Smuffin 	int	n, i, num, bias, idx, want_digits;
873f13ac639Smuffin 
874f13ac639Smuffin 	if (len == 2) {
875f13ac639Smuffin 		if (*str == 'I') {
876f13ac639Smuffin 			/* Solaris does not support %I */
877f13ac639Smuffin 			*lenp = 0;
878f13ac639Smuffin 			return ("");
879f13ac639Smuffin 		}
880f13ac639Smuffin 		return (NULL);
8817c478bd9Sstevel@tonic-gate 	}
882f13ac639Smuffin 
883f13ac639Smuffin 	if (len <= 4 || strncmp(str, "PRI", 3) != 0)
884f13ac639Smuffin 		return (NULL);
885f13ac639Smuffin 
886f13ac639Smuffin 	str += 3;
887f13ac639Smuffin 
888f13ac639Smuffin 	n = sizeof (pri_table) / sizeof (pri_table[0]);
889f13ac639Smuffin 	for (i = 0; i < n; i++) {
890f13ac639Smuffin 		if (pri_table[i].type == *str)
891f13ac639Smuffin 			break;
8927c478bd9Sstevel@tonic-gate 	}
893f13ac639Smuffin 	if (i == n)
894f13ac639Smuffin 		return (NULL);
895f13ac639Smuffin 	tbl = pri_table[i].str_table;
896f13ac639Smuffin 	ltbl = pri_table[i].len_table;
897f13ac639Smuffin 
898f13ac639Smuffin 	str++;
899f13ac639Smuffin 	idx = want_digits = 0;
900f13ac639Smuffin 
901f13ac639Smuffin 	if (isdigit((unsigned char)*str)) {
902f13ac639Smuffin 		/* PRIx/N/ */
903f13ac639Smuffin 		bias = 0;
904f13ac639Smuffin 		want_digits = 1;
905f13ac639Smuffin 	} else {
906f13ac639Smuffin 		n = sizeof (special_table) / sizeof (special_table[0]);
907f13ac639Smuffin 		for (i = 0; i < n; i++) {
908f13ac639Smuffin 			if (strncmp(special_table[i].name,
909f13ac639Smuffin 			    str, special_table[i].nlen) == 0) {
910f13ac639Smuffin 				break;
911f13ac639Smuffin 			}
912f13ac639Smuffin 		}
913f13ac639Smuffin 		if (i == n)
914f13ac639Smuffin 			return (NULL);
915f13ac639Smuffin 		bias = special_table[i].bias;
916f13ac639Smuffin 		want_digits = special_table[i].want_digits;
917f13ac639Smuffin 		str += special_table[i].nlen;
9187c478bd9Sstevel@tonic-gate 	}
919f13ac639Smuffin 
920f13ac639Smuffin 	if (want_digits) {
921f13ac639Smuffin 		if (!isdigit((unsigned char)*str))
922f13ac639Smuffin 			return (NULL);
923f13ac639Smuffin 		num = strtol(str, &next, 10);
924f13ac639Smuffin 		/* see if it is 8/16/32/64 */
925f13ac639Smuffin 		for (n = 8, idx = 0; idx < 4; idx++, n *= 2) {
926f13ac639Smuffin 			if (n == num)
927f13ac639Smuffin 				break;
928f13ac639Smuffin 		}
929f13ac639Smuffin 		if (idx == 4)
930f13ac639Smuffin 			return (NULL);
931f13ac639Smuffin 		str = next;
932f13ac639Smuffin 	}
933f13ac639Smuffin 	if (*str != '\0') {
934f13ac639Smuffin 		/* unknow format */
935f13ac639Smuffin 		return (NULL);
936f13ac639Smuffin 	}
937f13ac639Smuffin 
938f13ac639Smuffin 	*lenp = (uint32_t)ltbl[bias + idx];
939f13ac639Smuffin 	return (tbl[bias + idx]);
940f13ac639Smuffin }
941f13ac639Smuffin 
942f13ac639Smuffin static gnu_d_macro_t *
expand_macros(Msg_g_node * p)943f13ac639Smuffin expand_macros(Msg_g_node *p)
944f13ac639Smuffin {
945f13ac639Smuffin 	char	*base = (char *)p->msg_file_info;
946f13ac639Smuffin 	struct gnu_msg_rev1_info	*rev1_header = p->rev1_header;
947f13ac639Smuffin 	struct gnu_msg_ent	*d_macro_tbl;
948f13ac639Smuffin 	gnu_d_macro_t	*d_macro;
949f13ac639Smuffin 	uint32_t	num_of_d_macro, e_maclen, maclen, i;
950f13ac639Smuffin 	const char	*e_macname;
951f13ac639Smuffin 	char	*macname;
952f13ac639Smuffin 
953f13ac639Smuffin 	/* number of the dynamic macros */
954f13ac639Smuffin 	num_of_d_macro = SWAP(p, rev1_header->num_of_dynamic_macro);
955f13ac639Smuffin 
956f13ac639Smuffin 	d_macro = malloc((size_t)num_of_d_macro * sizeof (gnu_d_macro_t));
957f13ac639Smuffin 	if (d_macro == NULL)
958f13ac639Smuffin 		return (NULL);
959f13ac639Smuffin 
960f13ac639Smuffin 	/* pointer to the dynamic strings table */
961f13ac639Smuffin 	d_macro_tbl = (struct gnu_msg_ent *)(uintptr_t)
962f13ac639Smuffin 	    (base + SWAP(p, rev1_header->off_dynamic_macro));
963f13ac639Smuffin 
964f13ac639Smuffin 	for (i = 0; i < num_of_d_macro; i++) {
965f13ac639Smuffin 		macname = base + SWAP(p, d_macro_tbl[i].offset);
966f13ac639Smuffin 		maclen = SWAP(p, d_macro_tbl[i].len);
967f13ac639Smuffin 
968f13ac639Smuffin 		/*
969f13ac639Smuffin 		 * sanity check
970f13ac639Smuffin 		 * maclen includes a null termination.
971f13ac639Smuffin 		 */
972f13ac639Smuffin 		if (maclen != strlen(macname) + 1) {
973f13ac639Smuffin 			free(d_macro);
974f13ac639Smuffin 			return (NULL);
975f13ac639Smuffin 		}
976f13ac639Smuffin 		e_macname = conv_macro(macname, maclen, &e_maclen);
977f13ac639Smuffin 		if (e_macname == NULL) {
978f13ac639Smuffin 			free(d_macro);
979f13ac639Smuffin 			return (NULL);
980f13ac639Smuffin 		}
981f13ac639Smuffin 		d_macro[i].len = e_maclen;
982f13ac639Smuffin 		d_macro[i].ptr = e_macname;
983f13ac639Smuffin 	}
984f13ac639Smuffin 
985f13ac639Smuffin 	return (d_macro);
986f13ac639Smuffin }
987f13ac639Smuffin 
988f13ac639Smuffin static char *
expand_dynamic_message(Msg_g_node * p,struct gnu_msg_ent ** e_msgs)989f13ac639Smuffin expand_dynamic_message(Msg_g_node *p, struct gnu_msg_ent **e_msgs)
990f13ac639Smuffin {
991f13ac639Smuffin 
992f13ac639Smuffin 	char	*base = (char *)p->msg_file_info;
993f13ac639Smuffin 	struct gnu_msg_rev1_info	*rev1_header = p->rev1_header;
994f13ac639Smuffin 	struct gnu_dynamic_tbl	*d_info;
995f13ac639Smuffin 	struct gnu_dynamic_ent	*entry;
996f13ac639Smuffin 	gnu_d_macro_t	*d_macro;
997f13ac639Smuffin 	uint32_t	num_of_d_str, mlen, dlen, didx, i, j;
998f13ac639Smuffin 	uint32_t	off_d_tbl;
999f13ac639Smuffin 	uint32_t	*d_msg_off_tbl;
1000f13ac639Smuffin 	size_t	mchunk_size, used, need;
1001f13ac639Smuffin 	char	*mchunk, *msg;
1002f13ac639Smuffin 
1003f13ac639Smuffin #define	MEM_INCR	(1024)
1004f13ac639Smuffin 
1005f13ac639Smuffin 	d_macro = expand_macros(p);
1006f13ac639Smuffin 	if (d_macro == NULL)
1007f13ac639Smuffin 		return (NULL);
1008f13ac639Smuffin 
1009f13ac639Smuffin 	/* number of dynamic messages */
1010f13ac639Smuffin 	num_of_d_str = p->num_of_d_str;
1011f13ac639Smuffin 
1012f13ac639Smuffin 	mchunk = NULL;
1013f13ac639Smuffin 	mchunk_size = 0;	/* size of the allocated memory in mchunk */
1014f13ac639Smuffin 	used = 0;		/* size of the used memory in mchunk */
1015f13ac639Smuffin 	for (i = MSGID; i <= MSGSTR; i++) {
1016f13ac639Smuffin 		/* pointer to the offset table of dynamic msgids/msgstrs */
1017f13ac639Smuffin 		off_d_tbl = SWAP(p,
1018f13ac639Smuffin 		    i == MSGID ? rev1_header->off_dynamic_msgid_tbl :
1019f13ac639Smuffin 		    rev1_header->off_dynamic_msgstr_tbl);
1020f13ac639Smuffin 		/* pointer to the dynamic msgids/msgstrs */
1021f13ac639Smuffin 		d_msg_off_tbl = (uint32_t *)(uintptr_t)(base + off_d_tbl);
1022f13ac639Smuffin 		for (j = 0; j < num_of_d_str; j++) {
1023f13ac639Smuffin 			e_msgs[i][j].offset = used;
1024f13ac639Smuffin 			d_info = (struct gnu_dynamic_tbl *)(uintptr_t)
1025f13ac639Smuffin 			    (base + SWAP(p, d_msg_off_tbl[j]));
1026f13ac639Smuffin 			entry = d_info->entry;
1027f13ac639Smuffin 			msg = base + SWAP(p, d_info->offset);
1028f13ac639Smuffin 
1029f13ac639Smuffin 			for (;;) {
1030f13ac639Smuffin 				mlen = SWAP(p, entry->len);
1031f13ac639Smuffin 				didx = SWAP(p, entry->idx);
1032f13ac639Smuffin 				dlen = (didx == NOMORE_DYNAMIC_MACRO) ? 0 :
1033f13ac639Smuffin 				    d_macro[didx].len;
1034f13ac639Smuffin 				need = used + mlen + dlen;
1035f13ac639Smuffin 				if (need >= mchunk_size) {
1036f13ac639Smuffin 					char	*t;
1037f13ac639Smuffin 					size_t	n = mchunk_size;
1038f13ac639Smuffin 					do {
1039f13ac639Smuffin 						n += MEM_INCR;
1040f13ac639Smuffin 					} while (n <= need);
1041f13ac639Smuffin 					t = realloc(mchunk, n);
1042f13ac639Smuffin 					if (t == NULL) {
1043f13ac639Smuffin 						free(d_macro);
1044f13ac639Smuffin 						free(mchunk);
1045f13ac639Smuffin 						return (NULL);
1046f13ac639Smuffin 					}
1047f13ac639Smuffin 					mchunk = t;
1048f13ac639Smuffin 					mchunk_size = n;
1049f13ac639Smuffin 				}
1050f13ac639Smuffin 				(void) memcpy(mchunk + used, msg, (size_t)mlen);
1051f13ac639Smuffin 				msg += mlen;
1052f13ac639Smuffin 				used += mlen;
1053f13ac639Smuffin 
1054f13ac639Smuffin 				if (didx == NOMORE_DYNAMIC_MACRO) {
1055f13ac639Smuffin 					/*
1056f13ac639Smuffin 					 * Last segment of a static
1057f13ac639Smuffin 					 * msg string contains a null
1058f13ac639Smuffin 					 * termination, so an explicit
1059f13ac639Smuffin 					 * null termination is not required
1060f13ac639Smuffin 					 * here.
1061f13ac639Smuffin 					 */
1062f13ac639Smuffin 					break;
1063f13ac639Smuffin 				}
1064f13ac639Smuffin 				(void) memcpy(mchunk + used,
1065f13ac639Smuffin 				    d_macro[didx].ptr, (size_t)dlen);
1066f13ac639Smuffin 				used += dlen;
1067f13ac639Smuffin 				entry++; /* to next entry */
1068f13ac639Smuffin 			}
1069f13ac639Smuffin 			/*
1070f13ac639Smuffin 			 * e_msgs[][].len does not include a null termination
1071f13ac639Smuffin 			 */
1072f13ac639Smuffin 			e_msgs[i][j].len = used - e_msgs[i][j].offset - 1;
1073f13ac639Smuffin 		}
1074f13ac639Smuffin 	}
1075f13ac639Smuffin 
1076f13ac639Smuffin 	free(d_macro);
1077f13ac639Smuffin 
1078f13ac639Smuffin 	/* shrink mchunk to 'used' */
1079f13ac639Smuffin 	{
1080f13ac639Smuffin 		char	*t;
1081f13ac639Smuffin 		t = realloc(mchunk, used);
1082f13ac639Smuffin 		if (t == NULL) {
1083f13ac639Smuffin 			free(mchunk);
1084f13ac639Smuffin 			return (NULL);
1085f13ac639Smuffin 		}
1086f13ac639Smuffin 		mchunk = t;
1087f13ac639Smuffin 	}
1088f13ac639Smuffin 
1089f13ac639Smuffin 	return (mchunk);
1090f13ac639Smuffin }
1091f13ac639Smuffin 
1092f13ac639Smuffin static int
build_rev1_info(Msg_g_node * p)1093f13ac639Smuffin build_rev1_info(Msg_g_node *p)
1094f13ac639Smuffin {
1095f13ac639Smuffin 	uint32_t	*d_hash;
1096f13ac639Smuffin 	uint32_t	num_of_d_str, num_of_str;
1097f13ac639Smuffin 	uint32_t	idx, hash_value, hash_size;
1098f13ac639Smuffin 	size_t	hash_mem_size;
1099f13ac639Smuffin 	size_t	d_msgid_size, d_msgstr_size;
1100f13ac639Smuffin 	char	*chunk, *mchunk;
1101f13ac639Smuffin 	int	i;
1102f13ac639Smuffin 
1103f13ac639Smuffin #ifdef GETTEXT_DEBUG
1104f13ac639Smuffin 	gprintf(0, "******* entering build_rev1_info(0x%p)\n", p);
1105f13ac639Smuffin 	printgnumsg(p, 1);
1106f13ac639Smuffin #endif
1107f13ac639Smuffin 
1108f13ac639Smuffin 	if (p->hash_table == NULL) {
1109f13ac639Smuffin 		/* Revision 1 always requires the hash table */
1110f13ac639Smuffin 		return (-1);
1111f13ac639Smuffin 	}
1112f13ac639Smuffin 
1113f13ac639Smuffin 	num_of_str = p->num_of_str;
1114f13ac639Smuffin 	hash_size = p->hash_size;
1115f13ac639Smuffin 	num_of_d_str = p->num_of_d_str;
1116f13ac639Smuffin 
1117f13ac639Smuffin 	hash_mem_size = hash_size * sizeof (uint32_t);
1118f13ac639Smuffin 	ROUND(hash_mem_size, sizeof (struct gnu_msg_ent));
1119f13ac639Smuffin 
1120f13ac639Smuffin 	d_msgid_size = num_of_d_str * sizeof (struct gnu_msg_ent);
1121f13ac639Smuffin 	d_msgstr_size = num_of_d_str * sizeof (struct gnu_msg_ent);
1122f13ac639Smuffin 
1123f13ac639Smuffin 	chunk = malloc(hash_mem_size + d_msgid_size + d_msgstr_size);
1124f13ac639Smuffin 	if (chunk == NULL) {
1125f13ac639Smuffin 		return (-1);
1126f13ac639Smuffin 	}
1127f13ac639Smuffin 
1128f13ac639Smuffin 	d_hash = (uint32_t *)(uintptr_t)chunk;
1129f13ac639Smuffin 	p->d_msg[MSGID] = (struct gnu_msg_ent *)(uintptr_t)
1130f13ac639Smuffin 	    (chunk + hash_mem_size);
1131f13ac639Smuffin 	p->d_msg[MSGSTR] = (struct gnu_msg_ent *)(uintptr_t)
1132f13ac639Smuffin 	    (chunk + hash_mem_size + d_msgid_size);
1133f13ac639Smuffin 
1134f13ac639Smuffin 	if ((mchunk = expand_dynamic_message(p, p->d_msg)) == NULL) {
1135f13ac639Smuffin 		free(chunk);
1136f13ac639Smuffin 		return (-1);
1137f13ac639Smuffin 	}
1138f13ac639Smuffin 
1139f13ac639Smuffin 	/* copy the original hash table into the dynamic hash table */
1140f13ac639Smuffin 	for (i = 0; i < hash_size; i++) {
1141f13ac639Smuffin 		d_hash[i] = SWAP(p, p->hash_table[i]);
1142f13ac639Smuffin 	}
1143f13ac639Smuffin 
1144f13ac639Smuffin 	/* fill in the dynamic hash table with dynamic messages */
1145f13ac639Smuffin 	for (i = 0; i < num_of_d_str; i++) {
1146f13ac639Smuffin 		hash_value = get_hashid(mchunk + p->d_msg[MSGID][i].offset,
1147f13ac639Smuffin 		    NULL);
1148f13ac639Smuffin 		idx = get_hash_index(d_hash, hash_value, hash_size);
1149f13ac639Smuffin 		d_hash[idx] = num_of_str + i + 1;
1150f13ac639Smuffin 	}
1151f13ac639Smuffin 
1152f13ac639Smuffin 	p->mchunk = mchunk;
1153f13ac639Smuffin 	p->hash_table = d_hash;
1154f13ac639Smuffin 
1155f13ac639Smuffin #ifdef	GETTEXT_DEBUG
1156f13ac639Smuffin 	print_rev1_info(p);
1157f13ac639Smuffin 	gprintf(0, "******* exiting build_rev1_info()\n");
1158f13ac639Smuffin 	printgnumsg(p, 1);
1159f13ac639Smuffin #endif
1160f13ac639Smuffin 
1161f13ac639Smuffin 	return (0);
1162f13ac639Smuffin }
1163f13ac639Smuffin 
1164f13ac639Smuffin /*
1165f13ac639Smuffin  * gnu_setmsg
1166f13ac639Smuffin  *
1167f13ac639Smuffin  * INPUT
1168f13ac639Smuffin  *   mnp  - message node
1169f13ac639Smuffin  *   addr - address to the mmapped file
1170f13ac639Smuffin  *   size - size of the file
1171f13ac639Smuffin  *
1172f13ac639Smuffin  * RETURN
1173f13ac639Smuffin  *   0   - either T_GNU_MO or T_ILL_MO has been set
1174f13ac639Smuffin  *  -1   - failed
1175f13ac639Smuffin  */
1176f13ac639Smuffin int
gnu_setmsg(Msg_node * mnp,char * addr,size_t size)1177f13ac639Smuffin gnu_setmsg(Msg_node *mnp, char *addr, size_t size)
1178f13ac639Smuffin {
1179f13ac639Smuffin 	struct gnu_msg_info	*gnu_header;
1180f13ac639Smuffin 	Msg_g_node	*p;
1181f13ac639Smuffin 
1182f13ac639Smuffin #ifdef GETTEXT_DEBUG
1183f13ac639Smuffin 	gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
1184f13ac639Smuffin 	    (void *)mnp, addr, size);
1185f13ac639Smuffin 	printmnp(mnp, 1);
1186f13ac639Smuffin #endif
1187f13ac639Smuffin 
1188f13ac639Smuffin 	/* checks the GNU MAGIC number */
1189f13ac639Smuffin 	if (size < sizeof (struct gnu_msg_info)) {
1190f13ac639Smuffin 		/* invalid mo file */
1191f13ac639Smuffin 		mnp->type = T_ILL_MO;
1192f13ac639Smuffin #ifdef	GETTEXT_DEBUG
1193f13ac639Smuffin 		gprintf(0, "********* exiting gnu_setmsg\n");
1194f13ac639Smuffin 		printmnp(mnp, 1);
1195f13ac639Smuffin #endif
1196f13ac639Smuffin 		return (0);
1197f13ac639Smuffin 	}
1198f13ac639Smuffin 
1199f13ac639Smuffin 	gnu_header = (struct gnu_msg_info *)(uintptr_t)addr;
1200f13ac639Smuffin 
1201f13ac639Smuffin 	p = calloc(1, sizeof (Msg_g_node));
1202f13ac639Smuffin 	if (p == NULL) {
1203f13ac639Smuffin 		return (-1);
1204f13ac639Smuffin 	}
1205f13ac639Smuffin 	p->msg_file_info = gnu_header;
1206f13ac639Smuffin 
1207f13ac639Smuffin 	if (gnu_header->magic == GNU_MAGIC) {
1208f13ac639Smuffin 		switch (gnu_header->revision) {
1209f13ac639Smuffin 		case GNU_REVISION_0_1:
1210f13ac639Smuffin 		case GNU_REVISION_1_1:
1211f13ac639Smuffin 			p->flag |= ST_REV1;
1212f13ac639Smuffin 			break;
1213f13ac639Smuffin 		}
1214f13ac639Smuffin 	} else if (gnu_header->magic == GNU_MAGIC_SWAPPED) {
1215f13ac639Smuffin 		p->flag |= ST_SWP;
1216f13ac639Smuffin 		switch (gnu_header->revision) {
1217f13ac639Smuffin 		case GNU_REVISION_0_1_SWAPPED:
1218f13ac639Smuffin 		case GNU_REVISION_1_1_SWAPPED:
1219f13ac639Smuffin 			p->flag |= ST_REV1;
1220f13ac639Smuffin 			break;
1221f13ac639Smuffin 		}
1222f13ac639Smuffin 	} else {
1223f13ac639Smuffin 		/* invalid mo file */
1224f13ac639Smuffin 		free(p);
1225f13ac639Smuffin 		mnp->type = T_ILL_MO;
1226f13ac639Smuffin #ifdef	GETTEXT_DEBUG
1227f13ac639Smuffin 		gprintf(0, "********* exiting gnu_setmsg\n");
1228f13ac639Smuffin 		printmnp(mnp, 1);
1229f13ac639Smuffin #endif
1230f13ac639Smuffin 		return (0);
1231f13ac639Smuffin 	}
1232f13ac639Smuffin 
1233f13ac639Smuffin 	p->fsize = size;
1234f13ac639Smuffin 	p->num_of_str = SWAP(p, gnu_header->num_of_str);
1235f13ac639Smuffin 	p->hash_size = SWAP(p, gnu_header->sz_hashtbl);
1236f13ac639Smuffin 	p->hash_table = p->hash_size <= 2 ? NULL :
1237f13ac639Smuffin 	    (uint32_t *)(uintptr_t)
1238f13ac639Smuffin 	    (addr + SWAP(p, gnu_header->off_hashtbl));
1239f13ac639Smuffin 
1240f13ac639Smuffin 	p->msg_tbl[MSGID] = (struct gnu_msg_ent *)(uintptr_t)
1241f13ac639Smuffin 	    (addr + SWAP(p, gnu_header->off_msgid_tbl));
1242f13ac639Smuffin 	p->msg_tbl[MSGSTR] = (struct gnu_msg_ent *)(uintptr_t)
1243f13ac639Smuffin 	    (addr + SWAP(p, gnu_header->off_msgstr_tbl));
1244f13ac639Smuffin 
1245f13ac639Smuffin 	if (p->flag & ST_REV1) {
1246f13ac639Smuffin 		/* Revision 1 */
1247f13ac639Smuffin 		struct gnu_msg_rev1_info	*rev1_header;
1248f13ac639Smuffin 
1249f13ac639Smuffin 		rev1_header = (struct gnu_msg_rev1_info *)
1250f13ac639Smuffin 		    (uintptr_t)(addr + sizeof (struct gnu_msg_info));
1251f13ac639Smuffin 		p->rev1_header = rev1_header;
1252f13ac639Smuffin 		p->num_of_d_str = SWAP(p, rev1_header->num_of_dynamic_str);
1253f13ac639Smuffin 		if (build_rev1_info(p) == -1) {
1254f13ac639Smuffin 			free(p);
1255f13ac639Smuffin #ifdef GETTEXT_DEBUG
1256f13ac639Smuffin 			gprintf(0, "******** exiting gnu_setmsg: "
1257f13ac639Smuffin 			    "build_rev1_info() failed\n");
1258f13ac639Smuffin #endif
1259f13ac639Smuffin 			return (-1);
1260f13ac639Smuffin 		}
1261f13ac639Smuffin 	}
1262f13ac639Smuffin 
1263f13ac639Smuffin 	mnp->msg.gnumsg = p;
1264f13ac639Smuffin 	mnp->type = T_GNU_MO;
1265f13ac639Smuffin 
1266f13ac639Smuffin #ifdef GETTEXT_DEBUG
1267f13ac639Smuffin 	gprintf(0, "********* exiting gnu_setmsg\n");
1268f13ac639Smuffin 	printmnp(mnp, 1);
1269f13ac639Smuffin #endif
1270f13ac639Smuffin 	return (0);
1271f13ac639Smuffin }
1272f13ac639Smuffin 
1273f13ac639Smuffin /*
1274f13ac639Smuffin  * get_hash_index
1275f13ac639Smuffin  *
1276f13ac639Smuffin  * Returns the index to an empty slot in the hash table
1277f13ac639Smuffin  * for the specified hash_value.
1278f13ac639Smuffin  */
1279f13ac639Smuffin static uint32_t
get_hash_index(uint32_t * hash_tbl,uint32_t hash_value,uint32_t hash_size)1280f13ac639Smuffin get_hash_index(uint32_t *hash_tbl, uint32_t hash_value, uint32_t hash_size)
1281f13ac639Smuffin {
1282f13ac639Smuffin 	uint32_t	idx, inc;
1283f13ac639Smuffin 
1284f13ac639Smuffin 	idx = hash_value % hash_size;
1285f13ac639Smuffin 	inc = 1 + (hash_value % (hash_size - 2));
1286f13ac639Smuffin 
1287f13ac639Smuffin 	for (;;) {
1288f13ac639Smuffin 		if (hash_tbl[idx] == 0) {
1289f13ac639Smuffin 			/* found an empty slot */
1290f13ac639Smuffin 			return (idx);
1291f13ac639Smuffin 		}
1292f13ac639Smuffin 		idx = (idx + inc) % hash_size;
1293f13ac639Smuffin 	}
1294f13ac639Smuffin 	/* NOTREACHED */
12957c478bd9Sstevel@tonic-gate }
1296