--- old/usr/src/lib/libc/port/locale/strfmon.c	Fri Jul 11 11:43:02 2014
+++ new/usr/src/lib/libc/port/locale/strfmon.c	Fri Jul 11 11:43:02 2014
@@ -1,4 +1,5 @@
 /*
+ * Copyright 2013 Garrett D'Amore <garrett@damore.org>
  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
  * All rights reserved.
@@ -41,6 +42,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include "localeimpl.h"
+#include "lmonetary.h"
+#include "lnumeric.h"
 
 /* internal flags */
 #define	NEED_GROUPING		0x01	/* print digits grouped (default) */
@@ -60,7 +64,7 @@
 }
 
 #define	PRINTS(STR) {						\
-	char *tmps = STR;					\
+	const char *tmps = STR;					\
 	while (*tmps != '\0')					\
 		PRINT(*tmps++);					\
 }
@@ -87,22 +91,23 @@
 }
 
 #define	GRPSEP {						\
-	*--bufend = thousands_sep;				\
+	bufend -= thousands_len;				\
+	(void) memcpy(bufend, thousands_sep, thousands_len);	\
 	groups++;						\
 }
 
-static void __setup_vars(int, char *, char *, char *, char **);
-static int __calc_left_pad(int, char *);
-static char *__format_grouped_double(double, int *, int, int, int);
+static void setup_vars(const struct lc_monetary *, int, char *, char *, char *,
+    const char **);
+static int calc_left_pad(const struct lc_monetary *, int, const char *);
+static char *format_grouped_double(const struct lc_monetary *,
+    const struct lc_numeric *, double, int *, int, int, int);
 
 ssize_t
-strfmon(char *_RESTRICT_KYWD s, size_t maxsize,
-    const char *_RESTRICT_KYWD format, ...)
+strfmon_impl(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
+    const char *_RESTRICT_KYWD format, va_list ap)
 {
-	va_list		ap;
 	char 		*dst;		/* output destination pointer */
 	const char 	*fmt;		/* current format poistion pointer */
-	struct lconv 	*lc;		/* pointer to lconv structure */
 	char		*asciivalue;	/* formatted double pointer */
 
 	int		flags;		/* formatting options */
@@ -114,18 +119,20 @@
 	double		value;		/* just value */
 	char		space_char = ' '; /* space after currency */
 
-	char		cs_precedes;	/* values gathered from struct lconv */
+	char		cs_precedes;	/* values from struct lc_monetary */
 	char		sep_by_space;
 	char		sign_posn;
-	char		*signstr;
-	char		*currency_symbol;
+	const char	*signstr;
+	const char	*currency_symbol;
 
 	char		*tmpptr;	/* temporary vars */
 	int		sverrno;
+	const struct lc_monetary *lmon;		/* monetary structure */
+	const struct lc_numeric *lnum;		/* numeric structure */
 
-	va_start(ap, format);
+	lmon = loc->monetary;
+	lnum = loc->numeric;
 
-	lc = localeconv();
 	dst = s;
 	fmt = format;
 	asciivalue = NULL;
@@ -231,18 +238,14 @@
 				goto format_error;
 		}
 
-		if (currency_symbol != NULL)
-			free(currency_symbol);
 		if (flags & USE_INTL_CURRENCY) {
-			currency_symbol = strdup(lc->int_curr_symbol);
+			currency_symbol = lmon->int_curr_symbol;
+			/* by definition three letters followed by a space */
 			if (currency_symbol != NULL)
-				space_char = *(currency_symbol+3);
+				space_char = currency_symbol[3];
 		} else
-			currency_symbol = strdup(lc->currency_symbol);
+			currency_symbol = lmon->currency_symbol;
 
-		if (currency_symbol == NULL)
-			goto end_error;			/* ENOMEM. */
-
 		/* value itself */
 		value = va_arg(ap, double);
 
@@ -254,9 +257,9 @@
 
 		/* fill left_prec with amount of padding chars */
 		if (left_prec >= 0) {
-			pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
+			pad_size = calc_left_pad(lmon, (flags ^ IS_NEGATIVE),
 			    currency_symbol) -
-			    __calc_left_pad(flags, currency_symbol);
+			    calc_left_pad(lmon, flags, currency_symbol);
 			if (pad_size < 0)
 				pad_size = 0;
 		}
@@ -263,7 +266,7 @@
 
 		if (asciivalue != NULL)
 			free(asciivalue);
