commit f24e23da7a1e682bab630bc583c24523657d923c
parent 18877c9127fa4c02bd1d75ae287f2ce125c0fe17
Author: hhvn <dev@hhvn.uk>
Date: Sun, 13 Mar 2022 13:12:35 +0000
Use widechars for input
Diffstat:
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