1854 lines
52 KiB
C
1854 lines
52 KiB
C
|
//###########################################################################
|
||
|
//
|
||
|
// FILE: ustdlib.c
|
||
|
//
|
||
|
// TITLE: Simple standard library functions.
|
||
|
//
|
||
|
//###########################################################################
|
||
|
// $TI Release: F2837xD Support Library v3.05.00.00 $
|
||
|
// $Release Date: Tue Jun 26 03:15:23 CDT 2018 $
|
||
|
// $Copyright:
|
||
|
// Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com/
|
||
|
//
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions
|
||
|
// are met:
|
||
|
//
|
||
|
// Redistributions of source code must retain the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer.
|
||
|
//
|
||
|
// Redistributions in binary form must reproduce the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer in the
|
||
|
// documentation and/or other materials provided with the
|
||
|
// distribution.
|
||
|
//
|
||
|
// Neither the name of Texas Instruments Incorporated nor the names of
|
||
|
// its contributors may be used to endorse or promote products derived
|
||
|
// from this software without specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
// $
|
||
|
//###########################################################################
|
||
|
|
||
|
//
|
||
|
// Included Files
|
||
|
//
|
||
|
#include <stdint.h>
|
||
|
#include <stdbool.h>
|
||
|
#include "driverlib/debug.h"
|
||
|
#include "utils/ustdlib.h"
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! \addtogroup ustdlib_api
|
||
|
//! @{
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
|
||
|
//
|
||
|
// Globals
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// A mapping from an integer between 0 and 15 to its ASCII character
|
||
|
// equivalent.
|
||
|
//
|
||
|
static const char * const g_pcHex = "0123456789abcdef";
|
||
|
|
||
|
//
|
||
|
// Functions
|
||
|
//
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Copies a certain number of characters from one string to another.
|
||
|
//!
|
||
|
//! \param s1 is a pointer to the destination buffer into which characters
|
||
|
//! are to be copied.
|
||
|
//! \param s2 is a pointer to the string from which characters are to be
|
||
|
//! copied.
|
||
|
//! \param n is the number of characters to copy to the destination buffer.
|
||
|
//!
|
||
|
//! This function copies at most \e n characters from the string pointed to
|
||
|
//! by \e s2 into the buffer pointed to by \e s1. If the end of \e s2 is found
|
||
|
//! before \e n characters have been copied, remaining characters in \e s1
|
||
|
//! will be padded with zeroes until \e n characters have been written. Note
|
||
|
//! that the destination string will only be NULL terminated if the number of
|
||
|
//! characters to be copied is greater than the length of \e s2.
|
||
|
//!
|
||
|
//! \return Returns \e s1.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
char *
|
||
|
ustrncpy(char * restrict s1, const char * restrict s2, size_t n)
|
||
|
{
|
||
|
size_t count;
|
||
|
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
ASSERT(s1);
|
||
|
ASSERT(s2);
|
||
|
|
||
|
//
|
||
|
// Start at the beginning of the source string.
|
||
|
//
|
||
|
count = 0;
|
||
|
|
||
|
//
|
||
|
// Copy the source string until we run out of source characters or
|
||
|
// destination space.
|
||
|
//
|
||
|
while(n && s2[count])
|
||
|
{
|
||
|
s1[count] = s2[count];
|
||
|
count++;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Pad the destination if we are not yet done.
|
||
|
//
|
||
|
while(n)
|
||
|
{
|
||
|
s1[count++] = (char)0;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Pass the destination pointer back to the caller.
|
||
|
//
|
||
|
return(s1);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! A simple vsnprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and
|
||
|
//! \%X.
|
||
|
//!
|
||
|
//! \param s points to the buffer where the converted string is stored.
|
||
|
//! \param n is the size of the buffer.
|
||
|
//! \param format is the format string.
|
||
|
//! \param arg is the list of optional arguments, which depend on the
|
||
|
//! contents of the format string.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>vsnprintf()</tt>
|
||
|
//! function. Only the following formatting characters are supported:
|
||
|
//!
|
||
|
//! - \%c to print a character
|
||
|
//! - \%d or \%i to print a decimal value
|
||
|
//! - \%s to print a string
|
||
|
//! - \%u to print an unsigned decimal value
|
||
|
//! - \%x to print a hexadecimal value using lower case letters
|
||
|
//! - \%X to print a hexadecimal value using lower case letters (not upper case
|
||
|
//! letters as would typically be used)
|
||
|
//! - \%p to print a pointer as a hexadecimal value
|
||
|
//! - \%\% to print out a \% character
|
||
|
//!
|
||
|
//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside
|
||
|
//! between the \% and the format character, which specifies the minimum number
|
||
|
//! of characters to use for that value; if preceded by a 0 then the extra
|
||
|
//! characters will be filled with zeros instead of spaces. For example,
|
||
|
//! ``\%8d'' will use eight characters to print the decimal value with spaces
|
||
|
//! added to reach eight; ``\%08d'' will use eight characters as well but will
|
||
|
//! add zeroes instead of spaces.
|
||
|
//!
|
||
|
//! The type of the arguments after \e format must match the requirements of
|
||
|
//! the format string. For example, if an integer was passed where a string
|
||
|
//! was expected, an error of some kind will most likely occur.
|
||
|
//!
|
||
|
//! The \e n parameter limits the number of characters that will be
|
||
|
//! stored in the buffer pointed to by \e s to prevent the possibility of
|
||
|
//! a buffer overflow. The buffer size should be large enough to hold the
|
||
|
//! expected converted output string, including the null termination character.
|
||
|
//!
|
||
|
//! The function will return the number of characters that would be converted
|
||
|
//! as if there were no limit on the buffer size. Therefore it is possible for
|
||
|
//! the function to return a count that is greater than the specified buffer
|
||
|
//! size. If this happens, it means that the output was truncated.
|
||
|
//!
|
||
|
//! \return Returns the number of characters that were to be stored, not
|
||
|
//! including the NULL termination character, regardless of space in the
|
||
|
//! buffer.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
uvsnprintf(char * restrict s, size_t n, const char * restrict format,
|
||
|
va_list arg)
|
||
|
{
|
||
|
unsigned long ulIdx, ulValue, ulCount, ulBase, ulNeg;
|
||
|
char *pcStr, cFill;
|
||
|
int iConvertCount = 0;
|
||
|
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
ASSERT(s);
|
||
|
ASSERT(n);
|
||
|
ASSERT(format);
|
||
|
|
||
|
//
|
||
|
// Adjust buffer size limit to allow one space for null termination.
|
||
|
//
|
||
|
if(n)
|
||
|
{
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the count of characters converted.
|
||
|
//
|
||
|
iConvertCount = 0;
|
||
|
|
||
|
//
|
||
|
// Loop while there are more characters in the format string.
|
||
|
//
|
||
|
while(*format)
|
||
|
{
|
||
|
//
|
||
|
// Find the first non-% character, or the end of the string.
|
||
|
//
|
||
|
for(ulIdx = 0; (format[ulIdx] != '%') && (format[ulIdx] != '\0');
|
||
|
ulIdx++)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write this portion of the string to the output buffer. If there are
|
||
|
// more characters to write than there is space in the buffer, then
|
||
|
// only write as much as will fit in the buffer.
|
||
|
//
|
||
|
if(ulIdx > n)
|
||
|
{
|
||
|
ustrncpy(s, format, n);
|
||
|
s += n;
|
||
|
n = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ustrncpy(s, format, ulIdx);
|
||
|
s += ulIdx;
|
||
|
n -= ulIdx;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count. This will be the number of characters
|
||
|
// that should have been written, even if there was not room in the
|
||
|
// buffer.
|
||
|
//
|
||
|
iConvertCount += ulIdx;
|
||
|
|
||
|
//
|
||
|
// Skip the portion of the format string that was written.
|
||
|
//
|
||
|
format += ulIdx;
|
||
|
|
||
|
//
|
||
|
// See if the next character is a %.
|
||
|
//
|
||
|
if(*format == '%')
|
||
|
{
|
||
|
//
|
||
|
// Skip the %.
|
||
|
//
|
||
|
format++;
|
||
|
|
||
|
//
|
||
|
// Set the digit count to zero, and the fill character to space
|
||
|
// (that is, to the defaults).
|
||
|
//
|
||
|
ulCount = 0;
|
||
|
cFill = ' ';
|
||
|
|
||
|
//
|
||
|
// It may be necessary to get back here to process more characters.
|
||
|
// Goto's aren't pretty, but effective. I feel extremely dirty for
|
||
|
// using not one but two of the beasts.
|
||
|
//
|
||
|
again:
|
||
|
|
||
|
//
|
||
|
// Determine how to handle the next character.
|
||
|
//
|
||
|
switch(*format++)
|
||
|
{
|
||
|
//
|
||
|
// Handle the digit characters.
|
||
|
//
|
||
|
case '0':
|
||
|
case '1':
|
||
|
case '2':
|
||
|
case '3':
|
||
|
case '4':
|
||
|
case '5':
|
||
|
case '6':
|
||
|
case '7':
|
||
|
case '8':
|
||
|
case '9':
|
||
|
{
|
||
|
//
|
||
|
// If this is a zero, and it is the first digit, then the
|
||
|
// fill character is a zero instead of a space.
|
||
|
//
|
||
|
if((format[-1] == '0') && (ulCount == 0))
|
||
|
{
|
||
|
cFill = '0';
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the digit count.
|
||
|
//
|
||
|
ulCount *= 10;
|
||
|
ulCount += format[-1] - '0';
|
||
|
|
||
|
//
|
||
|
// Get the next character.
|
||
|
//
|
||
|
goto again;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %c command.
|
||
|
//
|
||
|
case 'c':
|
||
|
{
|
||
|
//
|
||
|
// Get the value from the varargs.
|
||
|
//
|
||
|
ulValue = va_arg(arg, unsigned long);
|
||
|
|
||
|
//
|
||
|
// Copy the character to the output buffer, if there is
|
||
|
// room. Update the buffer size remaining.
|
||
|
//
|
||
|
if(n != 0)
|
||
|
{
|
||
|
*s++ = (char)ulValue;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
|
||
|
//
|
||
|
// This command has been handled.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %d and %i commands.
|
||
|
//
|
||
|
case 'd':
|
||
|
case 'i':
|
||
|
{
|
||
|
//
|
||
|
// Get the value from the varargs.
|
||
|
//
|
||
|
ulValue = va_arg(arg, unsigned long);
|
||
|
|
||
|
//
|
||
|
// If the value is negative, make it positive and indicate
|
||
|
// that a minus sign is needed.
|
||
|
//
|
||
|
if((long)ulValue < 0)
|
||
|
{
|
||
|
//
|
||
|
// Make the value positive.
|
||
|
//
|
||
|
ulValue = -(long)ulValue;
|
||
|
|
||
|
//
|
||
|
// Indicate that the value is negative.
|
||
|
//
|
||
|
ulNeg = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Indicate that the value is positive so that a
|
||
|
// negative sign isn't inserted.
|
||
|
//
|
||
|
ulNeg = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the base to 10.
|
||
|
//
|
||
|
ulBase = 10;
|
||
|
|
||
|
//
|
||
|
// Convert the value to ASCII.
|
||
|
//
|
||
|
goto convert;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %s command.
|
||
|
//
|
||
|
case 's':
|
||
|
{
|
||
|
//
|
||
|
// Get the string pointer from the varargs.
|
||
|
//
|
||
|
pcStr = va_arg(arg, char *);
|
||
|
|
||
|
//
|
||
|
// Determine the length of the string.
|
||
|
//
|
||
|
for(ulIdx = 0; pcStr[ulIdx] != '\0'; ulIdx++)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the convert count to include any padding that
|
||
|
// should be necessary (regardless of whether we have space
|
||
|
// to write it or not).
|
||
|
//
|
||
|
if(ulCount > ulIdx)
|
||
|
{
|
||
|
iConvertCount += (ulCount - ulIdx);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the string to the output buffer. Only copy as much
|
||
|
// as will fit in the buffer. Update the output buffer
|
||
|
// pointer and the space remaining.
|
||
|
//
|
||
|
if(ulIdx > n)
|
||
|
{
|
||
|
ustrncpy(s, pcStr, n);
|
||
|
s += n;
|
||
|
n = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ustrncpy(s, pcStr, ulIdx);
|
||
|
s += ulIdx;
|
||
|
n -= ulIdx;
|
||
|
|
||
|
//
|
||
|
// Write any required padding spaces assuming there is
|
||
|
// still space in the buffer.
|
||
|
//
|
||
|
if(ulCount > ulIdx)
|
||
|
{
|
||
|
ulCount -= ulIdx;
|
||
|
if(ulCount > n)
|
||
|
{
|
||
|
ulCount = n;
|
||
|
}
|
||
|
n = -ulCount;
|
||
|
|
||
|
while(ulCount--)
|
||
|
{
|
||
|
*s++ = ' ';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count. This will be the number of
|
||
|
// characters that should have been written, even if there
|
||
|
// was not room in the buffer.
|
||
|
//
|
||
|
iConvertCount += ulIdx;
|
||
|
|
||
|
//
|
||
|
// This command has been handled.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %u command.
|
||
|
//
|
||
|
case 'u':
|
||
|
{
|
||
|
//
|
||
|
// Get the value from the varargs.
|
||
|
//
|
||
|
ulValue = va_arg(arg, unsigned long);
|
||
|
|
||
|
//
|
||
|
// Set the base to 10.
|
||
|
//
|
||
|
ulBase = 10;
|
||
|
|
||
|
//
|
||
|
// Indicate that the value is positive so that a minus sign
|
||
|
// isn't inserted.
|
||
|
//
|
||
|
ulNeg = 0;
|
||
|
|
||
|
//
|
||
|
// Convert the value to ASCII.
|
||
|
//
|
||
|
goto convert;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %x and %X commands. Note that they are treated
|
||
|
// identically; that is, %X will use lower case letters for a-f
|
||
|
// instead of the upper case letters is should use. We also
|
||
|
// alias %p to %x.
|
||
|
//
|
||
|
case 'x':
|
||
|
case 'X':
|
||
|
case 'p':
|
||
|
{
|
||
|
//
|
||
|
// Get the value from the varargs.
|
||
|
//
|
||
|
ulValue = va_arg(arg, unsigned long);
|
||
|
|
||
|
//
|
||
|
// Set the base to 16.
|
||
|
//
|
||
|
ulBase = 16;
|
||
|
|
||
|
//
|
||
|
// Indicate that the value is positive so that a minus sign
|
||
|
// isn't inserted.
|
||
|
//
|
||
|
ulNeg = 0;
|
||
|
|
||
|
//
|
||
|
// Determine the number of digits in the string version of
|
||
|
// the value.
|
||
|
//
|
||
|
convert:
|
||
|
for(ulIdx = 1;
|
||
|
(((ulIdx * ulBase) <= ulValue) &&
|
||
|
(((ulIdx * ulBase) / ulBase) == ulIdx));
|
||
|
ulIdx *= ulBase, ulCount--)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the value is negative, reduce the count of padding
|
||
|
// characters needed.
|
||
|
//
|
||
|
if(ulNeg)
|
||
|
{
|
||
|
ulCount--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the value is negative and the value is padded with
|
||
|
// zeros, then place the minus sign before the padding.
|
||
|
//
|
||
|
if(ulNeg && (n != 0) && (cFill == '0'))
|
||
|
{
|
||
|
//
|
||
|
// Place the minus sign in the output buffer.
|
||
|
//
|
||
|
*s++ = '-';
|
||
|
n--;
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
|
||
|
//
|
||
|
// The minus sign has been placed, so turn off the
|
||
|
// negative flag.
|
||
|
//
|
||
|
ulNeg = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if there are more characters in the specified field
|
||
|
// width than there are in the conversion of this value.
|
||
|
//
|
||
|
if((ulCount > 1) && (ulCount < 65536))
|
||
|
{
|
||
|
//
|
||
|
// Loop through the required padding characters.
|
||
|
//
|
||
|
for(ulCount--; ulCount; ulCount--)
|
||
|
{
|
||
|
//
|
||
|
// Copy the character to the output buffer if there
|
||
|
// is room.
|
||
|
//
|
||
|
if(n != 0)
|
||
|
{
|
||
|
*s++ = cFill;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the value is negative, then place the minus sign
|
||
|
// before the number.
|
||
|
//
|
||
|
if(ulNeg && (n != 0))
|
||
|
{
|
||
|
//
|
||
|
// Place the minus sign in the output buffer.
|
||
|
//
|
||
|
*s++ = '-';
|
||
|
n--;
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert the value into a string.
|
||
|
//
|
||
|
for(; ulIdx; ulIdx /= ulBase)
|
||
|
{
|
||
|
//
|
||
|
// Copy the character to the output buffer if there is
|
||
|
// room.
|
||
|
//
|
||
|
if(n != 0)
|
||
|
{
|
||
|
*s++ = g_pcHex[(ulValue / ulIdx) % ulBase];
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This command has been handled.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle the %% command.
|
||
|
//
|
||
|
case '%':
|
||
|
{
|
||
|
//
|
||
|
// Simply write a single %.
|
||
|
//
|
||
|
if(n != 0)
|
||
|
{
|
||
|
*s++ = format[-1];
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount++;
|
||
|
|
||
|
//
|
||
|
// This command has been handled.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle all other commands.
|
||
|
//
|
||
|
default:
|
||
|
{
|
||
|
//
|
||
|
// Indicate an error.
|
||
|
//
|
||
|
if(n >= 5)
|
||
|
{
|
||
|
ustrncpy(s, "ERROR", 5);
|
||
|
s += 5;
|
||
|
n -= 5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ustrncpy(s, "ERROR", n);
|
||
|
s += n;
|
||
|
n = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the conversion count.
|
||
|
//
|
||
|
iConvertCount += 5;
|
||
|
|
||
|
//
|
||
|
// This command has been handled.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Null terminate the string in the buffer.
|
||
|
//
|
||
|
*s = 0;
|
||
|
|
||
|
//
|
||
|
// Return the number of characters in the full converted string.
|
||
|
//
|
||
|
return(iConvertCount);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! A simple sprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and \%X.
|
||
|
//!
|
||
|
//! \param s is the buffer where the converted string is stored.
|
||
|
//! \param format is the format string.
|
||
|
//! \param ... are the optional arguments, which depend on the contents of the
|
||
|
//! format string.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>sprintf()</tt> function.
|
||
|
//! Only the following formatting characters are supported:
|
||
|
//!
|
||
|
//! - \%c to print a character
|
||
|
//! - \%d or \%i to print a decimal value
|
||
|
//! - \%s to print a string
|
||
|
//! - \%u to print an unsigned decimal value
|
||
|
//! - \%x to print a hexadecimal value using lower case letters
|
||
|
//! - \%X to print a hexadecimal value using lower case letters (not upper case
|
||
|
//! letters as would typically be used)
|
||
|
//! - \%p to print a pointer as a hexadecimal value
|
||
|
//! - \%\% to print out a \% character
|
||
|
//!
|
||
|
//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside
|
||
|
//! between the \% and the format character, which specifies the minimum number
|
||
|
//! of characters to use for that value; if preceded by a 0 then the extra
|
||
|
//! characters will be filled with zeros instead of spaces. For example,
|
||
|
//! ``\%8d'' will use eight characters to print the decimal value with spaces
|
||
|
//! added to reach eight; ``\%08d'' will use eight characters as well but will
|
||
|
//! add zeros instead of spaces.
|
||
|
//!
|
||
|
//! The type of the arguments after \e format must match the requirements of
|
||
|
//! the format string. For example, if an integer was passed where a string
|
||
|
//! was expected, an error of some kind will most likely occur.
|
||
|
//!
|
||
|
//! The caller must ensure that the buffer \e s is large enough to hold the
|
||
|
//! entire converted string, including the null termination character.
|
||
|
//!
|
||
|
//! \return Returns the count of characters that were written to the output
|
||
|
//! buffer, not including the NULL termination character.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
usprintf(char * restrict s, const char *format, ...)
|
||
|
{
|
||
|
va_list arg;
|
||
|
int ret;
|
||
|
|
||
|
//
|
||
|
// Start the varargs processing.
|
||
|
//
|
||
|
va_start(arg, format);
|
||
|
|
||
|
//
|
||
|
// Call vsnprintf to perform the conversion. Use a large number for the
|
||
|
// buffer size.
|
||
|
//
|
||
|
ret = uvsnprintf(s, 0xffff, format, arg);
|
||
|
|
||
|
//
|
||
|
// End the varargs processing.
|
||
|
//
|
||
|
va_end(arg);
|
||
|
|
||
|
//
|
||
|
// Return the conversion count.
|
||
|
//
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! A simple snprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and
|
||
|
//! \%X.
|
||
|
//!
|
||
|
//! \param s is the buffer where the converted string is stored.
|
||
|
//! \param n is the size of the buffer.
|
||
|
//! \param format is the format string.
|
||
|
//! \param ... are the optional arguments, which depend on the contents of the
|
||
|
//! format string.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>sprintf()</tt> function.
|
||
|
//! Only the following formatting characters are supported:
|
||
|
//!
|
||
|
//! - \%c to print a character
|
||
|
//! - \%d or \%i to print a decimal value
|
||
|
//! - \%s to print a string
|
||
|
//! - \%u to print an unsigned decimal value
|
||
|
//! - \%x to print a hexadecimal value using lower case letters
|
||
|
//! - \%X to print a hexadecimal value using lower case letters (not upper case
|
||
|
//! letters as would typically be used)
|
||
|
//! - \%p to print a pointer as a hexadecimal value
|
||
|
//! - \%\% to print out a \% character
|
||
|
//!
|
||
|
//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside
|
||
|
//! between the \% and the format character, which specifies the minimum number
|
||
|
//! of characters to use for that value; if preceded by a 0 then the extra
|
||
|
//! characters will be filled with zeros instead of spaces. For example,
|
||
|
//! ``\%8d'' will use eight characters to print the decimal value with spaces
|
||
|
//! added to reach eight; ``\%08d'' will use eight characters as well but will
|
||
|
//! add zeros instead of spaces.
|
||
|
//!
|
||
|
//! The type of the arguments after \e format must match the requirements of
|
||
|
//! the format string. For example, if an integer was passed where a string
|
||
|
//! was expected, an error of some kind will most likely occur.
|
||
|
//!
|
||
|
//! The function will copy at most \e n - 1 characters into the buffer
|
||
|
//! \e s. One space is reserved in the buffer for the null termination
|
||
|
//! character.
|
||
|
//!
|
||
|
//! The function will return the number of characters that would be converted
|
||
|
//! as if there were no limit on the buffer size. Therefore it is possible for
|
||
|
//! the function to return a count that is greater than the specified buffer
|
||
|
//! size. If this happens, it means that the output was truncated.
|
||
|
//!
|
||
|
//! \return Returns the number of characters that were to be stored, not
|
||
|
//! including the NULL termination character, regardless of space in the
|
||
|
//! buffer.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
usnprintf(char * restrict s, size_t n, const char * restrict format, ...)
|
||
|
{
|
||
|
va_list arg;
|
||
|
int ret;
|
||
|
|
||
|
//
|
||
|
// Start the varargs processing.
|
||
|
//
|
||
|
va_start(arg, format);
|
||
|
|
||
|
//
|
||
|
// Call vsnprintf to perform the conversion.
|
||
|
//
|
||
|
ret = uvsnprintf(s, n, format, arg);
|
||
|
|
||
|
//
|
||
|
// End the varargs processing.
|
||
|
//
|
||
|
va_end(arg);
|
||
|
|
||
|
//
|
||
|
// Return the conversion count.
|
||
|
//
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// This array contains the number of days in a year at the beginning of each
|
||
|
// month of the year, in a non-leap year.
|
||
|
//
|
||
|
static const time_t g_psDaysToMonth[12] =
|
||
|
{
|
||
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Converts from seconds to calendar date and time.
|
||
|
//!
|
||
|
//! \param timer is the number of seconds.
|
||
|
//! \param tm is a pointer to the time structure that is filled in with the
|
||
|
//! broken down date and time.
|
||
|
//!
|
||
|
//! This function converts a number of seconds since midnight GMT on January 1,
|
||
|
//! 1970 (traditional Unix epoch) into the equivalent month, day, year, hours,
|
||
|
//! minutes, and seconds representation.
|
||
|
//!
|
||
|
//! \return None.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
void
|
||
|
ulocaltime(time_t timer, struct tm *tm)
|
||
|
{
|
||
|
time_t temp, months;
|
||
|
|
||
|
//
|
||
|
// Extract the number of seconds, converting time to the number of minutes.
|
||
|
//
|
||
|
temp = timer / 60;
|
||
|
tm->tm_sec = timer - (temp * 60);
|
||
|
timer = temp;
|
||
|
|
||
|
//
|
||
|
// Extract the number of minutes, converting time to the number of hours.
|
||
|
//
|
||
|
temp = timer / 60;
|
||
|
tm->tm_min = timer - (temp * 60);
|
||
|
timer = temp;
|
||
|
|
||
|
//
|
||
|
// Extract the number of hours, converting time to the number of days.
|
||
|
//
|
||
|
temp = timer / 24;
|
||
|
tm->tm_hour = timer - (temp * 24);
|
||
|
timer = temp;
|
||
|
|
||
|
//
|
||
|
// Compute the day of the week.
|
||
|
//
|
||
|
tm->tm_wday = (timer + 4) % 7;
|
||
|
|
||
|
//
|
||
|
// Compute the number of leap years that have occurred since 1968, the
|
||
|
// first leap year before 1970. For the beginning of a leap year, cut the
|
||
|
// month loop below at March so that the leap day is classified as February
|
||
|
// 29 followed by March 1, instead of March 1 followed by another March 1.
|
||
|
//
|
||
|
timer += 366 + 365;
|
||
|
temp = timer / ((4 * 365) + 1);
|
||
|
if((timer - (temp * ((4 * 365) + 1))) > (31 + 28))
|
||
|
{
|
||
|
temp++;
|
||
|
months = 12;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
months = 2;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Extract the year.
|
||
|
//
|
||
|
tm->tm_year = ((timer - temp) / 365) + 68;
|
||
|
timer -= ((tm->tm_year - 68) * 365) + temp;
|
||
|
|
||
|
//
|
||
|
// Extract the month.
|
||
|
//
|
||
|
for(temp = 0; temp < months; temp++)
|
||
|
{
|
||
|
if(g_psDaysToMonth[temp] > timer)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
tm->tm_mon = temp - 1;
|
||
|
|
||
|
//
|
||
|
// Extract the day of the month.
|
||
|
//
|
||
|
tm->tm_mday = timer - g_psDaysToMonth[temp - 1] + 1;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Compares two time structures and determines if one is greater than,
|
||
|
//! less than, or equal to the other.
|
||
|
//!
|
||
|
//! \param t1 is the first time structure to compare.
|
||
|
//! \param t2 is the second time structure to compare.
|
||
|
//!
|
||
|
//! This function compares two time structures and returns a signed number
|
||
|
//! to indicate the result of the comparison. If the time represented by
|
||
|
//! \e t1 is greater than the time represented by \e t2 then a positive
|
||
|
//! number is returned. Likewise if \e t1 is less than \e t2 then a
|
||
|
//! negative number is returned. If the two times are equal then the function
|
||
|
//! returns 0.
|
||
|
//!
|
||
|
//! \return Returns 0 if the two times are equal, +1 if \e t1 is greater
|
||
|
//! than \e t2, and -1 if \e t1 is less than \e t2.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static int
|
||
|
ucmptime(struct tm *t1, struct tm *t2)
|
||
|
{
|
||
|
//
|
||
|
// Compare each field in descending significance to determine if
|
||
|
// greater than, less than, or equal.
|
||
|
//
|
||
|
if(t1->tm_year > t2->tm_year)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_year < t2->tm_year)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else if(t1->tm_mon > t2->tm_mon)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_mon < t2->tm_mon)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else if(t1->tm_mday > t2->tm_mday)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_mday < t2->tm_mday)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else if(t1->tm_hour > t2->tm_hour)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_hour < t2->tm_hour)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else if(t1->tm_min > t2->tm_min)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_min < t2->tm_min)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else if(t1->tm_sec > t2->tm_sec)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
else if(t1->tm_sec < t2->tm_sec)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Reaching this branch of the conditional means that all of the
|
||
|
// fields are equal, and thus the two times are equal.
|
||
|
//
|
||
|
return(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Converts calendar date and time to seconds.
|
||
|
//!
|
||
|
//! \param timeptr is a pointer to the time structure that is filled in with
|
||
|
//! the broken down date and time.
|
||
|
//!
|
||
|
//! This function converts the date and time represented by the \e timeptr
|
||
|
//! structure pointer to the number of seconds since midnight GMT on January 1,
|
||
|
//! 1970 (traditional Unix epoch).
|
||
|
//!
|
||
|
//! \return Returns the calendar time and date as seconds. If the conversion
|
||
|
//! was not possible then the function returns (uint32_t)(-1).
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
time_t
|
||
|
umktime(struct tm *timeptr)
|
||
|
{
|
||
|
struct tm sTimeGuess;
|
||
|
unsigned long ulTimeGuess = 0x80000000;
|
||
|
unsigned long ulAdjust = 0x40000000;
|
||
|
int iSign;
|
||
|
|
||
|
//
|
||
|
// Seed the binary search with the first guess.
|
||
|
//
|
||
|
ulocaltime(ulTimeGuess, &sTimeGuess);
|
||
|
iSign = ucmptime(timeptr, &sTimeGuess);
|
||
|
|
||
|
//
|
||
|
// While the time is not yet found, execute a binary search.
|
||
|
//
|
||
|
while(iSign && ulAdjust)
|
||
|
{
|
||
|
//
|
||
|
// Adjust the time guess up or down depending on the result of the
|
||
|
// last compare.
|
||
|
//
|
||
|
ulTimeGuess = ((iSign > 0) ? (ulTimeGuess + ulAdjust) :
|
||
|
(ulTimeGuess - ulAdjust));
|
||
|
ulAdjust /= 2;
|
||
|
|
||
|
//
|
||
|
// Compare the new time guess against the time pointed at by the
|
||
|
// function parameters.
|
||
|
//
|
||
|
ulocaltime(ulTimeGuess, &sTimeGuess);
|
||
|
iSign = ucmptime(timeptr, &sTimeGuess);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the above loop was exited with iSign == 0, that means that the
|
||
|
// time in seconds was found, so return that value to the caller.
|
||
|
//
|
||
|
if(iSign == 0)
|
||
|
{
|
||
|
return(ulTimeGuess);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Otherwise the time could not be converted so return an error.
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
return((unsigned long)-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Converts a string into its numeric equivalent.
|
||
|
//!
|
||
|
//! \param nptr is a pointer to the string containing the integer.
|
||
|
//! \param endptr is a pointer that will be set to the first character past
|
||
|
//! the integer in the string.
|
||
|
//! \param base is the radix to use for the conversion; can be zero to
|
||
|
//! auto-select the radix or between 2 and 16 to explicitly specify the radix.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strtoul()</tt> function.
|
||
|
//! It scans a string for the first token (that is, non-white space) and
|
||
|
//! converts the value at that location in the string into an integer value.
|
||
|
//!
|
||
|
//! \return Returns the result of the conversion.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
unsigned long
|
||
|
ustrtoul(const char * restrict nptr, const char ** restrict endptr, int base)
|
||
|
{
|
||
|
unsigned long ulRet, ulDigit, ulNeg, ulValid;
|
||
|
const char *pcPtr;
|
||
|
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
ASSERT(nptr);
|
||
|
ASSERT((base == 0) || ((base > 1) && (base <= 16)));
|
||
|
|
||
|
//
|
||
|
// Initially, the result is zero.
|
||
|
//
|
||
|
ulRet = 0;
|
||
|
ulNeg = 0;
|
||
|
ulValid = 0;
|
||
|
|
||
|
//
|
||
|
// Skip past any leading white space.
|
||
|
//
|
||
|
pcPtr = nptr;
|
||
|
while((*pcPtr == ' ') || (*pcPtr == '\t'))
|
||
|
{
|
||
|
pcPtr++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Take a leading + or - from the value.
|
||
|
//
|
||
|
if(*pcPtr == '-')
|
||
|
{
|
||
|
ulNeg = 1;
|
||
|
pcPtr++;
|
||
|
}
|
||
|
else if(*pcPtr == '+')
|
||
|
{
|
||
|
pcPtr++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if the radix was not specified, or is 16, and the value starts with
|
||
|
// "0x" or "0X" (to indicate a hex value).
|
||
|
//
|
||
|
if(((base == 0) || (base == 16)) && (*pcPtr == '0') &&
|
||
|
((pcPtr[1] == 'x') || (pcPtr[1] == 'X')))
|
||
|
{
|
||
|
//
|
||
|
// Skip the leading "0x".
|
||
|
//
|
||
|
pcPtr += 2;
|
||
|
|
||
|
//
|
||
|
// Set the radix to 16.
|
||
|
//
|
||
|
base = 16;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if the radix was not specified.
|
||
|
//
|
||
|
if(base == 0)
|
||
|
{
|
||
|
//
|
||
|
// See if the value starts with "0".
|
||
|
//
|
||
|
if(*pcPtr == '0')
|
||
|
{
|
||
|
//
|
||
|
// Values that start with "0" are assumed to be radix 8.
|
||
|
//
|
||
|
base = 8;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Otherwise, the values are assumed to be radix 10.
|
||
|
//
|
||
|
base = 10;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Loop while there are more valid digits to consume.
|
||
|
//
|
||
|
while(1)
|
||
|
{
|
||
|
//
|
||
|
// See if this character is a number.
|
||
|
//
|
||
|
if((*pcPtr >= '0') && (*pcPtr <= '9'))
|
||
|
{
|
||
|
//
|
||
|
// Convert the character to its integer equivalent.
|
||
|
//
|
||
|
ulDigit = *pcPtr++ - '0';
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Otherwise, see if this character is an upper case letter.
|
||
|
//
|
||
|
else if((*pcPtr >= 'A') && (*pcPtr <= 'Z'))
|
||
|
{
|
||
|
//
|
||
|
// Convert the character to its integer equivalent.
|
||
|
//
|
||
|
ulDigit = *pcPtr++ - 'A' + 10;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Otherwise, see if this character is a lower case letter.
|
||
|
//
|
||
|
else if((*pcPtr >= 'a') && (*pcPtr <= 'z'))
|
||
|
{
|
||
|
//
|
||
|
// Convert the character to its integer equivalent.
|
||
|
//
|
||
|
ulDigit = *pcPtr++ - 'a' + 10;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Otherwise, this is not a valid character.
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Stop converting this value.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if this digit is valid for the chosen radix.
|
||
|
//
|
||
|
if(ulDigit >= base)
|
||
|
{
|
||
|
//
|
||
|
// Since this was not a valid digit, move the pointer back to the
|
||
|
// character that therefore should not have been consumed.
|
||
|
//
|
||
|
pcPtr--;
|
||
|
|
||
|
//
|
||
|
// Stop converting this value.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Add this digit to the converted value.
|
||
|
//
|
||
|
ulRet *= base;
|
||
|
ulRet += ulDigit;
|
||
|
|
||
|
//
|
||
|
// Since a digit has been added, this is now a valid result.
|
||
|
//
|
||
|
ulValid = 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the return string pointer to the first character not consumed.
|
||
|
//
|
||
|
if(endptr)
|
||
|
{
|
||
|
*endptr = ulValid ? pcPtr : nptr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the converted value.
|
||
|
//
|
||
|
return(ulNeg ? (0 - ulRet) : ulRet);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// An array of the value of ten raised to the power-of-two exponents. This is
|
||
|
// used for converting the decimal exponent into the floating-point value of
|
||
|
// 10^exp.
|
||
|
//
|
||
|
static const float g_pfExponents[] =
|
||
|
{
|
||
|
1.0e+01,
|
||
|
1.0e+02,
|
||
|
1.0e+04,
|
||
|
1.0e+08,
|
||
|
1.0e+16,
|
||
|
1.0e+32,
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Converts a string into its floating-point equivalent.
|
||
|
//!
|
||
|
//! \param nptr is a pointer to the string containing the floating-point
|
||
|
//! value.
|
||
|
//! \param endptr is a pointer that will be set to the first character past
|
||
|
//! the floating-point value in the string.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strtof()</tt> function.
|
||
|
//! It scans a string for the first token (that is, non-white space) and
|
||
|
//! converts the value at that location in the string into a floating-point
|
||
|
//! value.
|
||
|
//!
|
||
|
//! \return Returns the result of the conversion.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
float
|
||
|
ustrtof(const char *nptr, const char **endptr)
|
||
|
{
|
||
|
unsigned long ulNeg, ulExp, ulExpNeg, ulValid, ulIdx;
|
||
|
float fRet, fDigit, fExp;
|
||
|
const char *pcPtr;
|
||
|
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
ASSERT(nptr);
|
||
|
|
||
|
//
|
||
|
// Initially, the result is zero.
|
||
|
//
|
||
|
fRet = 0;
|
||
|
ulNeg = 0;
|
||
|
ulValid = 0;
|
||
|
|
||
|
//
|
||
|
// Skip past any leading white space.
|
||
|
//
|
||
|
pcPtr = nptr;
|
||
|
while((*pcPtr == ' ') || (*pcPtr == '\t'))
|
||
|
{
|
||
|
pcPtr++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Take a leading + or - from the value.
|
||
|
//
|
||
|
if(*pcPtr == '-')
|
||
|
{
|
||
|
ulNeg = 1;
|
||
|
pcPtr++;
|
||
|
}
|
||
|
else if(*pcPtr == '+')
|
||
|
{
|
||
|
pcPtr++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Loop while there are valid digits to consume.
|
||
|
//
|
||
|
while((*pcPtr >= '0') && (*pcPtr <= '9'))
|
||
|
{
|
||
|
//
|
||
|
// Add this digit to the converted value.
|
||
|
//
|
||
|
fRet *= 10;
|
||
|
fRet += *pcPtr++ - '0';
|
||
|
|
||
|
//
|
||
|
// Since a digit has been added, this is now a valid result.
|
||
|
//
|
||
|
ulValid = 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if the next character is a period and the character after that is a
|
||
|
// digit, indicating the start of the fractional portion of the value.
|
||
|
//
|
||
|
if((*pcPtr == '.') && (pcPtr[1] >= '0') && (pcPtr[1] <= '9'))
|
||
|
{
|
||
|
//
|
||
|
// Skip the period.
|
||
|
//
|
||
|
pcPtr++;
|
||
|
|
||
|
//
|
||
|
// Loop while there are valid fractional digits to consume.
|
||
|
//
|
||
|
fDigit = 0.1;
|
||
|
while((*pcPtr >= '0') && (*pcPtr <= '9'))
|
||
|
{
|
||
|
//
|
||
|
// Add this digit to the converted value.
|
||
|
//
|
||
|
fRet += (*pcPtr++ - '0') * fDigit;
|
||
|
fDigit /= (float)10.0;
|
||
|
|
||
|
//
|
||
|
// Since a digit has been added, this is now a valid result.
|
||
|
//
|
||
|
ulValid = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if the next character is an "e" and a valid number has been
|
||
|
// converted, indicating the start of the exponent.
|
||
|
//
|
||
|
if(((pcPtr[0] == 'e') || (pcPtr[0] == 'E')) && (ulValid == 1) &&
|
||
|
(((pcPtr[1] >= '0') && (pcPtr[1] <= '9')) ||
|
||
|
(((pcPtr[1] == '+') || (pcPtr[1] == '-')) &&
|
||
|
(pcPtr[2] >= '0') && (pcPtr[2] <= '9'))))
|
||
|
{
|
||
|
//
|
||
|
// Skip the "e".
|
||
|
//
|
||
|
pcPtr++;
|
||
|
|
||
|
//
|
||
|
// Take a leading + or - from the exponent.
|
||
|
//
|
||
|
ulExpNeg = 0;
|
||
|
if(*pcPtr == '-')
|
||
|
{
|
||
|
ulExpNeg = 1;
|
||
|
pcPtr++;
|
||
|
}
|
||
|
else if(*pcPtr == '+')
|
||
|
{
|
||
|
pcPtr++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Loop while there are valid digits in the exponent.
|
||
|
//
|
||
|
ulExp = 0;
|
||
|
while((*pcPtr >= '0') && (*pcPtr <= '9'))
|
||
|
{
|
||
|
//
|
||
|
// Add this digit to the converted value.
|
||
|
//
|
||
|
ulExp *= 10;
|
||
|
ulExp += *pcPtr++ - '0';
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Raise ten to the power of the exponent. Do this via binary
|
||
|
// decomposition; for each binary bit set in the exponent, multiply the
|
||
|
// floating-point representation by ten raised to that binary value
|
||
|
// (extracted from the table above).
|
||
|
//
|
||
|
fExp = 1;
|
||
|
for(ulIdx = 0; ulIdx < 7; ulIdx++)
|
||
|
{
|
||
|
if(ulExp & (1 << ulIdx))
|
||
|
{
|
||
|
fExp *= g_pfExponents[ulIdx];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the exponent is negative, then the exponent needs to be inverted.
|
||
|
//
|
||
|
if(ulExpNeg == 1)
|
||
|
{
|
||
|
fExp = 1 / fExp;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Multiply the result by the computed exponent value.
|
||
|
//
|
||
|
fRet *= fExp;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the return string pointer to the first character not consumed.
|
||
|
//
|
||
|
if(endptr)
|
||
|
{
|
||
|
*endptr = ulValid ? pcPtr : nptr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the converted value.
|
||
|
//
|
||
|
return(ulNeg ? (0 - fRet) : fRet);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Returns the length of a null-terminated string.
|
||
|
//!
|
||
|
//! \param s is a pointer to the string whose length is to be found.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strlen()</tt> function.
|
||
|
//! It determines the length of the null-terminated string passed and returns
|
||
|
//! this to the caller.
|
||
|
//!
|
||
|
//! This implementation assumes that single byte character strings are passed
|
||
|
//! and will return incorrect values if passed some UTF-8 strings.
|
||
|
//!
|
||
|
//! \return Returns the length of the string pointed to by \e s.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
size_t
|
||
|
ustrlen(const char *s)
|
||
|
{
|
||
|
size_t len;
|
||
|
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
ASSERT(s);
|
||
|
|
||
|
//
|
||
|
// Initialize the length.
|
||
|
//
|
||
|
len = 0;
|
||
|
|
||
|
//
|
||
|
// Step through the string looking for a zero character (marking its end).
|
||
|
//
|
||
|
while(s[len])
|
||
|
{
|
||
|
//
|
||
|
// Zero not found so move on to the next character.
|
||
|
//
|
||
|
len++;
|
||
|
}
|
||
|
|
||
|
return(len);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Finds a substring within a string.
|
||
|
//!
|
||
|
//! \param s1 is a pointer to the string that will be searched.
|
||
|
//! \param s2 is a pointer to the substring that is to be found within
|
||
|
//! \e s1.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strstr()</tt> function.
|
||
|
//! It scans a string for the first instance of a given substring and returns
|
||
|
//! a pointer to that substring. If the substring cannot be found, a NULL
|
||
|
//! pointer is returned.
|
||
|
//!
|
||
|
//! \return Returns a pointer to the first occurrence of \e s2 within
|
||
|
//! \e s1 or NULL if no match is found.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
char *
|
||
|
ustrstr(const char *s1, const char *s2)
|
||
|
{
|
||
|
size_t n;
|
||
|
|
||
|
//
|
||
|
// Get the length of the string to be found.
|
||
|
//
|
||
|
n = ustrlen(s2);
|
||
|
|
||
|
//
|
||
|
// Loop while we have not reached the end of the string.
|
||
|
//
|
||
|
while(*s1)
|
||
|
{
|
||
|
//
|
||
|
// Check to see if the substring appears at this position.
|
||
|
//
|
||
|
if(ustrncmp(s2, s1, n) == 0)
|
||
|
{
|
||
|
//
|
||
|
// It does so return the pointer.
|
||
|
//
|
||
|
return((char *)s1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move to the next position in the string being searched.
|
||
|
//
|
||
|
s1++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We reached the end of the string without finding the substring so
|
||
|
// return NULL.
|
||
|
//
|
||
|
return((char *)0);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Compares two strings without regard to case.
|
||
|
//!
|
||
|
//! \param s1 points to the first string to be compared.
|
||
|
//! \param s2 points to the second string to be compared.
|
||
|
//! \param n is the maximum number of characters to compare.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strncasecmp()</tt>
|
||
|
//! function. It compares at most \e n characters of two strings without
|
||
|
//! regard to case. The comparison ends if a terminating NULL character is
|
||
|
//! found in either string before \e n characters are compared. In this case,
|
||
|
//! the shorter string is deemed the lesser.
|
||
|
//!
|
||
|
//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less
|
||
|
//! than \e s2 and 1 if \e s1 is greater than \e s2.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
ustrncasecmp(const char *s1, const char *s2, size_t n)
|
||
|
{
|
||
|
char c1, c2;
|
||
|
|
||
|
//
|
||
|
// Loop while there are more characters to compare.
|
||
|
//
|
||
|
while(n)
|
||
|
{
|
||
|
//
|
||
|
// If we reached a NULL in both strings, they must be equal so
|
||
|
// we end the comparison and return 0
|
||
|
//
|
||
|
if(!*s1 && !*s2)
|
||
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Lower case the characters at the current position before we compare.
|
||
|
//
|
||
|
c1 = (((*s1 >= 'A') && (*s1 <= 'Z')) ? (*s1 + ('a' - 'A')) : *s1);
|
||
|
c2 = (((*s2 >= 'A') && (*s2 <= 'Z')) ? (*s2 + ('a' - 'A')) : *s2);
|
||
|
|
||
|
//
|
||
|
// Compare the two characters and, if different, return the relevant
|
||
|
// return code.
|
||
|
//
|
||
|
if(c2 < c1)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
if(c1 < c2)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move on to the next character.
|
||
|
//
|
||
|
s1++;
|
||
|
s2++;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we fall out, the strings must be equal for at least the first n
|
||
|
// characters so return 0 to indicate this.
|
||
|
//
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Compares two strings without regard to case.
|
||
|
//!
|
||
|
//! \param s1 points to the first string to be compared.
|
||
|
//! \param s2 points to the second string to be compared.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strcasecmp()</tt>
|
||
|
//! function. It compares two strings without regard to case. The comparison
|
||
|
//! ends if a terminating NULL character is found in either string. In this
|
||
|
//! case, the int16_ter string is deemed the lesser.
|
||
|
//!
|
||
|
//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less
|
||
|
//! than \e s2 and 1 if \e s1 is greater than \e s2.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
ustrcasecmp(const char *s1, const char *s2)
|
||
|
{
|
||
|
//
|
||
|
// Just let ustrncasecmp() handle this.
|
||
|
//
|
||
|
return(ustrncasecmp(s1, s2, (size_t)-1));
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Compares two strings.
|
||
|
//!
|
||
|
//! \param s1 points to the first string to be compared.
|
||
|
//! \param s2 points to the second string to be compared.
|
||
|
//! \param n is the maximum number of characters to compare.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strncmp()</tt> function.
|
||
|
//! It compares at most \e n characters of two strings taking case into
|
||
|
//! account. The comparison ends if a terminating NULL character is found in
|
||
|
//! either string before \e n characters are compared. In this case, the
|
||
|
//! int16_ter string is deemed the lesser.
|
||
|
//!
|
||
|
//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less
|
||
|
//! than \e s2 and 1 if \e s1 is greater than \e s2.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
ustrncmp(const char *s1, const char *s2, size_t n)
|
||
|
{
|
||
|
//
|
||
|
// Loop while there are more characters.
|
||
|
//
|
||
|
while(n)
|
||
|
{
|
||
|
//
|
||
|
// If we reached a NULL in both strings, they must be equal so we end
|
||
|
// the comparison and return 0
|
||
|
//
|
||
|
if(!*s1 && !*s2)
|
||
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compare the two characters and, if different, return the relevant
|
||
|
// return code.
|
||
|
//
|
||
|
if(*s2 < *s1)
|
||
|
{
|
||
|
return(1);
|
||
|
}
|
||
|
if(*s1 < *s2)
|
||
|
{
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move on to the next character.
|
||
|
//
|
||
|
s1++;
|
||
|
s2++;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we fall out, the strings must be equal for at least the first n
|
||
|
// characters so return 0 to indicate this.
|
||
|
//
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Compares two strings.
|
||
|
//!
|
||
|
//! \param s1 points to the first string to be compared.
|
||
|
//! \param s2 points to the second string to be compared.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>strcmp()</tt>
|
||
|
//! function. It compares two strings, taking case into account. The
|
||
|
//! comparison ends if a terminating NULL character is found in either string.
|
||
|
//! In this case, the int16_ter string is deemed the lesser.
|
||
|
//!
|
||
|
//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less
|
||
|
//! than \e s2 and 1 if \e s1 is greater than \e s2.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
ustrcmp(const char *s1, const char *s2)
|
||
|
{
|
||
|
//
|
||
|
// Pass this on to ustrncmp.
|
||
|
//
|
||
|
return(ustrncmp(s1, s2, (size_t)-1));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Random Number Generator Seed Value
|
||
|
//
|
||
|
static unsigned int g_iRandomSeed = 1;
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Set the random number generator seed.
|
||
|
//!
|
||
|
//! \param seed is the new seed value to use for the random number
|
||
|
//! generator.
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>srand()</tt> function.
|
||
|
//! It will set the seed value used in the <tt>urand()</tt> function.
|
||
|
//!
|
||
|
//! \return None
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
void
|
||
|
usrand(unsigned int seed)
|
||
|
{
|
||
|
g_iRandomSeed = seed;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
//! Generate a new (pseudo) random number
|
||
|
//!
|
||
|
//! This function is very similar to the C library <tt>rand()</tt> function.
|
||
|
//! It will generate a pseudo-random number sequence based on the seed value.
|
||
|
//!
|
||
|
//! \return A pseudo-random number will be returned.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
int
|
||
|
urand(void)
|
||
|
{
|
||
|
//
|
||
|
// Generate a new pseudo-random number with a linear congruence random
|
||
|
// number generator. This new random number becomes the seed for the next
|
||
|
// random number.
|
||
|
//
|
||
|
g_iRandomSeed = (g_iRandomSeed * 1664525) + 1013904223;
|
||
|
|
||
|
//
|
||
|
// Return the new random number.
|
||
|
//
|
||
|
return((int)g_iRandomSeed);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Close the Doxygen group.
|
||
|
//! @}
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
|
||
|
//
|
||
|
// End of file
|
||
|
//
|