version 1.4.10.1, 2008/03/23 00:40:26 |
version 1.11, 2017/08/16 13:53:20 |
|
|
/* strfmon.c,v 1.4 2006/03/19 01:50:49 christos Exp */ |
/* $NetBSD$ */ |
|
|
/*- |
/*- |
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> |
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> |
|
|
#if 0 |
#if 0 |
__FBSDID("$FreeBSD: src/lib/libc/stdlib/strfmon.c,v 1.14 2003/03/20 08:18:55 ache Exp $"); |
__FBSDID("$FreeBSD: src/lib/libc/stdlib/strfmon.c,v 1.14 2003/03/20 08:18:55 ache Exp $"); |
#else |
#else |
__RCSID("strfmon.c,v 1.4 2006/03/19 01:50:49 christos Exp"); |
__RCSID("$NetBSD$"); |
#endif |
#endif |
#endif /* LIBC_SCCS and not lint */ |
#endif /* LIBC_SCCS and not lint */ |
|
|
#if defined(__NetBSD__) |
|
#include "namespace.h" |
#include "namespace.h" |
#include <monetary.h> |
|
#endif |
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
|
#include <assert.h> |
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
#include <limits.h> |
#include <limits.h> |
#include <locale.h> |
#include <locale.h> |
|
#include <monetary.h> |
#include <stdarg.h> |
#include <stdarg.h> |
|
#include <stddef.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
|
#include "setlocale_local.h" |
|
|
/* internal flags */ |
/* internal flags */ |
#define NEED_GROUPING 0x01 /* print digits grouped (default) */ |
#define NEED_GROUPING 0x01 /* print digits grouped (default) */ |
#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ |
#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ |
Line 61 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
Line 63 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ |
#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 ? */ |
|
|
|
#ifndef NBCHAR_MAX |
|
#define NBCHAR_MAX ((unsigned char)CHAR_MAX) |
|
#endif |
|
|
/* internal macros */ |
/* internal macros */ |
#define PRINT(CH) do { \ |
#define PRINT(CH) do { \ |
if (dst >= s + maxsize) \ |
if (dst >= s + maxsize) \ |
Line 75 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
Line 81 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
} while (/* CONSTCOND */ 0) |
} while (/* CONSTCOND */ 0) |
|
|
#define GET_NUMBER(VAR) do { \ |
#define GET_NUMBER(VAR) do { \ |
int ovar; \ |
VAR = 0; \ |
ovar = VAR = 0; \ |
|
while (isdigit((unsigned char)*fmt)) { \ |
while (isdigit((unsigned char)*fmt)) { \ |
VAR *= 10; \ |
VAR *= 10; \ |
VAR += *fmt - '0'; \ |
VAR += *fmt - '0'; \ |
if (ovar > VAR) \ |
if (VAR > 0x00ffffff) \ |
goto e2big_error; \ |
goto e2big_error; \ |
else \ |
|
ovar = VAR; \ |
|
fmt++; \ |
fmt++; \ |
} \ |
} \ |
} while (/* CONSTCOND */ 0) |
} while (/* CONSTCOND */ 0) |
Line 101 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
Line 104 __RCSID("strfmon.c,v 1.4 2006/03/19 01:5 |
|
groups++; \ |
groups++; \ |
} while (/* CONSTCOND */ 0) |
} while (/* CONSTCOND */ 0) |
|
|
static void __setup_vars(int, char *, char *, char *, const char **); |
static void __setup_vars(struct lconv *, int, char *, char *, char *, const char **); |
static int __calc_left_pad(int, char *); |
static int __calc_left_pad(struct lconv *, int, char *); |
static char *__format_grouped_double(double, int *, int, int, int); |
static char *__format_grouped_double(struct lconv *, double, int *, int, int, int); |
|
|
ssize_t |
static ssize_t |
strfmon(char * __restrict s, size_t maxsize, const char * __restrict format, |
vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, |
...) |
const char * __restrict format, va_list ap) |
{ |
{ |
va_list ap; |
|
char *dst; /* output destination pointer */ |
char *dst; /* output destination pointer */ |
const char *fmt; /* current format poistion pointer */ |
const char *fmt; /* current format poistion pointer */ |
struct lconv *lc; /* pointer to lconv structure */ |
struct lconv *lc; /* pointer to lconv structure */ |
Line 133 strfmon(char * __restrict s, size_t maxs |
|
Line 135 strfmon(char * __restrict s, size_t maxs |
|
char *tmpptr; /* temporary vars */ |
char *tmpptr; /* temporary vars */ |
int sverrno; |
int sverrno; |
|
|
va_start(ap, format); |
lc = localeconv_l(loc); |
|
|
lc = localeconv(); |
|
dst = s; |
dst = s; |
fmt = format; |
fmt = format; |
asciivalue = NULL; |
asciivalue = NULL; |
Line 200 strfmon(char * __restrict s, size_t maxs |
|
Line 200 strfmon(char * __restrict s, size_t maxs |
|
|
|
/* field Width */ |
/* field Width */ |
if (isdigit((unsigned char)*fmt)) { |
if (isdigit((unsigned char)*fmt)) { |
|
ptrdiff_t d = dst - s; |
GET_NUMBER(width); |
GET_NUMBER(width); |
/* Do we have enough space to put number with |
/* Do we have enough space to put number with |
* required width ? |
* required width ? |
*/ |
*/ |
if (dst + width >= s + maxsize) |
|
|
if ((size_t)(d + width) >= maxsize) |
goto e2big_error; |
goto e2big_error; |
} |
} |
|
|
Line 258 strfmon(char * __restrict s, size_t maxs |
|
Line 260 strfmon(char * __restrict s, size_t maxs |
|
|
|
/* fill left_prec with amount of padding chars */ |
/* fill left_prec with amount of padding chars */ |
if (left_prec >= 0) { |
if (left_prec >= 0) { |
pad_size = __calc_left_pad((flags ^ IS_NEGATIVE), |
pad_size = __calc_left_pad(lc, (flags ^ IS_NEGATIVE), |
currency_symbol) - |
currency_symbol) - |
__calc_left_pad(flags, currency_symbol); |
__calc_left_pad(lc, flags, currency_symbol); |
if (pad_size < 0) |
if (pad_size < 0) |
pad_size = 0; |
pad_size = 0; |
} |
} |
|
|
asciivalue = __format_grouped_double(value, &flags, |
asciivalue = __format_grouped_double(lc, value, &flags, |
left_prec, right_prec, pad_char); |
left_prec, right_prec, pad_char); |
if (asciivalue == NULL) |
if (asciivalue == NULL) |
goto end_error; /* errno already set */ |
goto end_error; /* errno already set */ |
/* to ENOMEM by malloc() */ |
/* to ENOMEM by malloc() */ |
|
|
/* set some variables for later use */ |
/* set some variables for later use */ |
__setup_vars(flags, &cs_precedes, &sep_by_space, |
__setup_vars(lc, flags, &cs_precedes, &sep_by_space, |
&sign_posn, &signstr); |
&sign_posn, &signstr); |
|
|
/* |
/* |
Line 376 strfmon(char * __restrict s, size_t maxs |
|
Line 378 strfmon(char * __restrict s, size_t maxs |
|
while (dst - tmpptr < width) |
while (dst - tmpptr < width) |
PRINT(' '); |
PRINT(' '); |
} else { |
} else { |
pad_size = dst-tmpptr; |
_DIAGASSERT(__type_fit(int, dst - tmpptr)); |
|
pad_size = dst - tmpptr; |
memmove(tmpptr + width-pad_size, tmpptr, |
memmove(tmpptr + width-pad_size, tmpptr, |
(size_t) pad_size); |
(size_t) pad_size); |
memset(tmpptr, ' ', (size_t) width-pad_size); |
memset(tmpptr, ' ', (size_t) width-pad_size); |
Line 386 strfmon(char * __restrict s, size_t maxs |
|
Line 389 strfmon(char * __restrict s, size_t maxs |
|
} |
} |
|
|
PRINT('\0'); |
PRINT('\0'); |
va_end(ap); |
|
free(asciivalue); |
free(asciivalue); |
free(currency_symbol); |
free(currency_symbol); |
return (dst - s - 1); /* return size of put data except trailing '\0' */ |
return (dst - s - 1); /* return size of put data except trailing '\0' */ |
|
|
if (currency_symbol != NULL) |
if (currency_symbol != NULL) |
free(currency_symbol); |
free(currency_symbol); |
errno = sverrno; |
errno = sverrno; |
va_end(ap); |
|
return (-1); |
return (-1); |
} |
} |
|
|
static void |
static void |
__setup_vars(int flags, char *cs_precedes, char *sep_by_space, |
__setup_vars(struct lconv *lc, int flags, char *cs_precedes, char *sep_by_space, |
char *sign_posn, const char **signstr) { |
char *sign_posn, const char **signstr) { |
struct lconv *lc = localeconv(); |
|
|
|
if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { |
if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { |
*cs_precedes = lc->int_n_cs_precedes; |
*cs_precedes = lc->int_n_cs_precedes; |
Line 441 __setup_vars(int flags, char *cs_precede |
|
Line 441 __setup_vars(int flags, char *cs_precede |
|
/* Set defult values for unspecified information. */ |
/* Set defult values for unspecified information. */ |
if (*cs_precedes != 0) |
if (*cs_precedes != 0) |
*cs_precedes = 1; |
*cs_precedes = 1; |
if (*sep_by_space == CHAR_MAX) |
if ((unsigned char)*sep_by_space == NBCHAR_MAX) |
*sep_by_space = 0; |
*sep_by_space = 0; |
if (*sign_posn == CHAR_MAX) |
if ((unsigned char)*sign_posn == NBCHAR_MAX) |
*sign_posn = 0; |
*sign_posn = 0; |
} |
} |
|
|
static int |
static int |
__calc_left_pad(int flags, char *cur_symb) { |
__calc_left_pad(struct lconv *lc, int flags, char *cur_symb) { |
|
|
char cs_precedes, sep_by_space, sign_posn; |
char cs_precedes, sep_by_space, sign_posn; |
const char *signstr; |
const char *signstr; |
int left_chars = 0; |
size_t left_chars = 0; |
|
|
__setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); |
__setup_vars(lc, flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); |
|
|
if (cs_precedes != 0) { |
if (cs_precedes != 0) { |
left_chars += strlen(cur_symb); |
left_chars += strlen(cur_symb); |
Line 471 __calc_left_pad(int flags, char *cur_sym |
|
Line 471 __calc_left_pad(int flags, char *cur_sym |
|
if (cs_precedes != 0) |
if (cs_precedes != 0) |
left_chars += strlen(signstr); |
left_chars += strlen(signstr); |
} |
} |
return (left_chars); |
_DIAGASSERT(__type_fit(int, left_chars)); |
|
return (int)left_chars; |
} |
} |
|
|
static int |
static int |
Line 479 get_groups(int size, char *grouping) { |
|
Line 480 get_groups(int size, char *grouping) { |
|
|
|
int chars = 0; |
int chars = 0; |
|
|
if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ |
if ((unsigned char)*grouping == NBCHAR_MAX || *grouping <= 0) /* no grouping ? */ |
return (0); |
return (0); |
|
|
while (size > (int)*grouping) { |
while (size > (int)*grouping) { |
chars++; |
chars++; |
size -= (int)*grouping++; |
size -= (int)*grouping++; |
/* no more grouping ? */ |
/* no more grouping ? */ |
if (*grouping == CHAR_MAX) |
if ((unsigned char)*grouping == NBCHAR_MAX) |
break; |
break; |
/* rest grouping with same value ? */ |
/* rest grouping with same value ? */ |
if (*grouping == 0) { |
if (*grouping == 0) { |
Line 499 get_groups(int size, char *grouping) { |
|
Line 500 get_groups(int size, char *grouping) { |
|
|
|
/* convert double to ASCII */ |
/* convert double to ASCII */ |
static char * |
static char * |
__format_grouped_double(double value, int *flags, |
__format_grouped_double(struct lconv *lc, double value, int *flags, |
int left_prec, int right_prec, int pad_char) { |
int left_prec, int right_prec, int pad_char) { |
|
|
char *rslt; |
char *rslt; |
char *avalue; |
char *avalue; |
int avalue_size; |
int avalue_size; |
char fmt[32]; |
|
|
|
size_t bufsize; |
size_t bufsize; |
char *bufend; |
char *bufend; |
|
|
int padded; |
int padded; |
|
|
struct lconv *lc = localeconv(); |
|
char *grouping; |
char *grouping; |
char decimal_point; |
char decimal_point; |
char thousands_sep; |
char thousands_sep; |
Line 546 __format_grouped_double(double value, in |
|
Line 545 __format_grouped_double(double value, in |
|
left_prec += get_groups(left_prec, grouping); |
left_prec += get_groups(left_prec, grouping); |
|
|
/* convert to string */ |
/* convert to string */ |
snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1, |
avalue_size = asprintf(&avalue, "%*.*f", left_prec + right_prec + 1, |
right_prec); |
right_prec, value); |
avalue_size = asprintf(&avalue, fmt, value); |
|
if (avalue_size < 0) |
if (avalue_size < 0) |
return (NULL); |
return (NULL); |
|
|
/* make sure that we've enough space for result string */ |
/* make sure that we've enough space for result string */ |
bufsize = strlen(avalue)*2+1; |
bufsize = avalue_size * 2 + 1; |
rslt = malloc(bufsize); |
rslt = malloc(bufsize); |
if (rslt == NULL) { |
if (rslt == NULL) { |
free(avalue); |
free(avalue); |
Line 577 __format_grouped_double(double value, in |
|
Line 575 __format_grouped_double(double value, in |
|
avalue_size -= (right_prec + 1); |
avalue_size -= (right_prec + 1); |
} |
} |
|
|
|
/* XXX: Why not use %' instead? */ |
if ((*flags & NEED_GROUPING) && |
if ((*flags & NEED_GROUPING) && |
thousands_sep != '\0' && /* XXX: need investigation */ |
thousands_sep != '\0' && /* XXX: need investigation */ |
*grouping != CHAR_MAX && |
(unsigned char)*grouping != NBCHAR_MAX && |
*grouping > 0) { |
*grouping > 0) { |
while (avalue_size > (int)*grouping) { |
while (avalue_size > (int)*grouping) { |
GRPCPY(*grouping); |
GRPCPY(*grouping); |
Line 587 __format_grouped_double(double value, in |
|
Line 586 __format_grouped_double(double value, in |
|
grouping++; |
grouping++; |
|
|
/* no more grouping ? */ |
/* no more grouping ? */ |
if (*grouping == CHAR_MAX) |
if ((unsigned char)*grouping == NBCHAR_MAX) |
break; |
break; |
|
|
/* rest grouping with same value ? */ |
/* rest grouping with same value ? */ |
Line 621 __format_grouped_double(double value, in |
|
Line 620 __format_grouped_double(double value, in |
|
free(avalue); |
free(avalue); |
return (rslt); |
return (rslt); |
} |
} |
|
|
|
ssize_t |
|
strfmon(char * __restrict s, size_t maxsize, const char * __restrict format, |
|
...) |
|
{ |
|
ssize_t rv; |
|
va_list ap; |
|
|
|
va_start(ap, format); |
|
rv = vstrfmon_l(s, maxsize, _current_locale(), format, ap); |
|
va_end(ap); |
|
|
|
return rv; |
|
} |
|
|
|
ssize_t |
|
strfmon_l(char * __restrict s, size_t maxsize, locale_t loc, |
|
const char * __restrict format, ...) |
|
{ |
|
ssize_t rv; |
|
va_list ap; |
|
|
|
va_start(ap, format); |
|
rv = vstrfmon_l(s, maxsize, loc, format, ap); |
|
va_end(ap); |
|
|
|
return rv; |
|
} |