mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
1878 lines
55 KiB
C
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);
|
|
}
|