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

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



Am Sonntag, 17. Mai 2009 18:19 schrieb Antoine Kaufmann:
> * 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 |  154
> ++++++++++++++++++++------------------------ 1 files changed, 70
> insertions(+), 84 deletions(-)
>
> diff --git a/src/modules/lib/readline.c b/src/modules/lib/readline.c
> index dac4305..e740f22 100644
> --- a/src/modules/lib/readline.c
> +++ b/src/modules/lib/readline.c
> @@ -133,69 +133,39 @@ 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 >= BUFFER_SIZE)) {

Off by one. BUFFER_SIZE ist mit Nullbyte.

>          return;
>      }
>
> -    for (i = len + clen; i > *pos; i--) {
> -        buffer[i] = buffer[i - clen];
> -    }
> -
> -    memcpy(buffer + *pos, mbbuf, clen);
> -
> -    *pos += clen;
> -    *size += clen;
> +    wmemmove(buffer + *pos + 1, buffer + *pos, *size - *pos);
> +    buffer[(*pos)++] = c;
> +    buffer[++(*size)] = L'\0';
>  }
>
>  /**
>   * Veraendert die Pufferposition; positiv => rechts, negativ => links
>   */
> -static void move_cursor(char* buffer, int* pos, int size, int offset)
> +static void move_cursor(wchar_t* buffer, int* pos, int size, int offset)
>  {
> -    int clen;
> -
>      if (offset >  0) {
> -        // Rechts
> -        if ((clen = mblen(buffer + *pos, size - *pos + 1)) > 0) {
> -            *pos += clen;
> -        }
> +        (*pos)++;
>      } else {
> -        // Links, bis wir ein ganzes Zeichen haben
> -        while (--(*pos) && (mblen(buffer + *pos, size - *pos + 1) == -1))
> { } +        (*pos)--;
>      }
>  }

Diese Funktion hat so eigentlich keine Daseinsberechtigung mehr. ;-)

>
> @@ -205,22 +175,9 @@ static void move_cursor(char* buffer, int* pos, int
> size, int offset) * @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)
> +static int count_visible_chars(wchar_t* 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;
> +    return size - start;
>  }

Da bleibt ebenfalls beeindruckend viel Funktion übrig.

>
>  /**
> @@ -254,11 +211,11 @@ 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;
> @@ -272,16 +229,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;
>      }
> @@ -325,21 +286,31 @@ static bool complete_word(char* buffer, int* pos,
> int* len) replacement_len = strlen(replacement);
>      }
>
> +    // Jetzt muessen wir den Text erstmal in einen String aus breiten
> Zeichen +    // verwandeln, um in dann direkt in den Puffer kopieren zu
> koennen. +    char rpl_copy[replacement_len + 1];
> +    memcpy(rpl_copy, replacement, replacement_len);
> +    rpl_copy[replacement_len] = '\0';

Wozu müssen wir den String erstmal kopieren? mbstowcs nimmt doch einen const 
char*, macht uns also nichts kaputt?

> +
> +    size_t wlen = mbstowcs(NULL, rpl_copy, 0);
> +    wchar_t wbuf[wlen + 1];
> +    mbstowcs(wbuf, rpl_copy, wlen + 1);

Yay, word_len, wword_len und wlen. Das ist total intuitiv.

> +
>      // Pruefen ob das ganze nachher noch in den Puffer passt
> -    if (*len + replacement_len > BUFFER_SIZE) {
> +    if (*len + wlen > 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 + wlen, buffer + *pos, (*len -
> word_len) - word_pos + 1);
>
>      // Wort ersetzen
> -    memcpy(buffer + word_pos, replacement, replacement_len);
> +    wmemcpy(buffer + word_pos, wbuf, wlen);
>
>      // Position und Laenge des Puffers korrigieren
> -    *pos = word_pos + replacement_len;
> -    *len = *len + replacement_len - word_len ;
> +    *pos = word_pos + wlen;
> +    *len = *len + wlen - word_len ;
>
>      free_matches_array(matches);
>
> @@ -384,7 +355,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;
> @@ -393,12 +364,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);
> +    memset(buffer, 0, BUFFER_SIZE * sizeof(wchar_t));

Haben wir kein wmemset?

>      pos = size = 0;
>      while (!enter)
>      {
> @@ -427,7 +396,9 @@ again:
>          }
>
>          if (old_pos != pos) {
> -            printf("\033[K\033[s%s\033[u\033[1C", &buffer[old_pos]);
> +            printf("\033[K\033[s", buffer[old_pos]);
> +            fputws(&buffer[old_pos], stdout);
> +            printf("\033[u\033[1C");
>              fflush(stdout);
>          }
>
> @@ -457,7 +428,7 @@ again:
>              case END_OF_LINE:
>                  printf("\033[%dC",
>                      count_visible_chars(buffer, pos, size));
> -                pos = strlen(buffer);
> +                pos = size;
>                  fflush(stdout);
>                  break;
>
> @@ -478,27 +449,30 @@ 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);
> +                        BUFFER_SIZE);

Wieso ist hier plötzlich mehr Platz als vorher?

>                  }
>
>                  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;
> @@ -507,7 +481,9 @@ again:
>                  if (pos > 0) {
>                      move_cursor(buffer, &pos, size, -1);
>                      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;
> @@ -527,12 +503,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);
> @@ -544,7 +524,13 @@ 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);
> +    free(buffer);

Der lag doch auf dem Stack?

> +
> +    return return_buf;
>  }
>
>  /**