parent
ea4c70dae5
commit
d6fb797b11
2
Makefile
2
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
|
||||
|
||||
|
|
1
README
1
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
|
||||
|
|
2
chat.c
2
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);
|
||||
|
|
13
chat.h
13
chat.h
|
@ -16,6 +16,7 @@
|
|||
|
||||
#define SOURCE_URL "https://code.causal.agency/june/chat"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
@ -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);
|
||||
|
|
32
handle.c
32
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 <name> to choose a new one");
|
||||
uiLog(L"You can't use that name here");
|
||||
uiFmt(L"Sheriff says, \"%s\"", mesg);
|
||||
uiLog(L"Type /nick <name> 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 {
|
||||
|
|
16
input.c
16
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);
|
||||
}
|
||||
|
|
4
irc.c
4
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];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
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 <https://en.cppreference.com/w/c/io/fwprintf#Notes>:
|
||||
//
|
||||
// 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;
|
||||
}
|
45
ui.c
45
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue