I was in need of an octal display feature in UARTprintf and in implementing that I put in several new features and improvements and couple of bug fixes (I think).
Here is some actual output showing features and examples :
Features :
%% = '%' displays %
%c = 'C' character value
%d = '4779' decimal value
%i = '4779' decimal value identical to %d
%o = '11253' octal value
%p = '101D' address value (identical to %X
%P = '&0000101D' address value
%s = 'Abcdef' string
%u = '4294962517' unsigned decimal value
%x = '12ab' hexadecimal value
%X = '12AB' hexadecimal value
%q = '%q' unimplemented formats
Modifiers :
These are inserted between the % and the format character.
They are recognized in the following order :
+ for signed numbers, always show a sign.
- or ^ align left or center for numbers and strings.
0 for numbers, left padding uses 0 instead of space.
# decimal width for display field.
Examples and tests:
%+-05%= '%'%d = '123'
%-d = '123'
%5d = ' 123'
%-5d = '123 '
%05d = '00123'
%-05d = '00123'
%+5d = ' +123'
%+-5d = '+123 '
%+05d = '+0123'
%+-05d= '+0123'
%2d = '123'
%-2d = '123'
%02d = '123'
%-02d = '123'
%+2d = '+123'
%+-2d = '+123'
%+02d = '+123'
%+-02d= '+123'
%d = '-123'
%-d = '-123'
%5d = ' -123'
%-5d = '-123 '
%05d = '-0123'
%-05d = '-0123'
%+5d = ' -123'
%+-5d = '-123 '
%+05d = '-0123'
%+-05d= '-0123'
%2d = '-123'
%-2d = '-123'
%02d = '-123'
%-02d = '-123'
%+2d = '-123'
%+-2d = '-123'
%+02d = '-123'
%+-02d= '-123'
%s = 'Abcdef'
%10s = ' Abcdef'
%-10s = 'Abcdef '
%^10s = ' Abcdef '
%^11s = ' Abcdef '
%^11s = ' Abcdef '
%p = '101D'
&%08p = '&0000101D'
%P = '&0000101D'
And here my version of UARTprintf :
//*****************************************************************************
//
//! A simple UART based vprintf function supporting \%c, \%d, \%o, \%p, \%s, \%u,
//! \%x, and \%X.
//!
//! \param pcString is the format string.
//! \param vaArgP is a variable argument list pointer whose content will depend
//! upon the format string passed in \e pcString.
//!
//! This function is very similar to the C library <tt>vprintf()</tt> function.
//! All of its output will be sent to the UART. Only the following formatting
//! characters are supported:
//!
//! - \%c to print a character
//! - \%d or \%i to print number as a decimal value
//! - \%o to print a number as an octal value
//! - \%s to print a string
//! - \%u to print an unsigned number as a decimal value
//! - \%x to print an unsigned number as a hexadecimal value using lower case letters
//! - \%X to print an unsigned number as a hexadecimal value using upper case letters
//! - \%p to print a pointer value as a hexadecimal value
//! - \%P to print a pointer value as a hexadecimal value "%P" is same as "&%p08X"
//! - \%\% to print out a \% character
//!
//! For \%d, \%i, an optional ``\+'' character at immediately after the \% causes
//! positive numbers to be displayed with a ``\+'' sign.
//! For \%s, \%d, \%i, \%u, \%p, \%o, \%x, and \%X, an optional number may precede
//! the format character, specifying the minimum number of characters to use for
//! that value; if preceded by a 0 on numeric conversions 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 in the variable arguments list 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.
//!
//! \return None.
//
//*****************************************************************************
void UARTvprintf (const char *pcString, va_list vaArgP) {
uint32_t
ui32Idx,
ui32Value,
ui32Base;
int
iWidth;
const char
*pcStr;
char
*pcDigits,
cFill, // fill character is space or zero (for numerics)
cAlign, // may be 0 for left, '-' for right or '^' for centered
cSign; // + : always show sign, 0: show only minus sign.
ASSERT (pcString != 0); // Check the format string not null pointer
while (*pcString) { // Loop while there are more characters in the string.
// Find the first non-% character, or the end of the string.
for (ui32Idx = 0;
(pcString[ui32Idx] && (pcString[ui32Idx] != '%'));
ui32Idx++)
;
if (ui32Idx) {
UARTwrite (pcString, ui32Idx); // Write this portion of the string.
pcString += ui32Idx; // Skip the portion of the string that was written.
}
if (!*pcString)
break;
pcStr = ++pcString; // Skip the %.
// Set initial values :
cSign = *pcString == '+' ? *pcString++ : 0; // sign character
cAlign = (*pcString == '-' || *pcString == '^') ? *pcString++ : '+'; // alignment
cFill = *pcString == '0' ? *pcString++ : ' '; // fill character
pcDigits = (char *) g_pchex; // Lower case hex digits
ui32Base = 0; // no numeric display radix
for (iWidth = 0; *pcString >= '0' && *pcString <= '9';) // Read width number
iWidth = iWidth * 10 + (*pcString++ - '0');
switch (*pcString++) { // Handle the format command character.
case 'c': // %c
ui32Value = va_arg (vaArgP, uint32_t); // Get the value from the varargs.
UARTwrite ((char *)&ui32Value, 1); // Print out the character.
break; // End of %c command
case 's': // %s
pcStr = va_arg (vaArgP, char *); // Get the string pointer from the varargs.
for (ui32Idx = 0; pcStr[ui32Idx]; ui32Idx++) // Determine length of the string.
;
if ((cAlign != '-') && (iWidth > ui32Idx)) {
uint32_t sp = iWidth - ui32Idx;
if (cAlign == '^')
sp >>= 1;
iWidth -= sp;
while (sp)
sp -= UARTwrite (" ", sp > 4 ? 5 : sp); // fill left
}
UARTwrite (pcStr, ui32Idx); // Write the string.
iWidth -= ui32Idx;
while (iWidth > 0)
iWidth -= UARTwrite (" ", iWidth > 4 ? 5 : iWidth); // fill right
break; // End of %s command
//--------------------- Numeric conversions --------------------------------------
case 'o': // Octal unsigned number
if (!ui32Base) // real %o since base not set
ui32Base = 8; // Set the base to 8.
case 'P':
if (!ui32Base) { // real %P since no base not set
iWidth = 9; // Set width to 8 digits + sign
cFill = '0'; // fill with 0
cSign = '&'; // Use sign to prefix with '&'
ui32Base = 16; // base 16
pcDigits = (char *)g_pcHEX;
}
case 'u': // Handle the %u command.
if (!ui32Base) // real %u since base not set
ui32Base = 10; // Set the base to 10.
case 'X': case 'p':
if (!ui32Base) // real %X since base not set
pcDigits = (char *)g_pcHEX;
case 'x':
if (!ui32Base) // real %x/%p since base not set
ui32Base = 16;
// All numerics fall through to here to perform the conversion,
// ui32Base is the conversion base.
case 'd': case 'i':
ui32Value = va_arg (vaArgP, uint32_t); // Get the value from the varargs.
if (!ui32Base ) { // real %d/%i since base not set
if ((int32_t)ui32Value < 0) { // Negative value
ui32Value = -(int32_t)ui32Value; // Make the value positive.
cSign = '-'; // Indicate that the value is negative.
}
ui32Base = 10; // Set the base to 10.
}
// Determine the number of digits in the string version of the value.
for (ui32Idx = 1;
(((ui32Idx * ui32Base) <= ui32Value) &&
(((ui32Idx * ui32Base) / ui32Base) == ui32Idx));
ui32Idx *= ui32Base)
iWidth -= 1;
iWidth -= 1 + !!cSign; // Sign present, reduce count of padding characters.
// If a sign is present and the value is padded with zeros,
// then place the sign before the padding.
if (cSign && (cFill == '0')) {
UARTwrite (&cSign, 1); // write the sign character
cSign = 0; // and turn off sign
}
if (cAlign != '-' || cFill == '0') // Pad to left
while (iWidth > 0)
iWidth -= UARTwrite (&cFill, 1); // write the fill character
if (cSign) // If a sign is present
UARTwrite (&cSign, 1); // ... write it
// Write out the digits
for (; ui32Idx; ui32Idx /= ui32Base)
UARTwrite (pcDigits + ((ui32Value / ui32Idx) % ui32Base), 1);
while (iWidth > 0)
iWidth -= UARTwrite (" ", iWidth > 4 ? 5 : iWidth); // fill right
break; // End of numeric conversions
default: // Handle all other commands.
pcString = pcStr; // Restore pointer
case '%':
UARTwrite ("%", 1); // Simply write a single %.
break; // End of unknown commands
}
}
}