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

/***
*sscanf.c - read formatted data from string
*

*
*Purpose:
*       defines scanf() - reads formatted data from string
*
*******************************************************************************/

#include <string.h>
#include <errno.h>
#include <limits.h>
#include "internal_securecrt.h"

#include "mbusafecrt_internal.h"

typedef int (*INPUTFN)(miniFILE *, const unsigned char*, va_list);
typedef int (*WINPUTFN)(miniFILE *, const unsigned short*, va_list);


/***
*static int v[nw]scan_fn([w]inputfn, string, [count], format, ...)
*
*Purpose:
*       this is a helper function which is called by the other functions
*       in this file - sscanf/swscanf/snscanf etc. It calls either _(w)input or
*       _(w)input_s depending on the first parameter.
*
*******************************************************************************/

static int __cdecl vscan_fn (
        INPUTFN inputfn,
        const char *string,
        const char *format,
        va_list arglist
        )
{
        miniFILE str;
        miniFILE *infile = &str;
        int retval;
        size_t count = strlen(string);

        _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
        _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);

        infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
        infile->_ptr = infile->_base = (char *) string;

        if(count>(INT_MAX/sizeof(char)))
        {
            /* old-style functions allow any large value to mean unbounded */
            infile->_cnt = INT_MAX;
        }
        else
        {
            infile->_cnt = (int)count*sizeof(char);
        }

        retval = (inputfn(infile, ( const unsigned char* )format, arglist));

        return(retval);
}

static int __cdecl vnscan_fn (
        INPUTFN inputfn,
        const char *string,
        size_t count,
        const char *format,
        va_list arglist
        )
{
        miniFILE str;
        miniFILE *infile = &str;
        int retval;
        size_t length = strlen(string);

        _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
        _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);

        infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
        infile->_ptr = infile->_base = (char *) string;

        if ( count > length )
        {
            count = length;
        }

        if(count>(INT_MAX/sizeof(char)))
        {
            /* old-style functions allow any large value to mean unbounded */
            infile->_cnt = INT_MAX;
        }
        else
        {
            infile->_cnt = (int)count*sizeof(char);
        }

        retval = (inputfn(infile, ( const unsigned char* )format, arglist));

        return(retval);
}

static int __cdecl vwscan_fn (
        WINPUTFN inputfn,
        const char16_t *string,
        const char16_t *format,
        va_list arglist
        )
{
        miniFILE str;
        miniFILE *infile = &str;
        int retval;
        size_t count = wcsnlen(string, INT_MAX);

        _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
        _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);

        infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
        infile->_ptr = infile->_base = (char *) string;

        if(count>(INT_MAX/sizeof(char16_t)))
        {
            /* old-style functions allow any large value to mean unbounded */
            infile->_cnt = INT_MAX;
        }
        else
        {
            infile->_cnt = (int)count*sizeof(char16_t);
        }

        retval = (inputfn(infile, format, arglist));

        return(retval);
}

static int __cdecl vnwscan_fn (
        WINPUTFN inputfn,
        const char16_t *string,
        size_t count,
        const char16_t *format,
        va_list arglist
        )
{
        miniFILE str;
        miniFILE *infile = &str;
        int retval;
        size_t length = wcsnlen(string, INT_MAX);

        _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
        _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);

        infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
        infile->_ptr = infile->_base = (char *) string;

        if ( count > length )
        {
            count = length;
        }

        if(count>(INT_MAX/sizeof(char16_t)))
        {
            /* old-style functions allow any large value to mean unbounded */
            infile->_cnt = INT_MAX;
        }
        else
        {
            infile->_cnt = (int)count*sizeof(char16_t);
        }

        retval = (inputfn(infile, format, arglist));

        return(retval);
}


/***
*int sscanf_s(string, format, ...)
*   Same as sscanf above except that it calls _input_s to do the real work.
*
*int snscanf_s(string, size, format, ...)
*   Same as snscanf above except that it calls _input_s to do the real work.
*
*   _input_s has a size check for array parameters.
*
*******************************************************************************/

int __cdecl sscanf_s (
        const char *string,
        const char *format,
        ...
        )
{
        int ret;
        va_list arglist;
        va_start(arglist, format);
        ret = vscan_fn(__tinput_s, string, format, arglist);
        va_end(arglist);
        return ret;
}

int __cdecl _snscanf_s (
        const char *string,
        size_t count,
        const char *format,
        ...
        )
{
        int ret;
        va_list arglist;
        va_start(arglist, format);
        ret = vnscan_fn(__tinput_s, string, count, format, arglist);
        va_end(arglist);
        return ret;
}

int __cdecl swscanf_s (
        const char16_t *string,
        const char16_t *format,
        ...
        )
{
        int ret;
        va_list arglist;
        va_start(arglist, format);
        ret = vwscan_fn(__twinput_s, string, format, arglist);
        va_end(arglist);
        return ret;
}

int __cdecl _snwscanf_s (
        const char16_t *string,
        size_t count,
        const char16_t *format,
        ...
        )
{
        int ret;
        va_list arglist;
        va_start(arglist, format);
        ret = vnwscan_fn(__twinput_s, string, count, format, arglist);
        va_end(arglist);
        return ret;
}

