Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

1878 lines
55 KiB
C

/*H********************************************************************************/
/*!
\File plat-str.c
\Description
This file provides platform independent versions of some standard-C
functions that are not "standard" across platforms and/or fixes
implementation problems with the standard versions (such as consistent
termination).
\Copyright
Copyright (c) 2005-2011 Electronic Arts Inc.
\Version 01/11/2005 (gschaefer) First Version
*/
/********************************************************************************H*/
/*** Include files ****************************************************************/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // tolower
#include "DirtySDK/platform.h"
#include "DirtySDK/dirtysock/dirtynet.h" // sockaddr
/*** Defines **********************************************************************/
/*** Type Definitions *************************************************************/
/*** Variables ********************************************************************/
//! lowercase hex translation table
static const char _ds_strhexlower[16] = "0123456789abcdef";
//! uppercase hex translation table
static const char _ds_strhexupper[16] = "0123456789ABCDEF";
/*** Public functions *************************************************************/
/*F********************************************************************************/
/*!
\Function _ds_writechar
\Description
Write a character to output buffer, if there is room
\Input *pBuffer - [out] output buffer
\Input iLength - size of output buffer
\Input cWrite - character to write to output
\Input iOutput - offset in output buffer to write to
\Output
int32_t - iOutput+1
\Version 02/14/2011 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _ds_writechar(char *pBuffer, int32_t iLength, char cWrite, int32_t iOutput)
{
if (iOutput < iLength)
{
pBuffer[iOutput] = cWrite;
}
return(iOutput+1);
}
/*F********************************************************************************/
/*!
\Function _ds_writestr
\Description
Write string to output buffer, supporting a source of chars or wchars and a
destination of chars. As per C99 specifications, if the output does not fit in
the given buffer, this function will return the number of characters that would
have been written if the buffer were big enough, but will truncate the string to
preserve buffer integrity.
\Input *pBuffer - [out] output buffer
\Input iLength - size of output buffer
\Input *pString - string to print
\Input iOutput - offset in output buffer to print to
\Input bWideChar - if TRUE, input string uses wide (wchar_t) chars
\Output
int32_t - number of characters written to output buffer
\Version 12/14/2011 (jbrookes) Extracted from _ds_printstr(), added wchar support
*/
/********************************************************************************F*/
static int32_t _ds_writestr(char *pBuffer, int32_t iLength, const char *pString, int32_t iOutput, uint8_t bWideChar)
{
const wchar_t *pWideStr;
int32_t iIndex;
if (!bWideChar)
{
for (iIndex = 0; pString[iIndex] != '\0'; iIndex += 1)
{
iOutput = _ds_writechar(pBuffer, iLength, pString[iIndex], iOutput);
}
}
else
{
for (iIndex = 0, pWideStr = (const wchar_t *)pString; pWideStr[iIndex] != '\0\0'; iIndex += 1)
{
iOutput = _ds_writechar(pBuffer, iLength, (char)pWideStr[iIndex], iOutput);
}
}
return(iOutput);
}
/*F********************************************************************************/
/*!
\Function _ds_printstr
\Description
Print string to output buffer, with leading character/justification/padding.
As per C99 specifications, if the output does not fit in the given buffer,
this function will return the number of characters that would have been written
if the buffer were big enough, but will truncate the string to preserve buffer
integrity.
\Input *pBuffer - [out] output buffer
\Input iLength - size of output buffer
\Input *pString - string to print
\Input iOutput - offset in output buffer to print to
\Input iWidth - field width
\Input bRightJust - if TRUE, right-justify output
\Input bWideChar - if TRUE, input string uses wide (wchar_t) chars
\Input cLead - leading character or zero if none
\Input cPad - character to pad output with if width is larger than string length
\Output
int32_t - number of characters written to output buffer
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _ds_printstr(char *pBuffer, int32_t iLength, const char *pString, int32_t iOutput, int32_t iWidth, uint8_t bRightJust, uint8_t bWideChar, char cPad, char cLead)
{
int32_t iStrLen;
// allow null pointer for source string
if (pString == NULL)
{
pString = "(null)";
}
// calculate field width
if (iWidth > 0)
{
iStrLen = (int32_t) strlen(pString);
iWidth = (iStrLen < iWidth) ? iWidth - iStrLen : 0;
}
// handle right justification
if (bRightJust)
{
// handle lead character
if (cLead != 0)
{
// only add the lead character if we are not padding with spaces
if (cPad != ' ')
{
iOutput = _ds_writechar(pBuffer, iLength, cLead, iOutput);
cLead = 0;
}
// always decrement width
if (iWidth > 0)
{
iWidth -= 1;
}
}
for ( ; iWidth > 0; iWidth -= 1)
{
iOutput = _ds_writechar(pBuffer, iLength, cPad, iOutput);
}
}
// handle lead character
if (cLead != 0)
{
iOutput = _ds_writechar(pBuffer, iLength, cLead, iOutput);
// only decrement width if we are not right justfied, in which case we've already done it
if ((bRightJust == FALSE) && (iWidth > 0))
{
iWidth -= 1;
}
}
// write string to buffer
iOutput = _ds_writestr(pBuffer, iLength, pString, iOutput, bWideChar);
// handle left justification
for ( ; iWidth > 0; iWidth -= 1)
{
iOutput = _ds_writechar(pBuffer, iLength, cPad, iOutput);
}
return(iOutput);
}
/*F********************************************************************************/
/*!
\Function _ds_uinttostr
\Description
Convert an input unsigned integer into a string.
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input uInteger - unsigned integer to print
\Input uBase - output integer base
\Input *pHexTbl - pointer to hex conversion table (upper or lower case)
\Output
char * - pointer to output string
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_uinttostr(char *pBuffer, int32_t iBufSize, uint64_t uInteger, uint32_t uBase, const char *pHexTbl)
{
int32_t iIndex = iBufSize-1;
uint32_t uDigit;
pBuffer[iIndex--] = '\0';
if (uInteger != 0)
{
for ( ; ; iIndex -= 1)
{
uDigit = uInteger % uBase;
pBuffer[iIndex] = pHexTbl[uDigit];
if ((uInteger /= uBase) == 0)
{
break;
}
}
}
else
{
pBuffer[iIndex] = '0';
}
return(pBuffer+iIndex);
}
/*F********************************************************************************/
/*!
\Function _ds_inttostr
\Description
Convert an input integer into a string.
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input iInteger - integer to print
\Input uBase - output integer base
\Input *pHexTbl - pointer to hex conversion table (upper or lower case)
\Input bPrintPlus - if TRUE, include + character on positive integers
\Input *pLead - [out] leading character to print (+/- or none)
\Output
char * - pointer to output string
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_inttostr(char *pBuffer, int32_t iBufSize, int64_t iInteger, uint32_t uBase, const char *pHexTbl, uint8_t bPrintPlus, char *pLead)
{
uint8_t bNegative = FALSE;
if (iInteger < 0)
{
bNegative = TRUE;
iInteger = -iInteger;
}
pBuffer = _ds_uinttostr(pBuffer, iBufSize, (uint64_t)iInteger, uBase, pHexTbl);
if (bNegative)
{
*pLead = '-';
}
else if (bPrintPlus)
{
*pLead = '+';
}
return(pBuffer);
}
/*F********************************************************************************/
/*!
\Function _ds_fcvt
\Description
Convert a double into a string (like fcvt()).
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input fDouble - double to print
\Input iPrecision - number of digits to include in mantissa
\Input *pDecimalPos - [out] storage for decimal point offset
\Input *pSign - [out] 0=positive, 1=negative
\Output
char * - pointer to output string
\Notes
Format of a double
seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff (1 11 52)
\Version 05/03/2010 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_fcvt(char *pBuffer, int32_t iBufSize, double fDouble, int32_t iPrecision, int32_t *pDecimalPos, int32_t *pSign)
{
char strFloat[128] = "0", strInt[128], *pStrInt, *pStrFloat, *pStrFloatRnd, *pStrFloatStrt;
uint64_t uDouble, uMantissa, uDivisor, uDigit = 0;
int32_t iExponent, iFloatBufSize = sizeof(strFloat) - 1, iShift;
uint32_t uSign, uUpper;
ds_memcpy_s(&uDouble, sizeof(uDouble), &fDouble, sizeof(fDouble));
uUpper = (uint32_t)(uDouble >> 32);
iExponent = (int32_t)((uUpper >> 20) & 0x7ff) - 1023;
uMantissa = ((uDouble << 12) >> 12) | ((uint64_t)1 << 52);
uSign = uUpper >> 31;
*pSign = uSign;
*pDecimalPos = 0;
pStrFloat = pStrFloatStrt = &strFloat[1];
// right-bias mantissa
for (iShift = 0; (uMantissa & 1) != 1 && (52-iExponent-iShift) > 0; iShift += 1)
{
uMantissa >>= 1;
}
// give us a little more room to handle very large numbers
if (iExponent > 52)
{
if ((iShift = iExponent - 52) > 11)
{
// too big to fit in a 64bit integer
ds_strnzcpy(pBuffer, "(BIG)", iBufSize);
return(pBuffer);
}
// left-bias mantissa to reduce exponent
uMantissa <<= iShift;
iExponent -= iShift;
iShift = 0;
}
// for very small numbers; right-shift (losing precision) until our integer divisor is within range
for ( ; (52-iExponent-iShift) > 62; iShift += 1)
{
uMantissa >>= 1;
}
uDivisor = (uint64_t)1 << (52-iExponent-iShift);
// integer part
if (uMantissa >= uDivisor)
{
uDigit = uMantissa / uDivisor;
pStrInt = _ds_uinttostr(strInt, sizeof(strInt), uDigit, 10, _ds_strhexlower);
ds_strnzcpy(pStrFloat, pStrInt, iFloatBufSize);
*pDecimalPos = (int32_t)strlen(pStrInt);
pStrFloat += *pDecimalPos;
uMantissa -= uDigit*uDivisor;
uDigit = 0;
}
// fractional part
for ( ; uMantissa > 0; iPrecision -= 1)
{
uMantissa = uMantissa * 10;
uDigit = uMantissa / uDivisor;
if (iPrecision == 0)
{
break;
}
*pStrFloat++ = (uint8_t)(uDigit + '0');
uMantissa -= uDigit*uDivisor;
}
// round?
if ((iPrecision == 0) && (uDigit >= 5))
{
// null-terminate
*pStrFloat = '\0';
for (pStrFloatRnd = pStrFloat - 1; ; pStrFloatRnd -= 1)
{
*pStrFloatRnd += 1;
if (*pStrFloatRnd <= '9')
{
break;
}
*pStrFloatRnd = '0';
}
if (pStrFloatRnd == strFloat)
{
pStrFloatStrt = strFloat;
*pDecimalPos += 1;
}
}
else
{
// pad with zeros
for ( ; iPrecision > 0; iPrecision -= 1)
{
*pStrFloat++ = '0';
}
// null-terminate
*pStrFloat++ = '\0';
}
// copy to output and return to caller
ds_strnzcpy(pBuffer, pStrFloatStrt, iBufSize);
return(pBuffer);
}
/*F********************************************************************************/
/*!
\Function _ds_floattostr
\Description
Convert an 32bit floating point number into a string.
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input fDouble - double to print
\Input iPrecision - number of digits to include in mantissa
\Input bPrintPlus - if TRUE, include + character on positive integers
\Input *pLead - [out] leading character to print (+/- or none)
\Output
char * - pointer to output string
\Version 05/03/2010 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_floattostr(char *pBuffer, int32_t iBufSize, double fDouble, int32_t iPrecision, uint32_t bPrintPlus, char *pLead)
{
int32_t iDecimalPos, iSign, iIndex = 0;
char strFloat[128], strFmtFloat[128];
// default precision
if (iPrecision < 0)
{
iPrecision = 6; //c99 default
}
// float to string
#if 0
ds_strnzcpy(strFloat, fcvt(fDouble, iPrecision, &iDecimalPos, &iSign), sizeof(strFloat));
#else
_ds_fcvt(strFloat, sizeof(strFloat), fDouble, iPrecision, &iDecimalPos, &iSign);
#endif
// handle sign
if (iSign != 0)
{
*pLead = '-';
}
else if (bPrintPlus)
{
*pLead = '+';
}
// copy integer portion
if (iDecimalPos > 0)
{
ds_strsubzcpy(&strFmtFloat[iIndex], sizeof(strFmtFloat) - iIndex, strFloat, iDecimalPos);
iIndex += iDecimalPos;
}
else
{
strFmtFloat[iIndex++] = '0';
}
// add decimal point and fractional portion
if (strFloat[iDecimalPos] != '\0')
{
strFmtFloat[iIndex++] = '.';
}
for ( ; iDecimalPos < 0 && iPrecision > 0; iDecimalPos += 1, iPrecision -= 1)
{
strFmtFloat[iIndex++] = '0';
}
if (iDecimalPos >= 0)
{
ds_strnzcpy(&strFmtFloat[iIndex], &strFloat[iDecimalPos], sizeof(strFmtFloat)-iIndex);
}
else
{
strFmtFloat[iIndex++] = '\0';
}
// copy to buffer
ds_strnzcpy(pBuffer, strFmtFloat, iBufSize);
return(pBuffer);
}
/*F********************************************************************************/
/*!
\Function _ds_ptrtostr
\Description
Convert an input integer into a string.
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input *pPointer - pointer value to print
\Output
char * - pointer to output string
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_ptrtostr(char *pBuffer, int32_t iBufSize, void *pPointer)
{
if (pPointer == NULL)
{
ds_strnzcpy(pBuffer, "(null)", iBufSize);
return(pBuffer);
}
pBuffer = _ds_uinttostr(pBuffer, iBufSize, (uintptr_t)pPointer, 16, _ds_strhexlower);
return(pBuffer);
}
/*F********************************************************************************/
/*!
\Function _ds_sockaddrtostr
\Description
Convert an input sockaddr into string format (supports AF_INET and AF_INET6)
Important on the nx only AF_INET addresses arew supported
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input *pSockAddr - address to print
\Output
char * - pointer to output string
\Notes
Reference: http://tools.ietf.org/html/rfc5952#section-4
\Version 04/24/2013 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_sockaddrtostr(char *pBuffer, int32_t iBufSize, struct sockaddr *pSockAddr)
{
char *pBufStart = pBuffer;
char strPort[16] = "", strAddr[48];
uint16_t uPort = 0;
// format port string, if non-zero
#ifdef DIRTYCODE_NX
uPort = SockaddrInGetPort(pSockAddr);
#else
struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)pSockAddr;
uPort = (pSockAddr->sa_family == AF_INET) ? SockaddrInGetPort(pSockAddr) : SocketHtons(pSockAddr6->sin6_port);
#endif
if (uPort != 0)
{
ds_snzprintf(strPort, sizeof(strPort), ":%d", uPort);
}
// format the address
ds_snzprintf(pBuffer, iBufSize, "[%s]%s", SockaddrInGetAddrText(pSockAddr, strAddr, sizeof(strAddr)), strPort);
// return buffer to caller
return(pBufStart);
}
/*F********************************************************************************/
/*!
\Function _ds_fourcctostr
\Description
Convert an input fourcc code into a string. Unprintable characters
are converted to asterisks ('*').
\Input *pBuffer - [out] output buffer
\Input iBufSize - size of output buffer
\Input uFourCC - fourcc code to print
\Output
char * - pointer to output string
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_fourcctostr(char *pBuffer, int32_t iBufSize, uint32_t uFourCC)
{
int32_t iCode;
for (iCode = 3; iCode >= 0; iCode -= 1)
{
pBuffer[iCode] = (char)(uFourCC & 0xff);
uFourCC >>= 8;
if (!isprint((uint8_t)pBuffer[iCode]))
{
pBuffer[iCode] = '*';
}
}
pBuffer[4] = '\0';
return(pBuffer);
}
/*F********************************************************************************/
/*!
\Function _ds_vsnprintf
\Description
Replacement for vsnprintf, with some modifications.
\Input *pBuffer - output buffer
\Input iLength - size of output buffer
\Input *pFormat - format string
\Input Args - variable argument list
\Output
int32_t - number of characters formatted; if > than the length, output
is truncated and the buffer is null-terminated unless iLength<=0
\Notes
Unlike a standard implementation of vsnprintf(), this version always
null-terminates unless the buffer size is zero. Overflow semantics follow
the C99 standard.
\verbatim
Most ISO printf functionality is supported, with the following exceptions:
- Floating point is supported with the following limitations:
- All floating-point formatters behave like 'f'
- Very large (> 2^63) floating point numbers will return (BIG)
- The # flag is not supported, although it is consumed.
- Length is not supported, although length specifiers are consumed.
Additionally, the following extension types replace their normal meaning:
- %a: print 32-bit address in dot notation (32-bit integer)
- %A: print a sockaddr (IPv4 or IPv6)
- %C: print fourcc code (32-bit integer)
\endverbatim
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _ds_vsnprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args)
{
int32_t iInput, iOutput, iTerm;
char strTempBuf[64], *pString;
char cInput;
// handle format list
for (iInput = 0, iOutput = 0; ; )
{
// read input character
if ((cInput = pFormat[iInput++]) == '\0')
{
break;
}
// process a format specifier
if (cInput == '%')
{
int32_t iPrecision = -1, iSize = 4, iWidth = 0;
uint8_t bRightJust = TRUE, bPrintPlus = FALSE, bWideChar = FALSE;
char cPad = ' ', cLead = 0;
// handle end of string
if (pFormat[iInput] == '\0')
{
break;
}
// process %%
if (pFormat[iInput] == '%')
{
iOutput = _ds_writechar(pBuffer, iLength, pFormat[iInput], iOutput);
iInput += 1;
continue;
}
// process flags
if (pFormat[iInput] == '-')
{
iInput += 1;
bRightJust = FALSE;
}
if (pFormat[iInput] == '+')
{
iInput += 1;
bPrintPlus = TRUE;
}
if (pFormat[iInput] == '#')
{
// unsupported
iInput += 1;
}
if (pFormat[iInput] == '0')
{
iInput += 1;
cPad = '0';
}
// process width
if (pFormat[iInput] == '*')
{
iInput += 1;
iWidth = va_arg(Args, int);
}
for ( ; (pFormat[iInput] >= '0') && (pFormat[iInput] <= '9'); iInput += 1)
{
iWidth *= 10;
iWidth += pFormat[iInput] - '0';
}
// precision specifier?
if (pFormat[iInput] == '.')
{
// process period and precision width
for (iPrecision = 0, iInput += 1; (pFormat[iInput] >= '0') && (pFormat[iInput] <= '9'); iInput += 1)
{
iPrecision *= 10;
iPrecision += pFormat[iInput] - '0';
}
// process asterisk (arg width) if present
if (pFormat[iInput] == '*')
{
iInput += 1;
iWidth = va_arg(Args, int);
}
}
// check for length specifier
cInput = pFormat[iInput++];
if ((cInput == 'h') || (cInput == 'l') || (cInput == 'L') || (cInput == 'z') || (cInput == 'j') || (cInput == 't') || (cInput == 'q') || (cInput == 'I'))
{
if (cInput == 'h')
{
iSize = 2;
}
if (cInput == 'q')
{
iSize = 8;
}
if ((cInput == 'l') && (pFormat[iInput] == 's'))
{
iSize = 8;
}
if (cInput == 'I')
{
if ((pFormat[iInput] == '6') && (pFormat[iInput+1] == '4'))
{
iSize = 8;
}
iInput += 2;
}
cInput = pFormat[iInput++];
if ((cInput == 'h') || (cInput == 'l'))
{
if (cInput == 'h')
{
iSize = 1;
}
if (cInput == 'l')
{
iSize = 8;
}
cInput = pFormat[iInput++];
}
}
// process type specifier
switch(cInput)
{
case 'a':
{
uint32_t uAddress = va_arg(Args, unsigned int);
pString = SocketInAddrGetText(uAddress, strTempBuf, sizeof(strTempBuf));
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'A':
{
struct sockaddr *pSockAddr = va_arg(Args, struct sockaddr *);
pString = _ds_sockaddrtostr(strTempBuf, sizeof(strTempBuf), pSockAddr);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'c':
{
strTempBuf[0] = (char)va_arg(Args, int);
strTempBuf[1] = '\0';
iOutput = _ds_printstr(pBuffer, iLength, strTempBuf, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'C':
{
uint32_t uFourCC = va_arg(Args, unsigned int);
pString = _ds_fourcctostr(strTempBuf, sizeof(strTempBuf), uFourCC);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'd':
case 'i':
{
int64_t iInteger = (iSize <= 4) ? va_arg(Args, int) : va_arg(Args, long long);
pString = _ds_inttostr(strTempBuf, sizeof(strTempBuf), iInteger, 10, _ds_strhexlower, bPrintPlus, &cLead);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, cLead);
}
break;
case 'n':
{
int *pInteger = va_arg(Args, int *);
*pInteger = iOutput;
}
break;
case 'o':
{
uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long);
pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 8, _ds_strhexlower);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'p':
{
void *pPointer = va_arg(Args, void *);
if (pPointer != NULL)
{
iWidth = sizeof(void *) * 2 + 1; // always print full width (+1 for the leading '$')
cLead = '$';
}
pString = _ds_ptrtostr(strTempBuf, sizeof(strTempBuf), pPointer);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, '0', cLead); // always print leading zeros
}
break;
case 'S':
iSize = 8;
case 's':
{
pString = va_arg(Args, char *);
bWideChar = (iSize == 8) ? TRUE : FALSE;
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'u':
{
uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long);
pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 10, _ds_strhexlower);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
case 'x':
case 'X':
{
uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long);
pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 16, cInput == 'x' ? _ds_strhexlower : _ds_strhexupper);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0);
}
break;
// all floating-point handled like 'f'
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
{
double fDouble = va_arg(Args, double);
pString = _ds_floattostr(strTempBuf, sizeof(strTempBuf), fDouble, iPrecision, bPrintPlus, &cLead);
iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, cLead);
}
break;
default:
{
// should not hit this; unsupported type specifier?
}
break;
}
}
else
{
iOutput = _ds_writechar(pBuffer, iLength, cInput, iOutput);
}
}
// locate terminating null
iTerm = (iOutput < iLength) ? iOutput : iLength-1;
// terminate if possible
if (iLength > 0)
{
pBuffer[iTerm] = '\0';
}
// return length of output to caller
return(iOutput);
}
/*F********************************************************************************/
/*!
\Function _ds_strcmpwc
\Description
String compare with wildcard matching ('*' only).
\Input *pString1 - string to match
\Input *pStrWild - wildcard string
\Input bNoCase - TRUE if a case-insensitive comparison should be performed
\Output
int32_t - see stricmp
\Version 09/18/2012 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _ds_strcmpwc(const char *pString1, const char *pStrWild, uint8_t bNoCase)
{
int32_t r;
char c1, c2;
do {
c1 = *pString1++;
c2 = *pStrWild;
if (bNoCase)
{
if ((c1 >= 'A') && (c1 <= 'Z'))
{
c1 ^= 32;
}
if ((c2 >= 'A') && (c2 <= 'Z'))
{
c2 ^= 32;
}
}
if ((c2 == '*') && (c1 != '\0'))
{
if ((r = _ds_strcmpwc(pString1, pStrWild+1, bNoCase)) == 0)
{
break;
}
r = 0;
}
else
{
pStrWild += 1;
r = c1-c2;
}
} while ((c1 != '\0') && (c2 != '\0') && (r == 0));
return(r);
}
/*F********************************************************************************/
/*!
\Function _ds_fmtoctstring
\Description
Format a binary blob as hexadecmial text
\Input *pOutput - [out] outbuffer for text
\Input iOutLen - size of output buffer
\Input *pInput - input binary data
\Input iInpLen - size of input data
\Input *pHexTbl - hex table to use for translation
\Output
char * - pointer to output buffer
\Version 04/11/2017 (jbrookes)
*/
/********************************************************************************F*/
static char *_ds_fmtoctstring(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen, const char *pHexTbl)
{
int32_t iInput, iOutput;
for (iInput = 0, iOutput = 0, iOutLen -= 1; (iInput < iInpLen) && (iOutput < (iOutLen-1)); iInput += 1, iOutput += 2)
{
uint8_t cByte = pInput[iInput];
pOutput[iOutput+0] = pHexTbl[cByte >> 4];
pOutput[iOutput+1] = pHexTbl[cByte & 0xf];
}
pOutput[iOutput] = '\0';
return(pOutput);
}
/*** Public functions *************************************************************/
/*F********************************************************************************/
/*!
\Function ds_strnlen
\Description
Replacement for strnlen.
\Input *pBuffer - buffer
\Input iLength - size of buffer
\Output
int32_t - number of characters or iLength if no '\0' is present.
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strnlen(const char *pBuffer, int32_t iLength)
{
int32_t iLen = 0;
while (iLen < iLength && *(pBuffer+iLen) != '\0')
{
++iLen;
}
return(iLen);
}
/*F********************************************************************************/
/*!
\Function ds_vsnprintf
\Description
Replacement for vsnprintf, with some modifications.
\Input *pBuffer - output buffer
\Input iLength - size of output buffer
\Input *pFormat - format string
\Input Args - variable argument list
\Output
int32_t - number of characters formatted; if > than the length, output
is truncated and the buffer is null-terminated unless iLength<=0.
\Notes
Unlike standard vsnprintf, this version always null-terminates if the buffer
is not zero-sized.
\Version 10/28/2009 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_vsnprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args)
{
return(_ds_vsnprintf(pBuffer, iLength, pFormat, Args));
}
/*F********************************************************************************/
/*!
\Function ds_vsnzprintf
\Description
Replacement for vsnprintf that always includes a terminator at the end of
the string.
\Input *pBuffer - output buffer
\Input iLength - size of output buffer
\Input *pFormat - format string
\Input Args - variable argument list
\Output
int32_t - number of characters formatted; if > than the length, output
is truncated and the buffer is null-terminated unless iLength<=0.
\Version 02/15/2011 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_vsnzprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args)
{
return(_ds_vsnprintf(pBuffer, iLength, pFormat, Args));
}
/*F********************************************************************************/
/*!
\Function ds_snzprintf
\Description
Replacement for snprintf that always includes a terminator at the end of
the string.
\Input *pBuffer - output buffer
\Input iLength - size of output buffer
\Input *pFormat - format string
\Input ... - variable argument list
\Output
int32_t - number of characters formatted; if > than the length, output
is truncated and the buffer is null-terminated unless iLength<=0.
\Version 02/15/2011 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_snzprintf(char *pBuffer, int32_t iLength, const char *pFormat, ...)
{
int32_t iResult;
va_list args;
// format the text
va_start(args, pFormat);
iResult = _ds_vsnprintf(pBuffer, iLength, pFormat, args);
va_end(args);
// return result to caller
return(iResult);
}
/*F********************************************************************************/
/*!
\Function ds_fmtoctstring
\Description
Format a binary blob as hexadecmial text
\Input *pOutput - [out] outbuffer for text
\Input iOutLen - size of output buffer
\Input *pInput - input binary data
\Input iInpLen - size of input data
\Output
char * - pointer to output buffer
\Version 04/11/2017 (jbrookes)
*/
/********************************************************************************F*/
char *ds_fmtoctstring(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen)
{
return(_ds_fmtoctstring(pOutput, iOutLen, pInput, iInpLen, "0123456789ABCDEF"));
}
/*F********************************************************************************/
/*!
\Function ds_fmtoctstring_lc
\Description
Format a binary blob as hexadecmial text, with the alphabetic characters
being lowercase
\Input *pOutput - [out] outbuffer for text
\Input iOutLen - size of output buffer
\Input *pInput - input binary data
\Input iInpLen - size of input data
\Output
char * - pointer to output buffer
\Version 04/11/2017 (jbrookes)
*/
/********************************************************************************F*/
char *ds_fmtoctstring_lc(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen)
{
return(_ds_fmtoctstring(pOutput, iOutLen, pInput, iInpLen, "0123456789abcdef"));
}
/*F********************************************************************************/
/*!
\Function ds_strtolower
\Description
Convert given string to lower case
\Input *pString - string to convert
\Output
char * - pointer to string
\Version 12/28/2019 (jbrookes)
*/
/********************************************************************************F*/
char *ds_strtolower(char *pString)
{
char *pStrStart = pString;
for (; *pString != '\0'; pString += 1)
{
if ((*pString >= 'A') && (*pString <= 'Z'))
{
*pString += 0x20;
}
}
return(pStrStart);
}
/*F********************************************************************************/
/*!
\Function ds_strtoupper
\Description
Convert given string to upper case
\Input *pString - string to convert
\Output
char * - pointer to string
\Version 12/28/2019 (jbrookes)
*/
/********************************************************************************F*/
char *ds_strtoupper(char *pString)
{
char *pStrStart = pString;
for (; *pString != '\0'; pString += 1)
{
if ((*pString >= 'a') && (*pString <= 'z'))
{
*pString -= 0x20;
}
}
return(pStrStart);
}
/*F********************************************************************************/
/*!
\Function ds_strtrim
\Description
Removes excess white space before and after values, and converts sequential
spaces to a single space.
\Input *pString - [out] string to trim
\Output
char * - pointer to string
\Version 12/28/2018 (jbrookes)
*/
/********************************************************************************F*/
char *ds_strtrim(char *pString)
{
char *pStrStart = pString, *pStrTrim;
// skip leading whitespace
for (; *pString == ' '; pString += 1)
;
// copy characters, converting sequential spaces into a single space
for (pStrTrim = pString; *pString != '\0'; )
{
*pStrTrim++ = *pString;
if (*pString == ' ')
{
// eat multiple spaces
for (; *pString == ' '; pString += 1)
;
}
else
{
pString += 1;
}
}
// terminate string
*pString = '\0';
// return start of string
return(pStrStart);
}
/*F********************************************************************************/
/*!
\Function ds_strlistinsert
\Description
Insert string into string list buffer in sorted order, terminated by
the specified terminator. The list terminator may not be nul and it must
be a character that does not appear in strings inserted in the list.
\Input *pStrList - [in/out] buffer to hold string list
\Input iListBufLen - length of list buffer
\Input *pString - string to insert in string list
\Input cTerm - string terminator
\Output
int32_t - positive on success, zero if string could not be inserted
\Version 12/29/2018 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strlistinsert(char *pStrList, int32_t iListBufLen, const char *pString, char cTerm)
{
char *pStrInsert, *pStrSkip;
int32_t iStrLen, iListLen;
// get length of string we're inserting
iStrLen = (int32_t)strlen(pString);
// get length of strlist
iListLen = (int32_t)strlen(pStrList);
// make sure we have room to insert
if (iListBufLen < (iListLen+iStrLen+1))
{
return(0);
}
// find insertion point
for (pStrInsert = pStrSkip = pStrList; pStrSkip != NULL; )
{
if (ds_stricmp(pStrInsert, pString) >= 0)
{
break;
}
pStrInsert = pStrSkip;
if ((pStrSkip = strchr(pStrSkip, cTerm)) != NULL)
{
pStrSkip += 1;
}
}
// make room for formatted header if necessary
if (*pStrInsert != '\0')
{
// get length of list from here to end of the buffer
iListLen = (int32_t)strlen(pStrInsert);
// make room for string, plus terminator character
memmove(pStrInsert+iStrLen+1, pStrInsert, iListLen);
// point to insertion point
iListBufLen -= pStrInsert-pStrList;
pStrList = pStrInsert;
}
else
{
// end of list, so null terminate
pStrInsert[iStrLen+1] = '\0';
}
// copy formatted header into list and terminate it
ds_memcpy_s(pStrInsert, iListBufLen, pString, iStrLen);
pStrInsert[iStrLen] = cTerm;
return(1);
}
/*F********************************************************************************/
/*!
\Function ds_stristr
\Description
Case insensitive substring search
\Input pHaystack - see stristr
\Input pNeedle - see stristr
\Output see stristr
\Version 01/25/2005 (gschaefer)
*/
/********************************************************************************F*/
char *ds_stristr(const char *pHaystack, const char *pNeedle)
{
int32_t iFirst;
int32_t iIndex;
// make sure strings are valid
if ((pHaystack != NULL) && (*pHaystack != 0) && (pNeedle != NULL) && (*pNeedle != 0))
{
iFirst = tolower((unsigned char)*pNeedle);
for (; *pHaystack != 0; ++pHaystack)
{
// small optimization on first character search
if (tolower((unsigned char)*pHaystack) == iFirst)
{
for (iIndex = 1;; ++iIndex)
{
if (pNeedle[iIndex] == 0)
{
return((char *)pHaystack);
}
if (pHaystack[iIndex] == 0)
{
break;
}
if (tolower((unsigned char)pHaystack[iIndex]) != tolower((unsigned char)pNeedle[iIndex]))
{
break;
}
}
}
}
}
return(NULL);
}
/*F********************************************************************************/
/*!
\Function ds_stricmp
\Description
Case insensitive string compare
\Input *pString1 - see stricmp
\Input *pString2 - see stricmp
\Output see stricmp
\Version 01/25/2005 (gschaefer)
*/
/********************************************************************************F*/
int32_t ds_stricmp(const char *pString1, const char *pString2)
{
int32_t r;
char c1, c2;
do {
c1 = *pString1++;
if ((c1 >= 'A') && (c1 <= 'Z'))
c1 ^= 32;
c2 = *pString2++;
if ((c2 >= 'A') && (c2 <= 'Z'))
c2 ^= 32;
r = c1-c2;
} while ((c1 != 0) && (r == 0));
return(r);
}
/*F********************************************************************************/
/*!
\Function ds_strnicmp
\Description
Case insensitive string compare of first N characters
\Input *pString1 - see strnicmp
\Input *pString2 - see strnicmp
\Input uCount - see strnicmp
\Output see strnicmp
\Version 01/25/2005 (gschaefer)
*/
/********************************************************************************F*/
int32_t ds_strnicmp(const char *pString1, const char *pString2, uint32_t uCount)
{
int32_t r;
char c1, c2;
uint32_t uPos;
if (uCount == 0)
return(0);
uPos = 0;
do {
c1 = *pString1++;
if ((c1 >= 'A') && (c1 <= 'Z'))
c1 ^= 32;
c2 = *pString2++;
if ((c2 >= 'A') && (c2 <= 'Z'))
c2 ^= 32;
r = c1-c2;
uPos++;
} while ((c1 != 0) && (r == 0) && (uPos < uCount));
return(r);
}
/*F********************************************************************************/
/*!
\Function ds_strcmpwc
\Description
String compare with wildcard matching ('*' only).
\Input *pString1 - see stricmp
\Input *pStrWild - match string including wildcard(s)
\Output see stricmp
\Version 09/18/2012 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strcmpwc(const char *pString1, const char *pStrWild)
{
return(_ds_strcmpwc(pString1, pStrWild, FALSE));
}
/*F********************************************************************************/
/*!
\Function ds_stricmpwc
\Description
String compare with wildcard matching ('*' only).
\Input *pString1 - see stricmp
\Input *pStrWild - match string including wildcard(s)
\Output see stricmp
\Version 09/18/2012 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_stricmpwc(const char *pString1, const char *pStrWild)
{
return(_ds_strcmpwc(pString1, pStrWild, TRUE));
}
/*F********************************************************************************/
/*!
\Function ds_strnzcpy
\Description
Always terminated strncpy function
\Input *pDest - [out] output for new string
\Input *pSource - pointer to input string to copy from
\Input iCount - length of output buffer
\Output
char * - pointer to output buffer
\Notes
Unlike strncpy(), the destination buffer is not filled with zeros if the
string being copied does not fill it.
\Version 01/11/2017 (jbrookes)
*/
/********************************************************************************F*/
char *ds_strnzcpy(char *pDest, const char *pSource, int32_t iCount)
{
int32_t iIndex;
// make sure buffer has enough room
if (--iCount < 0)
{
return(0);
}
// copy the string
for (iIndex = 0; (iIndex < iCount) && (pSource[iIndex] != '\0'); iIndex += 1)
{
pDest[iIndex] = pSource[iIndex];
}
// write null terminator and return number of bytes written
pDest[iIndex] = '\0';
return(pDest);
}
/*F********************************************************************************/
/*!
\Function ds_strsubzcpy
\Description
Copy a substring from pSrc into the output buffer. The output string
is guaranteed to be null-terminated.
\Input *pDst - [out] output for new string
\Input iDstLen - length of output buffer
\Input *pSrc - pointer to input string to copy from
\Input iSrcLen - number of characters to copy from input string
\Output
int32_t - number of characters written, excluding null character
\Version 09/27/2005 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strsubzcpy(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen)
{
int32_t iIndex;
// make sure buffer has enough room
if (--iDstLen < 0)
{
return(0);
}
// copy the string
for (iIndex = 0; (iIndex < iSrcLen) && (iIndex < iDstLen) && (pSrc[iIndex] != '\0'); iIndex++)
{
pDst[iIndex] = pSrc[iIndex];
}
// write null terminator and return number of bytes written
pDst[iIndex] = '\0';
return(iIndex);
}
/*F********************************************************************************/
/*!
\Function ds_strnzcat
\Description
Concatenate the string pointed to by pSrc to the string pointed to by pDst.
A maximum of iDstLen-1 characters are copied, and the resulting string is
guaranteed to be null-terminated.
\Input *pDst - [out] output for new string
\Input *pSrc - pointer to input string to copy from
\Input iDstLen - size of output buffer
\Output
int32_t - number of characters in pDst, excluding null character
\Version 09/27/2005 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strnzcat(char *pDst, const char *pSrc, int32_t iDstLen)
{
int32_t iDst, iSrc;
// make sure buffer has enough room
if (--iDstLen < 0)
{
return(0);
}
// find end of string
for (iDst = 0; (iDst < iDstLen) && (pDst[iDst] != '\0'); iDst++)
;
// copy the string
for (iSrc = 0; (iDst < iDstLen) && (pSrc[iSrc] != '\0'); iSrc++, iDst++)
{
pDst[iDst] = pSrc[iSrc];
}
// write null terminator and return updated length of string
pDst[iDst] = '\0';
return(iDst);
}
/*F********************************************************************************/
/*!
\Function ds_strsubzcat
\Description
Concatenate the substring pointed to by pSrc and with a length of iSrcLen
to the string pointed to by pDst. A maximum of iDstLen-1 or iSrcLen
characters are copied, whichever is smaller, and the resulting string is
guaranteed to be null-terminated.
\Input *pDst - [out] output for new string
\Input iDstLen - size of output buffer
\Input *pSrc - pointer to input string to copy from
\Input iSrcLen - size of substring pointed to by pSrc.
\Output
int32_t - number of characters in pDst, excluding null character
\Version 09/27/2005 (jbrookes)
*/
/********************************************************************************F*/
int32_t ds_strsubzcat(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen)
{
int32_t iDst, iSrc;
// make sure buffer has enough room
if (--iDstLen < 0)
{
return(0);
}
// find end of string
for (iDst = 0; (iDst < iDstLen) && (pDst[iDst] != '\0'); iDst++)
;
// copy the string
for (iSrc = 0; (iDst < iDstLen) && (iSrc < iSrcLen) && (pSrc[iSrc] != '\0'); iSrc++, iDst++)
{
pDst[iDst] = pSrc[iSrc];
}
// write null terminator and return updated length of string
pDst[iDst] = '\0';
return(iDst);
}
/*F********************************************************************************/
/*!
\Function ds_strsplit
\Description
Splits string on a delimiter
\Input *pSrc - the string we are splitting
\Input cDelimiter - delimited to split on
\Input *pDst - where we are writing the data split
\Input iDstSize - size of dest string
\Input **pNewSrc - used to save position of iteration
\Output
int32_t - length of destination or zero if nothing parsed
\Version 08/04/2016 (eesponda)
*/
/********************************************************************************F*/
int32_t ds_strsplit(const char *pSrc, char cDelimiter, char *pDst, int32_t iDstSize, const char **pNewSrc)
{
const char *pLocation;
int32_t iSrcSize;
// check for validity of inputs
if ((pSrc == NULL) || (*pSrc == '\0'))
{
return(0);
}
iSrcSize = (int32_t)strlen(pSrc);
/* if we start with delimiter skip it
this happens when you do multiple parses */
if (pSrc[0] == cDelimiter)
{
pSrc += 1;
iSrcSize -= 1;
(*pNewSrc) += 1;
}
// terminate the destination
if (pDst != NULL && iDstSize > 0)
{
pDst[0] = '\0';
}
/* write the value to the destination (if not null)
when length is less than max, add 1 for nul terminator */
if ((pLocation = strchr(pSrc, cDelimiter)) != NULL)
{
const int32_t iCount = (int32_t)(pLocation - pSrc);
(*pNewSrc) += iCount;
if (pDst != NULL)
{
ds_strnzcpy(pDst, pSrc, DS_MIN(iDstSize, iCount+1));
}
return(iCount);
}
else
{
(*pNewSrc) += iSrcSize;
if (pDst != NULL)
{
ds_strnzcpy(pDst, pSrc, DS_MIN(iDstSize, iSrcSize+1));
}
return(iSrcSize);
}
}
/*F********************************************************************************/
/*!
\Function ds_strtok_r
\Description
Reentrant version of strtok
\Input *pStr - input string
\Input *pDelim - delimiter
\Input **ppSavePtr - variable used to save down the state
\Output
char* - reutrns the pointer to the next token
\Version 09/07/2018 (tcho)
*/
/********************************************************************************F*/
char* ds_strtok_r(char *pStr, const char *pDelim, char **ppSavePtr)
{
#if defined(DIRTYCODE_NX)
char *pEnd;
if (pStr == NULL)
{
pStr = *ppSavePtr;
}
if (*pStr == '\0')
{
*ppSavePtr = pStr;
return(NULL);
}
// scan leading character
pStr += strspn(pStr, pDelim);
if (*pStr == '\0')
{
*ppSavePtr = pStr;
return(NULL);
}
// find end of token
pEnd = pStr + strcspn(pStr, pDelim);
if (*pEnd == '\0')
{
*ppSavePtr = pEnd;
return(pStr);
}
*pEnd = '\0';
*ppSavePtr = pEnd + 1;
return pStr;
#elif defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
return strtok_s(pStr, pDelim, ppSavePtr);
#else
return strtok_r(pStr, pDelim, ppSavePtr);
#endif
}
/*F********************************************************************************/
/*!
\Function ds_memcpy
\Description
Replacement for memcpy.
\Input *pDst - [out] output for new string
\Input *pSrc - pointer to input string to copy from
\Input iDstLen - size of substring pointed to by pSrc.
\Version 07/15/2014 (amakoukji)
*/
/********************************************************************************F*/
void ds_memcpy(void *pDst, const void *pSrc, int32_t iDstLen)
{
memcpy(pDst, pSrc, iDstLen);
}
/*F********************************************************************************/
/*!
\Function ds_memcpy_s
\Description
Replacement for memcpy_s.
\Input *pDst - [out] output for new string
\Input iDstLen - size of output buffer
\Input *pSrc - pointer to input string to copy from
\Input iSrcLen - size of substring pointed to by pSrc.
\Version 07/15/2014 (amakoukji)
*/
/********************************************************************************F*/
void ds_memcpy_s(void *pDst, int32_t iDstLen, const void *pSrc, int32_t iSrcLen)
{
iDstLen = DS_MIN(iDstLen, iSrcLen);
memcpy(pDst, pSrc, iDstLen);
}
/*F********************************************************************************/
/*!
\Function ds_memclr
\Description
Memset that always clears the memory with zero
\Input *pMem - memory that is being cleared
\Input iCount - the number of bytes to clear
\Version 01/06/2017 (eesponda)
*/
/********************************************************************************F*/
void ds_memclr(void *pMem, int32_t iCount)
{
memset(pMem, 0, iCount);
}
/*F********************************************************************************/
/*!
\Function ds_memset
\Description
Replacement for memset
\Input *pMem - memory that is being cleared
\Input iValue - value to clear the memory with
\Input iCount - the number of bytes to clear
\Version 01/06/2017 (eesponda)
*/
/********************************************************************************F*/
void ds_memset(void *pMem, int32_t iValue, int32_t iCount)
{
memset(pMem, iValue, iCount);
}