aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormcpcpc <michaelczigler@icloud.com>2020-08-06 16:08:22 -0400
committermcpcpc <michaelczigler@icloud.com>2020-08-06 16:08:22 -0400
commitc1999123facb3ea39ec943d042f7f8fc331eb442 (patch)
tree9b55950a7fed5bb8111204caa5ec239f6fb79932
downloadkirc-c1999123facb3ea39ec943d042f7f8fc331eb442.tar.gz
kirc-c1999123facb3ea39ec943d042f7f8fc331eb442.tar.bz2
kirc-c1999123facb3ea39ec943d042f7f8fc331eb442.tar.xz
kirc-c1999123facb3ea39ec943d042f7f8fc331eb442.tar.zst
kirc-c1999123facb3ea39ec943d042f7f8fc331eb442.zip
new at 0.0.1
-rw-r--r--LICENSE21
-rw-r--r--Makefile20
-rw-r--r--README44
-rw-r--r--kirc.c245
4 files changed, 330 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0834288
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Michael Czigler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..835603d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+CFLAGS += -std=c99 -Wall -Wextra -pedantic -Wold-style-declaration
+PREFIX ?= /usr
+BINDIR ?= $(PREFIX)/bin
+CC ?= gcc
+
+all: kirc
+
+kfc: kirc.c Makefile
+ $(CC) -O3 $(CFLAGS) -o $@ $< -lX11 $(LDFLAGS)
+
+install: all
+ install -Dm755 kirc $(DESTDIR)$(BINDIR)/kirc
+
+uninstall:
+ rm -f $(DESTDIR)$(BINDIR)/kirc
+
+clean:
+ rm -f kirc *.o
+
+.PHONY: all install uninstall clean
diff --git a/README b/README
new file mode 100644
index 0000000..324a771
--- /dev/null
+++ b/README
@@ -0,0 +1,44 @@
+kirc
+
+KISS for IRC, an IRC client written in POSIX C99.
+
+
+BACKGROUND
+----------
+
+After having tried multiple IRC clients, I decided to develope my own. The
+result is a portable <250 sloc application that has no dependencies other
+than a C99 compiler.
+
+
+FEATURES
+--------
+
+- automatic host PING response.
+- vi-like shortcuts:
+
+ :m <message> send a message to the connected channel
+ :q close the host connection and quit kirc
+
+
+INSTALLATION
+------------
+
+Building and installing from source:
+
+ git clone https://github.com/mcpcpc/kirc.git
+ cd kirc
+ make
+ make install
+
+
+USAGE
+-----
+
+usage: kirc [-s hostname] [-p port] [-c channel] [-n nick] [-v|V]
+-s server address (default: 'irc.freenode.org')
+-p server port (default: '6667')
+-c channel name (default: '#kisslinux')
+-n user nickname
+-v version information
+-V verbose output (e.g. raw stream)
diff --git a/kirc.c b/kirc.c
new file mode 100644
index 0000000..3f5f122
--- /dev/null
+++ b/kirc.c
@@ -0,0 +1,245 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdarg.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#define BUFF 512 /* buffer size (see RFC 2812) */
+#define CMAX 102 /* max number of columns */
+#define GUTL 10 /* left gutter width and alignment */
+
+static int conn; /* socket connection */
+static char sbuf[BUFF]; /* string buffer */
+static int verb = 0; /* verbose output (e.g. raw stream) */
+static char *host = "irc.freenode.org"; /* irc host address */
+static char *chan = "kisslinux"; /* channel */
+static char *port = "6667"; /* port */
+static char *nick = NULL; /* nickname */
+
+static int
+kbhit(void)
+{
+ int byteswaiting;
+ struct termios term;
+
+ tcgetattr(0, &term);
+
+ struct termios term2 = term;
+
+ term2.c_lflag &= ~ICANON;
+ tcsetattr(0, TCSANOW, &term2);
+ ioctl(0, FIONREAD, &byteswaiting);
+ tcsetattr(0, TCSANOW, &term);
+
+ return byteswaiting > 0;
+}
+
+static void
+raw(char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(sbuf, BUFF, fmt, ap);
+ va_end(ap);
+
+ if (verb) printf("<< %s", sbuf);
+
+ write(conn, sbuf, strlen(sbuf));
+}
+
+static void
+con(void)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ getaddrinfo(host, port, &hints, &res);
+ conn = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ connect(conn, res->ai_addr, res->ai_addrlen);
+
+ if (nick) raw("NICK %s\r\n", nick);
+ if (nick) raw("USER %s - - :%s\r\n", nick, nick);
+
+ fcntl(conn, F_SETFL, O_NONBLOCK);
+}
+
+static void
+printw(const char *format, ...)
+{
+ int s1 = 0, s2, i, o;
+ va_list argptr;
+ char *line = malloc(sizeof(char) * (BUFF + 1));
+ va_start(argptr, format);
+ vsnprintf(line, BUFF + 1, format, argptr);
+ if (strlen(line) <= CMAX) printf(line);
+ else if (strlen(line) > CMAX)
+ {
+
+ for (i = 0; i < CMAX; i++)
+ {
+ if (line[i] == ' ') s1 = i;
+ if (i == CMAX - 1) printf("%-*.*s\n", s1, s1, line);
+ }
+ s2 = o = s1;
+ for (i = s1; line[i] != '\0'; i++)
+ {
+ if (line[i] == ' ') s2 = i;
+ if ((i - o) == (CMAX - GUTL))
+ {
+ printf("%*s %-*.*s\n", GUTL, " ", s2 - o, s2 - o, &line[o + 1]);
+ o = i = s2;
+ }
+ else if (line[i + 1] == '\0')
+ {
+ printf("%*s %-*.*s", GUTL, " ", i - o, i - o, &line[o + 1]);
+ }
+ }
+ }
+ va_end(argptr);
+ free(line);
+}
+
+static void
+pars(int sl, char *buf)
+{
+ char buf_c[BUFF + 1], ltr[200], cha[200], nic[200], hos[200], \
+ usr[200], cmd[200], msg[200], pre[200];
+ int i = 0;
+ int o = -1;
+
+ for (i = 0; i < sl; i++)
+ {
+ o++;
+ buf_c[o] = buf[i];
+
+ if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\r') || o == BUFF)
+ {
+ buf_c[o + 1] = '\0';
+ o = -1;
+
+ if (verb) printf(">> %s", buf_c);
+
+ if (!strncmp(buf_c, "PING", 4))
+ {
+ buf_c[1] = 'O';
+ raw(buf_c);
+ }
+
+ else if (buf_c[0] == ':')
+ {
+ sscanf(buf_c, ":%[^ ] %[^:]:%[^\r]", pre, cmd, msg);
+ sscanf(pre, "%[^!]!%[^@]@%s", nic, usr, hos);
+ sscanf(cmd, "%[^#& ]%s", ltr, cha);
+
+ if (!strncmp(ltr, "001", 3)) raw("JOIN #%s\r\n", chan);
+
+ if (!strncmp(ltr, "QUIT", 4))
+ {
+ printw("%*.*s \x1b[34;1m%s\x1b[0m left %s\n", \
+ GUTL, GUTL, "<--", nic, cha);
+ }
+ else if (!strncmp(ltr, "JOIN", 4))
+ {
+ printw("%*.*s \x1b[32;1m%s\x1b[0m joined %s\n", \
+ GUTL, GUTL, "-->", nic, cha);
+ }
+ else
+ {
+ printw("\x1b[1m%*.*s\x1b[0m %s\n", \
+ GUTL, GUTL, nic, msg);
+ }
+ }
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ extern char *optarg;
+ extern int optind, optopt;
+ int fd[2], cval;
+
+ while ((cval = getopt(argc, argv, "s:p:n:k:c:vV")) != -1)
+ {
+ switch (cval)
+ {
+ case 'v' : printf("kirc 0.0.1\n"); break;
+ case 'V' : verb = 1; break;
+ case 's' : host = optarg; break;
+ case 'p' : port = optarg; break;
+ case 'n' : nick = optarg; break;
+ case 'c' : chan = optarg; break;
+ }
+ }
+
+ if (pipe(fd) < 0)
+ {
+ fprintf(stderr, "pipe() failed");
+ exit(2);
+ }
+
+ pid_t pid = fork();
+
+ if (pid == 0)
+ {
+ int sl;
+ char u[BUFF];
+
+ con();
+
+ while ((sl = read(conn, sbuf, BUFF)))
+ {
+ pars(sl, sbuf);
+ if ((read(fd[0], u, CMAX) > 0) && !strstr(u, "WAIT_SIG"))
+ {
+ raw("%s\r\n", u);
+ }
+ }
+ printf("CONNECTION TERMINATED (press <ENTER> to quit)\n");
+ }
+ else
+ {
+ char usrin[CMAX];
+ char cmd = '\n';
+ char *cmd_val = malloc(sizeof(char) * (CMAX - 3));
+
+ while (waitpid(pid, NULL, WNOHANG) == 0)
+ {
+ while (!kbhit() && waitpid(pid, NULL, WNOHANG) == 0)
+ {
+ write(fd[1], "WAIT_SIG", CMAX);
+ }
+
+ fgets(usrin, CMAX, stdin);
+
+ if (usrin[0] == ':')
+ {
+ sscanf(usrin, ":%c %s", &cmd, cmd_val);
+
+ switch (cmd)
+ {
+ case 'q':
+ snprintf(usrin, CMAX, "quit");
+ break;
+ case 'm':
+ snprintf(usrin, CMAX, "privmsg %s %s", chan, cmd_val);
+ break;
+ }
+ }
+ write(fd[1], usrin, CMAX);
+ fflush(stdout);
+ }
+ }
+
+ return 0;
+}