summaryrefslogtreecommitdiff
path: root/cic.c
diff options
context:
space:
mode:
authorJohn Vogel <jvogel4@stny.rr.com>2015-12-06 00:15:32 -0500
committerJohn Vogel <jvogel4@stny.rr.com>2015-12-06 00:15:32 -0500
commit08b54647775a09c94cf32ae5eccd64c8661ce1f7 (patch)
tree237d2a0984de75d5850f769b3256a47f4245522a /cic.c
parent7f0141bbe92dc6c3423ff39d21e8ec5c18b58ec1 (diff)
downloadcic-08b54647775a09c94cf32ae5eccd64c8661ce1f7.tar.gz
Initial commit for cic, ncurses fork of sic
Diffstat (limited to 'cic.c')
-rw-r--r--cic.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/cic.c b/cic.c
new file mode 100644
index 0000000..c12d6df
--- /dev/null
+++ b/cic.c
@@ -0,0 +1,380 @@
+ /* See LICENSE file for license details. */
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <curses.h>
+
+#include "arg.h"
+#include "config.h"
+
+#define CONTROL(c) ((c) ^ 0x40)
+
+char *argv0;
+static char *host = DEFAULT_HOST;
+static char *port = DEFAULT_PORT;
+static char *password;
+static char nick[32];
+static char bufsrv[4096];
+static char bufin[4096];
+static char bufout[4096];
+static char channel[256];
+static time_t trespond;
+static FILE *srv;
+static WINDOW *viewwin;
+static WINDOW *inputwin;
+static int rows;
+static int cols;
+static int running;
+static int got_sigwinch;
+static int curline;
+
+#include "util.c"
+
+static void
+pout(char *channel, char *fmt, ...) {
+ static char timestr[80];
+ time_t t;
+ va_list ap;
+
+ if (curline >= rows-2) {
+ wscrl(viewwin, 4);
+ curline -= 3;
+ }
+ else
+ curline++;
+
+ va_start(ap, fmt);
+ vsnprintf(bufout, sizeof bufout, fmt, ap);
+ va_end(ap);
+ t = time(NULL);
+ strftime(timestr, sizeof timestr, TIMESTAMP_FORMAT, localtime(&t));
+ mvwprintw(viewwin, curline, 0, "%-12s: %s %s", channel, timestr, bufout);
+ wrefresh(viewwin);
+ wrefresh(inputwin);
+}
+
+static void
+sout(char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(bufsrv, sizeof bufsrv, fmt, ap);
+ va_end(ap);
+ fprintf(srv, "%s\r\n", bufsrv);
+}
+
+static void
+privmsg(char *channel, char *msg) {
+ if(channel[0] == '\0') {
+ pout("", "No channel to send to");
+ return;
+ }
+ pout(channel, "<%s> %s", nick, msg);
+ sout("PRIVMSG %s :%s", channel, msg);
+}
+
+static void
+parsein(char *s) {
+ char c, *p;
+
+ if(s[0] == '\0')
+ return;
+ skip(s, '\n');
+ if(s[0] != COMMAND_PREFIX_CHARACTER) {
+ privmsg(channel, s);
+ return;
+ }
+ c = *++s;
+ if(c != '\0' && isspace(s[1])) {
+ p = s + 2;
+ switch(c) {
+ case 'j':
+ sout("JOIN %s", p);
+ if(channel[0] == '\0')
+ strlcpy(channel, p, sizeof channel);
+ return;
+ case 'l':
+ s = eat(p, isspace, 1);
+ p = eat(s, isspace, 0);
+ if(!*s)
+ s = channel;
+ if(*p)
+ *p++ = '\0';
+ if(!*p)
+ p = DEFAULT_PARTING_MESSAGE;
+ sout("PART %s :%s", s, p);
+ return;
+ case 'm':
+ s = eat(p, isspace, 1);
+ p = eat(s, isspace, 0);
+ if(*p)
+ *p++ = '\0';
+ privmsg(s, p);
+ return;
+ case 's':
+ strlcpy(channel, p, sizeof channel);
+ return;
+ }
+ }
+ sout("%s", s);
+}
+
+static void
+parsesrv(char *cmd) {
+ char *usr, *par, *txt;
+
+ usr = host;
+ if(!cmd || !*cmd)
+ return;
+ if(cmd[0] == ':') {
+ usr = cmd + 1;
+ cmd = skip(usr, ' ');
+ if(cmd[0] == '\0')
+ return;
+ skip(usr, '!');
+ }
+ skip(cmd, '\r');
+ par = skip(cmd, ' ');
+ txt = skip(par, ':');
+ trim(par);
+ if(!strcmp("PONG", cmd))
+ return;
+ if(!strcmp("PRIVMSG", cmd))
+ pout(par, "<%s> %s", usr, txt);
+ else if(!strcmp("PING", cmd))
+ sout("PONG %s", txt);
+ else {
+ pout(usr, ">< %s (%s): %s", cmd, par, txt);
+ if(!strcmp("NICK", cmd) && !strcmp(usr, nick))
+ strlcpy(nick, txt, sizeof nick);
+ }
+}
+
+int getinput(char *buf, size_t sz)
+{
+ int ch;
+ size_t ret = 0;
+ static size_t pos = 0;
+
+ wmove(inputwin, 0, pos);
+ wrefresh(inputwin);
+ while ((ch = getch()) != ERR) {
+ switch (ch) {
+ case KEY_RESIZE:
+ got_sigwinch = 1;
+ goto done;
+ case KEY_ENTER:
+ case CONTROL('M'):
+ ret = pos;
+ pos = 0;
+ wmove(inputwin, 0, 0);
+ wclrtoeol(inputwin);
+ wrefresh(inputwin);
+ goto done;
+ case KEY_BACKSPACE:
+ if (pos) {
+ pos--;
+ wmove(inputwin, 0, pos);
+ wclrtoeol(inputwin);
+ wrefresh(inputwin);
+ }
+ break;
+ default:
+ if (pos == sizeof bufin) {
+ pout("", "input line too long");
+ goto done;
+ }
+ mvwaddch(inputwin, 0, pos, ch);
+ bufin[pos++] = ch;
+ bufin[pos] = '\0';
+ wrefresh(inputwin);
+ break;
+ }
+ }
+
+done:
+ wrefresh(inputwin);
+
+ return ret;
+}
+
+void gettermsize(void)
+{
+ struct winsize w;
+
+ while (ioctl(STDIN_FILENO, TIOCGWINSZ, &w) == -1)
+ if (errno != EINTR) {
+ endwin();
+ eprint("cic: failed to get term size");
+ }
+ rows = w.ws_row;
+ cols = w.ws_col;
+ wsetscrreg(viewwin, 0, rows-1);
+}
+
+void doresize(void)
+{
+ int ch;
+
+ gettermsize();
+
+ resizeterm(rows, cols);
+
+ if ((ch = getch()) != KEY_RESIZE)
+ ungetch(ch);
+
+ wresize(viewwin, rows-1, cols);
+ wmove(viewwin, 0, 0);
+ wresize(inputwin, 1, cols);
+ wmove(inputwin, rows-1, 0);
+
+ wrefresh(viewwin);
+ wrefresh(inputwin);
+}
+
+void sigint(int unused)
+{
+ running = 0;
+}
+
+void sigwinch(int unused)
+{
+ got_sigwinch = 1;
+}
+
+
+static void
+usage(void) {
+ eprint("usage: cic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n", argv0);
+}
+
+int
+main(int argc, char *argv[]) {
+ struct sigaction sa;
+ struct timeval tv;
+ const char *user = getenv("USER");
+ int n;
+ fd_set rd;
+
+ if (!isatty(STDIN_FILENO))
+ eprint("cic: stdin is not a terminal");
+ if (!isatty(STDOUT_FILENO))
+ eprint("cic: stdout is not a terminal");
+
+ strlcpy(nick, user ? user : "unknown", sizeof nick);
+ ARGBEGIN {
+ case 'h':
+ host = EARGF(usage());
+ break;
+ case 'p':
+ port = EARGF(usage());
+ break;
+ case 'n':
+ strlcpy(nick, EARGF(usage()), sizeof nick);
+ break;
+ case 'k':
+ password = EARGF(usage());
+ break;
+ case 'v':
+ eprint("cic-"VERSION", © 2005-2014 Kris Maglione, Anselm R. Garbe, Nico Golde, John Vogel\n");
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ /* signals init */
+ sa.sa_handler = sigint;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sa.sa_handler = sigwinch;
+ sigaction(SIGWINCH, &sa, NULL);
+
+ srv = fdopen(dial(host, port), "r+");
+ if (!srv) {
+ endwin();
+ eprint("fdopen:");
+ }
+ /* login */
+ if(password)
+ sout("PASS %s", password);
+ sout("NICK %s", nick);
+ sout("USER %s localhost %s :%s", nick, host, nick);
+ fflush(srv);
+ setbuf(srv, NULL);
+
+ /* curses init */
+ initscr();
+ cbreak();
+ noecho();
+ nonl();
+ intrflush(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ curs_set(1);
+ timeout(0);
+ refresh();
+
+ gettermsize();
+
+ viewwin = newwin(rows - 1, cols, 0, 0);
+ idlok(viewwin, TRUE);
+ scrollok(viewwin, TRUE);
+ inputwin = newwin(1, cols, rows - 1, 0);
+ wrefresh(viewwin);
+ wrefresh(inputwin);
+
+ running = 1;
+ got_sigwinch = 0;
+ while (running) { /* main loop */
+ if (got_sigwinch) {
+ doresize();
+ got_sigwinch = 0;
+ }
+ FD_ZERO(&rd);
+ FD_SET(0, &rd);
+ FD_SET(fileno(srv), &rd);
+ tv.tv_sec = 120;
+ tv.tv_usec = 0;
+ n = select(fileno(srv) + 1, &rd, 0, 0, &tv);
+ if(n < 0) {
+ if(errno == EINTR)
+ continue;
+ endwin();
+ eprint("cic: error on select():");
+ }
+ else if(n == 0) {
+ if(time(NULL) - trespond >= 300) {
+ endwin();
+ eprint("cic shutting down: parse timeout\n");
+ }
+ sout("PING %s", host);
+ continue;
+ }
+ if(FD_ISSET(fileno(srv), &rd)) {
+ if(fgets(bufsrv, sizeof bufin, srv) == NULL) {
+ endwin();
+ eprint("cic: remote host closed connection\n");
+ }
+ parsesrv(bufsrv);
+ trespond = time(NULL);
+ }
+ if(FD_ISSET(0, &rd)) {
+ if (getinput(bufin, sizeof bufin) > 0)
+ parsein(bufin);
+ }
+ }
+ delwin(viewwin);
+ delwin(inputwin);
+ endwin();
+ return 0;
+}