aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/stdlib/strfmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/stdlib/strfmon.c')
-rw-r--r--lib/libc/stdlib/strfmon.c379
1 files changed, 197 insertions, 182 deletions
diff --git a/lib/libc/stdlib/strfmon.c b/lib/libc/stdlib/strfmon.c
index d646ec0b001a..68a36a6d5567 100644
--- a/lib/libc/stdlib/strfmon.c
+++ b/lib/libc/stdlib/strfmon.c
@@ -1,11 +1,11 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
* All rights reserved.
*
* Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
+ *
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
@@ -32,10 +32,8 @@
*
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/types.h>
+
#include <ctype.h>
#include <errno.h>
#include <limits.h>
@@ -45,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
#include "xlocale_private.h"
/* internal flags */
@@ -52,27 +51,27 @@ __FBSDID("$FreeBSD$");
#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */
#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
#define PARENTH_POSN 0x08 /* enclose negative amount in () */
-#define SUPRESS_CURR_SYMBOL 0x10 /* suppress the currency from output */
+#define SUPPRESS_CURR_SYMBOL 0x10 /* suppress the currency from output */
#define LEFT_JUSTIFY 0x20 /* left justify */
#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */
-#define IS_NEGATIVE 0x80 /* is argument value negative ? */
+#define IS_NEGATIVE 0x80 /* is argument value negative ? */
/* internal macros */
-#define PRINT(CH) do { \
- if (dst >= s + maxsize) \
+#define PRINT(CH) do { \
+ if (dst >= s + maxsize) \
goto e2big_error; \
*dst++ = CH; \
} while (0)
-#define PRINTS(STR) do { \
+#define PRINTS(STR) do { \
char *tmps = STR; \
while (*tmps != '\0') \
PRINT(*tmps++); \
} while (0)
-#define GET_NUMBER(VAR) do { \
+#define GET_NUMBER(VAR, LOC) do { \
VAR = 0; \
- while (isdigit((unsigned char)*fmt)) { \
+ while (isdigit_l((unsigned char)*fmt, LOC)) { \
if (VAR > INT_MAX / 10) \
goto e2big_error; \
VAR *= 10; \
@@ -83,41 +82,42 @@ __FBSDID("$FreeBSD$");
} \
} while (0)
-#define GRPCPY(howmany) do { \
+#define GRPCPY(howmany) do { \
int i = howmany; \
while (i-- > 0) { \
avalue_size--; \
- *--bufend = *(avalue+avalue_size+padded); \
+ *--bufend = *(avalue + avalue_size + padded); \
} \
} while (0)
-#define GRPSEP do { \
+#define GRPSEP do { \
bufend -= thousands_sep_size; \
memcpy(bufend, thousands_sep, thousands_sep_size); \
groups++; \
} while (0)
-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(int, char *, char *, char *, char **, struct lconv *);
+static int __calc_left_pad(int, char *, struct lconv *);
+static char *__format_grouped_double(double, int *, int, int, int,
+ struct lconv *, locale_t);
static ssize_t
-vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
- const char * __restrict format, va_list ap)
+vstrfmon_l(char *__restrict s, size_t maxsize, locale_t loc,
+ const char *__restrict format, va_list ap)
{
- char *dst; /* output destination pointer */
- const char *fmt; /* current format poistion pointer */
- struct lconv *lc; /* pointer to lconv structure */
+ 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 */
- int pad_char; /* padding character */
- int pad_size; /* pad size */
- int width; /* field width */
- int left_prec; /* left precision */
- int right_prec; /* right precision */
- double value; /* just value */
- char space_char = ' '; /* space after currency */
+ int flags; /* formatting options */
+ int pad_char; /* padding character */
+ int pad_size; /* pad size */
+ int width; /* field width */
+ int left_prec; /* left precision */
+ int right_prec; /* right precision */
+ double value; /* just value */
+ char space_char = ' '; /* space after currency */
char cs_precedes, /* values gathered from struct lconv */
sep_by_space,
@@ -127,17 +127,16 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
char *tmpptr; /* temporary vars */
int sverrno;
- FIX_LOCALE(loc);
+ FIX_LOCALE(loc);
lc = localeconv_l(loc);
dst = s;
fmt = format;
asciivalue = NULL;
currency_symbol = NULL;
- pad_size = 0;
- while (*fmt) {
+ while (*fmt != 0) {
/* pass nonformating characters AS IS */
if (*fmt != '%')
goto literal;
@@ -145,58 +144,59 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
/* '%' found ! */
/* "%%" mean just '%' */
- if (*(fmt+1) == '%') {
+ if (*(fmt + 1) == '%') {
fmt++;
- literal:
+literal:
PRINT(*fmt++);
continue;
}
/* set up initial values */
- flags = (NEED_GROUPING|LOCALE_POSN);
+ flags = NEED_GROUPING | LOCALE_POSN;
pad_char = ' '; /* padding character is "space" */
+ pad_size = 0; /* no padding initially */
left_prec = -1; /* no left precision specified */
right_prec = -1; /* no right precision specified */
width = -1; /* no width specified */
- value = 0; /* we have no value to print now */
/* Flags */
while (1) {
switch (*++fmt) {
- case '=': /* fill character */
- pad_char = *++fmt;
- if (pad_char == '\0')
- goto format_error;
- continue;
- case '^': /* not group currency */
- flags &= ~(NEED_GROUPING);
- continue;
- case '+': /* use locale defined signs */
- if (flags & SIGN_POSN_USED)
- goto format_error;
- flags |= (SIGN_POSN_USED|LOCALE_POSN);
- continue;
- case '(': /* enclose negatives with () */
- if (flags & SIGN_POSN_USED)
- goto format_error;
- flags |= (SIGN_POSN_USED|PARENTH_POSN);
- continue;
- case '!': /* suppress currency symbol */
- flags |= SUPRESS_CURR_SYMBOL;
- continue;
- case '-': /* alignment (left) */
- flags |= LEFT_JUSTIFY;
- continue;
- default:
- break;
+ case '=': /* fill character */
+ pad_char = *++fmt;
+ if (pad_char == '\0')
+ goto format_error;
+ continue;
+ case '^': /* not group currency */
+ flags &= ~(NEED_GROUPING);
+ continue;
+ case '+': /* use locale defined signs */
+ if (flags & SIGN_POSN_USED)
+ goto format_error;
+ flags |= (SIGN_POSN_USED | LOCALE_POSN);
+ continue;
+ case '(': /* enclose negatives with () */
+ if (flags & SIGN_POSN_USED)
+ goto format_error;
+ flags |= (SIGN_POSN_USED | PARENTH_POSN);
+ continue;
+ case '!': /* suppress currency symbol */
+ flags |= SUPPRESS_CURR_SYMBOL;
+ continue;
+ case '-': /* alignment (left) */
+ flags |= LEFT_JUSTIFY;
+ continue;
+ default:
+ break;
}
break;
}
/* field Width */
- if (isdigit((unsigned char)*fmt)) {
- GET_NUMBER(width);
- /* Do we have enough space to put number with
+ if (isdigit_l((unsigned char)*fmt, loc)) {
+ GET_NUMBER(width, loc);
+ /*
+ * Do we have enough space to put number with
* required width ?
*/
if ((unsigned int)width >= maxsize - (dst - s))
@@ -205,47 +205,52 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
/* Left precision */
if (*fmt == '#') {
- if (!isdigit((unsigned char)*++fmt))
+ if (!isdigit_l((unsigned char)*++fmt, loc))
goto format_error;
- GET_NUMBER(left_prec);
+ GET_NUMBER(left_prec, loc);
if ((unsigned int)left_prec >= maxsize - (dst - s))
goto e2big_error;
}
/* Right precision */
if (*fmt == '.') {
- if (!isdigit((unsigned char)*++fmt))
+ if (!isdigit_l((unsigned char)*++fmt, loc))
goto format_error;
- GET_NUMBER(right_prec);
- if ((unsigned int)right_prec >= maxsize - (dst - s) -
- left_prec)
+ GET_NUMBER(right_prec, loc);
+ if ((unsigned int)right_prec >=
+ maxsize - (dst - s) - left_prec)
goto e2big_error;
}
/* Conversion Characters */
switch (*fmt++) {
- case 'i': /* use internaltion currency format */
- flags |= USE_INTL_CURRENCY;
- break;
- case 'n': /* use national currency format */
- flags &= ~(USE_INTL_CURRENCY);
- break;
- default: /* required character is missing or
- premature EOS */
- goto format_error;
+ case 'i': /* use international currency format */
+ flags |= USE_INTL_CURRENCY;
+ break;
+ case 'n': /* use national currency format */
+ flags &= ~(USE_INTL_CURRENCY);
+ break;
+ default: /*
+ * required character is missing or
+ * premature EOS
+ */
+ goto format_error;
}
if (currency_symbol != NULL)
free(currency_symbol);
if (flags & USE_INTL_CURRENCY) {
currency_symbol = strdup(lc->int_curr_symbol);
- if (currency_symbol != NULL)
- space_char = *(currency_symbol+3);
+ if (currency_symbol != NULL &&
+ strlen(currency_symbol) > 3) {
+ space_char = currency_symbol[3];
+ currency_symbol[3] = '\0';
+ }
} else
currency_symbol = strdup(lc->currency_symbol);
if (currency_symbol == NULL)
- goto end_error; /* ENOMEM. */
+ goto end_error; /* ENOMEM. */
/* value itself */
value = va_arg(ap, double);
@@ -259,23 +264,25 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
/* fill left_prec with amount of padding chars */
if (left_prec >= 0) {
pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
- currency_symbol) -
- __calc_left_pad(flags, currency_symbol);
+ currency_symbol, lc) -
+ __calc_left_pad(flags, currency_symbol, lc);
if (pad_size < 0)
pad_size = 0;
}
if (asciivalue != NULL)
free(asciivalue);
- asciivalue = __format_grouped_double(value, &flags,
- left_prec, right_prec, pad_char);
+ asciivalue = __format_grouped_double(value, &flags, left_prec,
+ right_prec, pad_char, lc, loc);
if (asciivalue == NULL)
- goto end_error; /* errno already set */
- /* to ENOMEM by malloc() */
+ goto end_error; /*
+ * errno already set to ENOMEM by
+ * malloc()
+ */
/* set some variables for later use */
- __setup_vars(flags, &cs_precedes, &sep_by_space,
- &sign_posn, &signstr);
+ __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn,
+ &signstr, lc);
/*
* Description of some LC_MONETARY's values:
@@ -287,25 +294,25 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
* = 0 - symbol succeeds the value
*
* p_sep_by_space & n_sep_by_space
- *
+ *
* = 0 - no space separates $currency_symbol
* from the value for a monetary quantity with a
- * non-negative value
+ * non-negative value
* = 1 - space separates the symbol from the value
* = 2 - space separates the symbol and the sign string,
- * if adjacent.
- *
+ * if adjacent; otherwise, a space separates
+ * the sign string from the value
+ *
* p_sign_posn & n_sign_posn
- *
+ *
* = 0 - parentheses enclose the quantity and the
- * $currency_symbol
- * = 1 - the sign string precedes the quantity and the
* $currency_symbol
- * = 2 - the sign string succeeds the quantity and the
+ * = 1 - the sign string precedes the quantity and the
+ * $currency_symbol
+ * = 2 - the sign string succeeds the quantity and the
* $currency_symbol
* = 3 - the sign string precedes the $currency_symbol
* = 4 - the sign string succeeds the $currency_symbol
- *
*/
tmpptr = dst;
@@ -319,11 +326,11 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
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(' ');
}
- if (!(flags & SUPRESS_CURR_SYMBOL)) {
+ if (!(flags & SUPPRESS_CURR_SYMBOL)) {
PRINTS(currency_symbol);
if (sign_posn == 4) {
@@ -335,8 +342,11 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
} else if (sep_by_space == 1)
PRINT(space_char);
}
- } else if (sign_posn == 1)
+ } else if (sign_posn == 1) {
PRINTS(signstr);
+ if (sep_by_space == 2)
+ PRINT(' ');
+ }
PRINTS(asciivalue);
@@ -347,15 +357,13 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
PRINTS(signstr);
}
- if (!(flags & SUPRESS_CURR_SYMBOL)) {
- if ((sign_posn == 3 && sep_by_space == 2)
- || (sep_by_space == 1
- && (sign_posn == 0
- || sign_posn == 1
- || sign_posn == 2
- || sign_posn == 4)))
+ if (!(flags & SUPPRESS_CURR_SYMBOL)) {
+ if ((sign_posn == 3 && sep_by_space == 2) ||
+ (sep_by_space == 1 &&
+ (sign_posn == 0 || sign_posn == 1 ||
+ sign_posn == 2 || sign_posn == 4)))
PRINT(space_char);
- PRINTS(currency_symbol); /* XXX: len */
+ PRINTS(currency_symbol);
if (sign_posn == 4) {
if (sep_by_space == 2)
PRINT(' ');
@@ -370,19 +378,23 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
PRINTS(signstr);
}
- if (sign_posn == 0 && (flags & IS_NEGATIVE))
- PRINT(')');
+ if (sign_posn == 0) {
+ if (flags & IS_NEGATIVE)
+ PRINT(')');
+ else if (left_prec >= 0)
+ PRINT(' ');
+ }
if (dst - tmpptr < width) {
if (flags & LEFT_JUSTIFY) {
while (dst - tmpptr < width)
PRINT(' ');
} else {
- pad_size = dst-tmpptr;
- memmove(tmpptr + width-pad_size, tmpptr,
+ pad_size = dst - tmpptr;
+ memmove(tmpptr + width - pad_size, tmpptr,
pad_size);
- memset(tmpptr, ' ', width-pad_size);
- dst += width-pad_size;
+ memset(tmpptr, ' ', width - pad_size);
+ dst += width - pad_size;
}
}
}
@@ -390,7 +402,7 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc,
PRINT('\0');
free(asciivalue);
free(currency_symbol);
- return (dst - s - 1); /* return size of put data except trailing '\0' */
+ return (dst - s - 1); /* size of put data except trailing '\0' */
e2big_error:
errno = E2BIG;
@@ -408,43 +420,17 @@ end_error:
errno = sverrno;
return (-1);
}
-ssize_t
-strfmon_l(char * __restrict s, size_t maxsize, locale_t loc, const char * __restrict format,
- ...)
-{
- size_t ret;
- va_list ap;
- va_start(ap, format);
- ret = vstrfmon_l(s, maxsize, loc, format, ap);
- va_end(ap);
- return ret;
-}
-
-ssize_t
-strfmon(char * __restrict s, size_t maxsize, const char * __restrict format,
- ...)
-{
- size_t ret;
- va_list ap;
- va_start(ap, format);
- ret = vstrfmon_l(s, maxsize, __get_locale(), format, ap);
- va_end(ap);
- return ret;
-}
-
static void
-__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
- char *sign_posn, char **signstr) {
-
- struct lconv *lc = localeconv();
-
+__setup_vars(int flags, char *cs_precedes, char *sep_by_space, char *sign_posn,
+ char **signstr, struct lconv *lc)
+{
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;
+ *signstr = (lc->negative_sign[0] == '\0') ? "-" :
+ lc->negative_sign;
} else if (flags & USE_INTL_CURRENCY) {
*cs_precedes = lc->int_p_cs_precedes;
*sep_by_space = lc->int_p_sep_by_space;
@@ -454,8 +440,8 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space,
*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;
+ *signstr = (lc->negative_sign[0] == '\0') ? "-" :
+ lc->negative_sign;
} else {
*cs_precedes = lc->p_cs_precedes;
*sep_by_space = lc->p_sep_by_space;
@@ -463,7 +449,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space,
*signstr = lc->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)
@@ -473,12 +459,13 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space,
}
static int
-__calc_left_pad(int flags, char *cur_symb) {
-
+__calc_left_pad(int flags, char *cur_symb, struct lconv *lc)
+{
char cs_precedes, sep_by_space, sign_posn, *signstr;
int left_chars = 0;
- __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
+ __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr,
+ lc);
if (cs_precedes != 0) {
left_chars += strlen(cur_symb);
@@ -487,20 +474,24 @@ __calc_left_pad(int flags, char *cur_symb) {
}
switch (sign_posn) {
- case 1:
+ case 0:
+ if (flags & IS_NEGATIVE)
+ left_chars++;
+ break;
+ case 1:
+ left_chars += strlen(signstr);
+ break;
+ case 3:
+ case 4:
+ if (cs_precedes != 0)
left_chars += strlen(signstr);
- break;
- case 3:
- case 4:
- if (cs_precedes != 0)
- left_chars += strlen(signstr);
}
return (left_chars);
}
static int
-get_groups(int size, char *grouping) {
-
+get_groups(int size, char *grouping)
+{
int chars = 0;
if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
@@ -523,8 +514,9 @@ get_groups(int size, char *grouping) {
/* convert double to locale-encoded string */
static char *
-__format_grouped_double(double value, int *flags,
- int left_prec, int right_prec, int pad_char) {
+__format_grouped_double(double value, int *flags, int left_prec, int right_prec,
+ int pad_char, struct lconv *lc, locale_t loc)
+{
char *rslt;
char *avalue;
@@ -535,14 +527,13 @@ __format_grouped_double(double value, int *flags,
int padded;
- struct lconv *lc = localeconv();
char *grouping;
const char *decimal_point;
const char *thousands_sep;
size_t decimal_point_size;
size_t thousands_sep_size;
- int groups = 0;
+ int groups = 0;
grouping = lc->mon_grouping;
decimal_point = lc->mon_decimal_point;
@@ -561,10 +552,10 @@ __format_grouped_double(double value, int *flags,
/* fill right_prec with default value */
if (right_prec == -1) {
- if (*flags & USE_INTL_CURRENCY)
- right_prec = lc->int_frac_digits;
- else
- right_prec = lc->frac_digits;
+ if (*flags & USE_INTL_CURRENCY)
+ right_prec = lc->int_frac_digits;
+ else
+ right_prec = lc->frac_digits;
if (right_prec == CHAR_MAX) /* POSIX locale ? */
right_prec = 2;
@@ -574,8 +565,8 @@ __format_grouped_double(double value, int *flags,
left_prec += get_groups(left_prec, grouping);
/* convert to string */
- avalue_size = asprintf(&avalue, "%*.*f", left_prec + right_prec + 1,
- right_prec, value);
+ avalue_size = asprintf_l(&avalue, loc, "%*.*f",
+ left_prec + right_prec + 1, right_prec, value);
if (avalue_size < 0)
return (NULL);
@@ -598,17 +589,15 @@ __format_grouped_double(double value, int *flags,
if (right_prec > 0) {
bufend -= right_prec;
- memcpy(bufend, avalue + avalue_size+padded-right_prec,
+ memcpy(bufend, avalue + avalue_size + padded - right_prec,
right_prec);
bufend -= decimal_point_size;
memcpy(bufend, decimal_point, decimal_point_size);
avalue_size -= (right_prec + 1);
}
- if ((*flags & NEED_GROUPING) &&
- thousands_sep_size > 0 && /* XXX: need investigation */
- *grouping != CHAR_MAX &&
- *grouping > 0) {
+ if ((*flags & NEED_GROUPING) && thousands_sep_size > 0 &&
+ *grouping != CHAR_MAX && *grouping > 0) {
while (avalue_size > (int)*grouping) {
GRPCPY(*grouping);
GRPSEP;
@@ -630,10 +619,9 @@ __format_grouped_double(double value, int *flags,
if (avalue_size != 0)
GRPCPY(avalue_size);
padded -= groups;
-
} else {
bufend -= avalue_size;
- memcpy(bufend, avalue+padded, avalue_size);
+ memcpy(bufend, avalue + padded, avalue_size);
/* decrease assumed $decimal_point */
if (right_prec == 0)
padded -= decimal_point_size;
@@ -650,3 +638,30 @@ __format_grouped_double(double value, int *flags,
free(avalue);
return (rslt);
}
+
+ssize_t
+strfmon(char *restrict s, size_t maxsize, const char *restrict format, ...)
+{
+ ssize_t ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = vstrfmon_l(s, maxsize, __get_locale(), format, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+ssize_t
+strfmon_l(char *restrict s, size_t maxsize, locale_t loc,
+ const char *restrict format, ...)
+{
+ ssize_t ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = vstrfmon_l(s, maxsize, loc, format, ap);
+ va_end(ap);
+
+ return (ret);
+}