//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/***
*gcvt.c - convert floating point number to character string
*
*   Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*   Converts floating point number into character string representation.
*
*Revision History:
*   09-09-93  RKW   written
*   11-09-87  BCM   different interface under ifdef MTHREAD
*   12-11-87  JCR   Added "_LOAD_DS" to declaration
*   05-24-88  PHG   Merged DLL and normal versions
*   10-20-88  JCR   Changed 'DOUBLE' to 'double' for 386
*   06-27-89  PHG   Changed "ndec" to "ndec-1" to correct significant
*           digits in exponential format (C6 bug #2124)
*   03-05-90  GJF   Fixed calling type, added #include <cruntime.h>,
*           removed #include <register.h>, removed redundant
*           prototypes, removed some leftover 16-bit support and
*           fixed the copyright. Also, cleaned up the formatting
*           a bit.
*   07-20-90  SBM   Compiles cleanly with -W3 (added/removed appropriate
*           #includes)
*   08-01-90  SBM   Renamed <struct.h> to <fltintrn.h>
*   09-27-90  GJF   New-style function declarators.
*   01-21-91  GJF   ANSI naming.
*   08-13-92  SKS   An old bug that was fixed in C 6.0 but that did not
*           make it into C 7.0, an off by 1 error when switching
*           from fixed point to scientific notation
*   04-06-93  SKS   Replace _CRTAPI* with _cdecl
*   09-06-94  CFW   Replace MTHREAD with _MT.
*   12-21-95  JWM   Replaced '.' with *__decimal_point; includes nlsint.h.
*   09-05-00  GB    Changed the defination of fltout functions. Use DOUBLE 
*                   instead of double
*   11-19-01  GB    Replaced DOUBLE by _CRT_DOUBLE
*   11-12-01  GB    Added support for new locale implementation.
*   06-19-02  MSL   Fixed possible buffer overrun on resultstring
*                   Switched to use _CVTBUFSIZE instead of CVTBUFSIZE; 
*                   public symbol
*                   VS7 551701
*   08-18-03  AC    Added safe versions (_gcvt_s)
*   03-10-04  AC    Return ERANGE when buffer is too small
*   03-10-04  AC    Fixed gcvt when requesting large numeber of digits
*                   VSW#266172
*   04-25-05  AC    Added debug filling
*                   VSW#2459
*
*******************************************************************************/

#include <cruntime.h>
#include <fltintrn.h>
#include <internal.h>
#include <internal_securecrt.h>
#include <locale.h>
#include <stdlib.h>
#include <mtdll.h>
#include <setlocal.h>

/***
*errno_t _gcvt_s(buffer, sizeInChars, value, ndec) - convert floating point value to char
*   string
*
*Purpose:
*   _gcvt_s converts the value to a null terminated ASCII string
*   buffer.  It attempts to produce ndigit significant digits
*   in Fortran F format if possible, ortherwise E format,
*   ready for printing.  Trailing zeros may be suppressed.
*   NOTE - to avoid the possibility of generating floating
*   point instructions in this code we fool the compiler
*   about the type of the 'value' parameter using a struct.
*   This is OK since all we do is pass it off as a
*   parameter.
*
*Entry:
*   value - double - number to be converted
*   ndec - int - number of significant digits
*   buf - char * - buffer to place result
*   sizeInChars - size_t - destination buffer size
*
*Exit:
*   Return errno_t != 0 if something went wrong (check the validation section below).
*   Result is written into buffer. If the buffer is too small, we return an error
*   (this is part of the validation section, too).
*
*Exceptions:
*   Input parameters are validated. Refer to the validation section of the function.
*
*******************************************************************************/

extern "C" errno_t __cdecl _gcvt_s (
    char *buf,
    size_t sizeInChars,
    double value,
    int ndec
    )
{
    STRFLT string;
    int    magnitude;
    _CRT_DOUBLE *pdvalue = (_CRT_DOUBLE *)&value;

    char *str;
    char *stop;
    errno_t e;
    _locale_t plocinfo = NULL;
    _LocaleUpdate _loc_update(plocinfo);
    struct _strflt strfltstruct;    /* temporary buffers */
    char   resultstring[22 /* MAX_MAN_DIGITS+1 */];

    /* validation section */
    _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL);
    _VALIDATE_RETURN_ERRCODE(sizeInChars > 0, EINVAL);
    _RESET_STRING(buf, sizeInChars);
    _VALIDATE_RETURN_ERRCODE((size_t)ndec < sizeInChars, ERANGE);
    /* _cftoe and _cftof (used below) are more strict in validating sizeInChars */

    /* get the magnitude of the number */

    string = _fltout2( *pdvalue, &strfltstruct, resultstring, _countof(resultstring) );

    magnitude = string->decpt - 1;

    /* output the result according to the Fortran G format as outlined in
       Fortran language specification */

    if ( magnitude < -1  ||  magnitude > ndec-1 )
        /* then  Ew.d  d = ndec */
        e = _cftoe( &value, buf, sizeInChars, ndec-1, 0);
    else
        /* Fw.d  where d = ndec-string->decpt */
        e = _cftof( &value, buf, sizeInChars, ndec-string->decpt );

    if (e == 0)
    {
        /* remove the trailing zeroes before the exponent; we don't need to check for sizeInChars */
        str = buf;
        while (*str && *str != *__LCONV(_loc_update.GetLocaleT()->locinfo)->decimal_point)
            str++;

        if (*str++)
        {
            while (*str && *str != 'e')
                str++;

            stop = str--;

            while (*str == '0')
                str--;

            while (*++str = *stop++)
                ;
        }
    }
    else
    {
        errno = e;
    }

    return e;
}

/***
*char * _gcvt(value, ndec, buffer) - convert floating point value to char
*   string
*
*Purpose:
*   _gcvt converts the value to a null terminated ASCII string
*   buf.  It attempts to produce ndigit significant digits
*   in Fortran F format if possible, ortherwise E format,
*   ready for printing.  Trailing zeros may be suppressed.
*   No error checking or overflow protection is provided.
*   NOTE - to avoid the possibility of generating floating
*   point instructions in this code we fool the compiler
*   about the type of the 'value' parameter using a struct.
*   This is OK since all we do is pass it off as a
*   parameter.
*
*Entry:
*   value - double - number to be converted
*   ndec - int - number of significant digits
*   buf - char * - buffer to place result
*
*Exit:
*   result is written into buffer; it will be overwritten if it has
*   not been made big enough.
*
*Exceptions:
*   Input parameters are validated like in _gcvt_s. Returns NULL if something goes wrong,
*   and errno is set. The destination buffer size is assumed to be large enough, and 
*   no error checking or overflow protection is provided.
*
*******************************************************************************/

char * __cdecl _gcvt (
    double value,
    int ndec,
    char *buf
    )
{
    errno_t e = _gcvt_s(buf, (size_t)-1, value, ndec);
    if (e != 0)
    {
        return NULL;
    }
    return buf;
}
