//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

/***
*   mbusafecrt.c - implementaion of support functions and data for MBUSafeCRT
*

*
*   Purpose:
*       This file contains the implementation of support functions and
*       data for MBUSafeCRT declared in mbusafecrt.h and mbusafecrt_internal.h.
****/

#include "pal/palinternal.h"
#include <string.h>
#include <errno.h>
#include <limits.h>

#include "mbusafecrt_internal.h"

/* global data */
tSafeCRT_AssertFuncPtr sMBUSafeCRTAssertFunc = NULL;

/***
*   MBUSafeCRTSetAssertFunc - Set the function called when an assert fails.
****/

void MBUSafeCRTSetAssertFunc( tSafeCRT_AssertFuncPtr inAssertFuncPtr )
{
    /* set it */
    sMBUSafeCRTAssertFunc = inAssertFuncPtr;
}

/***
*   _putc_nolock - putc for the miniFILE stream.
****/

int _putc_nolock( char inChar, miniFILE* inStream )
{
    int returnValue = EOF;

        inStream->_cnt -= sizeof( char );

    if ( ( inStream->_cnt ) >= 0 )
    {
        *( inStream->_ptr ) = inChar;
        inStream->_ptr += sizeof( char );
        returnValue = ( int )inChar;
    }

    return returnValue;
}

/***
*   _putwc_nolock - putwc for the miniFILE stream.
****/

int _putwc_nolock( wchar_t inChar, miniFILE* inStream )
{
    int returnValue = WEOF;

        inStream->_cnt -= sizeof( wchar_t );

    if ( ( inStream->_cnt ) >= 0 )
    {
        *( ( wchar_t* )( inStream->_ptr ) ) = inChar;
        inStream->_ptr += sizeof( wchar_t );
        returnValue = ( int )inChar;
    }

    return returnValue;
}

/***
*   _getc_nolock - getc for the miniFILE stream.
****/

int _getc_nolock( miniFILE* inStream )
{
    int returnValue = EOF;

    if ( ( inStream->_cnt ) >= ( int )( sizeof( char ) ) )
    {
        inStream->_cnt -= sizeof( char );
        returnValue = ( int )( *( inStream->_ptr ) );
        inStream->_ptr += sizeof( char );
    }

    return returnValue;
}

/***
*   _getwc_nolock - getc for the miniFILE stream.
****/

int _getwc_nolock( miniFILE* inStream )
{
    int returnValue = EOF;

    if ( ( inStream->_cnt ) >= ( int )( sizeof( wchar_t ) ) )
    {
        inStream->_cnt -= sizeof( wchar_t );
        returnValue = ( int )( *( ( wchar_t* )( inStream->_ptr ) ) );
        inStream->_ptr += sizeof( wchar_t );
    }

    return returnValue;
}

/***
*   _ungetc_nolock - ungetc for the miniFILE stream.
****/

int _ungetc_nolock( char inChar, miniFILE* inStream )
{
    int returnValue = EOF;

    if ( ( size_t )( ( inStream->_ptr ) - ( inStream->_base ) ) >= ( sizeof( char ) ) )
    {
        inStream->_cnt += sizeof( char );
        inStream->_ptr -= sizeof( char );
        return ( int )inChar;
    }

    return returnValue;
}

/***
*   _ungetwc_nolock - ungetwc for the miniFILE stream.
****/

int _ungetwc_nolock( wchar_t inChar, miniFILE* inStream )
{
    int returnValue = WEOF;
    
    if ( ( size_t )( ( inStream->_ptr ) - ( inStream->_base ) ) >= ( sizeof( wchar_t ) ) )
    {
        inStream->_cnt += sizeof( wchar_t );
        inStream->_ptr -= sizeof( wchar_t );
        returnValue = ( unsigned short )inChar;
    }
    
    return returnValue;
}


/***
*   _safecrt_cfltcvt - convert a float to an ascii string.
*       Uses sprintf - this usage is OK.
****/

/* routine used for floating-point output */
#define FORMATSIZE 30

#define _snprintf   snprintf

// taken from output.inl
#define FL_ALTERNATE  0x00080   /* alternate form requested */

errno_t _safecrt_cfltcvt(double *arg, char *buffer, size_t sizeInBytes, int type, int precision, int flags)
{
    char format[FORMATSIZE];
    size_t formatlen = 0;
    int retvalue;

    if (flags & 1)
    {
        type -= 'a' - 'A';
    }
    formatlen = 0;
    format[formatlen++] = '%';
    if (flags & FL_ALTERNATE)
    {
        format[formatlen++] = '#';
    }
    format[formatlen++] = '.';
    _itoa_s(precision, format + formatlen, FORMATSIZE - formatlen, 10);
    formatlen = strlen(format);
    format[formatlen++] = (char)type;
    format[formatlen] = 0;

    buffer[sizeInBytes - 1] = 0;
    retvalue = _snprintf(buffer, sizeInBytes, format, *arg);
    if (buffer[sizeInBytes - 1] != 0 || retvalue <= 0)
    {
        buffer[0] = 0;
        return EINVAL;
    }
    return 0;
}


/***
*   _safecrt_fassign - convert a string into a float or double.
****/

void _safecrt_fassign(int flag, void* argument, char* number )
{
    if ( flag != 0 )    // double
    {
        double dblValue = 0.0;
        (void)sscanf( number, "%lf", &dblValue );
        *( ( double* )argument ) = dblValue;
    }
    else                // float
    {
        float fltValue = 0.0;
        (void)sscanf( number, "%f", &fltValue );
        *( ( float* )argument ) = fltValue;
    }
}


/***
*   _safecrt_wfassign - convert a wchar_t string into a float or double.
****/

void _safecrt_wfassign(int flag, void* argument, wchar_t* number )
{
    // We cannot use system functions for this - they
    // assume that wchar_t is four bytes, while we assume
    // two. So, we need to convert to a regular char string
    // without using any system functions. To do this,
    // we'll assume that the numbers are in the 0-9 range and
    // do a simple conversion.
    
    char* numberAsChars = ( char* )number;
    int position = 0;
    
    // do the convert
    while ( number[ position ] != 0 )
    {
        numberAsChars[ position ] = ( char )( number[ position ] & 0x00FF );
        position++;
    }
    numberAsChars[ position ] = ( char )( number[ position ] & 0x00FF );
    
    // call the normal char version
    _safecrt_fassign( flag, argument, numberAsChars );
}


/***
*   _minimal_chartowchar - do a simple char to wchar conversion.
****/

int _minimal_chartowchar( wchar_t* outWChar, const char* inChar )
{
    *outWChar = ( wchar_t )( ( unsigned short )( ( unsigned char )( *inChar ) ) );
    return 1;
}


