diff --git a/Makefile b/Makefile index 435172f..9ec7b26 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README b/README index be2c84b..9c86694 100644 --- a/README +++ b/README @@ -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 diff --git a/chat.c b/chat.c index fb9e68f..c7a3229 100644 --- a/chat.c +++ b/chat.c @@ -17,15 +17,11 @@ #define _WITH_GETLINE #include -#include -#include -#include #include #include #include #include #include -#include #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); } diff --git a/chat.h b/chat.h index df532d7..6c95ab6 100644 --- a/chat.h +++ b/chat.h @@ -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); diff --git a/event.c b/event.c new file mode 100644 index 0000000..4771aac --- /dev/null +++ b/event.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2018 Curtis McEnroe + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); + } +} diff --git a/url.c b/url.c index e6207a5..3f8d57a 100644 --- a/url.c +++ b/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; tagIndex++; } - if (argc > 1) spawn(argv); + if (argc > 1) eventSpawn(argv); }