514 lines
11 KiB
C
514 lines
11 KiB
C
|
/**************************************************************************//*****
|
||
|
* @file printf.c
|
||
|
* @brief Implementation of several stdio.h methods, such as printf(),
|
||
|
* sprintf() and so on. This reduces the memory footprint of the
|
||
|
* binary when using those methods, compared to the libc implementation.
|
||
|
********************************************************************************/
|
||
|
#include <stdio.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
#include "print.h"
|
||
|
|
||
|
/** Maximum string size allowed (in bytes). */
|
||
|
#define MAX_STRING_SIZE 100
|
||
|
|
||
|
|
||
|
/** Required for proper compilation. */
|
||
|
struct _reent r = {0, (FILE *) 0, (FILE *) 1, (FILE *) 0};
|
||
|
//struct _reent *_impure_ptr = &r;
|
||
|
|
||
|
/**
|
||
|
* @brief Writes a character inside the given string. Returns 1.
|
||
|
*
|
||
|
* @param pStr Storage string.
|
||
|
* @param c Character to write.
|
||
|
*/
|
||
|
signed int PutChar(char *pStr, char c)
|
||
|
{
|
||
|
*pStr = c;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Writes a string inside the given string.
|
||
|
*
|
||
|
* @param pStr Storage string.
|
||
|
* @param pSource Source string.
|
||
|
* @return The size of the written
|
||
|
*/
|
||
|
signed int PutString(char *pStr, const char *pSource)
|
||
|
{
|
||
|
signed int num = 0;
|
||
|
|
||
|
while (*pSource != 0) {
|
||
|
|
||
|
*pStr++ = *pSource++;
|
||
|
num++;
|
||
|
}
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Writes an unsigned int inside the given string, using the provided fill &
|
||
|
* width parameters.
|
||
|
*
|
||
|
* @param pStr Storage string.
|
||
|
* @param fill Fill character.
|
||
|
* @param width Minimum integer width.
|
||
|
* @param value Integer value.
|
||
|
*/
|
||
|
signed int PutUnsignedInt(
|
||
|
char *pStr,
|
||
|
char fill,
|
||
|
signed int width,
|
||
|
unsigned int value)
|
||
|
{
|
||
|
signed int num = 0;
|
||
|
|
||
|
/* Take current digit into account when calculating width */
|
||
|
width--;
|
||
|
|
||
|
/* Recursively write upper digits */
|
||
|
if ((value / 10) > 0) {
|
||
|
|
||
|
num = PutUnsignedInt(pStr, fill, width, value / 10);
|
||
|
pStr += num;
|
||
|
}
|
||
|
|
||
|
/* Write filler characters */
|
||
|
else {
|
||
|
|
||
|
while (width > 0) {
|
||
|
|
||
|
PutChar(pStr, fill);
|
||
|
pStr++;
|
||
|
num++;
|
||
|
width--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Write lower digit */
|
||
|
num += PutChar(pStr, (value % 10) + '0');
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Writes a signed int inside the given string, using the provided fill & width
|
||
|
* parameters.
|
||
|
*
|
||
|
* @param pStr Storage string.
|
||
|
* @param fill Fill character.
|
||
|
* @param width Minimum integer width.
|
||
|
* @param value Signed integer value.
|
||
|
*/
|
||
|
signed int PutSignedInt(
|
||
|
char *pStr,
|
||
|
char fill,
|
||
|
signed int width,
|
||
|
signed int value)
|
||
|
{
|
||
|
signed int num = 0;
|
||
|
unsigned int absolute;
|
||
|
|
||
|
/* Compute absolute value */
|
||
|
if (value < 0) {
|
||
|
|
||
|
absolute = -value;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
absolute = value;
|
||
|
}
|
||
|
|
||
|
/* Take current digit into account when calculating width */
|
||
|
width--;
|
||
|
|
||
|
/* Recursively write upper digits */
|
||
|
if ((absolute / 10) > 0) {
|
||
|
|
||
|
if (value < 0) {
|
||
|
|
||
|
num = PutSignedInt(pStr, fill, width, -(absolute / 10));
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
num = PutSignedInt(pStr, fill, width, absolute / 10);
|
||
|
}
|
||
|
pStr += num;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
/* Reserve space for sign */
|
||
|
if (value < 0) {
|
||
|
|
||
|
width--;
|
||
|
}
|
||
|
|
||
|
/* Write filler characters */
|
||
|
while (width > 0) {
|
||
|
|
||
|
PutChar(pStr, fill);
|
||
|
pStr++;
|
||
|
num++;
|
||
|
width--;
|
||
|
}
|
||
|
|
||
|
/* Write sign */
|
||
|
if (value < 0) {
|
||
|
|
||
|
num += PutChar(pStr, '-');
|
||
|
pStr++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Write lower digit */
|
||
|
num += PutChar(pStr, (absolute % 10) + '0');
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Writes an hexadecimal value into a string, using the given fill, width &
|
||
|
* capital parameters.
|
||
|
*
|
||
|
* @param pStr Storage string.
|
||
|
* @param fill Fill character.
|
||
|
* @param width Minimum integer width.
|
||
|
* @param maj Indicates if the letters must be printed in lower- or upper-case.
|
||
|
* @param value Hexadecimal value.
|
||
|
*
|
||
|
* @return The number of char written
|
||
|
*/
|
||
|
signed int PutHexa(
|
||
|
char *pStr,
|
||
|
char fill,
|
||
|
signed int width,
|
||
|
unsigned char maj,
|
||
|
unsigned int value)
|
||
|
{
|
||
|
signed int num = 0;
|
||
|
|
||
|
/* Decrement width */
|
||
|
width--;
|
||
|
|
||
|
/* Recursively output upper digits */
|
||
|
if ((value >> 4) > 0) {
|
||
|
|
||
|
num += PutHexa(pStr, fill, width, maj, value >> 4);
|
||
|
pStr += num;
|
||
|
}
|
||
|
/* Write filler chars */
|
||
|
else {
|
||
|
|
||
|
while (width > 0) {
|
||
|
|
||
|
PutChar(pStr, fill);
|
||
|
pStr++;
|
||
|
num++;
|
||
|
width--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Write current digit */
|
||
|
if ((value & 0xF) < 10) {
|
||
|
|
||
|
PutChar(pStr, (value & 0xF) + '0');
|
||
|
}
|
||
|
else if (maj) {
|
||
|
|
||
|
PutChar(pStr, (value & 0xF) - 10 + 'A');
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
PutChar(pStr, (value & 0xF) - 10 + 'a');
|
||
|
}
|
||
|
num++;
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Global Functions ----------------------------------------------------------- */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Stores the result of a formatted string into another string. Format
|
||
|
* arguments are given in a va_list instance.
|
||
|
*
|
||
|
* @param pStr Destination string.
|
||
|
* @param length Length of Destination string.
|
||
|
* @param pFormat Format string.
|
||
|
* @param ap Argument list.
|
||
|
*
|
||
|
* @return The number of characters written.
|
||
|
*/
|
||
|
signed int vsnprintf(char *pStr, size_t length, const char *pFormat, va_list ap)
|
||
|
{
|
||
|
char fill;
|
||
|
unsigned char width;
|
||
|
signed int num = 0;
|
||
|
signed int size = 0;
|
||
|
|
||
|
/* Clear the string */
|
||
|
if (pStr) {
|
||
|
|
||
|
*pStr = 0;
|
||
|
}
|
||
|
|
||
|
/* Phase string */
|
||
|
while (*pFormat != 0 && size < length) {
|
||
|
|
||
|
/* Normal character */
|
||
|
if (*pFormat != '%') {
|
||
|
|
||
|
*pStr++ = *pFormat++;
|
||
|
size++;
|
||
|
}
|
||
|
/* Escaped '%' */
|
||
|
else if (*(pFormat+1) == '%') {
|
||
|
|
||
|
*pStr++ = '%';
|
||
|
pFormat += 2;
|
||
|
size++;
|
||
|
}
|
||
|
/* Token delimiter */
|
||
|
else {
|
||
|
|
||
|
fill = ' ';
|
||
|
width = 0;
|
||
|
pFormat++;
|
||
|
|
||
|
/* Parse filler */
|
||
|
if (*pFormat == '0') {
|
||
|
|
||
|
fill = '0';
|
||
|
pFormat++;
|
||
|
}
|
||
|
|
||
|
/* Parse width */
|
||
|
while ((*pFormat >= '0') && (*pFormat <= '9')) {
|
||
|
|
||
|
width = (width*10) + *pFormat-'0';
|
||
|
pFormat++;
|
||
|
}
|
||
|
|
||
|
/* Check if there is enough space */
|
||
|
if (size + width > length) {
|
||
|
|
||
|
width = length - size;
|
||
|
}
|
||
|
|
||
|
/* Parse type */
|
||
|
switch (*pFormat) {
|
||
|
case 'd':
|
||
|
case 'i': num = PutSignedInt(pStr, fill, width, va_arg(ap, signed int)); break;
|
||
|
case 'u': num = PutUnsignedInt(pStr, fill, width, va_arg(ap, unsigned int)); break;
|
||
|
case 'x': num = PutHexa(pStr, fill, width, 0, va_arg(ap, unsigned int)); break;
|
||
|
case 'X': num = PutHexa(pStr, fill, width, 1, va_arg(ap, unsigned int)); break;
|
||
|
case 's': num = PutString(pStr, va_arg(ap, char *)); break;
|
||
|
case 'c': num = PutChar(pStr, va_arg(ap, unsigned int)); break;
|
||
|
default:
|
||
|
return EOF;
|
||
|
}
|
||
|
|
||
|
pFormat++;
|
||
|
pStr += num;
|
||
|
size += num;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* NULL-terminated (final \0 is not counted) */
|
||
|
if (size < length) {
|
||
|
|
||
|
*pStr = 0;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
*(--pStr) = 0;
|
||
|
size--;
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Stores the result of a formatted string into another string. Format
|
||
|
* arguments are given in a va_list instance.
|
||
|
*
|
||
|
* @param pStr Destination string.
|
||
|
* @param length Length of Destination string.
|
||
|
* @param pFormat Format string.
|
||
|
* @param ... Other arguments
|
||
|
*
|
||
|
* @return The number of characters written.
|
||
|
*/
|
||
|
signed int snprintf(char *pString, size_t length, const char *pFormat, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
signed int rc;
|
||
|
|
||
|
va_start(ap, pFormat);
|
||
|
rc = vsnprintf(pString, length, pFormat, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Stores the result of a formatted string into another string. Format
|
||
|
* arguments are given in a va_list instance.
|
||
|
*
|
||
|
* @param pString Destination string.
|
||
|
* @param length Length of Destination string.
|
||
|
* @param pFormat Format string.
|
||
|
* @param ap Argument list.
|
||
|
*
|
||
|
* @return The number of characters written.
|
||
|
*/
|
||
|
signed int vsprintf(char *pString, const char *pFormat, va_list ap)
|
||
|
{
|
||
|
return vsnprintf(pString, MAX_STRING_SIZE, pFormat, ap);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Outputs a formatted string on the given stream. Format arguments are given
|
||
|
* in a va_list instance.
|
||
|
*
|
||
|
* @param pStream Output stream.
|
||
|
* @param pFormat Format string
|
||
|
* @param ap Argument list.
|
||
|
*/
|
||
|
signed int vfprintf(FILE *pStream, const char *pFormat, va_list ap)
|
||
|
{
|
||
|
char pStr[MAX_STRING_SIZE];
|
||
|
char pError[] = "stdio.c: increase MAX_STRING_SIZE\n\r";
|
||
|
|
||
|
/* Write formatted string in buffer */
|
||
|
if (vsprintf(pStr, pFormat, ap) >= MAX_STRING_SIZE) {
|
||
|
|
||
|
fputs(pError, stderr);
|
||
|
while (1); /* Increase MAX_STRING_SIZE */
|
||
|
}
|
||
|
|
||
|
/* Display string */
|
||
|
return fputs(pStr, pStream);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Outputs a formatted string on the DBGU stream. Format arguments are given
|
||
|
* in a va_list instance.
|
||
|
*
|
||
|
* @param pFormat Format string.
|
||
|
* @param ap Argument list.
|
||
|
*/
|
||
|
signed int vprintf(const char *pFormat, va_list ap)
|
||
|
{
|
||
|
return vfprintf(stdout, pFormat, ap);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Outputs a formatted string on the given stream, using a variable
|
||
|
* number of arguments.
|
||
|
*
|
||
|
* @param pStream Output stream.
|
||
|
* @param pFormat Format string.
|
||
|
*/
|
||
|
signed int fprintf(FILE *pStream, const char *pFormat, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
signed int result;
|
||
|
|
||
|
/* Forward call to vfprintf */
|
||
|
va_start(ap, pFormat);
|
||
|
result = vfprintf(pStream, pFormat, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Outputs a formatted string on the DBGU stream, using a variable number of
|
||
|
* arguments.
|
||
|
*
|
||
|
* @param pFormat Format string.
|
||
|
*/
|
||
|
signed int printf(const char *pFormat, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
signed int result;
|
||
|
|
||
|
/* Forward call to vprintf */
|
||
|
va_start(ap, pFormat);
|
||
|
result = vprintf(pFormat, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Writes a formatted string inside another string.
|
||
|
*
|
||
|
* @param pStr torage string.
|
||
|
* @param pFormat Format string.
|
||
|
*/
|
||
|
signed int sprintf(char *pStr, const char *pFormat, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
signed int result;
|
||
|
|
||
|
// Forward call to vsprintf
|
||
|
va_start(ap, pFormat);
|
||
|
result = vsprintf(pStr, pFormat, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Outputs a string on stdout.
|
||
|
*
|
||
|
* @param pStr String to output.
|
||
|
*/
|
||
|
signed int puts(const char *pStr)
|
||
|
{
|
||
|
return fputs(pStr, stdout);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Implementation of fputc using the DBGU as the standard output. Required
|
||
|
* for printf().
|
||
|
*
|
||
|
* @param c Character to write.
|
||
|
* @param pStream Output stream.
|
||
|
* @param The character written if successful, or -1 if the output stream is
|
||
|
* not stdout or stderr.
|
||
|
*/
|
||
|
signed int fputc(signed int c, FILE *pStream)
|
||
|
{
|
||
|
if ((pStream == stdout) || (pStream == stderr)) {
|
||
|
|
||
|
PrintChar(c);
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
return EOF;
|
||
|
}
|
||
|
}
|