/* 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
* .
*/
/**
* \file
* StdioStream implementation
*/
#include
#include
#include
//------------------------------------------------------------------------------
int StdioStream::fclose() {
int rtn = 0;
if (!m_flags) {
return EOF;
}
if (m_flags & F_SWR) {
if (!flushBuf()) rtn = EOF;
}
if (!SdBaseFile::close()) rtn = EOF;
m_r = 0;
m_w = 0;
m_flags = 0;
return rtn;
}
//------------------------------------------------------------------------------
int StdioStream::fflush() {
if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) {
if (flushBuf() && SdBaseFile::sync()) return 0;
}
return EOF;
}
//------------------------------------------------------------------------------
char* StdioStream::fgets(char* str, int num, size_t* len) {
char* s = str;
size_t n;
if (num-- <= 0) return 0;
while (num) {
if ((n = m_r) == 0) {
if (!fillBuf()) {
if (s == str) return 0;
break;
}
n = m_r;
}
if (n > num) n = num;
uint8_t* end = reinterpret_cast(memchr(m_p, '\n', n));
if (end != 0) {
n = ++end - m_p;
memcpy(s, m_p, n);
m_r -= n;
m_p = end;
s += n;
break;
}
memcpy(s, m_p, n);
m_r -= n;
m_p += n;
s += n;
num -= n;
}
*s = 0;
if (len) *len = s - str;
return str;
}
//------------------------------------------------------------------------------
bool StdioStream::fopen(const char* filename, const char* mode) {
uint8_t oflags;
switch (*mode++) {
case 'a':
m_flags = F_SWR;
oflags = O_WRITE | O_CREAT | O_APPEND | O_AT_END;
break;
case 'r':
m_flags = F_SRD;
oflags = O_READ;
break;
case 'w':
m_flags = F_SWR;
oflags = O_WRITE | O_CREAT | O_TRUNC;
break;
default:
goto fail;
}
while (*mode) {
switch (*mode++) {
case '+':
m_flags |= F_SRW;
oflags |= O_RDWR;
break;
case 'b':
break;
case 'x':
oflags |= O_EXCL;
break;
default:
goto fail;
}
}
if ((oflags & O_EXCL) && !(oflags & O_WRITE)) goto fail;
if (!SdBaseFile::open(filename, oflags)) goto fail;
m_r = 0;
m_w = 0;
m_p = m_buf;
return true;
fail:
m_flags = 0;
return false;
}
//------------------------------------------------------------------------------
int StdioStream::fputs(const char* str) {
size_t len = strlen(str);
return fwrite(str, 1, len) == len ? len : EOF;
}
//------------------------------------------------------------------------------
int StdioStream::fputs_P(PGM_P str) {
PGM_P bgn = str;
for (char c; (c = pgm_read_byte(str)); str++) {
if (putc(c) < 0) return EOF;
}
return str - bgn;
}
//------------------------------------------------------------------------------
size_t StdioStream::fread(void* ptr, size_t size, size_t count) {
uint8_t* dst = reinterpret_cast(ptr);
size_t total = size*count;
if (total == 0) return 0;
size_t need = total;
while (need > m_r) {
memcpy(dst, m_p, m_r);
dst += m_r;
m_p += m_r;
need -= m_r;
if (!fillBuf()) {
return (total - need)/size;
}
}
memcpy(dst, m_p, need);
m_r -= need;
m_p += need;
return count;
}
//------------------------------------------------------------------------------
int StdioStream::fseek(int32_t offset, int origin) {
int32_t pos;
if (m_flags & F_SWR) {
if (!flushBuf()) {
goto fail;
}
}
switch (origin) {
case SEEK_CUR:
pos = ftell();
if (pos < 0) {
goto fail;
}
pos += offset;
if (!SdBaseFile::seekCur(pos)) {
goto fail;
}
break;
case SEEK_SET:
if (!SdBaseFile::seekSet(offset)) {
goto fail;
}
break;
case SEEK_END:
if (!SdBaseFile::seekEnd(offset)) {
goto fail;
}
break;
default:
goto fail;
}
m_r = 0;
m_p = m_buf;
return 0;
fail:
return EOF;
}
//------------------------------------------------------------------------------
int32_t StdioStream::ftell() {
uint32_t pos = SdBaseFile::curPosition();
if (m_flags & F_SRD) {
if (m_r > pos) return -1L;
pos -= m_r;
} else if (m_flags & F_SWR) {
pos += m_p - m_buf;
}
return pos;
}
//------------------------------------------------------------------------------
size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) {
return write(ptr, count*size) < 0 ? EOF : count;
#if 0 ////////////////////////////////////////////////////////////////////////////////////
const uint8_t* src = static_cast(ptr);
size_t total = count*size;
if (total == 0) return 0;
size_t todo = total;
while (todo > m_w) {
memcpy(m_p, src, m_w);
m_p += m_w;
src += m_w;
todo -= m_w;
if (!flushBuf()) {
return (total - todo)/size;
}
}
memcpy(m_p, src, todo);
m_p += todo;
m_w -= todo;
return count;
#endif //////////////////////////////////////////////////////////////////////////////////
}
//------------------------------------------------------------------------------
int StdioStream::write(const void* buf, size_t count) {
const uint8_t* src = static_cast(buf);
size_t todo = count;
while (todo > m_w) {
memcpy(m_p, src, m_w);
m_p += m_w;
src += m_w;
todo -= m_w;
if (!flushBuf()) return EOF;
}
memcpy(m_p, src, todo);
m_p += todo;
m_w -= todo;
return count;
}
//------------------------------------------------------------------------------
size_t StdioStream::print(const __FlashStringHelper *str) {
const char PROGMEM *p = (const char PROGMEM *)str;
uint8_t c;
while (c = pgm_read_byte(p)) {
if (putc(c) < 0) return 0;
p++;
}
return p - (const char PROGMEM *)str;
}
//------------------------------------------------------------------------------
int StdioStream::printDec(float value, uint8_t prec) {
#define FLOAT_NEW_WAY
#ifdef FLOAT_NEW_WAY
char buf[24];
char *ptr = fmtFloat(value, buf + sizeof(buf), prec);
// return fputs(ptr);
// uint8_t len = buf + sizeof(buf) - ptr;
return write(ptr, buf + sizeof(buf) - ptr);
#else
char* ptr;
uint8_t rtn = 0;
uint8_t sign = 0;
if (value < 0) {
value = -value;
sign = '-';
}
// check for NaN INF OVF
if (isnan(value)) {
if (fputs_P(PSTR("nan")) < 0) return -1;
rtn += 3;
} else if (isinf(value)) {
if (fputs_P(PSTR("inf")) < 0) return -1;
rtn += 3;
} else if (value > 4294967040.0) {
if (fputs_P(PSTR("ovf")) < 0) return -1;;
rtn += 3;
} else {
if (sign) {
if (putc(sign) < 0) return -1;
rtn++;
}
if (prec > 9) prec = 9;
/*
uint32_t s = 1;
for (uint8_t i = 0; i < prec; i++) {
// s *= 10;
s = ((s << 2) + s) << 1;
}
// round value
value += 0.5/s;
*/
value += scale10(0.5, -prec);
uint32_t whole = value;
int np;
if ((np = printDec(whole)) < 0) return -1;
rtn += np;
if (prec) {
if (putc('.') < 0) return -1;
char* str = fmtSpace(prec);
if (!str) return -1;
char* tmp = str - prec;
// uint32_t fraction = s*(value - whole);
uint32_t fraction = scale10(value - whole, prec);
ptr = fmtDec(fraction, str);
while (ptr > tmp) *--ptr = '0';
rtn += prec + 1;
}
}
return rtn;
#endif
}
//------------------------------------------------------------------------------
int StdioStream::printDec(signed char n) {
uint8_t s = 0;
if (n < 0) {
if (fputc('-') < 0) return -1;
n = -n;
s = 1;
}
printDec((unsigned char)n);
}
//------------------------------------------------------------------------------
int StdioStream::printDec(int16_t n) {
int s;
uint8_t rtn = 0;
if (n < 0) {
if (fputc('-') < 0) return -1;
n = -n;
rtn++;
}
if ((s = printDec((uint16_t)n)) < 0) return s;
return rtn;
}
//------------------------------------------------------------------------------
int StdioStream::printDec(uint16_t n) {
#define NEW_WAY
#ifdef NEW_WAY
char buf[5];
char *ptr = fmtDec(n, buf + sizeof(buf));
uint8_t len = buf + sizeof(buf) - ptr;
return write(ptr, len);
#else
uint8_t len;
if (n < 100) {
len = n < 10 ? 1 : 2;
} else {
len = n < 1000 ? 3 : n < 10000 ? 4 : 5;
}
char* str = fmtSpace(len);
if (!str) return -1;
fmtDec(n, str);
return len;
#endif
}
//------------------------------------------------------------------------------
int StdioStream::printDec(int32_t n) {
uint8_t s = 0;
if (n < 0) {
if (fputc('-') < 0) return -1;
n = -n;
s = 1;
}
int rtn = printDec((uint32_t)n);
return rtn > 0 ? rtn + s : -1;
}
//------------------------------------------------------------------------------
int StdioStream::printDec(uint32_t n) {
#ifdef NEW_WAY
char buf[10];
char *ptr = fmtDec(n, buf + sizeof(buf));
uint8_t len = buf + sizeof(buf) - ptr;
return write(ptr, len);
#else
uint8_t len;
if (n < 0X10000) {
return printDec((uint16_t)n);
}
if (n < 10000000) {
len = n < 100000 ? 5 : n < 1000000 ? 6 : 7;
} else {
len = n < 100000000 ? 8 : n < 1000000000 ? 9 : 10;
}
char* str = fmtSpace(len);
if (!str) return -1;
fmtDec(n, str);
return len;
#endif
}
//------------------------------------------------------------------------------
int StdioStream::printHex(uint32_t n) {
#ifdef NEW_WAY
char buf[8];
char *ptr = fmtHex(n, buf + sizeof(buf));
uint8_t len = buf + sizeof(buf) - ptr;
return write(ptr, len);
#else
size_t len;
if (n < 0X10000) {
len = n < 0X10 ? 1 : n < 0X100 ? 2 : n < 0X1000 ? 3 : 4;
} else {
len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8;
}
char* str = fmtSpace(len);
if (!str) return -1;
do {
uint8_t h = n & 0XF;
*str-- = h + (h < 10 ? '0' : 'A' - 10);
n >>= 4;
} while (n);
return len;
#endif
}
//------------------------------------------------------------------------------
bool StdioStream::rewind() {
if (m_flags & F_SWR) {
if (!flushBuf()) return false;
}
SdBaseFile::seekSet(0);
m_r = 0;
return true;
}
//------------------------------------------------------------------------------
int StdioStream::ungetc(int c) {
// error if EOF.
if (c == EOF) return EOF;
// error if not reading.
if ((m_flags & F_SRD) == 0) return EOF;
// error if no space.
if (m_p == m_buf) return EOF;
m_r++;
m_flags &= ~F_EOF;
return *--m_p = (uint8_t)c;
}
//==============================================================================
// private
//------------------------------------------------------------------------------
int StdioStream::fillGet() {
if (!fillBuf()) {
return EOF;
}
m_r--;
return *m_p++;
}
//------------------------------------------------------------------------------
// private
bool StdioStream::fillBuf() {
if (!(m_flags & F_SRD)) { /////////////check for F_ERR and F_EOF ??/////////////////
if (!(m_flags & F_SRW)) {
m_flags |= F_ERR;
return false;
}
if (m_flags & F_SWR) {
if (!flushBuf()) {
return false;
}
m_flags &= ~F_SWR;
m_flags |= F_SRD;
m_w = 0;
}
}
m_p = m_buf + UNGETC_BUF_SIZE;
int nr = SdBaseFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE);
if (nr <= 0) {
m_flags |= nr < 0 ? F_ERR : F_EOF;
m_r = 0;
return false;
}
m_r = nr;
return true;
}
//------------------------------------------------------------------------------
// private
bool StdioStream::flushBuf() {
if (!(m_flags & F_SWR)) { /////////////////check for F_ERR ??////////////////////////
if (!(m_flags & F_SRW)) {
m_flags |= F_ERR;
return false;
}
m_flags &= ~F_SRD;
m_flags |= F_SWR;
m_r = 0;
m_w = sizeof(m_buf);
m_p = m_buf;
return true;
}
uint8_t n = m_p - m_buf;
m_p = m_buf;
m_w = sizeof(m_buf);
if (SdBaseFile::write(m_buf, n) == n) return true;
m_flags |= F_ERR;
return false;
}
//------------------------------------------------------------------------------
int StdioStream::flushPut(uint8_t c) {
if (!flushBuf()) return EOF;
m_w--;
return *m_p++ = c;
}
//------------------------------------------------------------------------------
char* StdioStream::fmtSpace(uint8_t len) {
if (m_w < len) {
if (!flushBuf() || m_w < len) {
return 0;
}
}
if (len > m_w) return 0;
m_p += len;
m_w -= len;
return reinterpret_cast(m_p);
}