-		asciivalue = __format_grouped_double(value, &flags,
+		asciivalue = format_grouped_double(lmon, lnum, value, &flags,
 		    left_prec, right_prec, pad_char);
 		if (asciivalue == NULL)
 			goto end_error;		/* errno already set */
@@ -270,8 +273,8 @@
 						/* to ENOMEM by malloc() */
 
 		/* set some variables for later use */
-		__setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn,
-		    &signstr);
+		setup_vars(lmon, flags, &cs_precedes, &sep_by_space,
+		    &sign_posn, &signstr);
 
 		/*
 		 * Description of some LC_MONETARY's values:
@@ -315,7 +318,7 @@
 		if (cs_precedes == 1) {
 			if (sign_posn == 1 || sign_posn == 3) {
 				PRINTS(signstr);
-				if (sep_by_space == 2)		/* XXX: ? */
+				if (sep_by_space == 2)
 					PRINT(' ');
 			}
 
@@ -382,9 +385,7 @@
 	}
 
 	PRINT('\0');
-	va_end(ap);
 	free(asciivalue);
-	free(currency_symbol);
 	return (dst - s - 1);	/* size of put data except trailing '\0' */
 
 e2big_error:
@@ -398,45 +399,66 @@
 	sverrno = errno;
 	if (asciivalue != NULL)
 		free(asciivalue);
-	if (currency_symbol != NULL)
-		free(currency_symbol);
 	errno = sverrno;
-	va_end(ap);
 	return (-1);
 }
 
-static void
-__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
-    char *sign_posn, char **signstr)
+ssize_t
+strfmon(char *_RESTRICT_KYWD s, size_t maxsize,
+    const char *_RESTRICT_KYWD format, ...)
 {
+	va_list ap;
+	ssize_t ret;
 
-	struct lconv *lc = localeconv();
+	va_start(ap, format);
+	ret = strfmon_impl(s, maxsize, uselocale(NULL), format, ap);
+	va_end(ap);
+	return (ret);
+}
 
+ssize_t
+strfmon_l(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
+    const char *_RESTRICT_KYWD format, ...)
+{
+	ssize_t ret;
+	va_list ap;
+	va_start(ap, format);
+	ret = strfmon_impl(s, maxsize, loc, format, ap);
+	va_end(ap);
+	return (ret);
+}
+
+static void
+setup_vars(const struct lc_monetary *lmon, int flags, char *cs_precedes,
+    char *sep_by_space, char *sign_posn, const char **signstr)
+{
 	if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
-		*cs_precedes = lc->int_n_cs_precedes;
-		*sep_by_space = lc->int_n_sep_by_space;
-		*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn;
-		*signstr = (lc->negative_sign[0] == '\0') ? "-"
-		    : lc->negative_sign;
+		*cs_precedes = lmon->int_n_cs_precedes[0];
+		*sep_by_space = lmon->int_n_sep_by_space[0];
+		*sign_posn = (flags & PARENTH_POSN) ? 0 :
+		    lmon->int_n_sign_posn[0];
+		*signstr = (lmon->negative_sign[0] == '\0') ? "-" :
+		    lmon->negative_sign;
 	} else if (flags & USE_INTL_CURRENCY) {
-		*cs_precedes = lc->int_p_cs_precedes;
-		*sep_by_space = lc->int_p_sep_by_space;
-		*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn;
-		*signstr = lc->positive_sign;
+		*cs_precedes = lmon->int_p_cs_precedes[0];
+		*sep_by_space = lmon->int_p_sep_by_space[0];
+		*sign_posn = (flags & PARENTH_POSN) ? 0 :
+		    lmon->int_p_sign_posn[0];
+		*signstr = lmon->positive_sign;
 	} else if (flags & IS_NEGATIVE) {
-		*cs_precedes = lc->n_cs_precedes;
-		*sep_by_space = lc->n_sep_by_space;
-		*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn;
-		*signstr = (lc->negative_sign[0] == '\0') ? "-"
-		    : lc->negative_sign;
+		*cs_precedes = lmon->n_cs_precedes[0];
+		*sep_by_space = lmon->n_sep_by_space[0];
+		*sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->n_sign_posn[0];
+		*signstr = (lmon->negative_sign[0] == '\0') ? "-" :
+		    lmon->negative_sign;
 	} else {
-		*cs_precedes = lc->p_cs_precedes;
-		*sep_by_space = lc->p_sep_by_space;
-		*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn;
-		*signstr = lc->positive_sign;
+		*cs_precedes = lmon->p_cs_precedes[0];
+		*sep_by_space = lmon->p_sep_by_space[0];
+		*sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->p_sign_posn[0];
+		*signstr = lmon->positive_sign;
 	}
 
-	/* Set defult values for unspecified information. */
+	/* Set default values for unspecified information. */
 	if (*cs_precedes != 0)
 		*cs_precedes = 1;
 	if (*sep_by_space == CHAR_MAX)
@@ -446,13 +468,14 @@
 }
 
 static int
