From 4ba8614c006f9828f0796c140bc3e13c9e67938c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:06:01 +0200 Subject: Added utils --- utils/ustdlib.c | 1610 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1610 insertions(+) create mode 100644 utils/ustdlib.c (limited to 'utils/ustdlib.c') diff --git a/utils/ustdlib.c b/utils/ustdlib.c new file mode 100644 index 0000000..a330a53 --- /dev/null +++ b/utils/ustdlib.c @@ -0,0 +1,1610 @@ +//***************************************************************************** +// +// ustdlib.c - Simple standard library functions. +// +// Copyright (c) 2007-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#include +#include "driverlib/debug.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup ustdlib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// A mapping from an integer between 0 and 15 to its ASCII character +// equivalent. +// +//***************************************************************************** +static const char * const g_pcHex = "0123456789abcdef"; + +//***************************************************************************** +// +//! Copies a certain number of characters from one string to another. +//! +//! \param pcDst is a pointer to the destination buffer into which characters +//! are to be copied. +//! \param pcSrc is a pointer to the string from which characters are to be +//! copied. +//! \param iNum is the number of characters to copy to the destination buffer. +//! +//! This function copies at most \e iNum characters from the string pointed to +//! by \e pcSrc into the buffer pointed to by \e pcDst. If the end of \e +//! pcSrc is found before \e iNum characters have been copied, remaining +//! characters in \e pcDst will be padded with zeroes until \e iNum 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 pcSrc. +//! +//! \return Returns \e pcDst. +// +//***************************************************************************** +char * +ustrncpy (char *pcDst, const char *pcSrc, int iNum) +{ + int iCount; + + ASSERT(pcSrc); + ASSERT(pcDst); + + // + // Start at the beginning of the source string. + // + iCount = 0; + + // + // Copy the source string until we run out of source characters or + // destination space. + // + while(iNum && pcSrc[iCount]) + { + pcDst[iCount] = pcSrc[iCount]; + iCount++; + iNum--; + } + + // + // Pad the destination if we are not yet done. + // + while(iNum) + { + pcDst[iCount++] = (char)0; + iNum--; + } + + // + // Pass the destination pointer back to the caller. + // + return(pcDst); +} + +//***************************************************************************** +// +//! A simple vsnprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param pcBuf points to the buffer where the converted string is stored. +//! \param ulSize is the size of the buffer. +//! \param pcString is the format string. +//! \param vaArgP is the list of optional arguments, which depend on the +//! contents of the format string. +//! +//! This function is very similar to the C library vsnprintf() +//! 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 pcString 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 ulSize parameter limits the number of characters that will be stored +//! in the buffer pointed to by \e pcBuf 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 *pcBuf, unsigned long ulSize, const char *pcString, + va_list vaArgP) +{ + unsigned long ulIdx, ulValue, ulCount, ulBase, ulNeg; + char *pcStr, cFill; + int iConvertCount = 0; + + // + // Check the arguments. + // + ASSERT(pcString != 0); + ASSERT(pcBuf != 0); + ASSERT(ulSize != 0); + + // + // Adjust buffer size limit to allow one space for null termination. + // + if(ulSize) + { + ulSize--; + } + + // + // Initialize the count of characters converted. + // + iConvertCount = 0; + + // + // Loop while there are more characters in the format string. + // + while(*pcString) + { + // + // Find the first non-% character, or the end of the string. + // + for(ulIdx = 0; (pcString[ulIdx] != '%') && (pcString[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 > ulSize) + { + ustrncpy(pcBuf, pcString, ulSize); + pcBuf += ulSize; + ulSize = 0; + } + else + { + ustrncpy(pcBuf, pcString, ulIdx); + pcBuf += ulIdx; + ulSize -= 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. + // + pcString += ulIdx; + + // + // See if the next character is a %. + // + if(*pcString == '%') + { + // + // Skip the %. + // + pcString++; + + // + // 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(*pcString++) + { + // + // 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((pcString[-1] == '0') && (ulCount == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ulCount *= 10; + ulCount += pcString[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(vaArgP, unsigned long); + + // + // Copy the character to the output buffer, if there is + // room. Update the buffer size remaining. + // + if(ulSize != 0) + { + *pcBuf++ = (char)ulValue; + ulSize--; + } + + // + // 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(vaArgP, 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(vaArgP, 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 > ulSize) + { + ustrncpy(pcBuf, pcStr, ulSize); + pcBuf += ulSize; + ulSize = 0; + } + else + { + ustrncpy(pcBuf, pcStr, ulIdx); + pcBuf += ulIdx; + ulSize -= ulIdx; + + // + // Write any required padding spaces assuming there is + // still space in the buffer. + // + if(ulCount > ulIdx) + { + ulCount -= ulIdx; + if(ulCount > ulSize) + { + ulCount = ulSize; + } + ulSize =- ulCount; + + while(ulCount--) + { + *pcBuf++ = ' '; + } + } + } + + // + // 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(vaArgP, 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(vaArgP, 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 && (ulSize != 0) && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + *pcBuf++ = '-'; + ulSize--; + + // + // 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(ulSize != 0) + { + *pcBuf++ = cFill; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ulNeg && (ulSize != 0)) + { + // + // Place the minus sign in the output buffer. + // + *pcBuf++ = '-'; + ulSize--; + + // + // 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(ulSize != 0) + { + *pcBuf++ = g_pcHex[(ulValue / ulIdx) % ulBase]; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + if(ulSize != 0) + { + *pcBuf++ = pcString[-1]; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + if(ulSize >= 5) + { + ustrncpy(pcBuf, "ERROR", 5); + pcBuf += 5; + ulSize -= 5; + } + else + { + ustrncpy(pcBuf, "ERROR", ulSize); + pcBuf += ulSize; + ulSize = 0; + } + + // + // Update the conversion count. + // + iConvertCount += 5; + + // + // This command has been handled. + // + break; + } + } + } + } + + // + // Null terminate the string in the buffer. + // + *pcBuf = 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 pcBuf is the buffer where the converted string is stored. +//! \param pcString 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 sprintf() 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 pcString 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 pcBuf 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 *pcBuf, const char *pcString, ...) +{ + va_list vaArgP; + int iRet; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + // + // Call vsnprintf to perform the conversion. Use a large number for the + // buffer size. + // + iRet = uvsnprintf(pcBuf, 0xffff, pcString, vaArgP); + + // + // End the varargs processing. + // + va_end(vaArgP); + + // + // Return the conversion count. + // + return(iRet); +} + +//***************************************************************************** +// +//! A simple snprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param pcBuf is the buffer where the converted string is stored. +//! \param ulSize is the size of the buffer. +//! \param pcString 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 sprintf() 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 pcString 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 ulSize - 1 characters into the buffer +//! \e pcBuf. 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 *pcBuf, unsigned long ulSize, const char *pcString, ...) +{ + int iRet; + va_list vaArgP; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + // + // Call vsnprintf to perform the conversion. + // + iRet = uvsnprintf(pcBuf, ulSize, pcString, vaArgP); + + // + // End the varargs processing. + // + va_end(vaArgP); + + // + // Return the conversion count. + // + return(iRet); +} + +//***************************************************************************** +// +// 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 short g_psDaysToMonth[12] = +{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +//***************************************************************************** +// +//! Converts from seconds to calendar date and time. +//! +//! \param ulTime is the number of seconds. +//! \param psTime 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(unsigned long ulTime, tTime *psTime) +{ + unsigned long ulTemp, ulMonths; + + // + // Extract the number of seconds, converting time to the number of minutes. + // + ulTemp = ulTime / 60; + psTime->ucSec = ulTime - (ulTemp * 60); + ulTime = ulTemp; + + // + // Extract the number of minutes, converting time to the number of hours. + // + ulTemp = ulTime / 60; + psTime->ucMin = ulTime - (ulTemp * 60); + ulTime = ulTemp; + + // + // Extract the number of hours, converting time to the number of days. + // + ulTemp = ulTime / 24; + psTime->ucHour = ulTime - (ulTemp * 24); + ulTime = ulTemp; + + // + // Compute the day of the week. + // + psTime->ucWday = (ulTime + 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. + // + ulTime += 366 + 365; + ulTemp = ulTime / ((4 * 365) + 1); + if((ulTime - (ulTemp * ((4 * 365) + 1))) > (31 + 28)) + { + ulTemp++; + ulMonths = 12; + } + else + { + ulMonths = 2; + } + + // + // Extract the year. + // + psTime->usYear = ((ulTime - ulTemp) / 365) + 1968; + ulTime -= ((psTime->usYear - 1968) * 365) + ulTemp; + + // + // Extract the month. + // + for(ulTemp = 0; ulTemp < ulMonths; ulTemp++) + { + if(g_psDaysToMonth[ulTemp] > ulTime) + { + break; + } + } + psTime->ucMon = ulTemp - 1; + + // + // Extract the day of the month. + // + psTime->ucMday = ulTime - g_psDaysToMonth[ulTemp - 1] + 1; +} + +//***************************************************************************** +// +//! Compares two time structures and determines if one is greater than, +//! less than, or equal to the other. +//! +//! \param pTime1 is the first time structure to compare. +//! \param pTime2 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 pTime1 is greater than the time represented by \e pTime2 then a positive +//! number is returned. Likewise if \e pTime1 is less than \e pTime2 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 pTime1 is greater +//! than \e pTime2, and -1 if \e pTime1 is less than \e pTime2. +// +//***************************************************************************** +static int +ucmptime(tTime *pTime1, tTime *pTime2) +{ + // + // Compare each field in descending signficance to determine if + // greater than, less than, or equal. + // + if(pTime1->usYear > pTime2->usYear) + { + return(1); + } + else if(pTime1->usYear < pTime2->usYear) + { + return(-1); + } + else if(pTime1->ucMon > pTime2->ucMon) + { + return(1); + } + else if(pTime1->ucMon < pTime2->ucMon) + { + return(-1); + } + else if(pTime1->ucMday > pTime2->ucMday) + { + return(1); + } + else if(pTime1->ucMday < pTime2->ucMday) + { + return(-1); + } + else if(pTime1->ucHour > pTime2->ucHour) + { + return(1); + } + else if(pTime1->ucHour < pTime2->ucHour) + { + return(-1); + } + else if(pTime1->ucMin > pTime2->ucMin) + { + return(1); + } + else if(pTime1->ucMin < pTime2->ucMin) + { + return(-1); + } + else if(pTime1->ucSec > pTime2->ucSec) + { + return(1); + } + else if(pTime1->ucSec < pTime2->ucSec) + { + 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 psTime 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 psTime +//! 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 (unsigned long)(-1). +// +//***************************************************************************** +unsigned long +umktime(tTime *psTime) +{ + tTime sTimeGuess; + unsigned long ulTimeGuess = 0x80000000; + unsigned long ulAdjust = 0x40000000; + int iSign; + + // + // Seed the binary search with the first guess. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(psTime, &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(psTime, &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 pcStr is a pointer to the string containing the integer. +//! \param ppcStrRet is a pointer that will be set to the first character past +//! the integer in the string. +//! \param iBase 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 strtoul() 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 *pcStr, const char **ppcStrRet, int iBase) +{ + unsigned long ulRet, ulDigit, ulNeg, ulValid; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(pcStr); + ASSERT((iBase == 0) || ((iBase > 1) && (iBase <= 16))); + + // + // Initially, the result is zero. + // + ulRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = pcStr; + 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(((iBase == 0) || (iBase == 16)) && (*pcPtr == '0') && + ((pcPtr[1] == 'x') || (pcPtr[1] == 'X'))) + { + // + // Skip the leading "0x". + // + pcPtr += 2; + + // + // Set the radix to 16. + // + iBase = 16; + } + + // + // See if the radix was not specified. + // + if(iBase == 0) + { + // + // See if the value starts with "0". + // + if(*pcPtr == '0') + { + // + // Values that start with "0" are assumed to be radix 8. + // + iBase = 8; + } + else + { + // + // Otherwise, the values are assumed to be radix 10. + // + iBase = 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 >= iBase) + { + // + // 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 *= iBase; + 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(ppcStrRet) + { + *ppcStrRet = ulValid ? pcPtr : pcStr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - ulRet) : ulRet); +} + +//***************************************************************************** +// +//! Retruns the length of a null-terminated string. +//! +//! \param pcStr is a pointer to the string whose length is to be found. +//! +//! This function is very similar to the C library strlen() 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 pcStr. +// +//***************************************************************************** +int +ustrlen(const char * pcStr) +{ + int iLen; + + ASSERT(pcStr); + + // + // Initialize the length. + // + iLen = 0; + + // + // Step throug the string looking for a zero character (marking its end). + // + while(pcStr[iLen]) + { + // + // Zero not found so move on to the next character. + // + iLen++; + } + + return(iLen); +} + +//***************************************************************************** +// +//! Finds a substring within a string. +//! +//! \param pcHaystack is a pointer to the string that will be searched. +//! \param pcNeedle is a pointer to the substring that is to be found within +//! \e pcHaystack. +//! +//! This function is very similar to the C library strstr() 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 pcNeedle within +//! \e pcHaystack or NULL if no match is found. +// +//***************************************************************************** +char * +ustrstr(const char *pcHaystack, const char *pcNeedle) +{ + unsigned long ulLength; + + // + // Get the length of the string to be found. + // + ulLength = ustrlen(pcNeedle); + + // + // Loop while we have not reached the end of the string. + // + while(*pcHaystack) + { + // + // Check to see if the substring appears at this position. + // + if(ustrncmp(pcNeedle, pcHaystack, ulLength) == 0) + { + // + // It does so return the pointer. + // + return((char *)pcHaystack); + } + + // + // Move to the next position in the string being searched. + // + pcHaystack++; + } + + // + // 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 pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! \param iCount is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strnicmp() function. +//! It compares at most \e iCount characters of two strings without regard to +//! case. The comparison ends if a terminating NULL character is found in +//! either string before \e iCount 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 pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrnicmp(const char *pcStr1, const char *pcStr2, int iCount) +{ + char cL1, cL2; + + while(iCount) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*pcStr1 && !*pcStr2) + { + return(0); + } + + // + // Lower case the characters at the current position before we compare. + // + cL1 = (((*pcStr1 >= 'A') && (*pcStr1 <= 'Z')) ? + (*pcStr1 + ('a' - 'A')) : *pcStr1); + cL2 = (((*pcStr2 >= 'A') && (*pcStr2 <= 'Z')) ? + (*pcStr2 + ('a' - 'A')) : *pcStr2); + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(cL2 < cL1) + { + return(1); + } + if(cL1 < cL2) + { + return(-1); + } + + // + // Move on to the next character. + // + pcStr1++; + pcStr2++; + iCount--; + } + + // + // If we fall out, the strings must be equal for at least the first iCount + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcasecmp() +//! 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 shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrcasecmp(const char *pcStr1, const char *pcStr2) +{ + // + // Just let ustrnicmp() handle this. + // + return(ustrnicmp(pcStr1, pcStr2, -1)); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! \param iCount is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncmp() function. +//! It compares at most \e iCount characters of two strings taking case into +//! account. The comparison ends if a terminating NULL character is found in +//! either string before \e iCount 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 pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrncmp(const char *pcStr1, const char *pcStr2, int iCount) +{ + while(iCount) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*pcStr1 && !*pcStr2) + { + return(0); + } + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(*pcStr2 < *pcStr1) + { + return(1); + } + if(*pcStr1 < *pcStr2) + { + return(-1); + } + + // + // Move on to the next character. + // + pcStr1++; + pcStr2++; + iCount--; + } + + // + // If we fall out, the strings must be equal for at least the first iCount + // characters so return 0 to indicate this. + // + return(0); + +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcmp() +//! 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 shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrcmp(const char *pcStr1, const char *pcStr2) +{ + // + // Pass this on to ustrncmp. + // + return(ustrncmp(pcStr1, pcStr2, -1)); +} + +//***************************************************************************** +// +// Random Number Generator Seed Value +// +//***************************************************************************** +static unsigned long g_ulRandomSeed = 1; + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! \param ulSeed is the new seed value to use for the random number generator. +//! +//! This function is very similar to the C library srand() function. +//! It will set the seed value used in the urand() function. +//! +//! \return None +// +//***************************************************************************** +void +usrand(unsigned long ulSeed) +{ + g_ulRandomSeed = ulSeed; +} + +//***************************************************************************** +// +//! Generate a new (pseudo) random number +//! +//! This function is very similar to the C library rand() 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_ulRandomSeed = (g_ulRandomSeed * 1664525) + 1013904223; + + // + // Return the new random number. + // + return((int)g_ulRandomSeed); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1