From 8f5d49de812d2686c854ec37be5422f6b793603f Mon Sep 17 00:00:00 2001
From: otopetrik <oto.petrik@gmail.com>
Date: Mon, 19 Dec 2016 16:55:26 +0100
Subject: [PATCH] Allow changing write function of stdout (#304)

* Allow changing write function of stdout.

Required for stdout redirection.
Works on blocks, not chars - does _not_ use sdk_os_putc !
Should work even when linking with SPIFFS.
---
 core/include/stdout_redirect.h | 45 +++++++++++++++++++++++++++++++
 core/newlib_syscalls.c         | 48 +++++++++++++++++++++++++++++-----
 extras/spiffs/esp_spiffs.c     | 25 +++---------------
 3 files changed, 90 insertions(+), 28 deletions(-)
 create mode 100644 core/include/stdout_redirect.h

diff --git a/core/include/stdout_redirect.h b/core/include/stdout_redirect.h
new file mode 100644
index 0000000..8cc06ed
--- /dev/null
+++ b/core/include/stdout_redirect.h
@@ -0,0 +1,45 @@
+/*
+ * Part of esp-open-rtos
+ * Copyright (C) 2016 Oto Petrik <oto.petrik@gmail.com>
+ * BSD Licensed as described in the file LICENSE
+ */
+
+#ifndef _STDOUT_REDIRECT_H_
+#define _STDOUT_REDIRECT_H_
+
+#include <sys/reent.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef long _WriteFunction(struct _reent *r, int fd, const char *ptr, int len );
+
+/** Set implementation of write syscall for stdout.
+ *
+ *  Use this function to redirect stdout for further processing (save to file, send over network).
+ *  It may be good idea to save result of `get_write_stdout()` beforehand and call
+ *  it at the end of the supplied function.
+ *
+ *  NOTE: use NULL to reset to default implementation.
+ *
+ *  @param[in] f  New code to handle stdout output
+ *
+ */
+void set_write_stdout(_WriteFunction *f);
+
+/** Get current implementation of write syscall for stdout.
+ *
+ *  Save returned value before calling `set_write_stdout`, it allows for chaining
+ *  multiple independent handlers.
+ *
+ *  @returns current stdout handler
+ */
+_WriteFunction *get_write_stdout();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STDOUT_REDIRECT_H_ */
diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c
index f526bea..146a010 100644
--- a/core/newlib_syscalls.c
+++ b/core/newlib_syscalls.c
@@ -12,6 +12,7 @@
 #include <xtensa_ops.h>
 #include <esp/uart.h>
 #include <stdlib.h>
+#include <stdout_redirect.h>
 
 extern void *xPortSupervisorStackPointer;
 
@@ -41,12 +42,8 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
 }
 
 /* syscall implementation for stdio write to UART */
-__attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, int len )
+__attribute__((weak)) long _write_stdout_r(struct _reent *r, int fd, const char *ptr, int len )
 {
-    if(fd != r->_stdout->_file) {
-        r->_errno = EBADF;
-        return -1;
-    }
     for(int i = 0; i < len; i++) {
         /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */
         if(ptr[i] == '\r')
@@ -58,6 +55,37 @@ __attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, i
     return len;
 }
 
+static _WriteFunction *current_stdout_write_r = &_write_stdout_r;
+
+void set_write_stdout(_WriteFunction *f)
+{
+    if  (f != NULL) {
+        current_stdout_write_r = f;
+    } else {
+        current_stdout_write_r = &_write_stdout_r;
+    }
+}
+
+_WriteFunction *get_write_stdout()
+{
+    return current_stdout_write_r;
+}
+
+/* default implementation, replace in a filesystem */
+__attribute__((weak)) long _write_filesystem_r(struct _reent *r, int fd, const char *ptr, int len )
+{
+    r->_errno = EBADF;
+    return -1;
+}
+
+__attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, int len )
+{
+    if(fd != r->_stdout->_file) {
+        return _write_filesystem_r(r, fd, ptr, len);
+    }
+    return current_stdout_write_r(r, fd, ptr, len);
+}
+
 /* syscall implementation for stdio read from UART */
 __attribute__((weak)) long _read_stdin_r(struct _reent *r, int fd, char *ptr, int len)
 {
@@ -71,11 +99,17 @@ __attribute__((weak)) long _read_stdin_r(struct _reent *r, int fd, char *ptr, in
     return i;
 }
 
+/* default implementation, replace in a filesystem */
+__attribute__((weak)) long _read_filesystem_r( struct _reent *r, int fd, char *ptr, int len )
+{
+    r->_errno = EBADF;
+    return -1;
+}
+
 __attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len )
 {
     if(fd != r->_stdin->_file) {
-        r->_errno = EBADF;
-        return -1;
+        return _read_filesystem_r(r, fd, ptr, len);
     }
     return _read_stdin_r(r, fd, ptr, len);
 }
diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c
index 96ec743..3d81218 100644
--- a/extras/spiffs/esp_spiffs.c
+++ b/extras/spiffs/esp_spiffs.c
@@ -125,32 +125,15 @@ int32_t esp_spiffs_mount()
 }
 
 // This implementation replaces implementation in core/newlib_syscals.c
-long _write_r(struct _reent *r, int fd, const char *ptr, int len )
+long _write_filesystem_r(struct _reent *r, int fd, const char *ptr, int len )
 {
-    if(fd != r->_stdout->_file) {
-        return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len);
-    }
-    for(int i = 0; i < len; i++) {
-        /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */
-        if(ptr[i] == '\r')
-            continue;
-        if(ptr[i] == '\n')
-            uart_putc(0, '\r');
-        uart_putc(0, ptr[i]);
-    }
-    return len;
+    return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len);
 }
 
-// This function is weakly defined in core/newlib_syscalls.c
-long _read_stdin_r(struct _reent *r, int fd, char *ptr, int len);
-
 // This implementation replaces implementation in core/newlib_syscals.c
-long _read_r( struct _reent *r, int fd, char *ptr, int len )
+long _read_filesystem_r( struct _reent *r, int fd, char *ptr, int len )
 {
-    if(fd != r->_stdin->_file) {
-        return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len);
-    }
-    return _read_stdin_r(r, fd, ptr, len);
+    return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len);
 }
 
 int _open_r(struct _reent *r, const char *pathname, int flags, int mode)