|
- /* Arduino RamDisk Library
- * Copyright (C) 2014 by William Greiman
- *
- * This file is part of the Arduino RamDisk Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino RamDisk Library. If not, see
- * <http://www.gnu.org/licenses/>.
- */
- #ifndef StdioStream_h
- #define StdioStream_h
- /**
- * \file
- * StdioStream class
- */
- #include <limits.h>
- #include <Arduino.h>
- #include <SdFat.h>
- #include <SdBaseFile.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 <stdio.h>.
- #include <stdio.h>
- #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
- #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 SdBaseFile {
- public:
- /** Constructor
- *
- */
- StdioStream() {
- m_w = m_r = 0;
- m_p = m_buf;
- m_flags = 0;
- }
- //----------------------------------------------------------------------------
- /** Clear the stream's end-of-file and error indicators. */
- void clearerr() {m_flags &= ~(F_ERR | F_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_flags & F_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_flags & F_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, int num, size_t* len = 0);
- //----------------------------------------------------------------------------
- /** Open a stream.
- *
- * Open a file and associates the stream with it.
- *
- * \param[in] filename name of the file to be opened.
- *
- * \param[in] mode a string that indicates the open mode.
- *
- * <table>
- * <tr>
- * <td>"r" or "rb"</td>
- * <td>Open a file for reading. The file must exist.</td>
- * </tr>
- * <tr>
- * <td>"w" or "wb"</td>
- * <td>Truncate an existing to zero length or create an empty file
- * for writing.</td>
- * </tr>
- * <tr>
- * <td>"wx" or "wbx"</td>
- * <td>Create a file for writing. Fails if the file already exists.</td>
- * </tr>
- * <tr>
- * <td>"a" or "ab"</td>
- * <td>Append; open or create file for writing at end-of-file.</td>
- * </tr>
- * <tr>
- * <td>"r+" or "rb+" or "r+b"</td>
- * <td>Open a file for update (reading and writing).</td>
- * </tr>
- * <tr>
- * <td>"w+" or "w+b" or "wb+"</td>
- * <td>Truncate an existing to zero length or create a file for update.</td>
- * </tr>
- * <tr>
- * <td>"w+x" or "w+bx" or "wb+x"</td>
- * <td>Create a file for update. Fails if the file already exists.</td>
- * </tr>
- * <tr>
- * <td>"a+" or "a+b" or "ab+"</td>
- * <td>Append; open or create a file for update, writing at end-of-file.</td>
- * </tr>
- * </table>
- * 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 endof-file.
- *
- * \return true for success or false for failure.
- */
- bool fopen(const char* filename, 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);
- //----------------------------------------------------------------------------
- /** Write a string stored in flash.
- *
- * \param[in] str 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_P(PGM_P 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 occured. 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 inline 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 inline 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;
- }
- //----------------------------------------------------------------------------
- /** 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);
- //----------------------------------------------------------------------------
- /** 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<float>(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 <typename T>
- 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<float>(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 <typename T>
- 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<float>(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<float>(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 <typename T>
- 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 pushback 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);
- //----------------------------------------------------------------------------
- // F_SRD and F_WR are never simultaneously asserted
- static const uint8_t F_SRD = 0x01; // OK to read
- static const uint8_t F_SWR = 0x02; // OK to write
- static const uint8_t F_SRW = 0x04; // open for reading & writing
- static const uint8_t F_EOF = 0x10; // found EOF
- static const uint8_t F_ERR = 0x20; // found error
- //----------------------------------------------------------------------------
- uint8_t m_flags;
- uint8_t* m_p;
- uint8_t m_r;
- uint8_t m_w;
- uint8_t m_buf[STREAM_BUF_SIZE];
- };
- //------------------------------------------------------------------------------
- #endif // StdioStream_h
|