//***************************************************************************** // // 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 #include #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 vsnprintf() //! 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 sprintf() 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 sprintf() 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. //! @} // //*****************************************************************************