commit 3a6e082601a65e9a451291d066abc11d6d70fb08
parent 11f1f2d8fd56694a131658e06ac5e94465ece368
Author: Andrew Kloet <andrew@kloet.net>
Date: Sat, 2 May 2026 12:21:14 -0400
style: wrap at 78 columns
Diffstat:
| M | cio.c | | | 133 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
1 file changed, 70 insertions(+), 63 deletions(-)
diff --git a/cio.c b/cio.c
@@ -1,13 +1,13 @@
/* See LICENSE file for copyright and license details.
*
* cio is driven by a synchronous select(2) loop. It multiplexes stdin, the
- * network socket, and SIGWINCH for terminal resizing. Each channel's scrollback
- * is stored in an exponentially-reallocating heap buffer.
+ * network socket, and SIGWINCH for terminal resizing. Each channel's
+ * scrollback is stored in an exponentially-reallocating heap buffer.
*
- * Channels are organized in a fixed-size array. The '0' channel is reserved for
- * server messages (status), while others are created dynamically upon JOIN or
- * PRIVMSG. IRC protocol parsing is handled via a string tokenizer, dispatching
- * commands through a lookup table in scmd().
+ * Channels are organized in a fixed-size array. The '0' channel is reserved
+ * for server messages (status), while others are created dynamically upon
+ * JOIN or PRIVMSG. IRC protocol parsing is handled via a string tokenizer,
+ * dispatching commands through a lookup table in scmd().
*
* UI rendering is handled by ncurses and UTF-8 is supported by ncursesw.
*
@@ -58,9 +58,10 @@
typedef enum {
CMD_UNKNOWN, NOTICE, PRIVMSG, PING, PONG, PART, JOIN, QUIT, NICK, CAP,
- AUTHENTICATE, RPL_TOPIC, RPL_TOPICWHOTIME, RPL_NAMREPLY, RPL_ENDOFNAMES,
- ERR_NICKNAMEINUSE, ERR_CHANNELISFULL, ERR_INVITEONLYCHAN,
- ERR_BADCHANNELKEY, ERR_NOCHANMODES, SASL_OK, SASL_ERR
+ AUTHENTICATE, RPL_TOPIC, RPL_TOPICWHOTIME, RPL_NAMREPLY,
+ RPL_ENDOFNAMES, ERR_NICKNAMEINUSE, ERR_CHANNELISFULL,
+ ERR_INVITEONLYCHAN, ERR_BADCHANNELKEY, ERR_NOCHANMODES, SASL_OK,
+ SASL_ERR
} IrcCmd;
struct {
@@ -68,17 +69,19 @@ struct {
IrcCmd id;
} cmd_map[] = {
{ "NOTICE", NOTICE }, { "PRIVMSG", PRIVMSG }, { "PING", PING },
- { "PONG", PONG }, { "PART", PART }, { "JOIN", JOIN }, { "QUIT", QUIT },
- { "NICK", NICK}, { "CAP", CAP }, { "AUTHENTICATE", AUTHENTICATE },
- { "332", RPL_TOPIC }, { "333", RPL_TOPICWHOTIME },
- { "353", RPL_NAMREPLY }, { "366", RPL_ENDOFNAMES },
- { "477", ERR_NOCHANMODES }, { "433", ERR_NICKNAMEINUSE },
- { "471", ERR_CHANNELISFULL }, { "473", ERR_INVITEONLYCHAN },
- { "475", ERR_BADCHANNELKEY }, { "903", SASL_OK }, { "904", SASL_ERR }
+ { "PONG", PONG }, { "PART", PART }, { "JOIN", JOIN },
+ { "QUIT", QUIT }, { "NICK", NICK}, { "CAP", CAP },
+ { "AUTHENTICATE", AUTHENTICATE }, { "332", RPL_TOPIC },
+ { "333", RPL_TOPICWHOTIME }, { "353", RPL_NAMREPLY },
+ { "366", RPL_ENDOFNAMES }, { "477", ERR_NOCHANMODES },
+ { "433", ERR_NICKNAMEINUSE }, { "471", ERR_CHANNELISFULL },
+ { "473", ERR_INVITEONLYCHAN }, { "475", ERR_BADCHANNELKEY },
+ { "903", SASL_OK }, { "904", SASL_ERR }
};
enum {
ChanLen = 64,
+ NickLen = 64,
LineLen = 512,
MaxChans = 16,
MaxParams = 15,
@@ -119,7 +122,7 @@ static struct {
SSL *ssl;
SSL_CTX *ctx;
} srv;
-static char nick[64];
+static char nick[NickLen];
static char cert[PATH_MAX];
static char key[512];
static int quit, winchg;
@@ -177,7 +180,7 @@ srd(void)
int rd, argc;
if (p - l >= BufSz)
- p = l; /* Input buffer overflow, current behaviour truncates */
+ p = l; /* Input buffer overflow */
if (ssl)
rd = SSL_read(srv.ssl, p, BufSz - (p - l));
else
@@ -254,13 +257,14 @@ dial(const char *host, const char *service)
return "could not load system trust store";
if (!(srv.ssl = SSL_new(srv.ctx)))
return "could not create ssl object";
- if (sslverify && (
- SSL_set_hostflags(srv.ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS),
+ if (sslverify &&
+ (SSL_set_hostflags(srv.ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS),
!SSL_set1_host(srv.ssl, host) ||
!SSL_set_tlsext_host_name(srv.ssl, host)))
return "could not set expected hostname";
- if (!empty(cert) && (SSL_use_certificate_chain_file(srv.ssl, cert) <= 0
- || SSL_use_PrivateKey_file(srv.ssl, cert, SSL_FILETYPE_PEM) <= 0))
+ if (!empty(cert) &&
+ (SSL_use_certificate_chain_file(srv.ssl, cert) != 1 ||
+ SSL_use_PrivateKey_file(srv.ssl, cert, SSL_FILETYPE_PEM) != 1))
return "failed to load certificate or private key";
SSL_set_fd(srv.ssl, srv.fd);
if (SSL_connect(srv.ssl) <= 0)
@@ -406,9 +410,9 @@ pushf(int cn, const char *fmt, ...)
n += (wrote < 0) ? 0 : wrote; /* failed to append to buffer */
va_end(vl);
if (logfp) {
- char isodate[32];
- strftime(isodate, sizeof(isodate), "%Y-%m-%dT%H:%M:%SZ", &gmtm);
- fprintf(logfp, "%-12.12s\t%s\t%s\n", c->name, isodate, s);
+ char isotm[32];
+ strftime(isotm, sizeof(isotm), "%Y-%m-%dT%H:%M:%SZ", &gmtm);
+ fprintf(logfp, "%-12.12s\t%s\t%s\n", c->name, isotm, s);
fflush(logfp);
}
c->eol += n;
@@ -450,7 +454,7 @@ scmd(char *usr, char *cmd, int argc, char **argv)
else if (type == PRIVMSG) chan = usr;
else chan = NULL;
if (!chan) c = 0;
- else if (!(c = chfind(chan)) && (c = chadd(chan, 0)) < 0) break;
+ else if ((c = chadd(chan, 0)) < 0) break;
int high = (type == PRIVMSG && strcasestr(argv[1], nick));
pushf(c, high ? PFMTHIGH : PFMT, usr, argv[1]);
if (ch != c) {
@@ -476,10 +480,12 @@ scmd(char *usr, char *cmd, int argc, char **argv)
pushf(0, "-!- %s is now known as %s", usr, argv[0]);
break;
case RPL_TOPIC:
- if (argc >= 3) pushf(chfind(argv[1]), "-!- Topic: %s", argv[2]);
+ if (argc >= 3)
+ pushf(chfind(argv[1]), "-!- Topic: %s", argv[2]);
break;
case RPL_NAMREPLY:
- if (argc >= 4) pushf(chfind(argv[2]), "-!- Names: %s", argv[3]);
+ if (argc >= 4)
+ pushf(chfind(argv[2]), "-!- Names: %s", argv[3]);
break;
case ERR_NOCHANMODES: /* FALLTHROUGH */
case ERR_CHANNELISFULL:
@@ -748,6 +754,27 @@ tdrawinput(void)
wnoutrefresh(scr.iw);
}
+static size_t prev_char(size_t pos) {
+ if (pos == 0) return 0;
+ size_t i = pos - 1;
+ while (i > 0 && IS_CONT(inp.buf[i])) i--;
+ return i;
+}
+
+static size_t next_char(size_t pos) {
+ if (pos >= inp.len) return inp.len;
+ size_t i = pos + 1;
+ while (i < inp.len && IS_CONT(inp.buf[i])) i++;
+ return i;
+}
+
+static void buf_delete(size_t start, size_t end) {
+ if (start >= end) return;
+ memmove(&inp.buf[start], &inp.buf[end], inp.len - end);
+ inp.len -= (end - start);
+ inp.cu = start;
+}
+
static void
tgetch(void)
{
@@ -758,14 +785,14 @@ tgetch(void)
size_t i, old;
switch (wc) {
- case CTRL('n'):
+ case CTRL('n'): /* FALLTHROUGH */
case CTRL('p'):
ch = (ch + (wc == CTRL('n') ? 1 : -1) + nch) % nch;
chl[ch].high = chl[ch].new = 0;
tdrawbar();
tredraw();
return;
- case KEY_PPAGE:
+ case KEY_PPAGE: /* FALLTHROUGH */
case KEY_NPAGE:
chl[ch].n += (wc == KEY_PPAGE) ? SCROLL : -SCROLL;
if (chl[ch].n < 0)
@@ -778,53 +805,33 @@ tgetch(void)
case CTRL('e'):
inp.cu = inp.len;
break;
- case CTRL('b'):
+ case CTRL('b'): /* FALLTHROUGH */
case KEY_LEFT:
- if (inp.cu <= 0) return;
- do { inp.cu--; }
- while (inp.cu > 0 && IS_CONT(inp.buf[inp.cu]));
+ inp.cu = prev_char(inp.cu);
break;
- case CTRL('f'):
+ case CTRL('f'): /* FALLTHROUGH */
case KEY_RIGHT:
- if (inp.cu >= inp.len) return;
- do { inp.cu++; }
- while (inp.cu < inp.len && IS_CONT(inp.buf[inp.cu]));
+ inp.cu = next_char(inp.cu);
break;
case CTRL('k'):
inp.len = inp.cu;
break;
case CTRL('u'):
- if (inp.cu == 0) return;
- memmove(inp.buf, p, tail);
- inp.len = tail;
- inp.cu = 0;
+ buf_delete(0, inp.cu);
break;
case CTRL('d'):
- if (inp.cu >= inp.len) return;
- i = 1;
- while (inp.cu + i < inp.len && IS_CONT(inp.buf[inp.cu + i]))
- i++;
- memmove(p, p + i, tail - i);
- inp.len -= i;
+ buf_delete(inp.cu, next_char(inp.cu));
break;
- case CTRL('h'):
+ case CTRL('h'): /* FALLTHROUGH */
case KEY_BACKSPACE:
- if (inp.cu == 0) return;
- old = inp.cu;
- do { inp.cu--; } while (inp.cu > 0 && IS_CONT(inp.buf[inp.cu]));
- memmove(&inp.buf[inp.cu], &inp.buf[old], inp.len - old);
- inp.len -= (old - inp.cu);
+ if (inp.cu > 0) buf_delete(prev_char(inp.cu), inp.cu);
break;
case CTRL('w'):
- if (inp.cu == 0) break;
old = inp.cu;
while (inp.cu > 0 && inp.buf[inp.cu - 1] == ' ') inp.cu--;
- while (inp.cu > 0 && inp.buf[inp.cu - 1] != ' ') {
- inp.cu--;
- while (inp.cu > 0 && IS_CONT(inp.buf[inp.cu])) inp.cu--;
- }
- memmove(&inp.buf[inp.cu], &inp.buf[old], inp.len - old);
- inp.len -= (old - inp.cu);
+ while (inp.cu > 0 && inp.buf[inp.cu - 1] != ' ')
+ inp.cu = prev_char(inp.cu);
+ buf_delete(inp.cu, old);
break;
case '\n':
inp.buf[inp.len] = 0;
@@ -882,7 +889,7 @@ main(int argc, char *argv[])
die("cio: fopen: logfile:");
break;
case 'n':
- if (strlcpy(nick, optarg, sizeof(nick)) >= sizeof(nick))
+ if (strlcpy(nick, optarg, sizeof(nick)) >= NickLen)
goto usage;
break;
case 'T':
@@ -901,7 +908,7 @@ main(int argc, char *argv[])
port = optarg;
break;
case 'c':
- if (strlcpy(cert, optarg, sizeof(cert)) >= sizeof(cert))
+ if (strlcpy(cert, optarg, sizeof(cert)) >= PATH_MAX)
goto usage;
break;
default: