From d6fb797b11aa6dd031032faf425be3ce5a69661d Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Mon, 6 Aug 2018 14:19:52 -0400 Subject: Use wchar_t strings for all of UI vaswprintf is a nightmare. --- Makefile | 2 +- README | 1 + chat.c | 2 +- chat.h | 13 +++++++++---- handle.c | 32 +++++++++++++++---------------- input.c | 16 ++-------------- irc.c | 4 ++-- pls.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui.c | 45 ++++++++++++++++++++++++++----------------- 9 files changed, 126 insertions(+), 56 deletions(-) create mode 100644 pls.c diff --git a/Makefile b/Makefile index 26c70a1..76e130b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CFLAGS += -Wall -Wextra -Wpedantic CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/lib LDLIBS = -lcursesw -ltls -OBJS = chat.o handle.o input.o irc.o ui.o +OBJS = chat.o handle.o input.o irc.o pls.o ui.o all: tags chat diff --git a/README b/README index 89edd73..0d39bf2 100644 --- a/README +++ b/README @@ -8,3 +8,4 @@ This software targets FreeBSD and requires LibreSSL. irc.c TLS client connection input.c Input command handling handle.c Incoming command handling + pls.c Functions which should not have to be written diff --git a/chat.c b/chat.c index 30fe272..ba83907 100644 --- a/chat.c +++ b/chat.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) { signal(SIGINT, sigint); uiInit(); - uiLog("Traveling..."); + uiLog(L"Traveling..."); uiDraw(); int sock = ircConnect(host, port, webPass); diff --git a/chat.h b/chat.h index 2c9239c..3d3586c 100644 --- a/chat.h +++ b/chat.h @@ -16,6 +16,7 @@ #define SOURCE_URL "https://code.causal.agency/june/chat" +#include #include #include #include @@ -41,11 +42,15 @@ void uiInit(void); void uiHide(void); void uiDraw(void); void uiRead(void); -void uiTopic(const char *topic); -void uiLog(const char *line); +void uiTopic(const wchar_t *topic); +void uiTopicStr(const char *topic); +void uiLog(const wchar_t *line); -__attribute__((format(printf, 1, 2))) -void uiFmt(const char *format, ...); +//__attribute__((format(printf, 1, 2))) +void uiFmt(const wchar_t *format, ...); void handle(char *line); void input(wchar_t *line); + +wchar_t *wcssep(wchar_t **stringp, const wchar_t *delim); +int vaswprintf(wchar_t **ret, const wchar_t *format, va_list ap); diff --git a/handle.c b/handle.c index fa13ca8..c602c23 100644 --- a/handle.c +++ b/handle.c @@ -58,9 +58,9 @@ static void handle432(char *prefix, char *params) { shift(¶ms); shift(¶ms); char *mesg = shift(¶ms); - uiLog("You can't use that name here"); - uiFmt("Sheriff says, \"%s\"", mesg); - uiLog("Type /nick to choose a new one"); + uiLog(L"You can't use that name here"); + uiFmt(L"Sheriff says, \"%s\"", mesg); + uiLog(L"Type /nick to choose a new one"); } static void handle001(char *prefix, char *params) { @@ -82,7 +82,7 @@ static void handleJoin(char *prefix, char *params) { chat.user = strdup(user); } uiFmt( - "\3%d%s\3 arrives in \3%d%s\3", + L"\3%d%s\3 arrives in \3%d%s\3", color(user), nick, color(chan), chan ); } @@ -93,7 +93,7 @@ static void handlePart(char *prefix, char *params) { char *chan = shift(¶ms); char *mesg = shift(¶ms); uiFmt( - "\3%d%s\3 leaves \3%d%s\3, \"%s\"", + L"\3%d%s\3 leaves \3%d%s\3, \"%s\"", color(user), nick, color(chan), chan, mesg ); } @@ -104,7 +104,7 @@ static void handleQuit(char *prefix, char *params) { char *mesg = shift(¶ms); char *quot = (mesg[0] == '"') ? "" : "\""; uiFmt( - "\3%d%s\3 leaves, %s%s%s", + L"\3%d%s\3 leaves, %s%s%s", color(user), nick, quot, mesg, quot ); } @@ -116,7 +116,7 @@ static void handleKick(char *prefix, char *params) { char *kick = shift(¶ms); char *mesg = shift(¶ms); uiFmt( - "\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"", + L"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"", color(user), nick, color(kick), kick, color(chan), chan, mesg ); } @@ -127,10 +127,10 @@ static void handle332(char *prefix, char *params) { char *chan = shift(¶ms); char *topic = shift(¶ms); uiFmt( - "The sign in \3%d%s\3 reads, \"%s\"", + L"The sign in \3%d%s\3 reads, \"%s\"", color(chan), chan, topic ); - uiTopic(topic); + uiTopicStr(topic); } static void handleTopic(char *prefix, char *params) { @@ -139,10 +139,10 @@ static void handleTopic(char *prefix, char *params) { char *chan = shift(¶ms); char *topic = shift(¶ms); uiFmt( - "\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"", + L"\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"", color(user), nick, color(chan), chan, topic ); - uiTopic(topic); + uiTopicStr(topic); } static void handle366(char *prefix, char *params) { @@ -176,7 +176,7 @@ static void handle315(char *prefix, char *params) { char *chan = shift(¶ms); whoLen = 0; uiFmt( - "In \3%d%s\3 are %s", + L"In \3%d%s\3 are %s", color(chan), chan, whoBuf ); } @@ -190,7 +190,7 @@ static void handleNick(char *prefix, char *params) { chat.nick = strdup(next); } uiFmt( - "\3%d%s\3 is now known as \3%d%s\3", + L"\3%d%s\3 is now known as \3%d%s\3", color(user), prev, color(user), next ); } @@ -202,9 +202,9 @@ static void handlePrivmsg(char *prefix, char *params) { char *mesg = shift(¶ms); if (mesg[0] == '\1') { strsep(&mesg, " "); - uiFmt("* \3%d%s\3 %s", color(user), nick, strsep(&mesg, "\1")); + uiFmt(L"* \3%d%s\3 %s", color(user), nick, strsep(&mesg, "\1")); } else { - uiFmt("<\3%d%s\3> %s", color(user), nick, mesg); + uiFmt(L"<\3%d%s\3> %s", color(user), nick, mesg); } } @@ -214,7 +214,7 @@ static void handleNotice(char *prefix, char *params) { char *chan = shift(¶ms); char *mesg = shift(¶ms); if (strcmp(chat.chan, chan)) return; - uiFmt("-\3%d%s\3- %s", color(user), nick, mesg); + uiFmt(L"-\3%d%s\3- %s", color(user), nick, mesg); } static const struct { diff --git a/input.c b/input.c index 46bbc56..a53b0a2 100644 --- a/input.c +++ b/input.c @@ -23,18 +23,6 @@ #include "chat.h" -static wchar_t *wcssep(wchar_t **stringp, const wchar_t *delim) { - wchar_t *orig = *stringp; - if (!orig) return NULL; - size_t i = wcscspn(orig, delim); - *stringp = NULL; - if (orig[i]) { - orig[i] = '\0'; - *stringp = &orig[i + 1]; - } - return orig; -} - static void privmsg(bool action, const wchar_t *mesg) { char *line; int send; @@ -60,7 +48,7 @@ static void inputNick(wchar_t *params) { if (nick) { ircFmt("NICK %ls\r\n", nick); } else { - uiLog("/nick requires a name"); + uiLog(L"/nick requires a name"); } } @@ -110,5 +98,5 @@ void input(wchar_t *input) { COMMANDS[i].handler(input); return; } - uiFmt("/%ls isn't a recognized command", command); + uiFmt(L"/%ls isn't a recognized command", command); } diff --git a/irc.c b/irc.c index 02a9f64..1692a73 100644 --- a/irc.c +++ b/irc.c @@ -99,7 +99,7 @@ void ircFmt(const char *format, ...) { int len = vasprintf(&buf, format, ap); va_end(ap); if (!buf) err(EX_OSERR, "vasprintf"); - if (chat.verbose) uiFmt("<<< %.*s", len - 2, buf); + if (chat.verbose) uiFmt(L"<<< %.*s", len - 2, buf); ircWrite(buf, len); free(buf); } @@ -119,7 +119,7 @@ void ircRead(void) { char *crlf, *line = buf; while ((crlf = strnstr(line, "\r\n", &buf[len] - line))) { crlf[0] = '\0'; - if (chat.verbose) uiFmt(">>> %s", line); + if (chat.verbose) uiFmt(L">>> %s", line); handle(line); line = &crlf[2]; } diff --git a/pls.c b/pls.c new file mode 100644 index 0000000..bd40dbd --- /dev/null +++ b/pls.c @@ -0,0 +1,67 @@ +/* Copyright (C) 2018 Curtis McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +wchar_t *wcssep(wchar_t **stringp, const wchar_t *delim) { + wchar_t *orig = *stringp; + if (!orig) return NULL; + size_t i = wcscspn(orig, delim); + *stringp = NULL; + if (orig[i]) { + orig[i] = '\0'; + *stringp = &orig[i + 1]; + } + return orig; +} + +// From : +// +// While narrow strings provide snprintf, which makes it possible to determine +// the required output buffer size, there is no equivalent for wide strings +// (until C11's snwprintf_s), and in order to determine the buffer size, the +// program may need to call swprintf, check the result value, and reallocate a +// larger buffer, trying again until successful. +// +// snwprintf_s, unlike swprintf_s, will truncate the result to fit within the +// array pointed to by buffer, even though truncation is treated as an error by +// most bounds-checked functions. +int vaswprintf(wchar_t **ret, const wchar_t *format, va_list ap) { + *ret = NULL; + + for (size_t cap = 2 * wcslen(format);; cap *= 2) { + wchar_t *buf = realloc(*ret, 1 + cap); + if (!buf) goto fail; + *ret = buf; + + va_list _ap; + va_copy(_ap, ap); + int len = vswprintf(*ret, 1 + cap, format, _ap); + va_end(_ap); + + if (len >= 0) return len; + if (errno != EOVERFLOW) goto fail; + } + +fail: + free(*ret); + *ret = NULL; + return -1; +} diff --git a/ui.c b/ui.c index 19eefdb..5f5efc3 100644 --- a/ui.c +++ b/ui.c @@ -174,19 +174,20 @@ static const short IRC_COLORS[16] = { 0 + COLOR_WHITE, // light gray }; -static const char *parseColor(short *pair, const char *str) { +static const wchar_t *parseColor(short *pair, const wchar_t *str) { short fg = 0; - size_t fgLen = MIN(strspn(str, "0123456789"), 2); + size_t fgLen = MIN(wcsspn(str, L"0123456789"), 2); if (!fgLen) { *pair = -1; return str; } for (size_t i = 0; i < fgLen; ++i) { - fg = fg * 10 + (str[i] - '0'); + fg = fg * 10 + (str[i] - L'0'); } str = &str[fgLen]; short bg = 0; - size_t bgLen = (str[0] == ',') ? MIN(strspn(&str[1], "0123456789"), 2) : 0; + size_t bgLen = 0; + if (str[0] == L',') bgLen = MIN(wcsspn(&str[1], L"0123456789"), 2); for (size_t i = 0; i < bgLen; ++i) { - bg = bg * 10 + (str[1 + i] - '0'); + bg = bg * 10 + (str[1 + i] - L'0'); } if (bgLen) str = &str[1 + bgLen]; @@ -197,43 +198,51 @@ static const char *parseColor(short *pair, const char *str) { return str; } -static void addIRC(WINDOW *win, const char *str) { +static void addIRC(WINDOW *win, const wchar_t *str) { attr_t attr = A_NORMAL; short pair = -1; for (;;) { - size_t cc = strcspn(str, "\2\3\35\37"); + size_t cc = wcscspn(str, L"\2\3\35\37"); wattr_set(win, attr | attr8(pair), 1 + pair8(pair), NULL); - waddnstr(win, str, cc); + waddnwstr(win, str, cc); if (!str[cc]) break; str = &str[cc]; switch (*str++) { - break; case '\2': attr ^= A_BOLD; - break; case '\3': str = parseColor(&pair, str); - break; case '\35': attr ^= A_ITALIC; - break; case '\37': attr ^= A_UNDERLINE; + break; case L'\2': attr ^= A_BOLD; + break; case L'\3': str = parseColor(&pair, str); + break; case L'\35': attr ^= A_ITALIC; + break; case L'\37': attr ^= A_UNDERLINE; } } } -void uiTopic(const char *topic) { +void uiTopic(const wchar_t *topic) { wmove(ui.topic, 0, 0); addIRC(ui.topic, topic); wclrtoeol(ui.topic); } -void uiLog(const char *line) { +void uiTopicStr(const char *topic) { + size_t len = strlen(topic); + wchar_t wcs[1 + len]; + len = mbstowcs(wcs, topic, 1 + len); + if (len == (size_t)-1) err(EX_DATAERR, "mbstowcs"); + uiTopic(wcs); +} + +void uiLog(const wchar_t *line) { waddch(ui.log, '\n'); addIRC(ui.log, line); } -void uiFmt(const char *format, ...) { - char *buf; +void uiFmt(const wchar_t *format, ...) { + wchar_t *buf; va_list ap; va_start(ap, format); - vasprintf(&buf, format, ap); + vaswprintf(&buf, format, ap); va_end(ap); - if (!buf) err(EX_OSERR, "vasprintf"); + if (!buf) err(EX_OSERR, "vaswprintf"); uiLog(buf); free(buf); } -- cgit 1.4.1