/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifndef _LCONV_C99
#define _LCONV_C99
#endif
#include "lint.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <monetary.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "localeimpl.h"
#include "lmonetary.h"
#include "lnumeric.h"
/* internal flags */
/* internal macros */
goto e2big_error; \
}
while (*tmps != '\0') \
}
VAR = 0; \
goto e2big_error; \
VAR *= 10; \
if (VAR < 0) \
goto e2big_error; \
fmt++; \
} \
}
int i = howmany; \
while (i-- > 0) { \
avalue_size--; \
} \
}
#define GRPSEP { \
bufend -= thousands_len; \
groups++; \
}
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);
{
char sep_by_space;
char sign_posn;
const char *signstr;
const char *currency_symbol;
int sverrno;
dst = s;
asciivalue = NULL;
pad_size = 0;
while (*fmt) {
/* pass nonformating characters AS IS */
if (*fmt != '%')
goto literal;
/* '%' found ! */
/* "%%" mean just '%' */
fmt++;
continue;
}
/* set up initial values */
value = 0; /* we have no value to print now */
/* Flags */
for (;;) {
switch (*++fmt) {
case '=': /* fill character */
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;
continue;
case '(': /* enclose negatives with () */
if (flags & SIGN_POSN_USED)
goto format_error;
continue;
case '!': /* suppress currency symbol */
continue;
case '-': /* alignment (left) */
flags |= LEFT_JUSTIFY;
continue;
default:
break;
}
break;
}
/* field Width */
/*
* Do we have enough space to put number with
* required width ?
*/
goto e2big_error;
}
/* Left precision */
if (*fmt == '#') {
goto format_error;
goto e2big_error;
}
/* Right precision */
if (*fmt == '.') {
goto format_error;
goto e2big_error;
}
/* Conversion Characters */
switch (*fmt++) {
case 'i': /* use internaltion currency format */
break;
case 'n': /* use national currency format */
flags &= ~(USE_INTL_CURRENCY);
break;
default:
/* required char missing or premature EOS */
goto format_error;
}
if (flags & USE_INTL_CURRENCY) {
/* by definition three letters followed by a space */
if (currency_symbol != NULL)
} else
/* value itself */
/* detect sign */
if (value < 0) {
flags |= IS_NEGATIVE;
}
/* fill left_prec with amount of padding chars */
if (left_prec >= 0) {
if (pad_size < 0)
pad_size = 0;
}
if (asciivalue != NULL)
if (asciivalue == NULL)
goto end_error; /* errno already set */
/* to ENOMEM by malloc() */
/* set some variables for later use */
/*
* Description of some LC_MONETARY's values:
*
* p_cs_precedes & n_cs_precedes
*
* = 1 - $currency_symbol precedes the value
* for a monetary quantity with a non-negative value
* = 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
* = 1 - space separates the symbol from the value
* = 2 - space separates the symbol and the sign string,
* if adjacent.
*
* 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
* $currency_symbol
* = 3 - the sign string precedes the $currency_symbol
* = 4 - the sign string succeeds the $currency_symbol
*
*/
while (pad_size-- > 0)
PRINT(' ');
PRINT('(');
if (cs_precedes == 1) {
if (sep_by_space == 2)
PRINT(' ');
}
if (!(flags & SUPRESS_CURR_SYMBOL)) {
if (sign_posn == 4) {
if (sep_by_space == 2)
if (sep_by_space == 1)
PRINT(' ');
} else if (sep_by_space == 1)
}
} else if (sign_posn == 1)
if (cs_precedes == 0) {
if (sign_posn == 3) {
if (sep_by_space == 1)
PRINT(' ');
}
if (!(flags & SUPRESS_CURR_SYMBOL)) {
sign_posn == 4)))
if (sign_posn == 4) {
if (sep_by_space == 2)
PRINT(' ');
}
}
}
if (sign_posn == 2) {
if (sep_by_space == 2)
PRINT(' ');
}
PRINT(')');
if (flags & LEFT_JUSTIFY) {
PRINT(' ');
} else {
pad_size);
}
}
}
PRINT('\0');
goto end_error;
if (asciivalue != NULL)
return (-1);
}
const char *_RESTRICT_KYWD format, ...)
{
return (ret);
}
const char *_RESTRICT_KYWD format, ...)
{
return (ret);
}
static void
{
lmon->int_n_sign_posn[0];
} else if (flags & USE_INTL_CURRENCY) {
lmon->int_p_sign_posn[0];
} else if (flags & IS_NEGATIVE) {
} else {
}
/* Set default values for unspecified information. */
if (*cs_precedes != 0)
*cs_precedes = 1;
if (*sep_by_space == CHAR_MAX)
*sep_by_space = 0;
*sign_posn = 0;
}
static int
{
const char *signstr;
int left_chars = 0;
&signstr);
if (cs_precedes != 0) {
if (sep_by_space != 0)
left_chars++;
}
switch (sign_posn) {
case 1:
break;
case 3:
case 4:
if (cs_precedes != 0)
}
return (left_chars);
}
static int
{
int chars = 0;
return (0);
chars++;
/* no more grouping ? */
break;
/* rest grouping with same value ? */
if (*grouping == 0) {
break;
}
}
return (chars);
}
/* convert double to ASCII */
static char *
const struct lc_numeric *lnum,
{
char *rslt;
char *avalue;
int avalue_size;
char *bufend;
int padded;
const char *grouping;
const char *decimal_point;
const char *thousands_sep;
int decimal_len;
int thousands_len;
int groups = 0;
if (*decimal_point == '\0')
if (*thousands_sep == '\0')
/* fill left_prec with default value */
if (left_prec == -1)
left_prec = 0;
/* fill right_prec with default value */
if (right_prec == -1) {
if (*flags & USE_INTL_CURRENCY)
else
right_prec = 2;
}
if (*flags & NEED_GROUPING)
/* convert to string */
if (avalue_size < 0)
return (NULL);
/*
* 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.
*/
return (NULL);
}
/* skip spaces at beginning */
padded = 0;
padded++;
avalue_size--;
}
if (right_prec > 0) {
bufend -= right_prec;
bufend -= decimal_len;
}
if ((*flags & NEED_GROUPING) &&
thousands_len != 0 &&
*grouping > 0) {
while (avalue_size > (int)*grouping) {
grouping++;
/* no more grouping ? */
break;
/* rest grouping with same value ? */
if (*grouping == 0) {
grouping--;
while (avalue_size > *grouping) {
}
}
}
if (avalue_size != 0)
} else {
bufend -= avalue_size;
if (right_prec == 0)
padded--; /* decrease assumed $decimal_point */
}
/* do padding with pad_char */
if (padded > 0) {
}
return (rslt);
}