hirc

IRC client
Log | Files | Refs

commit f24e23da7a1e682bab630bc583c24523657d923c
parent 18877c9127fa4c02bd1d75ae287f2ce125c0fe17
Author: hhvn <dev@hhvn.uk>
Date:   Sun, 13 Mar 2022 13:12:35 +0000

Use widechars for input

Diffstat:
Mconfigure | 16++++++++++++++++
Msrc/hirc.h | 11+++++++++--
Msrc/main.c | 33+++++++++++++++++++++++++++++++++
Msrc/struct.h | 1+
Msrc/ui.c | 134++++++++++++++++++++++++++-----------------------------------------------------
Asrc/wcslcpy.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 154 insertions(+), 93 deletions(-)

diff --git a/configure b/configure @@ -57,3 +57,19 @@ ${CC} -o test test.c >/dev/null 2>/dev/null && ./test >/dev/null 2>/dev/null && SRC += src/strlcpy.c EOF } + +printf '%s' "checking for wcslcpy... " +cat > test.c <<- EOF + #include <wchar.h> + int main(void) { wchar_t a[2]; wcslcpy(a, L"hello", sizeof(a)); return 0; } +EOF +${CC} -o test test.c >/dev/null 2>/dev/null && ./test >/dev/null 2>/dev/null && { + printf '%s\n' "yes" +} || { + printf '%s\n' "no" + cat >> config.mk <<- EOF + # linking an included version of wcslcpy, as your system doesn't have it + CFLAGS += -DHIRC_WCSLCPY + SRC += src/wcslcpy.c + EOF +} diff --git a/src/hirc.h b/src/hirc.h @@ -28,11 +28,15 @@ /* real maximum = HIST_MAX * (channels + servers + queries) */ #define strcmp_n(s1, s2) (s1 == s2 ? 0 : (s1 ? s2 ? strcmp(s1, s2) : -1 : -1)) -/* strlcpy.c */ +/* strlcpy/wcslcpy.c */ #ifdef HIRC_STRLCPY #undef strlcpy -size_t strlcpy(char *dst, const char *src, size_t dsize); +size_t strlcpy(char *, const char *, size_t); #endif /* HIST_STRLCPY */ +#ifdef HIRC_WCSLCPY +#undef wcslcpy +size_t wcslcpy(wchar_t *, const wchar_t *, size_t); +#endif /* HIST_WCSLCPY */ /* main.c */ void * emalloc(size_t size); @@ -40,6 +44,9 @@ void * erealloc(void *ptr, size_t size); char * estrdup(const char *str); void * talloc(size_t size); char * tstrdup(const char *str); +wchar_t * ewcsdup(const wchar_t *str); +wchar_t * stowc(char *str); +char * wctos(wchar_t *str); void cleanup(char *quitmsg); void param_free(char **params); int param_len(char **params); diff --git a/src/main.c b/src/main.c @@ -100,6 +100,39 @@ talloc(size_t size) { return mem; } +wchar_t * +ewcsdup(const wchar_t *str) { + wchar_t *ret; + if ((ret = wcsdup(str)) == NULL) { + endwin(); + perror("wcsdup()"); + exit(EXIT_FAILURE); + } + return ret; +} + +wchar_t * +stowc(char *str) { + wchar_t *ret; + size_t len; + + len = mbstowcs(NULL, str, 0) + 1; + ret = emalloc(len * sizeof(wchar_t)); + mbstowcs(ret, str, len); + return ret; +} + +char * +wctos(wchar_t *str) { + char *ret; + size_t len; + + len = wcstombs(NULL, str, 0) + 1; + ret = emalloc(len); + wcstombs(ret, str, len); + return ret; +} + /* strdup using talloc */ char * tstrdup(const char *str) { diff --git a/src/struct.h b/src/struct.h @@ -272,6 +272,7 @@ struct Selected { struct Keybind { struct Keybind *prev; char *binding; + wchar_t *wbinding; char *cmd; struct Keybind *next; }; diff --git a/src/ui.c b/src/ui.c @@ -19,6 +19,7 @@ #include <errno.h> #include <ctype.h> +#include <wctype.h> #include <stdarg.h> #include <string.h> #include <stdlib.h> @@ -261,7 +262,7 @@ struct { }; struct { - char string[INPUT_MAX]; + wchar_t string[INPUT_MAX]; unsigned counter; char *history[INPUT_HIST_MAX]; int histindex; @@ -317,7 +318,7 @@ ui_init(void) { noecho(); nonl(); /* get ^j */ - input.string[0] = '\0'; + input.string[0] = L'\0'; memset(input.history, 0, sizeof(input.history)); input.counter = 0; input.histindex = -1; @@ -380,20 +381,24 @@ ui_placewindow(struct Window *window) { void ui_read(void) { - static char *backup = NULL; + static wchar_t *backup = NULL; struct Keybind *kp; - int key; + char *str; + wchar_t *wcs; + wint_t key; + int ret; int savecounter; savecounter = input.counter; /* Loop over input, return only if ERR is received. - * Normally wgetch exits fast enough that unless something + * Normally wget_wch exits fast enough that unless something * is being pasted in this won't waste any time that should * be used for other stuff */ for (;;) { - switch (key = wgetch(windows[Win_input].window)) { - case ERR: /* no input received */ + ret = wget_wch(windows[Win_input].window, &key); + if (ret == ERR) { + /* no input received */ /* Match keybinds here - this allows multikey * bindings such as those with alt, but since * there is no delay with wgetch() it's unlikely @@ -401,13 +406,14 @@ ui_read(void) { * trigger one. */ if (input.counter != savecounter) { for (kp = keybinds; kp; kp = kp->next) { - if ((input.counter - savecounter) == strlen(kp->binding) && - strncmp(kp->binding, &input.string[savecounter], (input.counter - savecounter)) == 0) { + if ((input.counter - savecounter) == wcslen(kp->wbinding) && + wcsncmp(kp->wbinding, &input.string[savecounter], (input.counter - savecounter)) == 0) { command_eval(selected.server, kp->cmd); memmove(&input.string[savecounter], &input.string[input.counter], - strlen(&input.string[input.counter]) + 1); + (wcslen(&input.string[input.counter]) + 1) * sizeof(wchar_t)); input.counter = savecounter; + free(str); return; } } @@ -417,29 +423,33 @@ ui_read(void) { backup = NULL; input.histindex = -1; } - } windows[Win_input].handler(); wrefresh(windows[Win_input].window); windows[Win_input].refresh = 0; return; + } + + switch (key) { case KEY_RESIZE: ui_redraw(); break; case KEY_BACKSPACE: if (input.counter) { - if (ui_input_delete(1, input.counter) > 0) - input.counter--; + memmove(input.string + input.counter - 1, + input.string + input.counter, + (wcslen(input.string + input.counter) + 1) * sizeof(wchar_t)); + input.counter--; } break; case KEY_UP: if (input.histindex < INPUT_HIST_MAX && input.history[input.histindex + 1]) { if (input.histindex == -1) - backup = estrdup(input.string); + backup = ewcsdup(input.string); input.histindex++; - strlcpy(input.string, input.history[input.histindex], sizeof(input.string)); - input.counter = strlen(input.string); + mbstowcs(input.string, input.history[input.histindex], sizeof(input.string)); + input.counter = wcslen(input.string); } return; /* return so histindex and backup aren't reset */ case KEY_DOWN: @@ -447,13 +457,13 @@ ui_read(void) { input.histindex--; if (input.histindex == -1) { if (backup) - strlcpy(input.string, backup, sizeof(input.string)); + wcslcpy(input.string, backup, sizeof(input.string)); free(backup); backup = NULL; } else { - strlcpy(input.string, input.history[input.histindex], sizeof(input.string)); + mbstowcs(input.string, input.history[input.histindex], sizeof(input.string)); } - input.counter = strlen(input.string); + input.counter = wcslen(input.string); } return; /* return so histindex and backup aren't reset */ case KEY_LEFT: @@ -466,79 +476,27 @@ ui_read(void) { break; case KEY_ENTER: case '\r': - if (*input.string != '\0') { - command_eval(selected.server, input.string); - /* free checks for null */ + if (*input.string != L'\0') { + /* no need to free str as assigned to input.history[0] */ + str = wctos(input.string); + command_eval(selected.server, str); free(input.history[INPUT_HIST_MAX - 1]); memmove(input.history + 1, input.history, (sizeof(input.history) / INPUT_HIST_MAX) * (INPUT_HIST_MAX - 1)); - input.history[0] = estrdup(input.string); + input.history[0] = str; input.string[0] = '\0'; input.counter = 0; input.histindex = -1; } break; default: - if ((key & 0xFF80) == 0x80 || isprint(key) || iscntrl(key)) { - if (ui_input_insert(key, input.counter) > 0) - input.counter++; - } + if (iswprint(key) || iscntrl(key)) + input.string[input.counter++] = (wchar_t)key; + input.string[input.counter] = 0; break; } } } -int -ui_input_insert(char c, int counter) { - char *p; - int i, bc; - - for (bc=i=0, p = input.string; i != counter && bc < sizeof(input.string) && *p; p++, bc++) { - if ((*p & 0xC0) != 0x80) - i++; - } - while ((*p & 0xC0) == 0x80) - p++; - - if (i != counter) - return -1; - - if ((strlen(input.string)) > sizeof(input.string)) - return -1; - - memmove(p + 1, p, strlen(p) + 1); - memcpy(p, &c, 1); - return ((c & 0xC0) != 0x80); -} - - -int -ui_input_delete(int num, int counter) { - char *dest, *p; - int i, bc; - - if (num < 0) - return -1; - - for (bc=i=0, dest = input.string; i != counter - 1 && bc < sizeof(input.string) && *dest; dest++, bc++) { - if ((*dest & 0xC0) != 0x80) - i++; - } - - while ((*dest & 0xC0) == 0x80) - dest++; - - p = dest; - do { - p++; - } while ((*p & 0xC0) == 0x80); - - /* if (i != counter + num) */ - /* return -1; */ - - memmove(dest, p, strlen(p) + 1); - return num; -} - void ui_redraw(void) { struct History *p; @@ -648,7 +606,7 @@ ui_redraw(void) { void ui_draw_input(void) { char utfbuf[5]; - char *p; + wchar_t *p; int utfc; int offset; int x; @@ -659,21 +617,13 @@ ui_draw_input(void) { * This gives "pages" that are each as long as the width of the input window */ offset = ((int) input.counter / windows[Win_input].w) * windows[Win_input].w; for (x=0, p = input.string + offset; p && *p && x < windows[Win_input].w; p++, x++) { - if ((*p & 0xC0) == 0xC0) { - /* see ui_wprintc */ - memset(utfbuf, '\0', sizeof(utfbuf)); - utfbuf[0] = *p; - for (utfc = 1, p++; (*p & 0xC0) != 0xC0 && (*p & 0x80) == 0x80 && utfc < sizeof(utfbuf); utfc++, p++) - utfbuf[utfc] = *p; - waddstr(windows[Win_input].window, utfbuf); - p--; - } else if (iscntrl(*p)) { + if (iscntrl(*p)) { /* adding 64 will turn ^C into C */ wattron(windows[Win_input].window, A_REVERSE); waddch(windows[Win_input].window, *p + 64); wattroff(windows[Win_input].window, A_REVERSE); - } else if (!(*p & 0x80)) { - waddch(windows[Win_input].window, *p); + } else { + waddnwstr(windows[Win_input].window, p, 1); } } wmove(windows[Win_input].window, 0, input.counter - offset); @@ -1728,6 +1678,7 @@ ui_bind(char *binding, char *cmd) { p = emalloc(sizeof(struct Keybind)); p->binding = estrdup(ui_rectrl(binding)); + p->wbinding = stowc(p->binding); if (*cmd != '/') { tmp = emalloc(strlen(cmd) + 2); snprintf(tmp, strlen(cmd) + 2, "/%s", cmd); @@ -1762,6 +1713,7 @@ ui_unbind(char *binding) { p->next->prev = p->prev; free(p->binding); + free(p->wbinding); free(p->cmd); free(p); return 0; diff --git a/src/wcslcpy.c b/src/wcslcpy.c @@ -0,0 +1,52 @@ +#ifndef __OpenBSD__ +/* $OpenBSD: wcslcpy.c,v 1.8 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <wchar.h> + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns wcslen(src); if retval >= dsize, truncation occurred. + */ +size_t +wcslcpy(wchar_t *dst, const wchar_t *src, size_t dsize) +{ + const wchar_t *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == L'\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = L'\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} +#endif