/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2008, Atmel Corporation * Copyright (c) 2018, sysmocom -s.f.m.c. GmbH, Author: Kevin Redon * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ //------------------------------------------------------------------------------ /// \unit /// /// !Purpose /// /// 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. /// /// !Usage /// /// Adds stdio.c to the list of file to compile for the project. This will /// automatically replace libc methods by the custom ones. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Headers //------------------------------------------------------------------------------ #include #include //------------------------------------------------------------------------------ // Local Definitions //------------------------------------------------------------------------------ // Maximum string size allowed (in bytes). #define MAX_STRING_SIZE 512 //------------------------------------------------------------------------------ // Global Variables //------------------------------------------------------------------------------ // FILE* const stdin = NULL; /* If we use NULL here, we get compiler warnings of calling stdio functions with * NULL values. Our fputs() implementation ignores the value of those pointers anyway */ FILE* const stdout = (FILE *) 0x1; FILE* const stderr = (FILE *) 0x2; //------------------------------------------------------------------------------ // Local Functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // 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; } //------------------------------------------------------------------------------ // Writes a string inside the given string. // Returns the size of the written // string. // \param pStr Storage string. // \param pSource Source string. //------------------------------------------------------------------------------ signed int PutString(char *pStr, const char *pSource) { signed int num = 0; while (*pSource != 0) { *pStr++ = *pSource++; num++; } return num; } //------------------------------------------------------------------------------ // Writes an unsigned int inside the given string, using the provided fill & // width parameters. // Returns the size in characters of the written integer. // \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; } //------------------------------------------------------------------------------ // Writes a signed int inside the given string, using the provided fill & width // parameters. // Returns the size of the written integer. // \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; } //------------------------------------------------------------------------------ // Writes an hexadecimal value into a string, using the given fill, width & // capital parameters. // Returns the number of char written. // \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. //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Stores the result of a formatted string into another string. Format /// arguments are given in a va_list instance. /// Return the number of characters written. /// \param pStr Destination string. /// \param length Length of Destination string. /// \param pFormat Format string. /// \param ap Argument list. //------------------------------------------------------------------------------ signed int vsnprintf(char *pStr, size_t length, const char *pFormat, va_list ap) { char fill; unsigned char width; signed int num = 0; size_t 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 do { num = 0; switch (*pFormat) { case 'l': num = -1; break; // ignore long qualifier since int == long (and long long is not supported) 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 'p': num = PutHexa(pStr, fill, width, 0, va_arg(ap, unsigned long)); 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++; } while (num < 0); pStr += num; size += num; } } // NULL-terminated (final \0 is not counted) if (size < length) { *pStr = 0; } else { *(--pStr) = 0; size--; } return size; } //------------------------------------------------------------------------------ /// Stores the result of a formatted string into another string. Format /// arguments are given in a va_list instance. /// Return the number of characters written. /// \param pString Destination string. /// \param length Length of Destination string. /// \param pFormat Format string. /// \param ... Other arguments //------------------------------------------------------------------------------ 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; } //------------------------------------------------------------------------------ /// Stores the result of a formatted string into another string. Format /// arguments are given in a va_list instance. /// Return the number of characters written. /// \param pString Destination string. /// \param pFormat Format string. /// \param ap Argument list. //------------------------------------------------------------------------------ signed int vsprintf(char *pString, const char *pFormat, va_list ap) { return vsnprintf(pString, MAX_STRING_SIZE, pFormat, ap); } //------------------------------------------------------------------------------ /// 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]; // Write formatted string in buffer int rc = vsprintf(pStr, pFormat, ap); if (rc < 0) { fputs("format string error in ", stderr); fputs(pFormat, stderr); return rc; } if (rc >= MAX_STRING_SIZE) { fputs("stdio.c: increase MAX_STRING_SIZE\n\r", stderr); return rc; } // Display string return fputs(pStr, pStream); } //------------------------------------------------------------------------------ /// Outputs a formatted string on the given stream. Format arguments are given /// in a va_list instance. /// \note This function is synchronous (i.e. blocks until the print completes) /// \param pStream Output stream. /// \param pFormat Format string /// \param ap Argument list. //------------------------------------------------------------------------------ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" signed int vfprintf_sync(FILE *pStream, const char *pFormat, va_list ap) { char pStr[MAX_STRING_SIZE]; // Write formatted string in buffer int rc = vsprintf(pStr, pFormat, ap); if (rc < 0) { fputs_sync("format string error in ", stderr); fputs_sync(pFormat, stderr); return rc; } if (rc >= MAX_STRING_SIZE) { fputs_sync("stdio.c: increase MAX_STRING_SIZE\n\r", stderr); return rc; } // Display string return fputs_sync(pStr, pStream); } #pragma GCC diagnostic pop //------------------------------------------------------------------------------ /// 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); } //------------------------------------------------------------------------------ /// Outputs a formatted string on the DBGU stream. Format arguments are given /// in a va_list instance. /// \note This function is synchronous (i.e. blocks until the print completes) /// \param pFormat Format string /// \param ap Argument list. //------------------------------------------------------------------------------ signed int vprintf_sync(const char *pFormat, va_list ap) { return vfprintf_sync(stdout, pFormat, ap); } //------------------------------------------------------------------------------ /// 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; } //------------------------------------------------------------------------------ /// 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; } //------------------------------------------------------------------------------ /// Outputs a formatted string on the DBGU stream, using a variable number of /// arguments. /// \note This function is synchronous (i.e. blocks until the print completes) /// \param pFormat Format string. //------------------------------------------------------------------------------ signed int printf_sync(const char *pFormat, ...) { va_list ap; signed int result; // Forward call to vprintf va_start(ap, pFormat); result = vprintf_sync(pFormat, ap); va_end(ap); return result; } //------------------------------------------------------------------------------ /// Writes a formatted string inside another string. /// \param pStr Storage 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; } //------------------------------------------------------------------------------ /// Outputs a string on stdout. /// \param pStr String to output. //------------------------------------------------------------------------------ signed int puts(const char *pStr) { return fputs(pStr, stdout); }