commit f3706757da84fac7c42758bc3bbe94720df82440
parent 585fc6d2e6a63db37043f079676973b34fee1175
Author: Andrew Kloet <andrew@kloet.net>
Date: Tue, 28 Apr 2026 15:12:58 -0400
style and simplification
Hard limit to 80 columns. The only featureful change is I removed "dirty
marking" cells in the input box. It added a fair amount of complexity
for what I consider to be a very negligible amount of "wasted" cycles.
Diffstat:
| M | cio.c | | | 280 | ++++++++++++++++++++++++++++++++++++++----------------------------------------- |
1 file changed, 136 insertions(+), 144 deletions(-)
diff --git a/cio.c b/cio.c
@@ -16,46 +16,47 @@
*/
#include <assert.h>
#include <ctype.h>
+#include <errno.h>
#include <limits.h>
+#include <locale.h>
#include <signal.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
#include <string.h>
#include <time.h>
-#include <errno.h>
-#include <curses.h>
-#include <unistd.h>
#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
-#include <netdb.h>
-#include <locale.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <curses.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
-#undef CTRL
-#define CTRL(x) (x & 037)
-#define GET_ARG(i) ((argc > (i)) ? argv[i] : "")
+#undef CTRL
+#define CTRL(x) (x & 037)
+#define GET_ARG(i) ((argc > (i)) ? argv[i] : "")
-#define SCROLL 15
-#define INDENT 23
-#define DATEFMT "%H:%M"
-#define PFMT " %-12s < %s"
-#define PFMTHIGH "> %-12s < %s"
-#define SRV "irc.oftc.net"
-#define PORT "6697"
+#define SCROLL 15
+#define INDENT 23
+#define DATEFMT "%H:%M"
+#define PFMT " %-12s < %s"
+#define PFMTHIGH "> %-12s < %s"
+#define SRV "irc.oftc.net"
+#define PORT "6697"
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
+ ERR_NICKNAMEINUSE, ERR_CHANNELISFULL, ERR_INVITEONLYCHAN,
+ ERR_BADCHANNELKEY, ERR_NOCHANMODES, SASL_OK, SASL_ERR
} IrcCmd;
struct {
@@ -65,11 +66,11 @@ struct {
{ "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 }
+ { "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 {
@@ -130,7 +131,7 @@ static const Rune utfmax[UtfSz + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static void scmd(char *, char *, int, char **);
static void tdrawbar(void);
-static void tdrawinput(size_t);
+static void tdrawinput(void);
static void tredraw(void);
static void treset(void);
@@ -201,18 +202,20 @@ static char *
b64_enc(const unsigned char *in, size_t len)
{
static char out[BufSz];
- const char *tab =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ const char *t =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *p = out;
size_t i;
for (i = 0; i < len; i += 3) {
- unsigned int v =
- in[i] << 16 | (i+1 < len ? in[i+1] << 8 : 0) | (i+2 < len ? in[i+2] : 0);
- *p++ = tab[(v >> 18) & 0x3F];
- *p++ = tab[(v >> 12) & 0x3F];
- *p++ = (i+1 < len) ? tab[(v >> 6) & 0x3F] : '=';
- *p++ = (i+2 < len) ? tab[v & 0x3F] : '=';
+ unsigned int b0 = in[i];
+ unsigned int b1 = (i + 1 < len) ? in[i + 1] : 0;
+ unsigned int b2 = (i + 2 < len) ? in[i + 2] : 0;
+ unsigned int v = (b0 << 16) | (b1 << 8) | b2;
+ *p++ = t[(v >> 18) & 0x3F];
+ *p++ = t[(v >> 12) & 0x3F];
+ *p++ = (i + 1 < len) ? t[(v >> 6) & 0x3F] : '=';
+ *p++ = (i + 2 < len) ? t[v & 0x3F] : '=';
}
*p = '\0';
return out;
@@ -242,7 +245,7 @@ srd(void)
int rd, argc;
if (p - l >= BufSz)
- p = l; /* Input buffer overflow, there should something better to do. */
+ p = l; /* Input buffer overflow, current behaviour truncates */
if (ssl)
rd = SSL_read(srv.ssl, p, BufSz - (p - l));
else
@@ -294,13 +297,13 @@ sinit(const char *nick, const char *user)
static char *
dial(const char *host, const char *service)
{
- struct addrinfo hints, *res = NULL, *rp;
+ struct addrinfo *res, *rp, hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_NUMERICSERV
+ };
int fd = -1;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
- hints.ai_flags = AI_NUMERICSERV; /* avoid name lookup for port */
- hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, service, &hints, &res))
return "getaddrinfo failed";
for (rp = res; rp; rp = rp->ai_next) {
@@ -315,40 +318,28 @@ dial(const char *host, const char *service)
freeaddrinfo(res);
if ((srv.fd = fd) == -1)
return "cannot connect to host";
- if (ssl) {
- SSL_load_error_strings();
- SSL_library_init();
- srv.ctx = SSL_CTX_new(TLS_client_method());
- if (!srv.ctx) return "could not initialize ssl context";
- if (sslverify) {
- SSL_CTX_set_verify(srv.ctx, SSL_VERIFY_PEER, NULL);
- if (SSL_CTX_set_default_verify_paths(srv.ctx) != 1)
- return "could not load default system trust store";
- }
- srv.ssl = SSL_new(srv.ctx);
- if (!srv.ssl) return "could not create SSL object";
- if (sslverify) {
- SSL_set_hostflags(srv.ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
- if (!SSL_set1_host(srv.ssl, host))
- return "could not set expected hostname";
- SSL_set_tlsext_host_name(srv.ssl, host);
- } else {
- SSL_CTX_set_verify(srv.ctx, SSL_VERIFY_NONE, NULL);
- }
- if (!empty(cert)) {
- if (SSL_use_certificate_chain_file(srv.ssl, cert) <= 0)
- return "failed to load certificate chain";
- if (SSL_use_PrivateKey_file(srv.ssl, cert, SSL_FILETYPE_PEM) <= 0)
- return "failed to load private key";
- }
- SSL_set_fd(srv.ssl, srv.fd);
- if (SSL_connect(srv.ssl) <= 0) {
- long verify_err = SSL_get_verify_result(srv.ssl);
- if (verify_err != X509_V_OK)
- return (char *)X509_verify_cert_error_string(verify_err);
- return "SSL handshake failed";
- }
- }
+ if (!ssl)
+ return 0;
+ SSL_load_error_strings();
+ SSL_library_init();
+ srv.ctx = SSL_CTX_new(TLS_client_method());
+ if (!srv.ctx) return "could not initialize ssl context";
+ if (sslverify && (SSL_CTX_set_verify(srv.ctx, SSL_VERIFY_PEER, NULL),
+ (SSL_CTX_set_default_verify_paths(srv.ctx) != 1)))
+ 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),
+ !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))
+ return "failed to load certificate or private key";
+ SSL_set_fd(srv.ssl, srv.fd);
+ if (SSL_connect(srv.ssl) <= 0)
+ return "ssl handshake failed";
return 0;
}
@@ -405,10 +396,12 @@ chdel(const char *name)
if (n <= 0) return 0;
free(chl[n].buf);
- if (n < nch - 1)
- memmove(&chl[n], &chl[n + 1], (nch - n - 1) * sizeof(struct Chan));
+ if (n < nch - 1) { /* Move trailing channels left by one */
+ size_t len = (nch - n - 1) * sizeof(struct Chan);
+ memmove(&chl[n], &chl[n + 1], len);
+ }
nch--;
- if (ch > n || ch >= nch)
+ if (ch > n || ch >= nch) /* Adjust current channel focus */
ch = (ch > 0) ? ch - 1 : 0;
tdrawbar();
return 1;
@@ -436,7 +429,7 @@ pushl(char *p, char *e)
w++;
x += p - w;
}
- if (p >= e || *p == ' ' || p - w + INDENT >= (ptrdiff_t)scr.x - 1) {
+ if (p >= e || *p == ' ' || p-w+INDENT >= (ptrdiff_t)scr.x-1) {
while (w < p) {
w += utf8decode(w, u, UtfSz);
if (wcwidth(*u) > 0 || *u == '\n') {
@@ -588,12 +581,13 @@ scmd(char *usr, char *cmd, int argc, char **argv)
break;
case CAP:
if (argc < 2) break;
+ int want = !empty(key) || !empty(cert);
+ int has = argc > 2 && strstr(argv[2], "sasl");
if (!strcmp(argv[1], "LS"))
- sndf((!empty(key) || !empty(cert)) && argc > 2 && strstr(argv[2], "sasl")
- ? "CAP REQ :sasl"
- : "CAP END");
- else if (!strcmp(argv[1], "ACK") && argc > 2 && strstr(argv[2], "sasl"))
- sndf(empty(cert) ? "AUTHENTICATE PLAIN" : "AUTHENTICATE EXTERNAL");
+ sndf((want && has) ? "CAP REQ :sasl" : "CAP END");
+ else if (!strcmp(argv[1], "ACK") && has)
+ sndf("AUTHENTICATE %s",
+ empty(cert) ? "PLAIN" : "EXTERNAL");
else if (!strcmp(argv[1], "NAK") || !strcmp(argv[1], "ACK"))
sndf("CAP END");
break;
@@ -603,24 +597,25 @@ scmd(char *usr, char *cmd, int argc, char **argv)
sndf("AUTHENTICATE +");
} else if (!empty(usr) && !empty(key)) {
unsigned char raw[512];
- size_t u_len = strlen(usr);
- size_t k_len = strlen(key);
+ size_t ulen = strlen(usr);
+ size_t klen = strlen(key);
raw[0] = '\0';
- memcpy(raw + 1, usr, u_len);
- raw[1 + u_len] = '\0';
- memcpy(raw + 2 + u_len, key, k_len);
- sndf("AUTHENTICATE %s", b64_enc(raw, u_len + k_len + 2));
+ memcpy(raw + 1, usr, ulen);
+ raw[1 + ulen] = '\0';
+ memcpy(raw + 2 + ulen, key, klen);
+ sndf("AUTHENTICATE %s", b64_enc(raw, ulen + klen + 2));
}
break;
case SASL_OK:
case SASL_ERR:
sndf("CAP END");
- pushf(0, "-!- SASL auth %s", (type == SASL_OK ? "successful" : "failed"));
+ pushf(0, "-!- SASL auth %s",
+ (type == SASL_OK ? "successful" : "failed"));
break;
default: {
if (!isdigit(cmd[0])) break;
char arg_buf[LineLen] = "";
- /* Numerics usually have the client nick at argv[0], so we skip it. */
+ /* Numerics usually have the client nick at argv[0] */
for (int i = 1; i < argc; i++) {
strlcat(arg_buf, argv[i], sizeof(arg_buf));
if (i < argc - 1)
@@ -649,11 +644,11 @@ uparse(char *m)
switch (*p) {
case 'j': /* Join channels. */
p += 1 + (p[1] == ' ');
- for (char *token = strtok(p, " "); token; token = strtok(NULL, " ")) {
- if (chadd(token, 1) < 0)
+ for (char *t = strtok(p, " "); t; t = strtok(NULL, " ")) {
+ if (chadd(t, 1) < 0)
break;
- if (strchr("&#!+", token[0]))
- sndf("JOIN %s", token);
+ if (strchr("&#!+", t[0]))
+ sndf("JOIN %s", t);
}
tredraw();
return;
@@ -665,9 +660,9 @@ uparse(char *m)
strlcpy(buf, chl[ch].name, sizeof(buf));
p = buf;
}
- for (char *token = strtok(p, " "); token; token = strtok(NULL, " "))
- if (chdel(token) && strchr("&#!+", token[0]))
- sndf("PART %s", token);
+ for (char *t = strtok(p, " "); t; t = strtok(NULL, " "))
+ if (chdel(t) && strchr("&#!+", t[0]))
+ sndf("PART %s", t);
tredraw();
return;
case 'm': /* Private message. */
@@ -738,7 +733,7 @@ tresize(void)
mvwin(scr.iw, scr.y - 1, 0);
tredraw();
tdrawbar();
- tdrawinput(0);
+ tdrawinput();
doupdate();
}
@@ -810,25 +805,20 @@ tdrawbar(void)
}
static void
-tdrawinput(size_t dirty)
+tdrawinput(void)
{
- size_t i;
+ int hw = scr.x / 2;
while (inp.cu < inp.shft)
- dirty = 0, inp.shft -= inp.shft >= scr.x / 2 ? scr.x / 2 : inp.shft;
- while (inp.cu >= scr.x + inp.shft)
- dirty = 0, inp.shft += scr.x / 2;
- if (dirty <= inp.shft)
- i = inp.shft;
- else if (dirty > scr.x + inp.shft || dirty > inp.len)
- goto mvcur;
- else
- i = dirty;
- wmove(scr.iw, 0, i - inp.shft);
- wclrtoeol(scr.iw);
- for (; i - inp.shft < scr.x && i < inp.len; i++)
+ inp.shft -= (inp.shft > hw) ? hw : inp.shft;
+ while (inp.cu >= inp.shft + scr.x)
+ inp.shft += hw;
+
+ wmove(scr.iw, 0, 0);
+ for (size_t i = inp.shft; i < inp.len && i < inp.shft + scr.x; i++)
waddch(scr.iw, inp.buf[i]);
-mvcur:
+
+ wclrtoeol(scr.iw);
wmove(scr.iw, 0, inp.cu - inp.shft);
wnoutrefresh(scr.iw);
}
@@ -836,10 +826,11 @@ mvcur:
static void
tgetch(void)
{
- size_t dirty = inp.len + 1, i;
- int c;
+ char *p = &inp.buf[inp.cu]; /* Current cursor position */
+ size_t tail = inp.len - inp.cu; /* Count of chars after cursor */
+ size_t i;
+ int c = wgetch(scr.iw);
- c = wgetch(scr.iw);
switch (c) {
case CTRL('n'):
case CTRL('p'): {
@@ -849,7 +840,7 @@ tgetch(void)
tdrawbar();
tredraw();
return;
- }
+ }
case KEY_PPAGE:
case KEY_NPAGE:
chl[ch].n += (c == KEY_PPAGE) ? SCROLL : -SCROLL;
@@ -874,53 +865,48 @@ tgetch(void)
inp.cu++;
break;
case CTRL('k'):
- dirty = inp.len = inp.cu;
+ inp.len = inp.cu;
break;
case CTRL('u'):
if (inp.cu == 0) return;
- inp.len -= inp.cu;
- memmove(inp.buf, &inp.buf[inp.cu], inp.len);
- dirty = inp.cu = 0;
+ memmove(inp.buf, p, tail); /* Move the tail to the beginning */
+ inp.len = tail;
+ inp.cu = 0;
break;
case CTRL('d'):
if (inp.cu >= inp.len) return;
- memmove(&inp.buf[inp.cu], &inp.buf[inp.cu + 1], inp.len - inp.cu - 1);
- dirty = inp.cu;
+ memmove(p, p + 1, tail - 1); /* Shift tail left by 1 at p */
inp.len--;
break;
case CTRL('h'):
case KEY_BACKSPACE:
if (inp.cu == 0) return;
- memmove(&inp.buf[inp.cu - 1], &inp.buf[inp.cu], inp.len - inp.cu);
- dirty = --inp.cu;
- inp.len--;
+ memmove(p - 1, p, tail); /* Shift tail left by 1 at p-1 */
+ inp.cu--, inp.len--;
break;
case CTRL('w'):
if (inp.cu == 0) break;
i = 1;
- while (inp.buf[inp.cu - i] == ' ' && inp.cu - i != 0) i++;
- while (inp.buf[inp.cu - i] != ' ' && inp.cu - i != 0) i++;
- if (inp.cu - i != 0) i--;
- memmove(&inp.buf[inp.cu - i], &inp.buf[inp.cu], inp.len - inp.cu);
- inp.cu -= i;
- dirty = inp.cu;
- inp.len -= i;
+ /* Find the start of the word (skipping trailing spaces) */
+ while (i < inp.cu && inp.buf[inp.cu - i] == ' ') i++;
+ while (i < inp.cu && inp.buf[inp.cu - (i + 1)] != ' ') i++;
+ memmove(p - i, p, tail); /* Shift tail left by 'i' positions */
+ inp.cu -= i, inp.len -= i;
break;
case '\n':
inp.buf[inp.len] = 0;
uparse(inp.buf);
- dirty = inp.cu = inp.len = 0;
+ inp.cu = inp.len = 0;
break;
default:
if (c > CHAR_MAX || inp.len >= BufSz - 1)
return;
- memmove(&inp.buf[inp.cu + 1], &inp.buf[inp.cu], inp.len - inp.cu);
- dirty = inp.cu;
- inp.len++;
+ memmove(p + 1, p, tail);
inp.buf[inp.cu++] = c;
+ inp.len++;
break;
}
- tdrawinput(dirty);
+ tdrawinput();
}
static void
@@ -948,8 +934,10 @@ main(int argc, char *argv[])
switch (o) {
case 'h':
usage:
- die("usage: %s [-n NICK] [-u USER] [-s SERVER] [-p PORT]\n"
- " [-l LOGFILE] [-c certificate] [-hvTV]", argv[0]);
+ die("usage: cio [-vTV] [-n nick] [-u user]"
+ " [-s server]\n"
+ " [-p port] [-l logfile]"
+ " [-c certificate]");
break;
case 'v':
die("cio-" VERSION);
@@ -1024,7 +1012,11 @@ main(int argc, char *argv[])
}
if (FD_ISSET(srv.fd, &wfs)) {
size_t len = outp - outb;
- int wr = ssl ? SSL_write(srv.ssl, outb, len) : write(srv.fd, outb, len);
+ int wr;
+ if (ssl)
+ wr = SSL_write(srv.ssl, outb, len);
+ else
+ wr = write(srv.fd, outb, len);
if (wr <= 0) {
die("cio: write failed: connection lost");
}