| 
					
				 | 
			
			
				@@ -0,0 +1,297 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "core/ReadLine.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <ctype.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <errno.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdatomic.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdint.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdio.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdlib.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <string.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <termios.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <threads.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <unistd.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "ErrorSimulator.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "core/Logger.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "core/RingBuffer.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define HISTORY_LENGTH 10 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define CONSOLE_BUFFER_SIZE 256 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+typedef struct ConsoleLine { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char data[CONSOLE_BUFFER_SIZE]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int length; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} ConsoleLine; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static atomic_bool running = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static thrd_t readThread = {0}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static struct termios original; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CoreRingBuffer buffer = {0}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ConsoleLine currentBuffer = {0}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int move = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int cursorMove = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static mtx_t bufferMutex = {0}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ConsoleLine history[HISTORY_LENGTH]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int historyOffset = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int historyIndex = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int historyLength = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void getAttributes(struct termios* t) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(tcgetattr(STDIN_FILENO, t)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_WARNING("cannot get terminal attributes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void restoreAttributes() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &original)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_WARNING("cannot restore terminal attributes: %s", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         strerror(errno)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char readChar() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char c = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ssize_t bytes = read(STDIN_FILENO, &c, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return bytes <= 0 ? '\0' : c; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void addChar(char c) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(currentBuffer.length >= CONSOLE_BUFFER_SIZE - 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for(int i = 0; i < move; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        currentBuffer.data[currentBuffer.length - i] = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            currentBuffer.data[currentBuffer.length - i - 1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer.length++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer.data[currentBuffer.length - move - 1] = c; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer.data[currentBuffer.length] = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void print(const char* s) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fputs(s, stdout); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void refreshLine(const char* prefix) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    print(prefix); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    print(currentBuffer.data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(cursorMove > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        printf("\33[%dD", cursorMove); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fflush(stdout); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    print("\33[2K\r"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool clear() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    move = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cursorMove = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer.length = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer.data[0] = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    historyOffset = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void addToHistory() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(historyLength < HISTORY_LENGTH) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        historyLength++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    history[historyIndex] = currentBuffer; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    historyIndex = (historyIndex + 1) % HISTORY_LENGTH; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void lock() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(mtx_lock(&bufferMutex) != thrd_success || CORE_MUTEX_LOCK_FAIL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_WARNING("could not lock buffer mutex"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void unlock() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(mtx_unlock(&bufferMutex) != thrd_success || CORE_MUTEX_UNLOCK_FAIL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_WARNING("could not unlock buffer mutex"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void addLine() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    addToHistory(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    lock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    coreRingBufferAddPointer(&buffer, ¤tBuffer); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    unlock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char isUTF8Part(char c) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return ((uint8_t)c & 0xC0) == 0x80; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool removeChar() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int pos = currentBuffer.length - move; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(pos > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int l = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while(pos - l >= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if(!isUTF8Part(currentBuffer.data[pos - l])) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            l++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        currentBuffer.length -= l; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for(int i = pos - l; i <= currentBuffer.length; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            currentBuffer.data[i] = currentBuffer.data[i + l]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool handleControlKey(char c) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch(c) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 3: return clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 10: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 13: return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 127: return removeChar(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void copyHistory() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    currentBuffer = history[(historyIndex - historyOffset + HISTORY_LENGTH) % 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            HISTORY_LENGTH]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    move = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cursorMove = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void handleUpArrow() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(historyOffset >= historyLength) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    historyOffset++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    copyHistory(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void handleDownArrow() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(historyOffset <= 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    historyOffset--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    copyHistory(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char getMoved() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return currentBuffer.data[currentBuffer.length - move]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void handleLeftArrow() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(move < currentBuffer.length) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        move++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while(move < currentBuffer.length && isUTF8Part(getMoved())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            move++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cursorMove++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void handleRightArrow() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(move > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        move--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while(move > 0 && isUTF8Part(getMoved())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            move--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cursorMove--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void readEscapeSequence() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(readChar() != '[') { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch(readChar()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 'A': handleUpArrow(); break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 'B': handleDownArrow(); break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 'C': handleRightArrow(); break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case 'D': handleLeftArrow(); break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case '3': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if(readChar() == '~' && move > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                handleRightArrow(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                removeChar(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void handleChars() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while(true) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        char c = readChar(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if(c == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else if(c == 27) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            readEscapeSequence(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else if(iscntrl(c)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if(handleControlKey(c)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                addLine(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            addChar(c); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int loop(void* data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while(running) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        handleChars(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refreshLine("wusi> "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        struct timespec t = {.tv_nsec = 1000000}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        thrd_sleep(&t, nullptr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+bool coreStartReadLine(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    getAttributes(&original); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    struct termios raw = original; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    raw.c_iflag &= ~(tcflag_t)(ICRNL | IXON); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    raw.c_lflag &= ~(tcflag_t)(ECHO | ICANON | IEXTEN | ISIG); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    raw.c_cc[VMIN] = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    raw.c_cc[VTIME] = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(tcsetattr(STDIN_FILENO, TCSANOW, &raw)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_WARNING("cannot set terminal attributes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    buffer = CORE_RING_BUFFER(10, sizeof(ConsoleLine)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    atomic_store(&running, true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(CORE_MUTEX_INIT_FAIL || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       mtx_init(&bufferMutex, mtx_plain) != thrd_success) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_ERROR("cannot init buffer mutex"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        coreStopReadLine(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if(CORE_THREAD_INIT_FAIL || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              thrd_create(&readThread, loop, nullptr) != thrd_success) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CORE_LOG_ERROR("cannot start read thread"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        coreStopReadLine(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+bool coreReadLine(char* buffer_, size_t n) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(buffer.length == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    lock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ConsoleLine* line = coreRingBufferGetVoidPointer(&buffer, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    snprintf(buffer_, n, "%s", line->data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    coreRingBufferRemove(&buffer); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    unlock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void coreStopReadLine() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    atomic_store(&running, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    thrd_join(readThread, nullptr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    restoreAttributes(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    coreDestroyRingBuffer(&buffer); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    mtx_destroy(&bufferMutex); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |