[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[tyndur-devel] [PATCH] libc: readline() multibyte-kompatibel machen



* libc: readline() kommt jetzt auch mit Zeichen klar, die mehr als ein
        Byte in Anspruch nehmen (z.B. UTF-8).
---
 src/modules/lib/readline.c |  111 +++++++++++++++++++++++++++++++------------
 1 files changed, 80 insertions(+), 31 deletions(-)

diff --git a/src/modules/lib/readline.c b/src/modules/lib/readline.c
index 5c7fab0..97242d5 100644
--- a/src/modules/lib/readline.c
+++ b/src/modules/lib/readline.c
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <collections.h>
+#include <wchar.h>
 
 #include <readline/readline.h>
 
@@ -53,13 +54,13 @@ char* rl_line_buffer;
  * @return Das eingelesene Zeichen oder NULL, wenn das Ende der Eingabedatei
  * erreicht ist.
  */
-static char keyboard_read_char(void)
+static wint_t keyboard_read_char(void)
 {
-    char c = 0;
-    
-    while (!fread(&c, 1, 1, stdin)) {
+    wint_t c = 0;
+
+    while ((c = fgetwc(stdin)) == WEOF) {
         if (feof(stdin)) {
-            return 0;
+            return WEOF;
         }
     }
 
@@ -69,42 +70,94 @@ static char keyboard_read_char(void)
 /**
  * Loescht ein Zeichen aus dem Puffer
  */
-static void delchar(char* buffer, int pos)
+static void delchar(char* buffer, int pos, int* size)
 {
     int i;
     size_t len = strlen(buffer);
+    int clen;
 
     if (len < pos) {
         return;
     }
 
-    for (i = pos; i < BUFFER_SIZE - 1; i++) {
-        buffer[i] = buffer[i + 1];
+    clen = mblen(buffer + pos, len - pos + 1);
+    for (i = pos; i < BUFFER_SIZE - clen; i++) {
+        buffer[i] = buffer[i + clen];
         if (buffer[i] == '\0') {
             break;
         }
     }
 
-    buffer[BUFFER_SIZE - 1] = '\0';
+    buffer[BUFFER_SIZE - clen] = '\0';
+    *size -= clen;
 }
 
 /**
  * Fuegt ein Zeichen in den Buffer ein
  */
-static void inschar(char* buffer, int pos, char c)
+static void inschar(char* buffer, int* pos, int* size, wint_t c)
 {
     int i;
     size_t len = strlen(buffer);
+    char mbbuf[MB_CUR_MAX];
+    int clen;
+
 
-    if ((len < pos) || (len + 1 >= BUFFER_SIZE)) {
+    clen = wctomb(mbbuf, c);
+    if ((len < *pos) || (len + clen >= BUFFER_SIZE) || (clen == -1)) {
         return;
     }
 
-    for (i = len + 1; i > pos; i--) {
-        buffer[i] = buffer[i - 1];
+    for (i = len + clen; i > *pos; i--) {
+        buffer[i] = buffer[i - clen];
+    }
+
+    memcpy(buffer + *pos, mbbuf, clen);
+
+    *pos += clen;
+    *size += clen;
+}
+
+/**
+ * Veraendert die Pufferposition; positiv => rechts, negativ => links
+ */
+static void move_cursor(char* buffer, int* pos, int size, int offset)
+{
+    int clen;
+
+    if (offset >  0) {
+        // Rechts
+        if ((clen = mblen(buffer + *pos, size - *pos + 1)) > 0) {
+            *pos += clen;
+        }
+    } else {
+        // Links, bis wir ein ganzes Zeichen haben
+        while (--(*pos) && (mblen(buffer + *pos, size - *pos + 1) == -1)) { }
+    }
+}
+
+/**
+ * Zaehlt die Anzahl der angezeigten Zeichen im Puffer
+ *
+ * @param start Position von der angefangen werden soll zu zaehlen
+ * @param size  Anzahl Zeichen im Puffer
+ */
+static int count_visible_chars(char* buffer, int start, int size)
+{
+    int clen;
+    int count = 0;
+
+    while ((start < size)) {
+        if ((clen = mblen(buffer + start, size - start + 1)) == -1) {
+            // Unvollstaendiges MB-Zeichen wird uebersprungen
+            start++;
+        } else {
+            count++;
+            start += clen;
+        }
     }
 
-    buffer[pos] = c;
+    return count;
 }
 
 /**
@@ -253,10 +306,10 @@ char* readline(const char* prompt)
     pos = size = 0;
     while (!enter)
     {
-        char c = keyboard_read_char();
+        wint_t c = keyboard_read_char();
 
         switch (c) {
-
+            case WEOF:
             case '\0':
                 c = keyboard_read_char();
                 enter = TRUE;
@@ -283,7 +336,7 @@ char* readline(const char* prompt)
                         if (pos > 0) {
                             printf("\033[1D");
                             fflush(stdout);
-                            pos--;
+                            move_cursor(buffer, &pos, size, -1);
                         }
                         break;
 
@@ -292,7 +345,7 @@ char* readline(const char* prompt)
                         if (pos < size) {
                             printf("\033[1C");
                             fflush(stdout);
-                            pos++;
+                            move_cursor(buffer, &pos, size, 1);
                         }
                         break;
 
@@ -338,8 +391,7 @@ char* readline(const char* prompt)
                         }
 
                         if (pos < size) {
-                            delchar(buffer, pos);
-                            size--;
+                            delchar(buffer, pos, &size);
                             printf("\033[K\033[s%s\033[u", &buffer[pos]);
                             fflush(stdout);
                         }
@@ -349,17 +401,16 @@ char* readline(const char* prompt)
 
                     // Pos1
                     case 'H':
-                        printf("\033[%dD", pos);
+                        printf("\033[%dD", count_visible_chars(buffer, 0, pos));
                         pos = 0;
                         fflush(stdout);
                         break;
 
                     // Ende
                     case 'F':
-                        printf("\033[%dC", strlen(buffer) - pos);
+                        printf("\033[%dC",
+                            count_visible_chars(buffer, pos, size));
                         pos = strlen(buffer);
-                        delchar(buffer, pos);
-                        printf("\033[K\033[s%s\033[u", &buffer[pos]);
                         fflush(stdout);
                         break;
 
@@ -377,9 +428,8 @@ seq_nomatch:
 
             case '\b':
                 if (pos > 0) {
-                    pos--;
-                    size--;
-                    delchar(buffer, pos);
+                    move_cursor(buffer, &pos, size, -1);
+                    delchar(buffer, pos, &size);
                     printf("\033[1D\033[K\033[s%s\033[u", &buffer[pos]);
                     fflush(stdout);
                 }
@@ -414,10 +464,9 @@ seq_nomatch:
 
             default:
                 if (pos < BUFFER_SIZE - 1) {
-                    inschar(buffer, pos, c);
-                    printf("\033[K\033[s%s\033[u\033[1C", &buffer[pos]);
-                    pos++;
-                    size++;
+                    int old_pos = pos;
+                    inschar(buffer, &pos, &size, c);
+                    printf("\033[K\033[s%s\033[u\033[1C", &buffer[old_pos]);
                     fflush(stdout);
                 }
                 break;
-- 
1.6.0.6