blob: 9c6c05a6d7c62eed8c784ca583cea876dd9f71b4 [file]
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
/*++
Module Name:
cruntime/misctls.ccpp
Abstract:
Implementation of C runtime functions that don't fit anywhere else
and depend on per-thread data
--*/
#include "pal/thread.hpp"
#include "pal/palinternal.h"
extern "C"
{
#include "pal/dbgmsg.h"
#include "pal/misc.h"
}
#include <errno.h>
/* <stdarg.h> needs to be included after "palinternal.h" to avoid name
collision for va_start and va_end */
#include <stdarg.h>
#include <time.h>
#if HAVE_CRT_EXTERNS_H
#include <crt_externs.h>
#endif // HAVE_CRT_EXTERNS_H
using namespace CorUnix;
SET_DEFAULT_DEBUG_CHANNEL(CRT);
/*++
Function:
localtime
See MSDN for more details.
--*/
struct PAL_tm *
__cdecl
PAL_localtime(const PAL_time_t *clock)
{
CPalThread *pThread = NULL;
struct tm tmpResult;
struct PAL_tm *result = NULL;
PERF_ENTRY(localtime);
ENTRY( "localtime( clock=%p )\n",clock );
/* Get the per-thread buffer from the thread structure. */
pThread = InternalGetCurrentThread();
result = &pThread->crtInfo.localtimeBuffer;
localtime_r(reinterpret_cast<const time_t*>(clock), &tmpResult);
// Copy the result into the Windows struct.
result->tm_sec = tmpResult.tm_sec;
result->tm_min = tmpResult.tm_min;
result->tm_hour = tmpResult.tm_hour;
result->tm_mday = tmpResult.tm_mday;
result->tm_mon = tmpResult.tm_mon;
result->tm_year = tmpResult.tm_year;
result->tm_wday = tmpResult.tm_wday;
result->tm_yday = tmpResult.tm_yday;
result->tm_isdst = tmpResult.tm_isdst;
LOGEXIT( "localtime returned %p\n", result );
PERF_EXIT(localtime);
return result;
}
/*++
Function:
ctime
There appears to be a difference between the FreeBSD and windows
implementations. FreeBSD gives Wed Dec 31 18:59:59 1969 for a
-1 param, and Windows returns NULL
See MSDN for more details.
--*/
char *
__cdecl
PAL_ctime( const PAL_time_t *clock )
{
CPalThread *pThread = NULL;
char * retval = NULL;
PERF_ENTRY(ctime);
ENTRY( "ctime( clock=%p )\n",clock );
if(*clock < 0)
{
/*If the input param is less than zero the value
*returned is less than the Unix epoch
*1st of January 1970*/
WARN("The input param is less than zero");
goto done;
}
/* Get the per-thread buffer from the thread structure. */
pThread = InternalGetCurrentThread();
retval = pThread->crtInfo.ctimeBuffer;
ctime_r(reinterpret_cast<const time_t*>(clock),retval);
done:
LOGEXIT( "ctime() returning %p (%s)\n",retval,retval);
PERF_EXIT(ctime);
return retval;
}
/**
Function:
_ecvt
See MSDN for more information.
NOTES:
There is a difference between PAL _ecvt and Win32 _ecvt.
If Window's _ecvt receives a double 0.000000000000000000005, and count 50
the result is "49999999999999998000000000000000000000000000000000"
Under BSD the same call will result in :
49999999999999998021734900744965462766153934333829
The difference is due to the difference between BSD and Win32 sprintf.
--*/
char * __cdecl
_ecvt( double value, int count, int * dec, int * sign )
{
CONST CHAR * FORMAT_STRING = "%.348e";
CHAR TempBuffer[ ECVT_MAX_BUFFER_SIZE ];
CPalThread *pThread = NULL;
LPSTR lpReturnBuffer = NULL;
LPSTR lpStartOfReturnBuffer = NULL;
LPSTR lpTempBuffer = NULL;
LPSTR lpEndOfTempBuffer = NULL;
INT nTempBufferLength = 0;
CHAR ExponentBuffer[ 6 ];
INT nExponentValue = 0;
INT LoopIndex = 0;
PERF_ENTRY(_ecvt);
ENTRY( "_ecvt( value=%.30g, count=%d, dec=%p, sign=%p )\n",
value, count, dec, sign );
/* Get the per-thread buffer from the thread structure. */
pThread = InternalGetCurrentThread();
lpStartOfReturnBuffer = lpReturnBuffer = pThread->crtInfo.ECVTBuffer;
/* Sanity checks */
if ( !dec || !sign )
{
ERROR( "dec and sign have to be valid pointers.\n" );
*lpReturnBuffer = '\0';
goto done;
}
else
{
*dec = *sign = 0;
}
if ( value < 0.0 )
{
*sign = 1;
}
if ( count > ECVT_MAX_COUNT_SIZE )
{
count = ECVT_MAX_COUNT_SIZE;
}
/* Get the string to work with. */
sprintf_s( TempBuffer, sizeof(TempBuffer), FORMAT_STRING, value );
/* Check to see if value was a valid number. */
if ( strcmp( "NaN", TempBuffer ) == 0 || strcmp( "-NaN", TempBuffer ) == 0 )
{
TRACE( "value was not a number!\n" );
if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#QNAN0" ) != SAFECRT_SUCCESS)
{
ERROR( "strcpy_s failed!\n" );
*lpStartOfReturnBuffer = '\0';
goto done;
}
*dec = 1;
goto done;
}
/* Check to see if it is infinite. */
if ( strcmp( "Inf", TempBuffer ) == 0 || strcmp( "-Inf", TempBuffer ) == 0 )
{
TRACE( "value is infinite!\n" );
if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#INF00" ) != SAFECRT_SUCCESS)
{
ERROR( "strcpy_s failed!\n" );
*lpStartOfReturnBuffer = '\0';
goto done;
}
*dec = 1;
if ( *TempBuffer == '-' )
{
*sign = 1;
}
goto done;
}
nTempBufferLength = strlen( TempBuffer );
lpEndOfTempBuffer = &(TempBuffer[ nTempBufferLength ]);
/* Extract the exponent, and convert it to integer. */
while ( *lpEndOfTempBuffer != 'e' && nTempBufferLength > 0 )
{
nTempBufferLength--;
lpEndOfTempBuffer--;
}
ExponentBuffer[ 0 ] = '\0';
if (strncat_s( ExponentBuffer, sizeof(ExponentBuffer), lpEndOfTempBuffer + 1, 5 ) != SAFECRT_SUCCESS)
{
ERROR( "strncat_s failed!\n" );
*lpStartOfReturnBuffer = '\0';
goto done;
}
nExponentValue = atoi( ExponentBuffer );
/* End the string at the 'e' */
*lpEndOfTempBuffer = '\0';
nTempBufferLength--;
/* Determine decimal location. */
if ( nExponentValue == 0 )
{
*dec = 1;
}
else
{
*dec = nExponentValue + 1;
}
if ( value == 0.0 )
{
*dec = 0;
}
/* Copy the string from the temp buffer upto count characters,
removing the sign, and decimal as required. */
lpTempBuffer = TempBuffer;
*lpReturnBuffer = '0';
lpReturnBuffer++;
while ( LoopIndex < ECVT_MAX_COUNT_SIZE )
{
if ( isdigit(*lpTempBuffer) )
{
*lpReturnBuffer = *lpTempBuffer;
LoopIndex++;
lpReturnBuffer++;
}
lpTempBuffer++;
if ( LoopIndex == count + 1 )
{
break;
}
}
*lpReturnBuffer = '\0';
/* Round if needed. If count is less then 0
then windows does not round for some reason.*/
nTempBufferLength = strlen( lpStartOfReturnBuffer ) - 1;
/* Add one for the preceeding zero. */
lpReturnBuffer = ( lpStartOfReturnBuffer + 1 );
if ( nTempBufferLength >= count && count >= 0 )
{
/* Determine whether I need to round up. */
if ( *(lpReturnBuffer + count) >= '5' )
{
CHAR cNumberToBeRounded;
if ( count != 0 )
{
cNumberToBeRounded = *(lpReturnBuffer + count - 1);
}
else
{
cNumberToBeRounded = *lpReturnBuffer;
}
if ( cNumberToBeRounded < '9' )
{
if ( count > 0 )
{
/* Add one to the character. */
(*(lpReturnBuffer + count - 1))++;
}
else
{
if ( cNumberToBeRounded >= '5' )
{
(*dec)++;
}
}
}
else
{
LPSTR lpRounding = NULL;
if ( count > 0 )
{
lpRounding = lpReturnBuffer + count - 1;
}
else
{
lpRounding = lpReturnBuffer + count;
}
while ( cNumberToBeRounded == '9' )
{
cNumberToBeRounded = *lpRounding;
if ( cNumberToBeRounded == '9' )
{
*lpRounding = '0';
lpRounding--;
}
}
if ( lpRounding == lpStartOfReturnBuffer )
{
/* Overflow. number is a whole number now. */
*lpRounding = '1';
memset( ++lpRounding, '0', count);
/* The decimal has moved. */
(*dec)++;
}
else
{
*lpRounding = ++cNumberToBeRounded;
}
}
}
else
{
/* Get rid of the preceding 0 */
lpStartOfReturnBuffer++;
}
}
if ( *lpStartOfReturnBuffer == '0' )
{
lpStartOfReturnBuffer++;
}
if ( count >= 0 )
{
*(lpStartOfReturnBuffer + count) = '\0';
}
else
{
*lpStartOfReturnBuffer = '\0';
}
done:
LOGEXIT( "_ecvt returning %p (%s)\n", lpStartOfReturnBuffer , lpStartOfReturnBuffer );
PERF_EXIT(_ecvt);
return lpStartOfReturnBuffer;
}