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

[tyndur-devel] [PATCH] libc: Strings aus breiten Zeichen in readline() benutzen



* libc: Strings aus breiten Zeichen in readline() benutzen intern, da
        das ganze sonst äusserst mühsam wird mit Multibyte-Zeichen wenn
        beispielsweise wortweise Navigation eingabaut werden soll.

Signed-off-by: Antoine Kaufmann <toni@xxxxxxxxxx>
---
 src/modules/lib/readline.c |  178 +++++++++++++++++---------------------------
 1 files changed, 68 insertions(+), 110 deletions(-)

diff --git a/src/modules/lib/readline.c b/src/modules/lib/readline.c
index b2df3fc..79a0cfe 100644
--- a/src/modules/lib/readline.c
+++ b/src/modules/lib/readline.c
@@ -133,94 +133,28 @@ static wint_t keyboard_read_char(void)
 /**
  * Loescht ein Zeichen aus dem Puffer
  */
-static void delchar(char* buffer, int pos, int* size)
+static void delchar(wchar_t* buffer, int pos, int* size)
 {
-    int i;
-    size_t len = strlen(buffer);
-    int clen;
-
-    if (len < pos) {
+    if (pos >= *size) {
         return;
     }
 
-    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 - clen] = '\0';
-    *size -= clen;
+    wmemmove(buffer + pos, buffer + pos + 1, *size - pos - 1);
+    buffer[--(*size)] = L'\0';
 }
 
 /**
  * Fuegt ein Zeichen in den Buffer ein
  */
-static void inschar(char* buffer, int* pos, int* size, wint_t c)
+static void inschar(wchar_t* buffer, int* pos, int* size, wint_t c)
 {
-    int i;
-    size_t len = strlen(buffer);
-    char mbbuf[MB_CUR_MAX];
-    int clen;
-
-
-    clen = wctomb(mbbuf, c);
-    if ((len < *pos) || (len + clen >= BUFFER_SIZE) || (clen == -1)) {
+    if ((*pos > *size) || (*size + 1 >= BUFFER_SIZE)) {
         return;
     }
 
-    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;
-        }
-    }
-
-    return count;
+    wmemmove(buffer + *pos + 1, buffer + *pos, *size - *pos);
+    buffer[(*pos)++] = c;
+    buffer[++(*size)] = L'\0';
 }
 
 /**
@@ -254,14 +188,13 @@ static void free_matches_array(char** matches)
  *         Zeile umgebrochen wurde, FALSE wenn entweder nichts gemacht wurde,
  *         oder nur ein passender Match geliefert wurde.
  */
-static bool complete_word(char* buffer, int* pos, int* len)
+static bool complete_word(wchar_t* buffer, int* pos, int* len)
 {
     int word_pos;
     int match_count;
-    int word_len;
+    size_t wword_len;
     char** matches;
     char* replacement;
-    int replacement_len;
     bool matches_list_displayed = FALSE;
 
 
@@ -272,16 +205,20 @@ static bool complete_word(char* buffer, int* pos, int* len)
     if ((buffer[word_pos] == ' ') && (*pos > word_pos)) {
         word_pos++;
     }
-    word_len = *pos - word_pos;
+    wword_len = *pos - word_pos;
 
     // Wort in einen eigenen Buffer kopieren
+    wchar_t wword[wword_len + 1];
+    wcsncpy(wword, buffer + word_pos, wword_len);
+    wword[wword_len] = 0;
+
+    size_t word_len = wcstombs(NULL, wword, 0);
     char word[word_len + 1];
-    strncpy(word, buffer + word_pos, word_len);
-    word[word_len] = 0;
+    wcstombs(word, wword, word_len + 1);
 
     matches = rl_attempted_completion_function(word, word_pos, *pos);
 
-    // Wir haben nochmal Glueck gehabt, es gibt nicht zu vervollstaendigen ;-)
+    // Wir haben nochmal Glueck gehabt, es gibt nichts zu vervollstaendigen ;-)
     if (matches == NULL) {
         return FALSE;
     }
@@ -319,27 +256,35 @@ static bool complete_word(char* buffer, int* pos, int* len)
 
 
         replacement = matches[0];
-        replacement_len = matching_chars;
+
+        // Wir haben mehrere Matches, wollen aber nur soviel vervollstaendigen
+        // wie allen gemeinsam ist. Deshalb muessen wir vorher ein Nullbyte
+        // reinbasteln, damit nur soviel benutzt wird von mbstowcs.
+        replacement[matching_chars] = '\0';
     } else {
         replacement = matches[0];
-        replacement_len = strlen(replacement);
     }
 
+    // Neuen Text in breiten String konvertieren
+    size_t wrepl_len = mbstowcs(NULL, replacement, 0);
+    wchar_t wrepl[wrepl_len + 1];
+    mbstowcs(wrepl, replacement, wrepl_len + 1);
+
     // Pruefen ob das ganze nachher noch in den Puffer passt