-__calc_left_pad(int flags, char *cur_symb)
+calc_left_pad(const struct lc_monetary *lmon, int flags, const char *cur_symb)
 {
-
-	char cs_precedes, sep_by_space, sign_posn, *signstr;
+	char cs_precedes, sep_by_space, sign_posn;
+	const char *signstr;
 	int left_chars = 0;
 
-	__setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
+	setup_vars(lmon, flags, &cs_precedes, &sep_by_space, &sign_posn,
+	    &signstr);
 
 	if (cs_precedes != 0) {
 		left_chars += strlen(cur_symb);
@@ -473,7 +496,7 @@
 }
 
 static int
-get_groups(int size, char *grouping)
+get_groups(int size, const char *grouping)
 {
 
 	int	chars = 0;
@@ -498,8 +521,9 @@
 
 /* convert double to ASCII */
 static char *
-__format_grouped_double(double value, int *flags,
-    int left_prec, int right_prec, int pad_char)
+format_grouped_double(const struct lc_monetary *lmon,
+    const struct lc_numeric *lnum,
+    double value, int *flags, int left_prec, int right_prec, int pad_char)
 {
 
 	char		*rslt;
@@ -512,21 +536,25 @@
 
 	int		padded;
 
-	struct lconv	*lc = localeconv();
-	char		*grouping;
-	char		decimal_point;
-	char		thousands_sep;
+	const char	*grouping;
+	const char	*decimal_point;
+	const char	*thousands_sep;
+	int		decimal_len;
+	int		thousands_len;
 
 	int groups = 0;
 
-	grouping = lc->mon_grouping;
-	decimal_point = *lc->mon_decimal_point;
-	if (decimal_point == '\0')
-		decimal_point = *lc->decimal_point;
-	thousands_sep = *lc->mon_thousands_sep;
-	if (thousands_sep == '\0')
-		thousands_sep = *lc->thousands_sep;
+	grouping = lmon->mon_grouping;
+	decimal_point = lmon->mon_decimal_point;
+	if (*decimal_point == '\0')
+		decimal_point = lnum->decimal_point;
+	thousands_sep = lmon->mon_thousands_sep;
+	if (*thousands_sep == '\0')
+		thousands_sep = lnum->thousands_sep;
 
+	decimal_len = strlen(decimal_point);	/* usually 1 */
+	thousands_len = strlen(thousands_sep);	/* 0 or 1 usually */
+
 	/* fill left_prec with default value */
 	if (left_prec == -1)
 		left_prec = 0;
@@ -534,9 +562,9 @@
 	/* fill right_prec with default value */
 	if (right_prec == -1) {
 		if (*flags & USE_INTL_CURRENCY)
-			right_prec = lc->int_frac_digits;
+			right_prec = lmon->int_frac_digits[0];
 		else
-			right_prec = lc->frac_digits;
+			right_prec = lmon->frac_digits[0];
 
 		if (right_prec == CHAR_MAX)	/* POSIX locale ? */
 			right_prec = 2;
@@ -552,7 +580,15 @@
 	if (avalue_size < 0)
 		return (NULL);
 
-	/* make sure that we've enough space for result string */
+	/*
+	 * Make sure that we've enough space for result string.
+	 * This assumes that digits take up at least much space as
+	 * grouping and radix characters.  The worst case currently known
+	 * is for Arabic, where two-byte UTF-8 sequences are used for both
+	 * decimal and thousands seperators, and groups can be a small as two
+	 * decimal digits.  This will do no worse than doubling the storage
+	 * requirement.
+	 */
 	bufsize = strlen(avalue)*2+1;
 	rslt = calloc(1, bufsize);
 	if (rslt == NULL) {
@@ -561,7 +597,7 @@
 	}
 	bufend = rslt + bufsize - 1;	/* reserve space for trailing '\0' */
 
-	/* skip spaces at beggining */
+	/* skip spaces at beginning */
 	padded = 0;
 	while (avalue[padded] == ' ') {
 		padded++;
@@ -572,12 +608,13 @@
 		bufend -= right_prec;
 		(void) memcpy(bufend, avalue + avalue_size+padded-right_prec,
 		    right_prec);
-		*--bufend = decimal_point;
-		avalue_size -= (right_prec + 1);
+		bufend -= decimal_len;
+		(void) memcpy(bufend, decimal_point, decimal_len);
+		avalue_size -= (right_prec + decimal_len);
 	}
 
 	if ((*flags & NEED_GROUPING) &&
-	    thousands_sep != '\0' &&	/* XXX: need investigation */
+	    thousands_len != 0 &&
 	    *grouping != CHAR_MAX &&
 	    *grouping > 0) {
 		while (avalue_size > (int)*grouping) {
