Home
Unicode support using str->wcs and char->wchar_t - iomenu - interactive terminal-based selection menu HTML git clone git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit b29fc218d36185be00fbb8952e26264d0d61508a DIR parent 04f14a2a3fbe38525721285139ebf8af4350876f HTML Author: Josuah Demangeonā ā µ <mail@josuah.net> Date: Fri, 24 Mar 2017 22:45:33 +0100 Unicode support using str->wcs and char->wchar_t Diffstat: M iomenu.c | 127 ++++++++++++++++--------------- 1 file changed, 67 insertions(+), 60 deletions(-) --- DIR diff --git a/iomenu.c b/iomenu.c @@ -1,10 +1,12 @@ -#include <ctype.h> #include <fcntl.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <unistd.h> +#include <wchar.h> +#include <wctype.h> #include <sys/ioctl.h> @@ -22,11 +24,11 @@ static struct termios termios; FILE *tty_fp = NULL; int tty_fd; -static char input[BUFSIZ], formatted[BUFSIZ * 8]; -static int current = 0, offset = 0, prev = 0, next = 0; -static int linec = 0, matchc = 0; -static char **linev = NULL, **matchv = NULL; -static int opt_l = 0; +static int current = 0, offset = 0, prev = 0, next = 0; +static int linec = 0, matchc = 0; +static wchar_t **linev = NULL, **matchv = NULL; +static wchar_t input[BUFSIZ], formatted[BUFSIZ * 8]; +static int opt_l = 0; static void @@ -73,45 +75,45 @@ set_terminal(void) static void read_lines(void) { - char buffer[BUFSIZ]; + wchar_t buffer[BUFSIZ]; int size = 1 << 6; - linev = malloc(sizeof (char **) * size); - matchv = malloc(sizeof (char **) * size); + linev = malloc(sizeof (wchar_t **) * size); + matchv = malloc(sizeof (wchar_t **) * size); if (linev == NULL || matchv == NULL) die("malloc"); linev[0] = matchv[0] = NULL; /* read the file into an array of lines */ - for (; fgets(buffer, sizeof buffer, stdin); linec++, matchc++) { - int len = strlen(buffer); + for (; fgetws(buffer, sizeof buffer, stdin); linec++, matchc++) { + int len = wcslen(buffer); if (len > 0 && buffer[len - 1] == '\n') buffer[len - 1] = '\0'; if (linec >= size) { size *= 2; - linev = realloc(linev, sizeof (char **) * size); - matchv = realloc(matchv, sizeof (char **) * size); + linev = realloc(linev, sizeof (wchar_t **) * size); + matchv = realloc(matchv, sizeof (wchar_t **) * size); if (linev == NULL || matchv == NULL) die("realloc"); } - linev[linec] = matchv[matchc] = malloc(len); + linev[linec] = matchv[matchc] = malloc(len * sizeof (wchar_t)); if (linev[linec] == NULL) die("malloc"); - strcpy(linev[linec], buffer); + wcscpy(linev[linec], buffer); } } static int -match_line(char *line, char **tokv, int tokc) +match_line(wchar_t *line, wchar_t **tokv, int tokc) { for (int i = 0; i < tokc; i++) - if (strstr(line, tokv[i]) == NULL) + if (wcsstr(line, tokv[i]) == NULL) return 0; return 1; @@ -119,45 +121,46 @@ match_line(char *line, char **tokv, int tokc) int -screen_width(char *str) +screen_width(wchar_t *wcs) { int len = 0; - for (int i = 0; str[i]; i++, len++) - if (str[i] == '\t') + for (int i = 0; wcs[i]; i++, len++) + if (wcs[i] == '\t') len += (len + 7) % 8; return len; } -static char * -format(char *str, int cols) +static wchar_t * +format(wchar_t *wcs, int cols) { int j = 0; - for (int i = 0; str[i] && j < cols; i++) { + for (int i = 0; wcs[i] && j < cols; i++) { - if (str[i] == '\t') { + if (wcs[i] == L'\t') { for (int t = (j + 7) % 8 + 1; t > 0 && j < cols; t--) - formatted[j++] = ' '; + formatted[j++] = L' '; } else { - formatted[j++] = str[i]; + formatted[j++] = wcs[i]; } } - formatted[j] = '\0'; + formatted[j] = L'\0'; return formatted; } static void -print_string(char *str, int current) +print_string(wchar_t *wcs, int current) { - fputs(current ? "\033[30;47m" : "", stderr); - fputs(opt_l ? "\033[K " : " ", stderr); - fprintf(stderr, "%s \033[m", format(str, ws.ws_col - 2)); + fputws(current ? L"\033[30;47m" : L"", stderr); + fputws(opt_l ? L"\033[K " : L" ", stderr); + fputws(format(wcs, ws.ws_col - 2), stderr); + fputws(L" \033[m", stderr); } @@ -168,12 +171,12 @@ print_lines(int count) offset = current / count * count; for (int i = offset; p < count && i < matchc; p++, i++) { - fputc('\n', stderr); + fputwc(L'\n', stderr); print_string(matchv[i], i == current); } while (p++ <= count) - fputs("\n\033[K", stderr); + fputws(L"\n\033[K", stderr); } @@ -210,13 +213,13 @@ print_columns(void) next = next_page(offset, ws.ws_col - OFFSET - 4); } - fputs(offset > 0 ? "< " : " ", stderr); + fputws(offset > 0 ? L"< " : L" ", stderr); for (int i = offset; i < next && i < matchc; i++) print_string(matchv[i], i == current); if (next < matchc) - fprintf(stderr, "\033[%dC>", ws.ws_col - OFFSET); + fwprintf(stderr, L"\033[%dC>", ws.ws_col - OFFSET); } @@ -230,42 +233,45 @@ print_screen(void) count = MIN(opt_l, ws.ws_row - 2); - fputs("\r\033[K", stderr); + fputws(L"\r\033[K", stderr); if (opt_l) { print_lines(count); - fprintf(stderr, "\033[%dA", count + 1); + fwprintf(stderr, L"\033[%dA", count + 1); } else { - fputs("\033[30C", stderr); + fputws(L"\033[30C", stderr); print_columns(); } format(input, opt_l || matchc == 0 ? ws.ws_col : OFFSET - 3); - fprintf(stderr, "\r %s", formatted); + fputws(L"\r ", stderr); + fputws(formatted, stderr); + + fflush(stderr); } static void -print_clear(int lines) +clear(int lines) { for (int i = 0; i < lines + 1; i++) - fputs("\r\033[K\n", stderr); - fprintf(stderr, "\033[%dA", lines + 1); + fputws(L"\r\033[K\n", stderr); + fwprintf(stderr, L"\033[%dA", lines + 1); } static void filter_lines(void) { - char **tokv = NULL, *s, buffer[sizeof (input)]; - int tokc = 0, n = 0; + wchar_t **tokv = NULL, *tok, *s, buffer[sizeof (input)]; + int tokc = 0, n = 0; current = offset = prev = next = 0; - strcpy(buffer, input); + wcscpy(buffer, input); - for (s = strtok(buffer, " "); s; s = strtok(NULL, " "), tokc++) { + for (s = wcstok(buffer, L" ", &tok); s; s = wcstok(NULL, L" ", &tok), tokc++) { if (tokc >= n) { tokv = realloc(tokv, ++n * sizeof (*tokv)); @@ -289,13 +295,13 @@ filter_lines(void) static void remove_word_input() { - int len = strlen(input) - 1; + int len = wcslen(input) - 1; - for (int i = len; i >= 0 && isspace(input[i]); i--) + for (int i = len; i >= 0 && iswspace(input[i]); i--) input[i] = '\0'; - len = strlen(input) - 1; - for (int i = len; i >= 0 && !isspace(input[i]); i--) + len = wcslen(input) - 1; + for (int i = len; i >= 0 && !iswspace(input[i]); i--) input[i] = '\0'; filter_lines(); @@ -305,9 +311,9 @@ remove_word_input() static void add_character(char key) { - int len = strlen(input); + int len = wcslen(input); - if (isprint(key)) { + if (iswprint(key)) { input[len] = key; input[len + 1] = '\0'; } @@ -319,8 +325,8 @@ add_character(char key) static void print_selection(void) { - fputs("\r\033[K", stderr); - puts(matchc > 0 ? matchv[current] : input); + fputws(L"\r\033[K", stderr); + fputws(matchc > 0 ? matchv[current] : input, stdout); } @@ -330,12 +336,12 @@ print_selection(void) static int input_key(void) { - char key = fgetc(tty_fp); + wchar_t key = fgetwc(tty_fp); switch (key) { case CONTROL('C'): - print_clear(opt_l); + clear(opt_l); return EXIT_FAILURE; case CONTROL('U'): @@ -349,7 +355,7 @@ input_key(void) case 127: case CONTROL('H'): /* backspace */ - input[strlen(input) - 1] = '\0'; + input[wcslen(input) - 1] = '\0'; filter_lines(); break; @@ -363,7 +369,7 @@ input_key(void) case CONTROL('I'): /* tab */ if (linec > 0) - strcpy(input, matchv[current]); + wcscpy(input, matchv[current]); filter_lines(); break; @@ -408,7 +414,7 @@ input_get(void) static void usage(void) { - fputs("usage: iomenu [-l lines]\n", stderr); + fputws(L"usage: iomenu [-l lines]\n", stderr); exit(EXIT_FAILURE); } @@ -433,6 +439,7 @@ main(int argc, char *argv[]) } } + setlocale(LC_ALL, ""); read_lines(); tty_fp = fopen("/dev/tty", "r"); @@ -442,7 +449,7 @@ main(int argc, char *argv[]) exit_code = input_get(); tcsetattr(tty_fd, TCSANOW, &termios); - print_clear(opt_l); + clear(opt_l); fclose(tty_fp); close(tty_fd); free_all();