/** * Copyright (c) 2011-2018 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef StdioStream_h #define StdioStream_h /** * \file * \brief StdioStream class */ #include #include "FatFile.h" //------------------------------------------------------------------------------ /** Total size of stream buffer. The entire buffer is used for output. * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. */ const uint8_t STREAM_BUF_SIZE = 64; /** Amount of buffer allocated for ungetc during input. */ const uint8_t UNGETC_BUF_SIZE = 2; //------------------------------------------------------------------------------ // Get rid of any macros defined in . #include #undef clearerr #undef fclose #undef feof #undef ferror #undef fflush #undef fgetc #undef fgetpos #undef fgets #undef fopen #undef fprintf #undef fputc #undef fputs #undef fread #undef freopen #undef fscanf #undef fseek #undef fsetpos #undef ftell #undef fwrite #undef getc #undef getchar #undef gets #undef perror //#undef printf // NOLINT #undef putc #undef putchar #undef puts #undef remove #undef rename #undef rewind #undef scanf #undef setbuf #undef setvbuf //#undef sprintf // NOLINT #undef sscanf #undef tmpfile #undef tmpnam #undef ungetc #undef vfprintf #undef vprintf #undef vsprintf // make sure needed macros are defined #ifndef EOF /** End-of-file return value. */ #define EOF (-1) #endif // EOF #ifndef NULL /** Null pointer */ #define NULL 0 #endif // NULL #ifndef SEEK_CUR /** Seek relative to current position. */ #define SEEK_CUR 1 #endif // SEEK_CUR #ifndef SEEK_END /** Seek relative to end-of-file. */ #define SEEK_END 2 #endif // SEEK_END #ifndef SEEK_SET /** Seek relative to start-of-file. */ #define SEEK_SET 0 #endif // SEEK_SET //------------------------------------------------------------------------------ /** \class StdioStream * \brief StdioStream implements a minimal stdio stream. * * StdioStream does not support subdirectories or long file names. */ class StdioStream : private FatFile { public: /** Constructor * */ StdioStream() { m_w = m_r = 0; m_p = m_buf; m_status = 0; } //---------------------------------------------------------------------------- /** Clear the stream's end-of-file and error indicators. */ void clearerr() { m_status &= ~(S_ERR | S_EOF); } //---------------------------------------------------------------------------- /** Close a stream. * * A successful call to the fclose function causes the stream to be * flushed and the associated file to be closed. Any unwritten buffered * data is written to the file; any unread buffered data is discarded. * Whether or not the call succeeds, the stream is disassociated from * the file. * * \return zero if the stream was successfully closed, or EOF if any any * errors are detected. */ int fclose(); //---------------------------------------------------------------------------- /** Test the stream's end-of-file indicator. * \return non-zero if and only if the end-of-file indicator is set. */ int feof() { return (m_status & S_EOF) != 0; } //---------------------------------------------------------------------------- /** Test the stream's error indicator. * \return return non-zero if and only if the error indicator is set. */ int ferror() { return (m_status & S_ERR) != 0; } //---------------------------------------------------------------------------- /** Flush the stream. * * If stream is an output stream or an update stream in which the most * recent operation was not input, any unwritten data is written to the * file; otherwise the call is an error since any buffered input data * would be lost. * * \return sets the error indicator for the stream and returns EOF if an * error occurs, otherwise it returns zero. */ int fflush(); //---------------------------------------------------------------------------- /** Get a byte from the stream. * * \return If the end-of-file indicator for the stream is set, or if the * stream is at end-of-file, the end-of-file indicator for the stream is * set and the fgetc function returns EOF. Otherwise, the fgetc function * returns the next character from the input stream. */ int fgetc() { return m_r-- == 0 ? fillGet() : *m_p++; } //---------------------------------------------------------------------------- /** Get a string from a stream. * * The fgets function reads at most one less than the number of * characters specified by num from the stream into the array pointed * to by str. No additional characters are read after a new-line * character (which is retained) or after end-of-file. A null character * is written immediately after the last character read into the array. * * \param[out] str Pointer to an array of where the string is copied. * * \param[in] num Maximum number of characters including the null * character. * * \param[out] len If len is not null and fgets is successful, the * length of the string is returned. * * \return str if successful. If end-of-file is encountered and no * characters have been read into the array, the contents of the array * remain unchanged and a null pointer is returned. If a read error * occurs during the operation, the array contents are indeterminate * and a null pointer is returned. */ char* fgets(char* str, size_t num, size_t* len = 0); //---------------------------------------------------------------------------- /** Open a stream. * * Open a file and associates the stream with it. * * \param[in] path file to be opened. * * \param[in] mode a string that indicates the open mode. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"r" or "rb"Open a file for reading. The file must exist.
"w" or "wb"Truncate an existing to zero length or create an empty file * for writing.
"wx" or "wbx"Create a file for writing. Fails if the file already exists.
"a" or "ab"Append; open or create file for writing at end-of-file.
"r+" or "rb+" or "r+b"Open a file for update (reading and writing).
"w+" or "w+b" or "wb+"Truncate an existing to zero length or create a file for update.
"w+x" or "w+bx" or "wb+x"Create a file for update. Fails if the file already exists.
"a+" or "a+b" or "ab+"Append; open or create a file for update, writing at end-of-file.
* The character 'b' shall have no effect, but is allowed for ISO C * standard conformance. * * Opening a file with append mode causes all subsequent writes to the * file to be forced to the then current end-of-file, regardless of * intervening calls to the fseek function. * * When a file is opened with update mode, both input and output may be * performed on the associated stream. However, output shall not be * directly followed by input without an intervening call to the fflush * function or to a file positioning function (fseek, or rewind), and * input shall not be directly followed by output without an intervening * call to a file positioning function, unless the input operation * encounters end-of-file. * * \return true for success or false for failure. */ bool fopen(const char* path, const char * mode); //---------------------------------------------------------------------------- /** Write a byte to a stream. * * \param[in] c the byte to be written (converted to an unsigned char). * * \return Upon successful completion, fputc() returns the value it * has written. Otherwise, it returns EOF and sets the error indicator for * the stream. */ int fputc(int c) { return m_w-- == 0 ? flushPut(c) : *m_p++ = c; } //---------------------------------------------------------------------------- /** Write a string to a stream. * * \param[in] str a pointer to the string to be written. * * \return for success, fputs() returns a non-negative * number. Otherwise, it returns EOF and sets the error indicator for * the stream. */ int fputs(const char* str); //---------------------------------------------------------------------------- /** Binary input. * * Reads an array of up to count elements, each one with a size of size * bytes. * \param[out] ptr pointer to area of at least (size*count) bytes where * the data will be stored. * * \param[in] size the size, in bytes, of each element to be read. * * \param[in] count the number of elements to be read. * * \return number of elements successfully read, which may be less than * count if a read error or end-of-file is encountered. If size or count * is zero, fread returns zero and the contents of the array and the * state of the stream remain unchanged. */ size_t fread(void* ptr, size_t size, size_t count); //---------------------------------------------------------------------------- /** Set the file position for the stream. * * \param[in] offset number of offset from the origin. * * \param[in] origin position used as reference for the offset. It is * specified by one of the following constants. * * SEEK_SET - Beginning of file. * * SEEK_CUR - Current position of the file pointer. * * SEEK_END - End of file. * * \return zero for success. Otherwise, it returns non-zero and sets the * error indicator for the stream. */ int fseek(int32_t offset, int origin); //---------------------------------------------------------------------------- /** Get the current position in a stream. * * \return If successful, ftell return the current value of the position * indicator. On failure, ftell returns −1L. */ int32_t ftell(); //---------------------------------------------------------------------------- /** Binary output. * * Writes an array of up to count elements, each one with a size of size * bytes. * \param[in] ptr pointer to (size*count) bytes of data to be written. * * \param[in] size the size, in bytes, of each element to be written. * * \param[in] count the number of elements to be written. * * \return number of elements successfully written. if this number is * less than count, an error has occurred. If size or count is zero, * fwrite returns zero. */ size_t fwrite(const void * ptr, size_t size, size_t count); //---------------------------------------------------------------------------- /** Get a byte from the stream. * * getc and fgetc are equivalent but getc is in-line so it is faster but * require more flash memory. * * \return If the end-of-file indicator for the stream is set, or if the * stream is at end-of-file, the end-of-file indicator for the stream is * set and the fgetc function returns EOF. Otherwise, the fgetc function * returns the next character from the input stream. */ inline __attribute__((always_inline)) int getc() { return m_r-- == 0 ? fillGet() : *m_p++; } //---------------------------------------------------------------------------- /** Write a byte to a stream. * * putc and fputc are equivalent but putc is in-line so it is faster but * require more flash memory. * * \param[in] c the byte to be written (converted to an unsigned char). * * \return Upon successful completion, fputc() returns the value it * has written. Otherwise, it returns EOF and sets the error indicator for * the stream. */ inline __attribute__((always_inline)) int putc(int c) { return m_w-- == 0 ? flushPut(c) : *m_p++ = c; } //---------------------------------------------------------------------------- /** Write a CR/LF. * * \return two, the number of bytes written, for success or -1 for failure. */ inline __attribute__((always_inline)) int putCRLF() { if (m_w < 2) { if (!flushBuf()) { return -1; } } *m_p++ = '\r'; *m_p++ = '\n'; m_w -= 2; return 2; } //---------------------------------------------------------------------------- /** Write a character. * \param[in] c the character to write. * \return the number of bytes written. */ size_t print(char c) { return putc(c) < 0 ? 0 : 1; } //---------------------------------------------------------------------------- /** Write a string. * * \param[in] str the string to be written. * * \return the number of bytes written. */ size_t print(const char* str) { int n = fputs(str); return n < 0 ? 0 : n; } //---------------------------------------------------------------------------- #if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) /** Print a string stored in flash memory. * * \param[in] str the string to print. * * \return the number of bytes written. */ size_t print(const __FlashStringHelper *str); #endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) //---------------------------------------------------------------------------- /** Print a floating point number. * * \param[in] prec Number of digits after decimal point. * * \param[in] val the number to be printed. * * \return the number of bytes written. */ size_t print(double val, uint8_t prec = 2) { return print(static_cast(val), prec); } //---------------------------------------------------------------------------- /** Print a floating point number. * * \param[in] prec Number of digits after decimal point. * * \param[in] val the number to be printed. * * \return the number of bytes written. */ size_t print(float val, uint8_t prec = 2) { int n = printDec(val, prec); return n > 0 ? n : 0; } //---------------------------------------------------------------------------- /** Print a number. * * \param[in] val the number to be printed. * * \return the number of bytes written. */ template size_t print(T val) { int n = printDec(val); return n > 0 ? n : 0; } //---------------------------------------------------------------------------- /** Write a CR/LF. * * \return two, the number of bytes written, for success or zero for failure. */ size_t println() { return putCRLF() > 0 ? 2 : 0; } //---------------------------------------------------------------------------- /** Print a floating point number followed by CR/LF. * * \param[in] val the number to be printed. * * \param[in] prec Number of digits after decimal point. * * \return the number of bytes written. */ size_t println(double val, uint8_t prec = 2) { return println(static_cast(val), prec); } //---------------------------------------------------------------------------- /** Print a floating point number followed by CR/LF. * * \param[in] val the number to be printed. * * \param[in] prec Number of digits after decimal point. * * \return the number of bytes written. */ size_t println(float val, uint8_t prec = 2) { int n = printDec(val, prec); return n > 0 && putCRLF() > 0 ? n + 2 : 0; } //---------------------------------------------------------------------------- /** Print an item followed by CR/LF * * \param[in] val the item to be printed. * * \return the number of bytes written. */ template size_t println(T val) { int n = print(val); return putCRLF() > 0 ? n + 2 : 0; } //---------------------------------------------------------------------------- /** Print a char as a number. * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(char n) { if (CHAR_MIN == 0) { return printDec((unsigned char)n); } else { return printDec((signed char)n); } } //---------------------------------------------------------------------------- /** print a signed 8-bit integer * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(signed char n); //---------------------------------------------------------------------------- /** Print an unsigned 8-bit number. * \param[in] n number to be print. * \return The number of bytes written or -1 if an error occurs. */ int printDec(unsigned char n) { return printDec((uint16_t)n); } //---------------------------------------------------------------------------- /** Print a int16_t * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(int16_t n); //---------------------------------------------------------------------------- /** print a uint16_t. * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(uint16_t n); //---------------------------------------------------------------------------- /** Print a signed 32-bit integer. * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(int32_t n); //---------------------------------------------------------------------------- /** Write an unsigned 32-bit number. * \param[in] n number to be printed. * \return The number of bytes written or -1 if an error occurs. */ int printDec(uint32_t n); //---------------------------------------------------------------------------- /** Print a double. * \param[in] value The number to be printed. * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ int printDec(double value, uint8_t prec) { return printDec(static_cast(value), prec); } //---------------------------------------------------------------------------- /** Print a float. * \param[in] value The number to be printed. * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ int printDec(float value, uint8_t prec); //---------------------------------------------------------------------------- /** Print a number followed by a field terminator. * \param[in] value The number to be printed. * \param[in] term The field terminator. * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ int printField(double value, char term, uint8_t prec = 2) { return printField(static_cast(value), term, prec) > 0; } //---------------------------------------------------------------------------- /** Print a number followed by a field terminator. * \param[in] value The number to be printed. * \param[in] term The field terminator. * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ int printField(float value, char term, uint8_t prec = 2) { int rtn = printDec(value, prec); return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; } //---------------------------------------------------------------------------- /** Print a number followed by a field terminator. * \param[in] value The number to be printed. * \param[in] term The field terminator. * \return The number of bytes written or -1 if an error occurs. */ template int printField(T value, char term) { int rtn = printDec(value); return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; } //---------------------------------------------------------------------------- /** Print HEX * \param[in] n number to be printed as HEX. * * \return The number of bytes written or -1 if an error occurs. */ int printHex(uint32_t n); //---------------------------------------------------------------------------- /** Print HEX with CRLF * \param[in] n number to be printed as HEX. * * \return The number of bytes written or -1 if an error occurs. */ int printHexln(uint32_t n) { int rtn = printHex(n); return rtn < 0 || putCRLF() != 2 ? -1 : rtn + 2; } //---------------------------------------------------------------------------- /** Set position of a stream to the beginning. * * The rewind function sets the file position to the beginning of the * file. It is equivalent to fseek(0L, SEEK_SET) except that the error * indicator for the stream is also cleared. * * \return true for success or false for failure. */ bool rewind(); //---------------------------------------------------------------------------- /** Push a byte back into an input stream. * * \param[in] c the byte (converted to an unsigned char) to be pushed back. * * One character of push-back is guaranteed. If the ungetc function is * called too many times without an intervening read or file positioning * operation on that stream, the operation may fail. * * A successful intervening call to a file positioning function (fseek, * fsetpos, or rewind) discards any pushed-back characters for the stream. * * \return Upon successful completion, ungetc() returns the byte pushed * back after conversion. Otherwise it returns EOF. */ int ungetc(int c); //============================================================================ private: bool fillBuf(); int fillGet(); bool flushBuf(); int flushPut(uint8_t c); char* fmtSpace(uint8_t len); int write(const void* buf, size_t count); //---------------------------------------------------------------------------- // S_SRD and S_WR are never simultaneously asserted static const uint8_t S_SRD = 0x01; // OK to read static const uint8_t S_SWR = 0x02; // OK to write static const uint8_t S_SRW = 0x04; // open for reading & writing static const uint8_t S_EOF = 0x10; // found EOF static const uint8_t S_ERR = 0x20; // found error //---------------------------------------------------------------------------- uint8_t m_status; uint8_t* m_p; uint8_t m_r; uint8_t m_w; uint8_t m_buf[STREAM_BUF_SIZE]; }; //------------------------------------------------------------------------------ #endif // StdioStream_h