xref: /illumos-gate/usr/src/lib/libc/port/locale/strfmon.c (revision 2d08521b)
14297a3b0SGarrett D'Amore /*
2*2d08521bSGarrett D'Amore  * Copyright 2013 Garrett D'Amore <garrett@damore.org>
36b5e5868SGarrett D'Amore  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
44297a3b0SGarrett D'Amore  * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
54297a3b0SGarrett D'Amore  * All rights reserved.
64297a3b0SGarrett D'Amore  *
74297a3b0SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
84297a3b0SGarrett D'Amore  * modification, are permitted provided that the following conditions
94297a3b0SGarrett D'Amore  * are met:
104297a3b0SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright
114297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer.
124297a3b0SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright
134297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer in the
144297a3b0SGarrett D'Amore  *    documentation and/or other materials provided with the distribution.
154297a3b0SGarrett D'Amore  *
164297a3b0SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
174297a3b0SGarrett D'Amore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184297a3b0SGarrett D'Amore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194297a3b0SGarrett D'Amore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204297a3b0SGarrett D'Amore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214297a3b0SGarrett D'Amore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224297a3b0SGarrett D'Amore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234297a3b0SGarrett D'Amore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244297a3b0SGarrett D'Amore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254297a3b0SGarrett D'Amore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264297a3b0SGarrett D'Amore  * SUCH DAMAGE.
274297a3b0SGarrett D'Amore  *
284297a3b0SGarrett D'Amore  */
294297a3b0SGarrett D'Amore 
304297a3b0SGarrett D'Amore #ifndef	_LCONV_C99
314297a3b0SGarrett D'Amore #define	_LCONV_C99
324297a3b0SGarrett D'Amore #endif
334297a3b0SGarrett D'Amore 
344297a3b0SGarrett D'Amore #include "lint.h"
354297a3b0SGarrett D'Amore #include <sys/types.h>
364297a3b0SGarrett D'Amore #include <ctype.h>
374297a3b0SGarrett D'Amore #include <errno.h>
384297a3b0SGarrett D'Amore #include <limits.h>
394297a3b0SGarrett D'Amore #include <locale.h>
404297a3b0SGarrett D'Amore #include <monetary.h>
414297a3b0SGarrett D'Amore #include <stdarg.h>
424297a3b0SGarrett D'Amore #include <stdio.h>
434297a3b0SGarrett D'Amore #include <stdlib.h>
444297a3b0SGarrett D'Amore #include <string.h>
45*2d08521bSGarrett D'Amore #include "localeimpl.h"
46*2d08521bSGarrett D'Amore #include "lmonetary.h"
47*2d08521bSGarrett D'Amore #include "lnumeric.h"
484297a3b0SGarrett D'Amore 
494297a3b0SGarrett D'Amore /* internal flags */
504297a3b0SGarrett D'Amore #define	NEED_GROUPING		0x01	/* print digits grouped (default) */
514297a3b0SGarrett D'Amore #define	SIGN_POSN_USED		0x02	/* '+' or '(' usage flag */
524297a3b0SGarrett D'Amore #define	LOCALE_POSN		0x04	/* use locale defined +/- (default) */
534297a3b0SGarrett D'Amore #define	PARENTH_POSN		0x08	/* enclose negative amount in () */
544297a3b0SGarrett D'Amore #define	SUPRESS_CURR_SYMBOL	0x10	/* supress the currency from output */
554297a3b0SGarrett D'Amore #define	LEFT_JUSTIFY		0x20	/* left justify */
564297a3b0SGarrett D'Amore #define	USE_INTL_CURRENCY	0x40	/* use international currency symbol */
574297a3b0SGarrett D'Amore #define	IS_NEGATIVE		0x80	/* is argument value negative ? */
584297a3b0SGarrett D'Amore 
594297a3b0SGarrett D'Amore /* internal macros */
604297a3b0SGarrett D'Amore #define	PRINT(CH) {						\
614297a3b0SGarrett D'Amore 	if (dst >= s + maxsize) 				\
624297a3b0SGarrett D'Amore 		goto e2big_error;				\
634297a3b0SGarrett D'Amore 	*dst++ = CH;						\
644297a3b0SGarrett D'Amore }
654297a3b0SGarrett D'Amore 
664297a3b0SGarrett D'Amore #define	PRINTS(STR) {						\
67*2d08521bSGarrett D'Amore 	const char *tmps = STR;					\
684297a3b0SGarrett D'Amore 	while (*tmps != '\0')					\
694297a3b0SGarrett D'Amore 		PRINT(*tmps++);					\
704297a3b0SGarrett D'Amore }
714297a3b0SGarrett D'Amore 
724297a3b0SGarrett D'Amore #define	GET_NUMBER(VAR)	{					\
734297a3b0SGarrett D'Amore 	VAR = 0;						\
744297a3b0SGarrett D'Amore 	while (isdigit((unsigned char)*fmt)) {			\
754297a3b0SGarrett D'Amore 		if (VAR > INT_MAX / 10)				\
764297a3b0SGarrett D'Amore 			goto e2big_error;			\
774297a3b0SGarrett D'Amore 		VAR *= 10;					\
784297a3b0SGarrett D'Amore 		VAR += *fmt - '0';				\
794297a3b0SGarrett D'Amore 		if (VAR < 0)					\
804297a3b0SGarrett D'Amore 			goto e2big_error;			\
814297a3b0SGarrett D'Amore 		fmt++;						\
824297a3b0SGarrett D'Amore 	}							\
834297a3b0SGarrett D'Amore }
844297a3b0SGarrett D'Amore 
854297a3b0SGarrett D'Amore #define	GRPCPY(howmany) {					\
864297a3b0SGarrett D'Amore 	int i = howmany;					\
874297a3b0SGarrett D'Amore 	while (i-- > 0) {					\
884297a3b0SGarrett D'Amore 		avalue_size--;					\
894297a3b0SGarrett D'Amore 		*--bufend = *(avalue+avalue_size+padded);	\
904297a3b0SGarrett D'Amore 	}							\
914297a3b0SGarrett D'Amore }
924297a3b0SGarrett D'Amore 
934297a3b0SGarrett D'Amore #define	GRPSEP {						\
94*2d08521bSGarrett D'Amore 	bufend -= thousands_len;				\
95*2d08521bSGarrett D'Amore 	(void) memcpy(bufend, thousands_sep, thousands_len);	\
964297a3b0SGarrett D'Amore 	groups++;						\
974297a3b0SGarrett D'Amore }
984297a3b0SGarrett D'Amore 
99*2d08521bSGarrett D'Amore static void setup_vars(const struct lc_monetary *, int, char *, char *, char *,
100*2d08521bSGarrett D'Amore     const char **);
101*2d08521bSGarrett D'Amore static int calc_left_pad(const struct lc_monetary *, int, const char *);
102*2d08521bSGarrett D'Amore static char *format_grouped_double(const struct lc_monetary *,
103*2d08521bSGarrett D'Amore     const struct lc_numeric *, double, int *, int, int, int);
1044297a3b0SGarrett D'Amore 
1054297a3b0SGarrett D'Amore ssize_t
strfmon_impl(char * _RESTRICT_KYWD s,size_t maxsize,locale_t loc,const char * _RESTRICT_KYWD format,va_list ap)106*2d08521bSGarrett D'Amore strfmon_impl(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
107*2d08521bSGarrett D'Amore     const char *_RESTRICT_KYWD format, va_list ap)
1084297a3b0SGarrett D'Amore {
1094297a3b0SGarrett D'Amore 	char 		*dst;		/* output destination pointer */
1104297a3b0SGarrett D'Amore 	const char 	*fmt;		/* current format poistion pointer */
1114297a3b0SGarrett D'Amore 	char		*asciivalue;	/* formatted double pointer */
1124297a3b0SGarrett D'Amore 
1134297a3b0SGarrett D'Amore 	int		flags;		/* formatting options */
1144297a3b0SGarrett D'Amore 	int		pad_char;	/* padding character */
1154297a3b0SGarrett D'Amore 	int		pad_size;	/* pad size */
1164297a3b0SGarrett D'Amore 	int		width;		/* field width */
1174297a3b0SGarrett D'Amore 	int		left_prec;	/* left precision */
1184297a3b0SGarrett D'Amore 	int		right_prec;	/* right precision */
1194297a3b0SGarrett D'Amore 	double		value;		/* just value */
1204297a3b0SGarrett D'Amore 	char		space_char = ' '; /* space after currency */
1214297a3b0SGarrett D'Amore 
122*2d08521bSGarrett D'Amore 	char		cs_precedes;	/* values from struct lc_monetary */
1234297a3b0SGarrett D'Amore 	char		sep_by_space;
1244297a3b0SGarrett D'Amore 	char		sign_posn;
125*2d08521bSGarrett D'Amore 	const char	*signstr;
126*2d08521bSGarrett D'Amore 	const char	*currency_symbol;
1274297a3b0SGarrett D'Amore 
1284297a3b0SGarrett D'Amore 	char		*tmpptr;	/* temporary vars */
1294297a3b0SGarrett D'Amore 	int		sverrno;
130*2d08521bSGarrett D'Amore 	const struct lc_monetary *lmon;		/* monetary structure */
131*2d08521bSGarrett D'Amore 	const struct lc_numeric *lnum;		/* numeric structure */
1324297a3b0SGarrett D'Amore 
133*2d08521bSGarrett D'Amore 	lmon = loc->monetary;
134*2d08521bSGarrett D'Amore 	lnum = loc->numeric;
1354297a3b0SGarrett D'Amore 
1364297a3b0SGarrett D'Amore 	dst = s;
1374297a3b0SGarrett D'Amore 	fmt = format;
1384297a3b0SGarrett D'Amore 	asciivalue = NULL;
1394297a3b0SGarrett D'Amore 	currency_symbol = NULL;
1404297a3b0SGarrett D'Amore 	pad_size = 0;
1414297a3b0SGarrett D'Amore 
1424297a3b0SGarrett D'Amore 	while (*fmt) {
1434297a3b0SGarrett D'Amore 		/* pass nonformating characters AS IS */
1444297a3b0SGarrett D'Amore 		if (*fmt != '%')
1454297a3b0SGarrett D'Amore 			goto literal;
1464297a3b0SGarrett D'Amore 
1474297a3b0SGarrett D'Amore 		/* '%' found ! */
1484297a3b0SGarrett D'Amore 
1494297a3b0SGarrett D'Amore 		/* "%%" mean just '%' */
1504297a3b0SGarrett D'Amore 		if (*(fmt+1) == '%') {
1514297a3b0SGarrett D'Amore 			fmt++;
1524297a3b0SGarrett D'Amore 	literal:
1534297a3b0SGarrett D'Amore 			PRINT(*fmt++);
1544297a3b0SGarrett D'Amore 			continue;
1554297a3b0SGarrett D'Amore 		}
1564297a3b0SGarrett D'Amore 
1574297a3b0SGarrett D'Amore 		/* set up initial values */
1584297a3b0SGarrett D'Amore 		flags = (NEED_GROUPING|LOCALE_POSN);
1594297a3b0SGarrett D'Amore 		pad_char = ' ';		/* padding character is "space" */
1604297a3b0SGarrett D'Amore 		left_prec = -1;		/* no left precision specified */
1614297a3b0SGarrett D'Amore 		right_prec = -1;	/* no right precision specified */
1624297a3b0SGarrett D'Amore 		width = -1;		/* no width specified */
1634297a3b0SGarrett D'Amore 		value = 0;		/* we have no value to print now */
1644297a3b0SGarrett D'Amore 
1654297a3b0SGarrett D'Amore 		/* Flags */
1664297a3b0SGarrett D'Amore 		for (;;) {
1674297a3b0SGarrett D'Amore 			switch (*++fmt) {
1684297a3b0SGarrett D'Amore 				case '=':	/* fill character */
1694297a3b0SGarrett D'Amore 					pad_char = *++fmt;
1704297a3b0SGarrett D'Amore 					if (pad_char == '\0')
1714297a3b0SGarrett D'Amore 						goto format_error;
1724297a3b0SGarrett D'Amore 					continue;
1734297a3b0SGarrett D'Amore 				case '^':	/* not group currency  */
1744297a3b0SGarrett D'Amore 					flags &= ~(NEED_GROUPING);
1754297a3b0SGarrett D'Amore 					continue;
1764297a3b0SGarrett D'Amore 				case '+':	/* use locale defined signs */
1774297a3b0SGarrett D'Amore 					if (flags & SIGN_POSN_USED)
1784297a3b0SGarrett D'Amore 						goto format_error;
1794297a3b0SGarrett D'Amore 					flags |= (SIGN_POSN_USED|LOCALE_POSN);
1804297a3b0SGarrett D'Amore 					continue;
1814297a3b0SGarrett D'Amore 				case '(':	/* enclose negatives with () */
1824297a3b0SGarrett D'Amore 					if (flags & SIGN_POSN_USED)
1834297a3b0SGarrett D'Amore 						goto format_error;
1844297a3b0SGarrett D'Amore 					flags |= (SIGN_POSN_USED|PARENTH_POSN);
1854297a3b0SGarrett D'Amore 					continue;
1864297a3b0SGarrett D'Amore 				case '!':	/* suppress currency symbol */
1874297a3b0SGarrett D'Amore 					flags |= SUPRESS_CURR_SYMBOL;
1884297a3b0SGarrett D'Amore 					continue;
1894297a3b0SGarrett D'Amore 				case '-':	/* alignment (left)  */
1904297a3b0SGarrett D'Amore 					flags |= LEFT_JUSTIFY;
1914297a3b0SGarrett D'Amore 					continue;
1924297a3b0SGarrett D'Amore 				default:
1934297a3b0SGarrett D'Amore 					break;
1944297a3b0SGarrett D'Amore 			}
1954297a3b0SGarrett D'Amore 			break;
1964297a3b0SGarrett D'Amore 		}
1974297a3b0SGarrett D'Amore 
1984297a3b0SGarrett D'Amore 		/* field Width */
1994297a3b0SGarrett D'Amore 		if (isdigit((unsigned char)*fmt)) {
2004297a3b0SGarrett D'Amore 			GET_NUMBER(width);
2014297a3b0SGarrett D'Amore 			/*
2024297a3b0SGarrett D'Amore 			 * Do we have enough space to put number with
2034297a3b0SGarrett D'Amore 			 * required width ?
2044297a3b0SGarrett D'Amore 			 */
2054297a3b0SGarrett D'Amore 			if ((unsigned int)width >= maxsize - (dst - s))
2064297a3b0SGarrett D'Amore 				goto e2big_error;
2074297a3b0SGarrett D'Amore 		}
2084297a3b0SGarrett D'Amore 
2094297a3b0SGarrett D'Amore 		/* Left precision */
2104297a3b0SGarrett D'Amore 		if (*fmt == '#') {
2114297a3b0SGarrett D'Amore 			if (!isdigit((unsigned char)*++fmt))
2124297a3b0SGarrett D'Amore 				goto format_error;
2134297a3b0SGarrett D'Amore 			GET_NUMBER(left_prec);
2144297a3b0SGarrett D'Amore 			if ((unsigned int)left_prec >= maxsize - (dst - s))
2154297a3b0SGarrett D'Amore 				goto e2big_error;
2164297a3b0SGarrett D'Amore 		}
2174297a3b0SGarrett D'Amore 
2184297a3b0SGarrett D'Amore 		/* Right precision */
2194297a3b0SGarrett D'Amore 		if (*fmt == '.') {
2204297a3b0SGarrett D'Amore 			if (!isdigit((unsigned char)*++fmt))
2214297a3b0SGarrett D'Amore 				goto format_error;
2224297a3b0SGarrett D'Amore 			GET_NUMBER(right_prec);
2234297a3b0SGarrett D'Amore 			if ((unsigned int)right_prec >= maxsize - (dst - s) -
2244297a3b0SGarrett D'Amore 			    left_prec)
2254297a3b0SGarrett D'Amore 				goto e2big_error;
2264297a3b0SGarrett D'Amore 		}
2274297a3b0SGarrett D'Amore 
2284297a3b0SGarrett D'Amore 		/* Conversion Characters */
2294297a3b0SGarrett D'Amore 		switch (*fmt++) {
2304297a3b0SGarrett D'Amore 			case 'i':	/* use internaltion currency format */
2314297a3b0SGarrett D'Amore 				flags |= USE_INTL_CURRENCY;
2324297a3b0SGarrett D'Amore 				break;
2334297a3b0SGarrett D'Amore 			case 'n':	/* use national currency format */
2344297a3b0SGarrett D'Amore 				flags &= ~(USE_INTL_CURRENCY);
2354297a3b0SGarrett D'Amore 				break;
2364297a3b0SGarrett D'Amore 			default:
2374297a3b0SGarrett D'Amore 				/* required char missing or premature EOS */
2384297a3b0SGarrett D'Amore 				goto format_error;
2394297a3b0SGarrett D'Amore 		}
2404297a3b0SGarrett D'Amore 
2414297a3b0SGarrett D'Amore 		if (flags & USE_INTL_CURRENCY) {
242*2d08521bSGarrett D'Amore 			currency_symbol = lmon->int_curr_symbol;
243*2d08521bSGarrett D'Amore 			/* by definition three letters followed by a space */
2444297a3b0SGarrett D'Amore 			if (currency_symbol != NULL)
245*2d08521bSGarrett D'Amore 				space_char = currency_symbol[3];
2464297a3b0SGarrett D'Amore 		} else
247*2d08521bSGarrett D'Amore 			currency_symbol = lmon->currency_symbol;
2484297a3b0SGarrett D'Amore 
2494297a3b0SGarrett D'Amore 		/* value itself */
2504297a3b0SGarrett D'Amore 		value = va_arg(ap, double);
2514297a3b0SGarrett D'Amore 
2524297a3b0SGarrett D'Amore 		/* detect sign */
2534297a3b0SGarrett D'Amore 		if (value < 0) {
2544297a3b0SGarrett D'Amore 			flags |= IS_NEGATIVE;
2554297a3b0SGarrett D'Amore 			value = -value;
2564297a3b0SGarrett D'Amore 		}
2574297a3b0SGarrett D'Amore 
2584297a3b0SGarrett D'Amore 		/* fill left_prec with amount of padding chars */
2594297a3b0SGarrett D'Amore 		if (left_prec >= 0) {
260*2d08521bSGarrett D'Amore 			pad_size = calc_left_pad(lmon, (flags ^ IS_NEGATIVE),
2614297a3b0SGarrett D'Amore 			    currency_symbol) -
262*2d08521bSGarrett D'Amore 			    calc_left_pad(lmon, flags, currency_symbol);
2634297a3b0SGarrett D'Amore 			if (pad_size < 0)
2644297a3b0SGarrett D'Amore 				pad_size = 0;
2654297a3b0SGarrett D'Amore 		}
2664297a3b0SGarrett D'Amore 
2674297a3b0SGarrett D'Amore 		if (asciivalue != NULL)
2684297a3b0SGarrett D'Amore 			free(asciivalue);
269*2d08521bSGarrett D'Amore 		asciivalue = format_grouped_double(lmon, lnum, value, &flags,
2704297a3b0SGarrett D'Amore 		    left_prec, right_prec, pad_char);
2714297a3b0SGarrett D'Amore 		if (asciivalue == NULL)
2724297a3b0SGarrett D'Amore 			goto end_error;		/* errno already set */
2734297a3b0SGarrett D'Amore 						/* to ENOMEM by malloc() */
2744297a3b0SGarrett D'Amore 
2754297a3b0SGarrett D'Amore 		/* set some variables for later use */
276*2d08521bSGarrett D'Amore 		setup_vars(lmon, flags, &cs_precedes, &sep_by_space,
277*2d08521bSGarrett D'Amore 		    &sign_posn, &signstr);
2784297a3b0SGarrett D'Amore 
2794297a3b0SGarrett D'Amore 		/*
2804297a3b0SGarrett D'Amore 		 * Description of some LC_MONETARY's values:
2814297a3b0SGarrett D'Amore 		 *
2824297a3b0SGarrett D'Amore 		 * p_cs_precedes & n_cs_precedes
2834297a3b0SGarrett D'Amore 		 *
2844297a3b0SGarrett D'Amore 		 * = 1 - $currency_symbol precedes the value
2854297a3b0SGarrett D'Amore 		 *	 for a monetary quantity with a non-negative value
2864297a3b0SGarrett D'Amore 		 * = 0 - symbol succeeds the value
2874297a3b0SGarrett D'Amore 		 *
2884297a3b0SGarrett D'Amore 		 * p_sep_by_space & n_sep_by_space
2894297a3b0SGarrett D'Amore 		 *
2904297a3b0SGarrett D'Amore 		 * = 0 - no space separates $currency_symbol
2914297a3b0SGarrett D'Amore 		 *	 from the value for a monetary quantity with a
2924297a3b0SGarrett D'Amore 		 *	 non-negative value
2934297a3b0SGarrett D'Amore 		 * = 1 - space separates the symbol from the value
2944297a3b0SGarrett D'Amore 		 * = 2 - space separates the symbol and the sign string,
2954297a3b0SGarrett D'Amore 		 *	 if adjacent.
2964297a3b0SGarrett D'Amore 		 *
2974297a3b0SGarrett D'Amore 		 * p_sign_posn & n_sign_posn
2984297a3b0SGarrett D'Amore 		 *
2994297a3b0SGarrett D'Amore 		 * = 0 - parentheses enclose the quantity and the
3004297a3b0SGarrett D'Amore 		 *	 $currency_symbol
3014297a3b0SGarrett D'Amore 		 * = 1 - the sign string precedes the quantity and the
3024297a3b0SGarrett D'Amore 		 *	 $currency_symbol
3034297a3b0SGarrett D'Amore 		 * = 2 - the sign string succeeds the quantity and the
3044297a3b0SGarrett D'Amore 		 *	 $currency_symbol
3054297a3b0SGarrett D'Amore 		 * = 3 - the sign string precedes the $currency_symbol
3064297a3b0SGarrett D'Amore 		 * = 4 - the sign string succeeds the $currency_symbol
3074297a3b0SGarrett D'Amore 		 *
3084297a3b0SGarrett D'Amore 		 */
3094297a3b0SGarrett D'Amore 
3104297a3b0SGarrett D'Amore 		tmpptr = dst;
3114297a3b0SGarrett D'Amore 
3124297a3b0SGarrett D'Amore 		while (pad_size-- > 0)
3134297a3b0SGarrett D'Amore 			PRINT(' ');
3144297a3b0SGarrett D'Amore 
3154297a3b0SGarrett D'Amore 		if (sign_posn == 0 && (flags & IS_NEGATIVE))
3164297a3b0SGarrett D'Amore 			PRINT('(');
3174297a3b0SGarrett D'Amore 
3184297a3b0SGarrett D'Amore 		if (cs_precedes == 1) {
3194297a3b0SGarrett D'Amore 			if (sign_posn == 1 || sign_posn == 3) {
3204297a3b0SGarrett D'Amore 				PRINTS(signstr);
321*2d08521bSGarrett D'Amore 				if (sep_by_space == 2)
3224297a3b0SGarrett D'Amore 					PRINT(' ');
3234297a3b0SGarrett D'Amore 			}
3244297a3b0SGarrett D'Amore 
3254297a3b0SGarrett D'Amore 			if (!(flags & SUPRESS_CURR_SYMBOL)) {
3264297a3b0SGarrett D'Amore 				PRINTS(currency_symbol);
3274297a3b0SGarrett D'Amore 
3284297a3b0SGarrett D'Amore 				if (sign_posn == 4) {
3294297a3b0SGarrett D'Amore 					if (sep_by_space == 2)
3304297a3b0SGarrett D'Amore 						PRINT(space_char);
3314297a3b0SGarrett D'Amore 					PRINTS(signstr);
3324297a3b0SGarrett D'Amore 					if (sep_by_space == 1)
3334297a3b0SGarrett D'Amore 						PRINT(' ');
3344297a3b0SGarrett D'Amore 				} else if (sep_by_space == 1)
3354297a3b0SGarrett D'Amore 					PRINT(space_char);
3364297a3b0SGarrett D'Amore 			}
3374297a3b0SGarrett D'Amore 		} else if (sign_posn == 1)
3384297a3b0SGarrett D'Amore 			PRINTS(signstr);
3394297a3b0SGarrett D'Amore 
3404297a3b0SGarrett D'Amore 		PRINTS(asciivalue);
3414297a3b0SGarrett D'Amore 
3424297a3b0SGarrett D'Amore 		if (cs_precedes == 0) {
3434297a3b0SGarrett D'Amore 			if (sign_posn == 3) {
3444297a3b0SGarrett D'Amore 				if (sep_by_space == 1)
3454297a3b0SGarrett D'Amore 					PRINT(' ');
3464297a3b0SGarrett D'Amore 				PRINTS(signstr);
3474297a3b0SGarrett D'Amore 			}
3484297a3b0SGarrett D'Amore 
3494297a3b0SGarrett D'Amore 			if (!(flags & SUPRESS_CURR_SYMBOL)) {
3504297a3b0SGarrett D'Amore 				if ((sign_posn == 3 && sep_by_space == 2) ||
3514297a3b0SGarrett D'Amore 				    (sep_by_space == 1 && (sign_posn == 0 ||
3524297a3b0SGarrett D'Amore 				    sign_posn == 1 || sign_posn == 2 ||
3534297a3b0SGarrett D'Amore 				    sign_posn == 4)))
3544297a3b0SGarrett D'Amore 					PRINT(space_char);
3554297a3b0SGarrett D'Amore 				PRINTS(currency_symbol); /* XXX: len */
3564297a3b0SGarrett D'Amore 				if (sign_posn == 4) {
3574297a3b0SGarrett D'Amore 					if (sep_by_space == 2)
3584297a3b0SGarrett D'Amore 						PRINT(' ');
3594297a3b0SGarrett D'Amore 					PRINTS(signstr);
3604297a3b0SGarrett D'Amore 				}
3614297a3b0SGarrett D'Amore 			}
3624297a3b0SGarrett D'Amore 		}
3634297a3b0SGarrett D'Amore 
3644297a3b0SGarrett D'Amore 		if (sign_posn == 2) {
3654297a3b0SGarrett D'Amore 			if (sep_by_space == 2)
3664297a3b0SGarrett D'Amore 				PRINT(' ');
3674297a3b0SGarrett D'Amore 			PRINTS(signstr);
3684297a3b0SGarrett D'Amore 		}
3694297a3b0SGarrett D'Amore 
3704297a3b0SGarrett D'Amore 		if (sign_posn == 0 && (flags & IS_NEGATIVE))
3714297a3b0SGarrett D'Amore 			PRINT(')');
3724297a3b0SGarrett D'Amore 
3734297a3b0SGarrett D'Amore 		if (dst - tmpptr < width) {
3744297a3b0SGarrett D'Amore 			if (flags & LEFT_JUSTIFY) {
3754297a3b0SGarrett D'Amore 				while (dst - tmpptr < width)
3764297a3b0SGarrett D'Amore 					PRINT(' ');
3774297a3b0SGarrett D'Amore 			} else {
3784297a3b0SGarrett D'Amore 				pad_size = dst-tmpptr;
379eda71b4aSGarrett D'Amore 				(void) memmove(tmpptr + width-pad_size, tmpptr,
3804297a3b0SGarrett D'Amore 				    pad_size);
3814297a3b0SGarrett D'Amore 				(void) memset(tmpptr, ' ', width-pad_size);
3824297a3b0SGarrett D'Amore 				dst += width-pad_size;
3834297a3b0SGarrett D'Amore 			}
3844297a3b0SGarrett D'Amore 		}
3854297a3b0SGarrett D'Amore 	}
3864297a3b0SGarrett D'Amore 
3874297a3b0SGarrett D'Amore 	PRINT('\0');
3884297a3b0SGarrett D'Amore 	free(asciivalue);
3894297a3b0SGarrett D'Amore 	return (dst - s - 1);	/* size of put data except trailing '\0' */
3904297a3b0SGarrett D'Amore 
3914297a3b0SGarrett D'Amore e2big_error:
3924297a3b0SGarrett D'Amore 	errno = E2BIG;
3934297a3b0SGarrett D'Amore 	goto end_error;
3944297a3b0SGarrett D'Amore 
3954297a3b0SGarrett D'Amore format_error:
3964297a3b0SGarrett D'Amore 	errno = EINVAL;
3974297a3b0SGarrett D'Amore 
3984297a3b0SGarrett D'Amore end_error:
3994297a3b0SGarrett D'Amore 	sverrno = errno;
4004297a3b0SGarrett D'Amore 	if (asciivalue != NULL)
4014297a3b0SGarrett D'Amore 		free(asciivalue);
4024297a3b0SGarrett D'Amore 	errno = sverrno;
4034297a3b0SGarrett D'Amore 	return (-1);
4044297a3b0SGarrett D'Amore }
4054297a3b0SGarrett D'Amore 
406*2d08521bSGarrett D'Amore ssize_t
strfmon(char * _RESTRICT_KYWD s,size_t maxsize,const char * _RESTRICT_KYWD format,...)407*2d08521bSGarrett D'Amore strfmon(char *_RESTRICT_KYWD s, size_t maxsize,
408*2d08521bSGarrett D'Amore     const char *_RESTRICT_KYWD format, ...)
4094297a3b0SGarrett D'Amore {
410*2d08521bSGarrett D'Amore 	va_list ap;
411*2d08521bSGarrett D'Amore 	ssize_t ret;
4124297a3b0SGarrett D'Amore 
413*2d08521bSGarrett D'Amore 	va_start(ap, format);
414*2d08521bSGarrett D'Amore 	ret = strfmon_impl(s, maxsize, uselocale(NULL), format, ap);
415*2d08521bSGarrett D'Amore 	va_end(ap);
416*2d08521bSGarrett D'Amore 	return (ret);
4174297a3b0SGarrett D'Amore }
4184297a3b0SGarrett D'Amore 
419*2d08521bSGarrett D'Amore ssize_t
strfmon_l(char * _RESTRICT_KYWD s,size_t maxsize,locale_t loc,const char * _RESTRICT_KYWD format,...)420*2d08521bSGarrett D'Amore strfmon_l(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
421*2d08521bSGarrett D'Amore     const char *_RESTRICT_KYWD format, ...)
422*2d08521bSGarrett D'Amore {
423*2d08521bSGarrett D'Amore 	ssize_t ret;
424*2d08521bSGarrett D'Amore 	va_list ap;
425*2d08521bSGarrett D'Amore 	va_start(ap, format);
426*2d08521bSGarrett D'Amore 	ret = strfmon_impl(s, maxsize, loc, format, ap);
427*2d08521bSGarrett D'Amore 	va_end(ap);
428*2d08521bSGarrett D'Amore 	return (ret);
429*2d08521bSGarrett D'Amore }
430*2d08521bSGarrett D'Amore 
431*2d08521bSGarrett D'Amore static void
setup_vars(const struct lc_monetary * lmon,int flags,char * cs_precedes,char * sep_by_space,char * sign_posn,const char ** signstr)432*2d08521bSGarrett D'Amore setup_vars(const struct lc_monetary *lmon, int flags, char *cs_precedes,
433*2d08521bSGarrett D'Amore     char *sep_by_space, char *sign_posn, const char **signstr)
434*2d08521bSGarrett D'Amore {
435*2d08521bSGarrett D'Amore 	if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
436*2d08521bSGarrett D'Amore 		*cs_precedes = lmon->int_n_cs_precedes[0];
437*2d08521bSGarrett D'Amore 		*sep_by_space = lmon->int_n_sep_by_space[0];
438*2d08521bSGarrett D'Amore 		*sign_posn = (flags & PARENTH_POSN) ? 0 :
439*2d08521bSGarrett D'Amore 		    lmon->int_n_sign_posn[0];
440*2d08521bSGarrett D'Amore 		*signstr = (lmon->negative_sign[0] == '\0') ? "-" :
441*2d08521bSGarrett D'Amore 		    lmon->negative_sign;
442*2d08521bSGarrett D'Amore 	} else if (flags & USE_INTL_CURRENCY) {
443*2d08521bSGarrett D'Amore 		*cs_precedes = lmon->int_p_cs_precedes[0];
444*2d08521bSGarrett D'Amore 		*sep_by_space = lmon->int_p_sep_by_space[0];
445*2d08521bSGarrett D'Amore 		*sign_posn = (flags & PARENTH_POSN) ? 0 :
446*2d08521bSGarrett D'Amore 		    lmon->int_p_sign_posn[0];
447*2d08521bSGarrett D'Amore 		*signstr = lmon->positive_sign;
448*2d08521bSGarrett D'Amore 	} else if (flags & IS_NEGATIVE) {
449*2d08521bSGarrett D'Amore 		*cs_precedes = lmon->n_cs_precedes[0];
450*2d08521bSGarrett D'Amore 		*sep_by_space = lmon->n_sep_by_space[0];
451*2d08521bSGarrett D'Amore 		*sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->n_sign_posn[0];
452*2d08521bSGarrett D'Amore 		*signstr = (lmon->negative_sign[0] == '\0') ? "-" :
453*2d08521bSGarrett D'Amore 		    lmon->negative_sign;
454*2d08521bSGarrett D'Amore 	} else {
455*2d08521bSGarrett D'Amore 		*cs_precedes = lmon->p_cs_precedes[0];
456*2d08521bSGarrett D'Amore 		*sep_by_space = lmon->p_sep_by_space[0];
457*2d08521bSGarrett D'Amore 		*sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->p_sign_posn[0];
458*2d08521bSGarrett D'Amore 		*signstr = lmon->positive_sign;
459*2d08521bSGarrett D'Amore 	}
460*2d08521bSGarrett D'Amore 
461*2d08521bSGarrett D'Amore 	/* Set default values for unspecified information. */
4624297a3b0SGarrett D'Amore 	if (*cs_precedes != 0)
4634297a3b0SGarrett D'Amore 		*cs_precedes = 1;
4644297a3b0SGarrett D'Amore 	if (*sep_by_space == CHAR_MAX)
4654297a3b0SGarrett D'Amore 		*sep_by_space = 0;
4664297a3b0SGarrett D'Amore 	if (*sign_posn == CHAR_MAX)
4674297a3b0SGarrett D'Amore 		*sign_posn = 0;
4684297a3b0SGarrett D'Amore }
4694297a3b0SGarrett D'Amore 
4704297a3b0SGarrett D'Amore static int
calc_left_pad(const struct lc_monetary * lmon,int flags,const char * cur_symb)471*2d08521bSGarrett D'Amore calc_left_pad(const struct lc_monetary *lmon, int flags, const char *cur_symb)
4724297a3b0SGarrett D'Amore {
473*2d08521bSGarrett D'Amore 	char cs_precedes, sep_by_space, sign_posn;
474*2d08521bSGarrett D'Amore 	const char *signstr;
4754297a3b0SGarrett D'Amore 	int left_chars = 0;
4764297a3b0SGarrett D'Amore 
477*2d08521bSGarrett D'Amore 	setup_vars(lmon, flags, &cs_precedes, &sep_by_space, &sign_posn,
478*2d08521bSGarrett D'Amore 	    &signstr);
4794297a3b0SGarrett D'Amore 
4804297a3b0SGarrett D'Amore 	if (cs_precedes != 0) {
4814297a3b0SGarrett D'Amore 		left_chars += strlen(cur_symb);
4824297a3b0SGarrett D'Amore 		if (sep_by_space != 0)
4834297a3b0SGarrett D'Amore 			left_chars++;
4844297a3b0SGarrett D'Amore 	}
4854297a3b0SGarrett D'Amore 
4864297a3b0SGarrett D'Amore 	switch (sign_posn) {
4874297a3b0SGarrett D'Amore 		case 1:
4884297a3b0SGarrett D'Amore 			left_chars += strlen(signstr);
4894297a3b0SGarrett D'Amore 			break;
4904297a3b0SGarrett D'Amore 		case 3:
4914297a3b0SGarrett D'Amore 		case 4:
4924297a3b0SGarrett D'Amore 			if (cs_precedes != 0)
4934297a3b0SGarrett D'Amore 				left_chars += strlen(signstr);
4944297a3b0SGarrett D'Amore 	}
4954297a3b0SGarrett D'Amore 	return (left_chars);
4964297a3b0SGarrett D'Amore }
4974297a3b0SGarrett D'Amore 
4984297a3b0SGarrett D'Amore static int
get_groups(int size,const char * grouping)499*2d08521bSGarrett D'Amore get_groups(int size, const char *grouping)
5004297a3b0SGarrett D'Amore {
5014297a3b0SGarrett D'Amore 
5024297a3b0SGarrett D'Amore 	int	chars = 0;
5034297a3b0SGarrett D'Amore 
5044297a3b0SGarrett D'Amore 	if (*grouping == CHAR_MAX || *grouping <= 0)	/* no grouping ? */
5054297a3b0SGarrett D'Amore 		return (0);
5064297a3b0SGarrett D'Amore 
5074297a3b0SGarrett D'Amore 	while (size > (int)*grouping) {
5084297a3b0SGarrett D'Amore 		chars++;
5094297a3b0SGarrett D'Amore 		size -= (int)*grouping++;
5104297a3b0SGarrett D'Amore 		/* no more grouping ? */
5114297a3b0SGarrett D'Amore 		if (*grouping == CHAR_MAX)
5124297a3b0SGarrett D'Amore 			break;
5134297a3b0SGarrett D'Amore 		/* rest grouping with same value ? */
5144297a3b0SGarrett D'Amore 		if (*grouping == 0) {
5154297a3b0SGarrett D'Amore 			chars += (size - 1) / *(grouping - 1);
5164297a3b0SGarrett D'Amore 			break;
5174297a3b0SGarrett D'Amore 		}
5184297a3b0SGarrett D'Amore 	}
5194297a3b0SGarrett D'Amore 	return (chars);
5204297a3b0SGarrett D'Amore }
5214297a3b0SGarrett D'Amore 
5224297a3b0SGarrett D'Amore /* convert double to ASCII */
5234297a3b0SGarrett D'Amore static 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)524*2d08521bSGarrett D'Amore format_grouped_double(const struct lc_monetary *lmon,
525*2d08521bSGarrett D'Amore     const struct lc_numeric *lnum,
526*2d08521bSGarrett D'Amore     double value, int *flags, int left_prec, int right_prec, int pad_char)
5274297a3b0SGarrett D'Amore {
5284297a3b0SGarrett D'Amore 
5294297a3b0SGarrett D'Amore 	char		*rslt;
5304297a3b0SGarrett D'Amore 	char		*avalue;
5314297a3b0SGarrett D'Amore 	int		avalue_size;
5324297a3b0SGarrett D'Amore 	char		fmt[32];
5334297a3b0SGarrett D'Amore 
5344297a3b0SGarrett D'Amore 	size_t		bufsize;
5354297a3b0SGarrett D'Amore 	char		*bufend;
5364297a3b0SGarrett D'Amore 
5374297a3b0SGarrett D'Amore 	int		padded;
5384297a3b0SGarrett D'Amore 
539*2d08521bSGarrett D'Amore 	const char	*grouping;
540*2d08521bSGarrett D'Amore 	const char	*decimal_point;
541*2d08521bSGarrett D'Amore 	const char	*thousands_sep;
542*2d08521bSGarrett D'Amore 	int		decimal_len;
543*2d08521bSGarrett D'Amore 	int		thousands_len;
5444297a3b0SGarrett D'Amore 
5454297a3b0SGarrett D'Amore 	int groups = 0;
5464297a3b0SGarrett D'Amore 
547*2d08521bSGarrett D'Amore 	grouping = lmon->mon_grouping;
548*2d08521bSGarrett D'Amore 	decimal_point = lmon->mon_decimal_point;
549*2d08521bSGarrett D'Amore 	if (*decimal_point == '\0')
550*2d08521bSGarrett D'Amore 		decimal_point = lnum->decimal_point;
551*2d08521bSGarrett D'Amore 	thousands_sep = lmon->mon_thousands_sep;
552*2d08521bSGarrett D'Amore 	if (*thousands_sep == '\0')
553*2d08521bSGarrett D'Amore 		thousands_sep = lnum->thousands_sep;
554*2d08521bSGarrett D'Amore 
555*2d08521bSGarrett D'Amore 	decimal_len = strlen(decimal_point);	/* usually 1 */
556*2d08521bSGarrett D'Amore 	thousands_len = strlen(thousands_sep);	/* 0 or 1 usually */
5574297a3b0SGarrett D'Amore 
5584297a3b0SGarrett D'Amore 	/* fill left_prec with default value */
5594297a3b0SGarrett D'Amore 	if (left_prec == -1)
5604297a3b0SGarrett D'Amore 		left_prec = 0;
5614297a3b0SGarrett D'Amore 
5624297a3b0SGarrett D'Amore 	/* fill right_prec with default value */
5634297a3b0SGarrett D'Amore 	if (right_prec == -1) {
5644297a3b0SGarrett D'Amore 		if (*flags & USE_INTL_CURRENCY)
565*2d08521bSGarrett D'Amore 			right_prec = lmon->int_frac_digits[0];
5664297a3b0SGarrett D'Amore 		else
567*2d08521bSGarrett D'Amore 			right_prec = lmon->frac_digits[0];
5684297a3b0SGarrett D'Amore 
5694297a3b0SGarrett D'Amore 		if (right_prec == CHAR_MAX)	/* POSIX locale ? */
5704297a3b0SGarrett D'Amore 			right_prec = 2;
5714297a3b0SGarrett D'Amore 	}
5724297a3b0SGarrett D'Amore 
5734297a3b0SGarrett D'Amore 	if (*flags & NEED_GROUPING)
5744297a3b0SGarrett D'Amore 		left_prec += get_groups(left_prec, grouping);
5754297a3b0SGarrett D'Amore 
5764297a3b0SGarrett D'Amore 	/* convert to string */
5774297a3b0SGarrett D'Amore 	(void) snprintf(fmt, sizeof (fmt), "%%%d.%df",
5784297a3b0SGarrett D'Amore 	    left_prec + right_prec + 1, right_prec);
5794297a3b0SGarrett D'Amore 	avalue_size = asprintf(&avalue, fmt, value);
5804297a3b0SGarrett D'Amore 	if (avalue_size < 0)
5814297a3b0SGarrett D'Amore 		return (NULL);
5824297a3b0SGarrett D'Amore 
583*2d08521bSGarrett D'Amore 	/*
584*2d08521bSGarrett D'Amore 	 * Make sure that we've enough space for result string.
585*2d08521bSGarrett D'Amore 	 * This assumes that digits take up at least much space as
586*2d08521bSGarrett D'Amore 	 * grouping and radix characters.  The worst case currently known
587*2d08521bSGarrett D'Amore 	 * is for Arabic, where two-byte UTF-8 sequences are used for both
588*2d08521bSGarrett D'Amore 	 * decimal and thousands seperators, and groups can be a small as two
589*2d08521bSGarrett D'Amore 	 * decimal digits.  This will do no worse than doubling the storage
590*2d08521bSGarrett D'Amore 	 * requirement.
591*2d08521bSGarrett D'Amore 	 */
5924297a3b0SGarrett D'Amore 	bufsize = strlen(avalue)*2+1;
5934297a3b0SGarrett D'Amore 	rslt = calloc(1, bufsize);
5944297a3b0SGarrett D'Amore 	if (rslt == NULL) {
5954297a3b0SGarrett D'Amore 		free(avalue);
5964297a3b0SGarrett D'Amore 		return (NULL);
5974297a3b0SGarrett D'Amore 	}
5984297a3b0SGarrett D'Amore 	bufend = rslt + bufsize - 1;	/* reserve space for trailing '\0' */
5994297a3b0SGarrett D'Amore 
600*2d08521bSGarrett D'Amore 	/* skip spaces at beginning */
6014297a3b0SGarrett D'Amore 	padded = 0;
6024297a3b0SGarrett D'Amore 	while (avalue[padded] == ' ') {
6034297a3b0SGarrett D'Amore 		padded++;
6044297a3b0SGarrett D'Amore 		avalue_size--;
6054297a3b0SGarrett D'Amore 	}
6064297a3b0SGarrett D'Amore 
6074297a3b0SGarrett D'Amore 	if (right_prec > 0) {
6084297a3b0SGarrett D'Amore 		bufend -= right_prec;
6094297a3b0SGarrett D'Amore 		(void) memcpy(bufend, avalue + avalue_size+padded-right_prec,
6104297a3b0SGarrett D'Amore 		    right_prec);
611*2d08521bSGarrett D'Amore 		bufend -= decimal_len;
612*2d08521bSGarrett D'Amore 		(void) memcpy(bufend, decimal_point, decimal_len);
613*2d08521bSGarrett D'Amore 		avalue_size -= (right_prec + decimal_len);
6144297a3b0SGarrett D'Amore 	}
6154297a3b0SGarrett D'Amore 
6164297a3b0SGarrett D'Amore 	if ((*flags & NEED_GROUPING) &&
617*2d08521bSGarrett D'Amore 	    thousands_len != 0 &&
6184297a3b0SGarrett D'Amore 	    *grouping != CHAR_MAX &&
6194297a3b0SGarrett D'Amore 	    *grouping > 0) {
6204297a3b0SGarrett D'Amore 		while (avalue_size > (int)*grouping) {
6214297a3b0SGarrett D'Amore 			GRPCPY(*grouping);
6224297a3b0SGarrett D'Amore 			GRPSEP;
6234297a3b0SGarrett D'Amore 			grouping++;
6244297a3b0SGarrett D'Amore 
6254297a3b0SGarrett D'Amore 			/* no more grouping ? */
6264297a3b0SGarrett D'Amore 			if (*grouping == CHAR_MAX)
6274297a3b0SGarrett D'Amore 				break;
6284297a3b0SGarrett D'Amore 
6294297a3b0SGarrett D'Amore 			/* rest grouping with same value ? */
6304297a3b0SGarrett D'Amore 			if (*grouping == 0) {
6314297a3b0SGarrett D'Amore 				grouping--;
6324297a3b0SGarrett D'Amore 				while (avalue_size > *grouping) {
6334297a3b0SGarrett D'Amore 					GRPCPY(*grouping);
6344297a3b0SGarrett D'Amore 					GRPSEP;
6354297a3b0SGarrett D'Amore 				}
6364297a3b0SGarrett D'Amore 			}
6374297a3b0SGarrett D'Amore 		}
6384297a3b0SGarrett D'Amore 		if (avalue_size != 0)
6394297a3b0SGarrett D'Amore 			GRPCPY(avalue_size);
6404297a3b0SGarrett D'Amore 		padded -= groups;
6414297a3b0SGarrett D'Amore 
6424297a3b0SGarrett D'Amore 	} else {
6434297a3b0SGarrett D'Amore 		bufend -= avalue_size;
6444297a3b0SGarrett D'Amore 		(void) memcpy(bufend, avalue+padded, avalue_size);
6454297a3b0SGarrett D'Amore 		if (right_prec == 0)
6464297a3b0SGarrett D'Amore 			padded--;	/* decrease assumed $decimal_point */
6474297a3b0SGarrett D'Amore 	}
6484297a3b0SGarrett D'Amore 
6494297a3b0SGarrett D'Amore 	/* do padding with pad_char */
6504297a3b0SGarrett D'Amore 	if (padded > 0) {
6514297a3b0SGarrett D'Amore 		bufend -= padded;
6524297a3b0SGarrett D'Amore 		(void) memset(bufend, pad_char, padded);
6534297a3b0SGarrett D'Amore 	}
6544297a3b0SGarrett D'Amore 
6554297a3b0SGarrett D'Amore 	bufsize = bufsize - (bufend - rslt) + 1;
656eda71b4aSGarrett D'Amore 	(void) memmove(rslt, bufend, bufsize);
6574297a3b0SGarrett D'Amore 	free(avalue);
6584297a3b0SGarrett D'Amore 	return (rslt);
6594297a3b0SGarrett D'Amore }
660