parent
ba524ed804
commit
b20be7cbad
38
chat.c
38
chat.c
|
@ -17,6 +17,7 @@
|
|||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
|
@ -39,7 +40,7 @@
|
|||
static void genCert(const char *path) {
|
||||
const char *name = strrchr(path, '/');
|
||||
name = (name ? &name[1] : path);
|
||||
char subj[256];
|
||||
char subj[4 + NAME_MAX];
|
||||
snprintf(subj, sizeof(subj), "/CN=%.*s", (int)strcspn(name, "."), name);
|
||||
umask(0066);
|
||||
execlp(
|
||||
|
@ -56,13 +57,11 @@ char *idNames[IDCap] = {
|
|||
[Debug] = "<debug>",
|
||||
[Network] = "<network>",
|
||||
};
|
||||
|
||||
enum Color idColors[IDCap] = {
|
||||
[None] = Black,
|
||||
[Debug] = Green,
|
||||
[Network] = Gray,
|
||||
};
|
||||
|
||||
uint idNext = Network + 1;
|
||||
|
||||
struct Network network;
|
||||
|
@ -79,21 +78,9 @@ static void exitSave(void) {
|
|||
|
||||
uint32_t hashInit;
|
||||
|
||||
int utilPipe[2] = { -1, -1 };
|
||||
uint execID;
|
||||
int execPipe[2] = { -1, -1 };
|
||||
|
||||
static void utilRead(void) {
|
||||
char buf[1024];
|
||||
ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1);
|
||||
if (len < 0) err(EX_IOERR, "read");
|
||||
if (!len) return;
|
||||
buf[len] = '\0';
|
||||
char *ptr = buf;
|
||||
while (ptr) {
|
||||
char *line = strsep(&ptr, "\n");
|
||||
if (line[0]) uiFormat(Network, Warm, NULL, "%s", line);
|
||||
}
|
||||
}
|
||||
int utilPipe[2] = { -1, -1 };
|
||||
|
||||
static void execRead(void) {
|
||||
char buf[1024];
|
||||
|
@ -108,6 +95,19 @@ static void execRead(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void utilRead(void) {
|
||||
char buf[1024];
|
||||
ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1);
|
||||
if (len < 0) err(EX_IOERR, "read");
|
||||
if (!len) return;
|
||||
buf[len] = '\0';
|
||||
char *ptr = buf;
|
||||
while (ptr) {
|
||||
char *line = strsep(&ptr, "\n");
|
||||
if (line[0]) uiFormat(Network, Warm, NULL, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
static volatile sig_atomic_t signals[NSIG];
|
||||
static void signalHandler(int signal) {
|
||||
signals[signal] = 1;
|
||||
|
@ -188,7 +188,7 @@ int main(int argc, char *argv[]) {
|
|||
if (!user) user = nick;
|
||||
if (!real) real = nick;
|
||||
|
||||
set(&network.name, host);
|
||||
// Modes defined in RFC 1459:
|
||||
set(&network.chanTypes, "#&");
|
||||
set(&network.prefixes, "@+");
|
||||
set(&network.prefixModes, "ov");
|
||||
|
@ -196,6 +196,8 @@ int main(int argc, char *argv[]) {
|
|||
set(&network.paramModes, "k");
|
||||
set(&network.setParamModes, "l");
|
||||
set(&network.channelModes, "imnpst");
|
||||
|
||||
set(&network.name, host);
|
||||
set(&self.nick, "*");
|
||||
commandComplete();
|
||||
|
||||
|
|
67
chat.h
67
chat.h
|
@ -65,8 +65,8 @@ static inline uint idFor(const char *name) {
|
|||
if (id) return id;
|
||||
if (idNext == IDCap) return Network;
|
||||
idNames[idNext] = strdup(name);
|
||||
if (!idNames[idNext]) err(EX_OSERR, "strdup");
|
||||
idColors[idNext] = Default;
|
||||
if (!idNames[idNext]) err(EX_OSERR, "strdup");
|
||||
return idNext++;
|
||||
}
|
||||
|
||||
|
@ -79,9 +79,22 @@ static inline enum Color hash(const char *str) {
|
|||
hash ^= *str;
|
||||
hash *= 0x27220A95;
|
||||
}
|
||||
return 2 + hash % 74;
|
||||
return Blue + hash % 74;
|
||||
}
|
||||
|
||||
extern struct Network {
|
||||
char *name;
|
||||
char *chanTypes;
|
||||
char *prefixes;
|
||||
char *prefixModes;
|
||||
char *listModes;
|
||||
char *paramModes;
|
||||
char *setParamModes;
|
||||
char *channelModes;
|
||||
char excepts;
|
||||
char invex;
|
||||
} network;
|
||||
|
||||
#define ENUM_CAP \
|
||||
X("extended-join", CapExtendedJoin) \
|
||||
X("invite-notify", CapInviteNotify) \
|
||||
|
@ -96,25 +109,12 @@ enum Cap {
|
|||
#undef X
|
||||
};
|
||||
|
||||
extern struct Network {
|
||||
char *name;
|
||||
char *chanTypes;
|
||||
char *prefixes;
|
||||
char *prefixModes;
|
||||
char *listModes;
|
||||
char *paramModes;
|
||||
char *setParamModes;
|
||||
char *channelModes;
|
||||
char excepts;
|
||||
char invex;
|
||||
} network;
|
||||
|
||||
extern struct Self {
|
||||
bool debug;
|
||||
bool restricted;
|
||||
char *plain;
|
||||
const char *join;
|
||||
enum Cap caps;
|
||||
char *plain;
|
||||
char *join;
|
||||
char *nick;
|
||||
char *user;
|
||||
enum Color color;
|
||||
|
@ -155,25 +155,8 @@ void ircFormat(const char *format, ...)
|
|||
__attribute__((format(printf, 1, 2)));
|
||||
void ircClose(void);
|
||||
|
||||
extern struct Replies {
|
||||
uint away;
|
||||
uint join;
|
||||
uint list;
|
||||
uint names;
|
||||
uint topic;
|
||||
uint whois;
|
||||
} replies;
|
||||
|
||||
uint execID;
|
||||
int execPipe[2];
|
||||
|
||||
void handle(struct Message msg);
|
||||
void command(uint id, char *input);
|
||||
const char *commandIsPrivmsg(uint id, const char *input);
|
||||
const char *commandIsNotice(uint id, const char *input);
|
||||
const char *commandIsAction(uint id, const char *input);
|
||||
void commandComplete(void);
|
||||
|
||||
int utilPipe[2];
|
||||
|
||||
enum { UtilCap = 16 };
|
||||
|
@ -190,6 +173,22 @@ static inline void utilPush(struct Util *util, const char *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
extern struct Replies {
|
||||
uint away;
|
||||
uint join;
|
||||
uint list;
|
||||
uint names;
|
||||
uint topic;
|
||||
uint whois;
|
||||
} replies;
|
||||
|
||||
void handle(struct Message msg);
|
||||
void command(uint id, char *input);
|
||||
const char *commandIsPrivmsg(uint id, const char *input);
|
||||
const char *commandIsNotice(uint id, const char *input);
|
||||
const char *commandIsAction(uint id, const char *input);
|
||||
void commandComplete(void);
|
||||
|
||||
enum Heat { Cold, Warm, Hot };
|
||||
extern struct Util uiNotifyUtil;
|
||||
void uiInit(void);
|
||||
|
|
67
command.c
67
command.c
|
@ -39,53 +39,45 @@ static void commandQuote(uint id, char *params) {
|
|||
if (params) ircFormat("%s\r\n", params);
|
||||
}
|
||||
|
||||
static void commandPrivmsg(uint id, char *params) {
|
||||
static void echoMessage(char *cmd, uint id, char *params) {
|
||||
if (!params || !params[0]) return;
|
||||
ircFormat("PRIVMSG %s :%s\r\n", idNames[id], params);
|
||||
ircFormat("%s %s :%s\r\n", cmd, idNames[id], params);
|
||||
struct Message msg = {
|
||||
.nick = self.nick,
|
||||
.user = self.user,
|
||||
.cmd = "PRIVMSG",
|
||||
.cmd = cmd,
|
||||
.params[0] = idNames[id],
|
||||
.params[1] = params,
|
||||
};
|
||||
handle(msg);
|
||||
}
|
||||
|
||||
static void commandPrivmsg(uint id, char *params) {
|
||||
echoMessage("PRIVMSG", id, params);
|
||||
}
|
||||
|
||||
static void commandNotice(uint id, char *params) {
|
||||
if (!params || !params[0]) return;
|
||||
ircFormat("NOTICE %s :%s\r\n", idNames[id], params);
|
||||
struct Message msg = {
|
||||
.nick = self.nick,
|
||||
.user = self.user,
|
||||
.cmd = "NOTICE",
|
||||
.params[0] = idNames[id],
|
||||
.params[1] = params,
|
||||
};
|
||||
handle(msg);
|
||||
echoMessage("NOTICE", id, params);
|
||||
}
|
||||
|
||||
static void commandMe(uint id, char *params) {
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "\1ACTION %s\1", (params ? params : ""));
|
||||
commandPrivmsg(id, buf);
|
||||
echoMessage("PRIVMSG", id, buf);
|
||||
}
|
||||
|
||||
static void commandMsg(uint id, char *params) {
|
||||
(void)id;
|
||||
char *nick = strsep(¶ms, " ");
|
||||
if (!params) return;
|
||||
commandPrivmsg(idFor(nick), params);
|
||||
id = idFor(strsep(¶ms, " "));
|
||||
echoMessage("PRIVMSG", id, params);
|
||||
}
|
||||
|
||||
static void commandJoin(uint id, char *params) {
|
||||
if (!params) params = idNames[id];
|
||||
uint count = 1;
|
||||
if (params) {
|
||||
for (char *ch = params; *ch && *ch != ' '; ++ch) {
|
||||
if (*ch == ',') count++;
|
||||
}
|
||||
}
|
||||
ircFormat("JOIN %s\r\n", (params ? params : idNames[id]));
|
||||
ircFormat("JOIN %s\r\n", params);
|
||||
replies.join += count;
|
||||
replies.topic += count;
|
||||
replies.names += count;
|
||||
|
@ -101,7 +93,7 @@ static void commandPart(uint id, char *params) {
|
|||
|
||||
static void commandQuit(uint id, char *params) {
|
||||
(void)id;
|
||||
set(&self.quit, (params ? params : "Goodbye"));
|
||||
set(&self.quit, (params ? params : "nyaa~"));
|
||||
}
|
||||
|
||||
static void commandNick(uint id, char *params) {
|
||||
|
@ -131,7 +123,7 @@ static void commandTopic(uint id, char *params) {
|
|||
|
||||
static void commandNames(uint id, char *params) {
|
||||
(void)params;
|
||||
ircFormat("NAMES :%s\r\n", idNames[id]);
|
||||
ircFormat("NAMES %s\r\n", idNames[id]);
|
||||
replies.names++;
|
||||
}
|
||||
|
||||
|
@ -170,14 +162,12 @@ static void commandWhois(uint id, char *params) {
|
|||
|
||||
static void commandNS(uint id, char *params) {
|
||||
(void)id;
|
||||
if (!params) return;
|
||||
ircFormat("PRIVMSG NickServ :%s\r\n", params);
|
||||
if (params) ircFormat("PRIVMSG NickServ :%s\r\n", params);
|
||||
}
|
||||
|
||||
static void commandCS(uint id, char *params) {
|
||||
(void)id;
|
||||
if (!params) return;
|
||||
ircFormat("PRIVMSG ChanServ :%s\r\n", params);
|
||||
if (params) ircFormat("PRIVMSG ChanServ :%s\r\n", params);
|
||||
}
|
||||
|
||||
static void commandQuery(uint id, char *params) {
|
||||
|
@ -330,36 +320,41 @@ const char *commandIsAction(uint id, const char *input) {
|
|||
void command(uint id, char *input) {
|
||||
if (id == Debug && input[0] != '/') {
|
||||
commandQuote(id, input);
|
||||
return;
|
||||
} else if (commandIsPrivmsg(id, input)) {
|
||||
commandPrivmsg(id, input);
|
||||
return;
|
||||
} else if (input[0] == '/' && isdigit(input[1])) {
|
||||
commandWindow(id, &input[1]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *cmd = strsep(&input, " ");
|
||||
const char *unique = complete(None, cmd);
|
||||
if (unique && !complete(None, cmd)) {
|
||||
cmd = unique;
|
||||
completeReject();
|
||||
}
|
||||
|
||||
const struct Handler *handler = bsearch(
|
||||
cmd, Commands, ARRAY_LEN(Commands), sizeof(*handler), compar
|
||||
);
|
||||
if (self.restricted && handler && handler->restricted) {
|
||||
handler = NULL;
|
||||
if (!handler) {
|
||||
uiFormat(id, Warm, NULL, "No such command %s", cmd);
|
||||
return;
|
||||
}
|
||||
if (handler) {
|
||||
if (self.restricted && handler->restricted) {
|
||||
uiFormat(id, Warm, NULL, "Command %s is restricted", cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (input) {
|
||||
input += strspn(input, " ");
|
||||
size_t len = strlen(input);
|
||||
while (input[len - 1] == ' ') input[--len] = '\0';
|
||||
if (!input[0]) input = NULL;
|
||||
}
|
||||
if (input && !input[0]) input = NULL;
|
||||
handler->fn(id, input);
|
||||
} else {
|
||||
uiFormat(id, Hot, NULL, "No such command %s", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void commandComplete(void) {
|
||||
|
|
11
complete.c
11
complete.c
|
@ -35,10 +35,10 @@ static struct Node *alloc(uint id, const char *str, enum Color color) {
|
|||
if (!node) err(EX_OSERR, "malloc");
|
||||
node->id = id;
|
||||
node->str = strdup(str);
|
||||
if (!node->str) err(EX_OSERR, "strdup");
|
||||
node->color = color;
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
if (!node->str) err(EX_OSERR, "strdup");
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,9 @@ static struct Node *append(struct Node *node) {
|
|||
|
||||
static struct Node *find(uint id, const char *str) {
|
||||
for (struct Node *node = head; node; node = node->next) {
|
||||
if (node->id == id && !strcmp(node->str, str)) return node;
|
||||
if (node->id != id) continue;
|
||||
if (strcmp(node->str, str)) continue;
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -86,7 +88,7 @@ void completeAdd(uint id, const char *str, enum Color color) {
|
|||
|
||||
void completeTouch(uint id, const char *str, enum Color color) {
|
||||
struct Node *node = find(id, str);
|
||||
if (node && node->color != color) node->color = color;
|
||||
if (node) node->color = color;
|
||||
prepend(node ? detach(node) : alloc(id, str, color));
|
||||
}
|
||||
|
||||
|
@ -128,11 +130,10 @@ void completeReplace(uint id, const char *old, const char *new) {
|
|||
next = node->next;
|
||||
if (id && node->id != id) continue;
|
||||
if (strcmp(node->str, old)) continue;
|
||||
if (match == node) match = NULL;
|
||||
free(node->str);
|
||||
node->str = strdup(new);
|
||||
if (!node->str) err(EX_OSERR, "strdup");
|
||||
prepend(detach(node));
|
||||
if (!node->str) err(EX_OSERR, "strdup");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
irc.c
18
irc.c
|
@ -157,15 +157,17 @@ int ircConnect(const char *bindHost, const char *host, const char *port) {
|
|||
return sock;
|
||||
}
|
||||
|
||||
static void debug(char dir, const char *line) {
|
||||
enum { MessageCap = 8191 + 512 };
|
||||
|
||||
static void debug(const char *pre, const char *line) {
|
||||
if (!self.debug) return;
|
||||
size_t len = strcspn(line, "\r\n");
|
||||
uiFormat(
|
||||
Debug, Cold, NULL, "\3%d%c%c\3\t%.*s",
|
||||
Gray, dir, dir, (int)len, line
|
||||
Debug, Cold, NULL, "\3%02d%s\3\t%.*s",
|
||||
Gray, pre, (int)len, line
|
||||
);
|
||||
if (!isatty(STDERR_FILENO)) {
|
||||
fprintf(stderr, "%c%c %.*s\n", dir, dir, (int)len, line);
|
||||
fprintf(stderr, "%s %.*s\n", pre, (int)len, line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,13 +183,13 @@ void ircSend(const char *ptr, size_t len) {
|
|||
}
|
||||
|
||||
void ircFormat(const char *format, ...) {
|
||||
char buf[1024];
|
||||
char buf[MessageCap];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int len = vsnprintf(buf, sizeof(buf), format, ap);
|
||||
va_end(ap);
|
||||
assert((size_t)len < sizeof(buf));
|
||||
debug('<', buf);
|
||||
debug("<<", buf);
|
||||
ircSend(buf, len);
|
||||
}
|
||||
|
||||
|
@ -249,7 +251,7 @@ static struct Message parse(char *line) {
|
|||
}
|
||||
|
||||
void ircRecv(void) {
|
||||
static char buf[8191 + 512];
|
||||
static char buf[MessageCap];
|
||||
static size_t len = 0;
|
||||
|
||||
assert(client);
|
||||
|
@ -265,7 +267,7 @@ void ircRecv(void) {
|
|||
crlf = memmem(line, &buf[len] - line, "\r\n", 2);
|
||||
if (!crlf) break;
|
||||
*crlf = '\0';
|
||||
debug('>', line);
|
||||
debug(">>", line);
|
||||
handle(parse(line));
|
||||
line = crlf + 2;
|
||||
}
|
||||
|
|
4
ui.c
4
ui.c
|
@ -90,10 +90,10 @@ struct Window {
|
|||
};
|
||||
|
||||
static struct {
|
||||
uint show;
|
||||
uint swap;
|
||||
struct Window *ptrs[IDCap];
|
||||
uint len;
|
||||
uint show;
|
||||
uint swap;
|
||||
} windows;
|
||||
|
||||
static uint windowPush(struct Window *window) {
|
||||
|
|
7
url.c
7
url.c
|
@ -17,6 +17,7 @@
|
|||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -142,8 +143,10 @@ static void urlCopy(const char *url) {
|
|||
int error = pipe(rw);
|
||||
if (error) err(EX_OSERR, "pipe");
|
||||
|
||||
ssize_t len = write(rw[1], url, strlen(url));
|
||||
if (len < 0) err(EX_IOERR, "write");
|
||||
size_t len = strlen(url);
|
||||
if (len > PIPE_BUF) len = PIPE_BUF;
|
||||
ssize_t n = write(rw[1], url, len);
|
||||
if (n < 0) err(EX_IOERR, "write");
|
||||
|
||||
error = close(rw[1]);
|
||||
if (error) err(EX_IOERR, "close");
|
||||
|
|
Loading…
Reference in New Issue