Move event loop to event.c
parent
0cf067315d
commit
bd48cb5e7d
1
Makefile
1
Makefile
|
@ -10,6 +10,7 @@ LDLIBS = -lcursesw -ltls
|
||||||
|
|
||||||
OBJS += chat.o
|
OBJS += chat.o
|
||||||
OBJS += edit.o
|
OBJS += edit.o
|
||||||
|
OBJS += event.o
|
||||||
OBJS += handle.o
|
OBJS += handle.o
|
||||||
OBJS += input.o
|
OBJS += input.o
|
||||||
OBJS += irc.o
|
OBJS += irc.o
|
||||||
|
|
3
README
3
README
|
@ -3,7 +3,8 @@ IRC client originally intended for use over anonymous SSH.
|
||||||
This software requires LibreSSL and targets FreeBSD and Darwin.
|
This software requires LibreSSL and targets FreeBSD and Darwin.
|
||||||
|
|
||||||
chat.h Shared state and function prototypes
|
chat.h Shared state and function prototypes
|
||||||
chat.c Command line parsing and event loop
|
chat.c Command line parsing
|
||||||
|
event.c Event loop and process spawning
|
||||||
tag.c Tag (channel, query) ID assignment
|
tag.c Tag (channel, query) ID assignment
|
||||||
handle.c Incoming command handling
|
handle.c Incoming command handling
|
||||||
input.c Input command handling
|
input.c Input command handling
|
||||||
|
|
102
chat.c
102
chat.c
|
@ -17,15 +17,11 @@
|
||||||
#define _WITH_GETLINE
|
#define _WITH_GETLINE
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
|
|
||||||
|
@ -45,98 +41,6 @@ void selfJoin(const char *join) {
|
||||||
if (!self.join) err(EX_OSERR, "strdup");
|
if (!self.join) err(EX_OSERR, "strdup");
|
||||||
}
|
}
|
||||||
|
|
||||||
static union {
|
|
||||||
struct {
|
|
||||||
struct pollfd ui;
|
|
||||||
struct pollfd irc;
|
|
||||||
struct pollfd pipe;
|
|
||||||
};
|
|
||||||
struct pollfd fds[3];
|
|
||||||
} fds = {
|
|
||||||
.ui = { .events = POLLIN, .fd = STDIN_FILENO },
|
|
||||||
.irc = { .events = POLLIN },
|
|
||||||
.pipe = { .events = 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
void spawn(char *const argv[]) {
|
|
||||||
if (fds.pipe.events) {
|
|
||||||
uiLog(TagStatus, UIWarm, L"spawn: existing pipe");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rw[2];
|
|
||||||
int error = pipe(rw);
|
|
||||||
if (error) err(EX_OSERR, "pipe");
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid < 0) err(EX_OSERR, "fork");
|
|
||||||
if (!pid) {
|
|
||||||
close(rw[0]);
|
|
||||||
close(STDIN_FILENO);
|
|
||||||
dup2(rw[1], STDOUT_FILENO);
|
|
||||||
dup2(rw[1], STDERR_FILENO);
|
|
||||||
close(rw[1]);
|
|
||||||
execvp(argv[0], argv);
|
|
||||||
perror(argv[0]);
|
|
||||||
exit(EX_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(rw[1]);
|
|
||||||
fds.pipe.fd = rw[0];
|
|
||||||
fds.pipe.events = POLLIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pipeRead(void) {
|
|
||||||
char buf[256];
|
|
||||||
ssize_t len = read(fds.pipe.fd, buf, sizeof(buf) - 1);
|
|
||||||
if (len < 0) err(EX_IOERR, "read");
|
|
||||||
if (len) {
|
|
||||||
buf[len] = '\0';
|
|
||||||
len = strcspn(buf, "\n");
|
|
||||||
uiFmt(TagStatus, UIWarm, "spawn: %.*s", (int)len, buf);
|
|
||||||
} else {
|
|
||||||
close(fds.pipe.fd);
|
|
||||||
fds.pipe.events = 0;
|
|
||||||
fds.pipe.revents = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eventLoop(void) {
|
|
||||||
for (;;) {
|
|
||||||
uiDraw();
|
|
||||||
|
|
||||||
int n = poll(fds.fds, (fds.pipe.events ? 3 : 2), -1);
|
|
||||||
if (n < 0) {
|
|
||||||
if (errno != EINTR) err(EX_IOERR, "poll");
|
|
||||||
uiRead();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fds.ui.revents) uiRead();
|
|
||||||
if (fds.irc.revents) ircRead();
|
|
||||||
if (fds.pipe.revents) pipeRead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sigchld(int sig) {
|
|
||||||
(void)sig;
|
|
||||||
int status;
|
|
||||||
pid_t pid = wait(&status);
|
|
||||||
if (pid < 0) err(EX_OSERR, "wait");
|
|
||||||
if (WIFEXITED(status) && WEXITSTATUS(status)) {
|
|
||||||
uiFmt(TagStatus, UIWarm, "spawn: exit %d", WEXITSTATUS(status));
|
|
||||||
} else if (WIFSIGNALED(status)) {
|
|
||||||
uiFmt(TagStatus, UIWarm, "spawn: signal %d", WTERMSIG(status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sigint(int sig) {
|
|
||||||
(void)sig;
|
|
||||||
input(TagStatus, "/quit");
|
|
||||||
uiExit();
|
|
||||||
exit(EX_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *prompt(const char *prompt) {
|
static char *prompt(const char *prompt) {
|
||||||
char *line = NULL;
|
char *line = NULL;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
|
@ -186,10 +90,8 @@ int main(int argc, char *argv[]) {
|
||||||
uiLog(TagStatus, UIWarm, L"Traveling...");
|
uiLog(TagStatus, UIWarm, L"Traveling...");
|
||||||
uiDraw();
|
uiDraw();
|
||||||
|
|
||||||
fds.irc.fd = ircConnect(host, port, pass, webirc);
|
int irc = ircConnect(host, port, pass, webirc);
|
||||||
free(host);
|
free(host);
|
||||||
|
|
||||||
signal(SIGINT, sigint);
|
eventLoop(STDIN_FILENO, irc);
|
||||||
signal(SIGCHLD, sigchld);
|
|
||||||
eventLoop();
|
|
||||||
}
|
}
|
||||||
|
|
5
chat.h
5
chat.h
|
@ -37,6 +37,9 @@ void selfNick(const char *nick);
|
||||||
void selfUser(const char *user);
|
void selfUser(const char *user);
|
||||||
void selfJoin(const char *join);
|
void selfJoin(const char *join);
|
||||||
|
|
||||||
|
void eventSpawn(char *const argv[]);
|
||||||
|
void eventLoop(int ui, int irc);
|
||||||
|
|
||||||
struct Tag {
|
struct Tag {
|
||||||
size_t id;
|
size_t id;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -161,8 +164,6 @@ void logFmt(
|
||||||
struct Tag tag, const time_t *ts, const char *format, ...
|
struct Tag tag, const time_t *ts, const char *format, ...
|
||||||
) __attribute__((format(printf, 3, 4)));
|
) __attribute__((format(printf, 3, 4)));
|
||||||
|
|
||||||
void spawn(char *const argv[]);
|
|
||||||
|
|
||||||
wchar_t *wcsnchr(const wchar_t *wcs, size_t len, wchar_t chr);
|
wchar_t *wcsnchr(const wchar_t *wcs, size_t len, wchar_t chr);
|
||||||
wchar_t *wcsnrchr(const wchar_t *wcs, size_t len, wchar_t chr);
|
wchar_t *wcsnrchr(const wchar_t *wcs, size_t len, wchar_t chr);
|
||||||
wchar_t *ambstowcs(const char *src);
|
wchar_t *ambstowcs(const char *src);
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/* 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 <err.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "chat.h"
|
||||||
|
|
||||||
|
static union {
|
||||||
|
struct {
|
||||||
|
struct pollfd ui;
|
||||||
|
struct pollfd irc;
|
||||||
|
struct pollfd pipe;
|
||||||
|
};
|
||||||
|
struct pollfd fds[3];
|
||||||
|
} fds;
|
||||||
|
|
||||||
|
void eventSpawn(char *const argv[]) {
|
||||||
|
if (fds.pipe.events) {
|
||||||
|
uiLog(TagStatus, UIHot, L"eventSpawn: existing pipe");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rw[2];
|
||||||
|
int error = pipe(rw);
|
||||||
|
if (error) err(EX_OSERR, "pipe");
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) err(EX_OSERR, "fork");
|
||||||
|
if (!pid) {
|
||||||
|
close(rw[0]);
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
dup2(rw[1], STDOUT_FILENO);
|
||||||
|
dup2(rw[1], STDERR_FILENO);
|
||||||
|
close(rw[1]);
|
||||||
|
execvp(argv[0], argv);
|
||||||
|
perror(argv[0]);
|
||||||
|
exit(EX_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rw[1]);
|
||||||
|
fds.pipe.fd = rw[0];
|
||||||
|
fds.pipe.events = POLLIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipeRead(void) {
|
||||||
|
char buf[256];
|
||||||
|
ssize_t len = read(fds.pipe.fd, buf, sizeof(buf) - 1);
|
||||||
|
if (len < 0) err(EX_IOERR, "read");
|
||||||
|
if (len) {
|
||||||
|
buf[len] = '\0';
|
||||||
|
len = strcspn(buf, "\n");
|
||||||
|
uiFmt(TagStatus, UIHot, "eventSpawn: %.*s", (int)len, buf);
|
||||||
|
} else {
|
||||||
|
close(fds.pipe.fd);
|
||||||
|
memset(&fds.pipe, 0, sizeof(fds.pipe));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigchld(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
int status;
|
||||||
|
pid_t pid = wait(&status);
|
||||||
|
if (pid < 0) err(EX_OSERR, "wait");
|
||||||
|
if (WIFEXITED(status) && WEXITSTATUS(status)) {
|
||||||
|
uiFmt(TagStatus, UIHot, "eventSpawn: exit %d", WEXITSTATUS(status));
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
uiFmt(TagStatus, UIHot, "eventSpawn: singal %d", WTERMSIG(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigint(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
input(TagStatus, "/quit");
|
||||||
|
uiExit();
|
||||||
|
exit(EX_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void eventLoop(int ui, int irc) {
|
||||||
|
signal(SIGINT, sigint);
|
||||||
|
signal(SIGCHLD, sigchld);
|
||||||
|
|
||||||
|
fds.ui.fd = ui;
|
||||||
|
fds.irc.fd = irc;
|
||||||
|
fds.ui.events = POLLIN;
|
||||||
|
fds.irc.events = POLLIN;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uiDraw();
|
||||||
|
|
||||||
|
int ready = poll(fds.fds, (fds.pipe.events ? 3 : 2), -1);
|
||||||
|
if (ready < 0) {
|
||||||
|
if (errno != EINTR) err(EX_IOERR, "poll");
|
||||||
|
uiRead();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds.ui.revents) uiRead();
|
||||||
|
if (fds.irc.revents) ircRead();
|
||||||
|
if (fds.pipe.revents) pipeRead();
|
||||||
|
}
|
||||||
|
}
|
2
url.c
2
url.c
|
@ -82,5 +82,5 @@ void urlOpen(struct Tag tag, size_t at, size_t to) {
|
||||||
if (tagIndex >= at && tagIndex < to) argv[argc++] = entry.url;
|
if (tagIndex >= at && tagIndex < to) argv[argc++] = entry.url;
|
||||||
tagIndex++;
|
tagIndex++;
|
||||||
}
|
}
|
||||||
if (argc > 1) spawn(argv);
|
if (argc > 1) eventSpawn(argv);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue