| //***************************************************************************** | |
| // | |
| // ustdlib.c - Simple standard library functions. | |
| // | |
| // Copyright (c) 2007 Luminary Micro, Inc. All rights reserved. | |
| // | |
| // Software License Agreement | |
| // | |
| // Luminary Micro, Inc. (LMI) is supplying this software for use solely and | |
| // exclusively on LMI's microcontroller products. | |
| // | |
| // The software is owned by LMI and/or its suppliers, and is protected under | |
| // applicable copyright laws. All rights are reserved. Any use in violation | |
| // of the foregoing restrictions may subject the user to criminal sanctions | |
| // under applicable laws, as well as to civil liability for the breach of the | |
| // terms and conditions of this license. | |
| // | |
| // THIS SOFTWARE IS PROVIDED "AS IS". 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. | |
| // LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR | |
| // CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. | |
| // | |
| //***************************************************************************** | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include "debug.h" | |
| //***************************************************************************** | |
| // | |
| //! \addtogroup utilities_api | |
| //! @{ | |
| // | |
| //***************************************************************************** | |
| //***************************************************************************** | |
| // | |
| // A mapping from an integer between 0 and 15 to its ASCII character | |
| // equivalent. | |
| // | |
| //***************************************************************************** | |
| static const char * const g_pcHex = "0123456789abcdef"; | |
| //***************************************************************************** | |
| // | |
| //! A simple vsnprintf function supporting \%c, \%d, \%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 <tt>vsnprintf()</tt> | |
| //! function. Only the following formatting characters are supported: | |
| //! | |
| //! - \%c to print a character | |
| //! - \%d 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) | |
| //! - \%\% to print out a \% character | |
| //! | |
| //! For \%d, \%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 preceeded 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 \b 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 \b ulSize parameter limits the number of characters that will be | |
| //! stored in the buffer pointed to by \b 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 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; | |
| 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) | |
| { | |
| strncpy(pcBuf, pcString, ulSize); | |
| pcBuf += ulSize; | |
| ulSize = 0; | |
| } | |
| else | |
| { | |
| strncpy(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 | |
| // (i.e. 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 command. | |
| // | |
| case 'd': | |
| { | |
| // | |
| // Get the value from the varargs. | |
| // | |
| ulValue = va_arg(vaArgP, unsigned long); | |
| // | |
| // If the value is negative, make it positive and stick a | |
| // minus sign in the beginning of the buffer. | |
| // | |
| if((long)ulValue < 0) | |
| { | |
| ulValue = -(long)ulValue; | |
| if(ulSize != 0) | |
| { | |
| *pcBuf++ = '-'; | |
| ulSize--; | |
| } | |
| // | |
| // Update the conversion count. | |
| // | |
| iConvertCount++; | |
| } | |
| // | |
| // 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++) | |
| { | |
| } | |
| // | |
| // 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) | |
| { | |
| strncpy(pcBuf, pcStr, ulSize); | |
| pcBuf += ulSize; | |
| ulSize = 0; | |
| } | |
| else | |
| { | |
| strncpy(pcBuf, pcStr, 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; | |
| // | |
| // | |
| // 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; | |
| // | |
| // Convert the value to ASCII. | |
| // | |
| goto convert; | |
| } | |
| // | |
| // Handle the %x and %X commands. Note that they are treated | |
| // identically; i.e. %X will use lower case letters for a-f | |
| // instead of the upper case letters is should use. | |
| // | |
| case 'x': | |
| case 'X': | |
| { | |
| // | |
| // Get the value from the varargs. | |
| // | |
| ulValue = va_arg(vaArgP, unsigned long); | |
| // | |
| // Set the base to 16. | |
| // | |
| ulBase = 16; | |
| // | |
| // 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--) | |
| { | |
| } | |
| // | |
| // Provide additional padding at the beginning of the | |
| // string conversion if needed. | |
| // | |
| if((ulCount > 1) && (ulCount < 16)) | |
| { | |
| 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++; | |
| } | |
| } | |
| // | |
| // 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) | |
| { | |
| strncpy(pcBuf, "ERROR", 5); | |
| pcBuf += 5; | |
| ulSize -= 5; | |
| } | |
| else | |
| { | |
| strncpy(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(iConvertCount); | |
| } | |
| //***************************************************************************** | |
| // | |
| //! A simple sprintf function supporting \%c, \%d, \%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 <tt>sprintf()</tt> function. | |
| //! Only the following formatting characters are supported: | |
| //! | |
| //! - \%c to print a character | |
| //! - \%d 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) | |
| //! - \%\% to print out a \% character | |
| //! | |
| //! For \%d, \%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 preceeded 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 \b 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 pcBuf is large enough to hold the | |
| //! entire converted string, including the null termination character. | |
| //! | |
| //! \return 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, \%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 <tt>sprintf()</tt> function. | |
| //! Only the following formatting characters are supported: | |
| //! | |
| //! - \%c to print a character | |
| //! - \%d 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) | |
| //! - \%\% to print out a \% character | |
| //! | |
| //! For \%d, \%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 preceeded 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 \b 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 \b ulSize - 1 characters into the | |
| //! buffer \b 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 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); | |
| } | |
| //***************************************************************************** | |
| // | |
| // Close the Doxygen group. | |
| //! @} | |
| // | |
| //***************************************************************************** |