Move event loop to event.c

master
Curtis McEnroe 2018-09-06 00:41:06 -04:00
parent 0cf067315d
commit bd48cb5e7d
No known key found for this signature in database
GPG Key ID: CEA2F97ADCFCD77C
6 changed files with 132 additions and 104 deletions

View File

@ -10,6 +10,7 @@ LDLIBS = -lcursesw -ltls
OBJS += chat.o
OBJS += edit.o
OBJS += event.o
OBJS += handle.o
OBJS += input.o
OBJS += irc.o

3
README
View File

@ -3,7 +3,8 @@ IRC client originally intended for use over anonymous SSH.
This software requires LibreSSL and targets FreeBSD and Darwin.
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
handle.c Incoming command handling
input.c Input command handling

102
chat.c
View File

@ -17,15 +17,11 @@
#define _WITH_GETLINE
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/wait.h>
#include "chat.h"
@ -45,98 +41,6 @@ void selfJoin(const char *join) {
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) {
char *line = NULL;
size_t cap;
@ -186,10 +90,8 @@ int main(int argc, char *argv[]) {
uiLog(TagStatus, UIWarm, L"Traveling...");
uiDraw();
fds.irc.fd = ircConnect(host, port, pass, webirc);
int irc = ircConnect(host, port, pass, webirc);
free(host);
signal(SIGINT, sigint);
signal(SIGCHLD, sigchld);
eventLoop();
eventLoop(STDIN_FILENO, irc);
}

5
chat.h
View File

@ -37,6 +37,9 @@ void selfNick(const char *nick);
void selfUser(const char *user);
void selfJoin(const char *join);
void eventSpawn(char *const argv[]);
void eventLoop(int ui, int irc);
struct Tag {
size_t id;
const char *name;
@ -161,8 +164,6 @@ void logFmt(
struct Tag tag, const time_t *ts, const char *format, ...
) __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 *wcsnrchr(const wchar_t *wcs, size_t len, wchar_t chr);
wchar_t *ambstowcs(const char *src);

123
event.c 100644
View File

@ -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
View File

@ -82,5 +82,5 @@ void urlOpen(struct Tag tag, size_t at, size_t to) {
if (tagIndex >= at && tagIndex < to) argv[argc++] = entry.url;
tagIndex++;
}
if (argc > 1) spawn(argv);
if (argc > 1) eventSpawn(argv);
}