I've been playing with this some more and, for posterity, I'll post my latest "correct and it's really perfect now" (HA!) code. I'll post here if there's more improvements.
Here's my "stdio implementation" block:
Code:
// ---------------------------------------------------------------------------
// stdio implementation
// ---------------------------------------------------------------------------
extern "C" {
// https://forum.pjrc.com/threads/28473-Quick-Guide-Using-printf()-on-Teensy-ARM
int _write(int file, const char *buf, int len) {
// Send both stdout and stderr to debugOut
if (file == stdout->_file || file == stderr->_file) {
if (!stdserialReady) {
return 0;
}
return stdserial.write(reinterpret_cast<const uint8_t *>(buf), len);
}
// Doing the following keeps this compatible with Print.cpp's requirements;
// it also allows us to pass our own Print* as a file descriptor
Print *p = reinterpret_cast<Print *>(file);
if (p == nullptr) {
errno = EBADF;
return -1;
}
return p->write(reinterpret_cast<const uint8_t *>(buf), len);
}
// https://forum.pjrc.com/threads/27827-Float-in-sscanf-on-Teensy-3-1
// This link shows how to enable float scanning.
int _read(int file, char *buf, int len) {
if (file == stdin->_file) {
if (len <= 0 || !stdserialReady) {
return 0;
}
int toRead = stdserial.available();
if (toRead <= 0) {
return 0;
}
if (toRead > len) {
toRead = len;
}
return stdserial.readBytes(buf, toRead);
}
// No other possible input file dscriptors (for our program)
// so return an error
errno = EBADF;
return -1;
}
}
One critical point for the above code is that `_read` returns `0` if nothing's available. This indicates the EOF condition and the error condition must be cleared when reading if you want stdin to continue properly. This is a non-blocking style and its use is shown in the `readLine()` example below.
A blocking approach to `_read` might look something like this:
Code:
int _read(int file, char *buf, int len) {
if (file == stdin->_file) {
if (len <= 0) {
return 0;
}
int toRead;
while ((toRead = stdserial.available()) <= 0) {
yield();
}
if (toRead > len) {
toRead = len;
}
return stdserial.readBytes(buf, toRead);
}
errno = EBADF;
return -1;
}
Here's a function for reading a single line that uses the non-blocking approach. It's assumed that the line string is cleared before entering the state that loops and calls `readLine()`. i.e. clear the string and then loop; don't clear the string before each call to `readLine()`. It correctly handles the three line ending forms: LF, CR, and CRLF:
Code:
String line;
bool readLine() {
static bool inCR = false;
while (true) {
int c;
switch (c = fgetc(stdin)) {
case '\r':
inCR = true;
break;
case '\n':
inCR = false;
return true;
default:
if (c < 0) {
clearerr(stdin); // This is so we can continue using stdin
if (inCR) {
inCR = false;
return true;
}
return false;
}
if (inCR) {
inCR = false;
ungetc(c, stdin);
return true;
}
inCR = false;
line.append(static_cast<char>(c));
}
}
}
This returns true if a line is found, even an empty one, and false if more checking is needed.