/**************************************************************************//***** * @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 #include #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; } }