-    if (*len + replacement_len > BUFFER_SIZE) {
+    if (*len + wrepl_len + 1 > BUFFER_SIZE) {
         return matches_list_displayed;
     }
 
     // Text, der dem zu ersetzenden Wort folgt nach hinten schieben
-    memmove(buffer + word_pos + replacement_len, buffer + *pos, (*len - word_len) -
+    wmemmove(buffer + word_pos + wrepl_len, buffer + *pos, (*len - word_len) -
         word_pos + 1);
 
     // Wort ersetzen
-    memcpy(buffer + word_pos, replacement, replacement_len);
+    wmemcpy(buffer + word_pos, wrepl, wrepl_len);
 
     // Position und Laenge des Puffers korrigieren
-    *pos = word_pos + replacement_len;
-    *len = *len + replacement_len - word_len ;
+    *pos = word_pos + wrepl_len;
+    *len = *len + wrepl_len - word_len ;
 
     free_matches_array(matches);
 
@@ -393,7 +338,7 @@ char* readline(const char* prompt)
         return NULL;
     }
 
-    char* buffer = malloc(BUFFER_SIZE);
+    wchar_t buffer[BUFFER_SIZE];
     int old_pos, pos, size;
     bool enter = FALSE;
     int history_pos = -1;
@@ -402,12 +347,10 @@ char* readline(const char* prompt)
     wchar_t cmd_buf[8];
     size_t cmd_pos = 0;
 
-    rl_line_buffer = buffer;
-
     printf("%s", prompt);
     fflush(stdout);
 
-    memset(buffer, 0, BUFFER_SIZE);
+    wmemset(buffer, 0, BUFFER_SIZE);
     pos = size = 0;
     while (!enter)
     {
@@ -434,7 +377,9 @@ again:
 
             // Neu eingefuegtes Zeichen und allenfalls Folgende auf den
             // Bildschirm bringen
-            printf("\033[K\033[s%s\033[u\033[1C", &buffer[old_pos]);
+            printf("\033[K\033[s");
+            fputws(&buffer[old_pos], stdout);
+            printf("\033[u\033[1C");
             fflush(stdout);
 
             // Erstes Zeichen weg(raus aus dem Befehlspuffer und rein in den
@@ -450,7 +395,7 @@ again:
                 if (pos > 0) {
                     printf("\033[1D");
                     fflush(stdout);
-                    move_cursor(buffer, &pos, size, -1);
+                    pos--;
                 }
                 break;
 
@@ -458,20 +403,19 @@ again:
                 if (pos < size) {
                     printf("\033[1C");
                     fflush(stdout);
-                    move_cursor(buffer, &pos, size, 1);
+                    pos++;
                 }
                 break;
 
             case BEGINNING_OF_LINE:
-                printf("\033[%dD", count_visible_chars(buffer, 0, pos));
+                printf("\033[%dD", pos);
                 pos = 0;
                 fflush(stdout);
                 break;
 
             case END_OF_LINE:
-                printf("\033[%dC",
-                    count_visible_chars(buffer, pos, size));
-                pos = strlen(buffer);
+                printf("\033[%dC", size - pos);
+                pos = size;
                 fflush(stdout);
                 break;
 
@@ -492,36 +436,41 @@ again:
                     }
                 }
 
-                memset(buffer, 0, BUFFER_SIZE);
+                memset(buffer, 0, BUFFER_SIZE * sizeof(wchar_t));
                 if (history_pos > -1) {
-                    strncpy(buffer,
+                    mbstowcs(buffer,
                         list_get_element_at(history, history_pos),
                         BUFFER_SIZE - 1);
                 }
 
                 char* format;
                 asprintf(&format, "\033[%dD", pos);
-                printf("%s\033[K%s", format, buffer);
+                printf("%s\033[K", format);
+                fputws(buffer, stdout);
                 free(format);
                 fflush(stdout);
 
-                pos = size = strlen(buffer);
+                pos = size = wcslen(buffer);
                 break;
 
 
             case DELETE_CHAR:
                 if (pos < size) {
                     delchar(buffer, pos, &size);
-                    printf("\033[K\033[s%s\033[u", &buffer[pos]);
+                    printf("\033[K\033[s");
+                    fputws(&buffer[pos], stdout);
+                    printf("\033[u");
                     fflush(stdout);
                 }
                 break;
 
             case BACKWARD_DELETE_CHAR:
                 if (pos > 0) {
-                    move_cursor(buffer, &pos, size, -1);
+                    pos--;
                     delchar(buffer, pos, &size);
-                    printf("\033[1D\033[K\033[s%s\033[u", &buffer[pos]);
+                    printf("\033[1D\033[K\033[s");
+                    fputws(&buffer[pos], stdout);
+                    printf("\033[u");
                     fflush(stdout);
                 }
                 break;
@@ -541,12 +490,16 @@ again:
                     if (!complete_word(buffer, &pos, &size)) {
                         // Wenn die Match-Liste nicht angezeigt wurde, muss die
                         // Zeile aktualisiert werden
-                        printf("\033[s\033[%dD%s\033[u", old_pos, buffer);
+                        printf("\033[s\033[%dD", old_pos);
+                        fputws(buffer, stdout);
+                        printf("\033[u");
                         if (old_pos != pos) {
                             printf("\033[%dC", pos - old_pos);
                         }
                     } else {
-                        printf("%s%s\033[%dD", prompt, buffer, size - pos);
+                        printf("%s", prompt);
+                        fputws(buffer, stdout);
+                        printf("\033[%dD", size - pos);
                     }
 
                     fflush(stdout);
@@ -558,7 +511,12 @@ again:
         }
     }
 
-    return realloc(buffer, strlen(buffer) + 1);
+    size_t needed_size = wcstombs(NULL, buffer, 0) + 1;
+    char* return_buf = malloc(needed_size);
+    rl_line_buffer = return_buf;
+    wcstombs(return_buf, buffer, needed_size);
+
+    return return_buf;
 }
 
 /**
-- 
1.6.0.6