aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/kirc.pngbin0 -> 442070 bytes
-rw-r--r--README100
-rw-r--r--README.md89
-rw-r--r--kirc.c99
4 files changed, 137 insertions, 151 deletions
diff --git a/.github/kirc.png b/.github/kirc.png
new file mode 100644
index 0000000..52a6075
--- /dev/null
+++ b/.github/kirc.png
Binary files differ
diff --git a/README b/README
deleted file mode 100644
index 0d35f4f..0000000
--- a/README
+++ /dev/null
@@ -1,100 +0,0 @@
-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 application that has no dependencies other than a C99
-compiler.
-
-
-OBJECTIVES
-----------
-
-- Per the UNIX philosophy, "Do one thing and do it well" [3].
-- True to the KISS principle [4], the code-base is intentionally smaller
- than most IRC clients (<250 sloc). The benefits of a small code base
- reflects in the ability for kirc to be reviewed, understood and
- maintained by a single person. Therefore, all feature requests and
- commits shall be considered with respect to the readability and
- maintainance of the overall project.
-- Let's not "reinvent the wheel". Commands and functionality should
- feel familiar (e.g. vi command shortcuts) and accessable via a
- standard 104-key US QWERTY keyboard layout [5]. Where possible, the
- number of keystrokes shall be minimized per command.
-
-
-FEATURES
---------
-
-- automatic host PING response.
-- vi-like shortcuts:
-
- <message> send a message to the current channel
- /m <nick|channel> <message> send a message to a specified nick or channel
- /n <message> send a message to NickServ
- /j <channel> join a specified channel
- /p <channel> leave (part) a specified channel
- /Q <message> send a message and close the host connection
- /q close the host connection
-
-- automatic word wrapping using the greedy algorithm.
-- color scheme definition via ANSI 8-bit colors [1]. Therefore, one could
- theoretically achieve uniform color definition across all shell applications
- and tools.
-
-
-INSTALLATION
-------------
-
-Building and installing on KISS Linux using the Community repository [2]:
-
- kiss b kirc
- kiss i kirc
-
-Building and installing on Arch Linux using the AUR [6]:
-
- git clone https://aur.archlinux.org/kirc-git.git
- cd kirc
- makepkg -si
-
-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] [-r real name]
-[-u username] [-k password] [-w columns] [-W columns] [-o path] [-v|V]
--s server address (default: 'irc.freenode.org')
--p server port (default: '6667')
--c channel name (default: '#kisslinux')
--n nickname (required)
--u server username (optional)
--k server password (optional)
--r real name (optional)
--v version information
--V verbose output (e.g. raw stream)
--o output path to log irc stream
--w maximum width of the printed left column (default: '10')
--W maximum width of the entire printed stream (default '80')
-
-
-REFERENCES
-----------
-
-[0] https://tools.ietf.org/html/rfc2812
-[1] https://en.wikipedia.org/wiki/ANSI_escape_code
-[2] https://github.com/kisslinux/community
-[3] https://en.wikipedia.org/wiki/Unix_philosophy
-[4] https://en.wikipedia.org/wiki/KISS_principle
-[5] https://en.wikipedia.org/wiki/Keyboard_layout
-[6] https://aur.archlinux.org/packages/kirc-git/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1e6d703
--- /dev/null
+++ b/README.md
@@ -0,0 +1,89 @@
+<h3 align="center"><img src="https://raw.githubusercontent.com/mcpcpc/kirc/master/.github/kirc.png" alt="logo" height="170px"></h3>
+<p align="center">KISS for IRC, a tiny IRC client written in POSIX C99.</p>
+<p align="center">
+<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
+<a href="https://github.com/mcpcpc/kirc/releases"><img src="https://img.shields.io/github/v/release/mcpcpc/kirc.svg"></a>
+<a href="https://repology.org/metapackage/kirc"><img src="https://repology.org/badge/tiny-repos/kirc.svg" alt="Packaging status"></a>
+</p>
+
+
+## Objectives
+
+*"Do one thing and do it well"* — Per the [Unix philosophy](https://en.wikipedia.org/wiki/Unix_philosophy), emphasis was placed on building simple, short, clear, modular, and extensible code that can be easily maintained and repurposed.
+
+*Portability* — [POSIX](https://en.wikipedia.org/wiki/POSIX) compliance ensures seamless compatibility and interoperability between variants of Unix and other operating systems.
+
+*Usability* — Commands and shortcuts should feel natural and accessible using a [standard 104-key US keyboard layout](https://en.wikipedia.org/wiki/Keyboard_layout). Where possible, the number of keystrokes shall be minimized.
+
+Usage
+-----
+
+```shell
+usage: kirc [-s hostname] [-p port] [-c channel] [-n nick] [-r real name] [-u username] [-k password] [-w columns] [-W columns] [-o path] [-v|V]
+-s server address (default: 'irc.freenode.org')
+-p server port (default: '6667')
+-c channel name (default: '#kisslinux')
+-n nickname (required)
+-u server username (optional)
+-k server password (optional)
+-r real name (optional)
+-v version information
+-V verbose output (e.g. raw stream)
+-o output path to log irc stream
+-w maximum width of the printed left column (default: '10')
+-W maximum width of the entire printed stream (default '80')
+```
+
+Features
+--------
+
+- No dependencies other than a [C99 compiler](https://gcc.gnu.org/).
+- Automatic server *PING* response.
+- Complies with [RFC 2812](https://tools.ietf.org/html/rfc2812) standard.
+- vi-like command shortcuts:
+
+```shell
+<message> Send a message to the current channel.
+/m <nick|channel> <message> Send a message to a specified channel or nick.
+/M <message> Send a message to NickServ.
+/Q <message> Send a message and close the host connection.
+/x <message> Send a message directly to the server.
+/j <channel> Join a specified channel.
+/p <channel> Leave (part) a specified channel.
+/n List all users on the current channel.
+/q Close the host connection.
+```
+
+- Color scheme definition via [ANSI 8-bit colors](https://en.wikipedia.org/wiki/ANSI_escape_code). Therefore, one could theoretically achieve uniform color definition across all shell applications and tools.
+
+Screenshots
+-----------
+
+![Screenshot 1](/.github/example.png)
+
+Installation
+------------
+
+Building and installing on **KISS Linux** using the Community repository:
+
+```shell
+kiss b kirc
+kiss i kirc
+```
+
+Building and installing on **Arch Linux** or **Manjaro** using the Arch AUR:
+
+```shell
+git clone https://aur.archlinux.org/kirc-git.git
+cd kirc
+makepkg -si
+```
+
+Building and installing from source (works on **Rasbian**, **Debian**, **Ubuntu** and many other distributions):
+
+```shell
+git clone https://github.com/mcpcpc/kirc.git
+cd kirc
+make
+make install
+```
diff --git a/kirc.c b/kirc.c
index 3e03cef..fe3279d 100644
--- a/kirc.c
+++ b/kirc.c
@@ -16,9 +16,9 @@
#define CHA_MAX 200 /* gauranteed max channel length */
static int conn; /* connection socket */
-static size_t verb = 0; /* verbose output (e.g. raw stream) */
-static size_t cmax = 80; /* max number of chars per line */
-static size_t gutl = 10; /* max char width of left column */
+static size_t verb = 0; /* verbose output (e.g. raw stream) */
+static size_t cmax = 80; /* max number of chars per line */
+static size_t gutl = 10; /* max char width of left column */
static char * host = "irc.freenode.org"; /* irc host address */
static char * chan = "kisslinux"; /* channel */
static char * port = "6667"; /* server port */
@@ -77,13 +77,6 @@ irc_init() {
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 (user && real) raw("USER %s - - :%s\r\n", user, real);
- if (user && !real && nick) raw("USER %s - - :%s\r\n", user, nick);
- if (!user && !real && nick) raw("USER %s - - :%s\r\n", nick, nick);
- if (pass) raw("PASS %s\r\n", pass);
-
fcntl(conn, F_SETFL, O_NONBLOCK);
}
@@ -100,47 +93,45 @@ printw(const char *format, ...) {
if (olog) printa(line);
- for (i = 0; isspace(line[i]); i++) printf("%c", line[i]);
+ for (i = 0; isspace(line[i]); i++) putchar(line[i]);
spaceleft = cmax + gutl - (i - 1);
for(tok = strtok(&line[i], " "); tok != NULL; tok = strtok(NULL, " ")) {
wordwidth = strlen(tok);
if ((wordwidth + spacewidth) > spaceleft) {
- printf("\n%*.s%s", (int) gutl + 2, "", tok);
- spaceleft = cmax - (gutl + 2 + wordwidth);
+ printf("\n%*.s%s ", (int) gutl + 1, "", tok);
+ spaceleft = cmax - (gutl + 1 + wordwidth);
} else {
- printf(" %s", tok);
+ printf("%s ", tok);
spaceleft = spaceleft - (wordwidth + spacewidth);
}
}
+ printf("\n");
}
static void
-raw_parser(char *in) {
- if (verb) printf(">> %s\n", in);
- if (!strncmp(in, "PING", 4)) {
- in[1] = 'O';
- raw("%s\r\n", in);
- } else if (in[0] == ':') {
- char *prefix = strtok(in, " ") + 1;
- char *suffix = strtok(NULL, ":");
- char *message = strtok(NULL, "\r");
- char *nickname = strtok(prefix, "!"); /* , *usr = strtok(NULL, "@"), *hos = prefix; */
- char *command = strtok(suffix, "#& "), *channel = strtok(NULL, " ");
+raw_parser(char *usrin) {
+ if (verb) printf(">> %s\n", usrin);
+ if (!strncmp(usrin, "PING", 4)) {
+ usrin[1] = 'O';
+ raw("%s\r\n", usrin);
+ } else if (usrin[0] == ':') {
+ char *prefix = strtok(usrin, " ") + 1, *suffix = strtok(NULL, ":"),
+ *message = strtok(NULL, "\r"), *nickname = strtok(prefix, "!"),
+ *command = strtok(suffix, "#& "), *channel = strtok(NULL, " ");
if (!strncmp(command, "001", 3)) raw("JOIN #%s\r\n", chan);
else if (!strncmp(command, "QUIT", 4)) {
- printw("%*s \x1b[34;1m%s\x1b[0m\n", gutl, "<--", nickname);
+ printw("%*s \x1b[34;1m%s\x1b[0m", (int)gutl, "<--", nickname);
} else if (!strncmp(command, "JOIN", 4)) {
- printw("%*s \x1b[32;1m%s\x1b[0m\n", gutl, "-->", nickname);
- } else if (!strncmp(command, "PRIVMSG", 7) && !strncmp(channel, nick, strlen(nick))) {
- size_t len = strlen(nickname);
- printw("%*s\x1b[43;1m%-.*s\x1b[0m %s\n", \
- gutl-(len <= gutl ? len : gutl), "", gutl, nickname, message);
+ printw("%*s \x1b[32;1m%s\x1b[0m", (int)gutl, "-->", nickname);
+ } else if (!strncmp(command, "PRIVMSG", 7) &&
+ !strncmp(channel, nick, strlen(nick))) {
+ int s = gutl - (strlen(nickname) <= gutl ? strlen(nickname) : gutl);
+ printw("%*s\x1b[43;1m%-.*s\x1b[0m %s", s, "", (int)gutl, nickname, message);
} else {
- size_t len = strlen(nickname);
- printw("%*s\x1b[33;1m%-.*s\x1b[0m %s\n", \
- gutl-(len <= gutl ? len : gutl), "", gutl, nickname, message);
+ int s = gutl - (strlen(nickname) <= gutl ? strlen(nickname) : gutl);
+ printw("%*s\x1b[33;1m%-.*s\x1b[0m %s", s, "", (int)gutl, nickname, message);
}
}
}
@@ -168,7 +159,7 @@ main(int argc, char **argv) {
}
}
- if (nick == NULL) {
+ if (!nick) {
fprintf(stderr, "nick not specified\n");
return 1;
}
@@ -186,6 +177,12 @@ main(int argc, char **argv) {
irc_init();
+ if (nick) raw("NICK %s\r\n", nick);
+ if (user && real) raw("USER %s - - :%s\r\n", user, real);
+ if (user && !real && nick) raw("USER %s - - :%s\r\n", user, nick);
+ if (!user && !real && nick) raw("USER %s - - :%s\r\n", nick, nick);
+ if (pass) raw("PASS %s\r\n", pass);
+
while ((sl = read(conn, &s, 1))) {
if (sl > 0) b[o] = s;
@@ -204,33 +201,33 @@ main(int argc, char **argv) {
else {
char usrin[MSG_MAX], v1[MSG_MAX - CHA_MAX], v2[CHA_MAX], c1;
struct termios tp, save;
+
tcgetattr(STDIN_FILENO, &tp);
save = tp;
tp.c_cc[VERASE] = 127;
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tp) < 0) return 2;
while (waitpid(pid, NULL, WNOHANG) == 0) {
if (!kbhit()) dprintf(fd[1], "/\n");
- else {
- tcsetattr(STDIN_FILENO, TCSANOW, &tp);
- if (fgets(usrin, MSG_MAX, stdin) == NULL) return 1;
- tcsetattr(STDIN_FILENO, TCSANOW, &save);
-
- if (sscanf(usrin, "/%[m] %s %[^\n]\n", &c1, v2, v1) == 3 ||
- sscanf(usrin, "/%[Qnjp] %[^\n]\n", &c1, v1) == 2 ||
- sscanf(usrin, "/%[q]\n", &c1) == 1) {
+ else if (fgets(usrin, MSG_MAX, stdin) != NULL) {
+ if (sscanf(usrin, "/%[m] %s %[^\n]\n", &c1, v2, v1) > 2 ||
+ sscanf(usrin, "/%[xMQqnjp] %[^\n]\n", &c1, v1) > 0) {
switch (c1) {
- case 'q': dprintf(fd[1], "quit\n"); break;
- case 'Q': dprintf(fd[1], "quit %s\n", v1); break;
- case 'j': dprintf(fd[1], "join %s\n", v1); break;
- case 'p': dprintf(fd[1], "part %s\n", v1); break;
- case 'n': dprintf(fd[1], "privmsg nickserv :%s\n", v1); break;
- case 'm': dprintf(fd[1], "privmsg %s :%s\n", v2, v1); break;
+ case 'x': dprintf(fd[1], "%s\n", v1); break;
+ case 'q': dprintf(fd[1], "quit\n"); break;
+ case 'Q': dprintf(fd[1], "quit %s\n", v1); break;
+ case 'j': dprintf(fd[1], "join %s\n", v1); break;
+ case 'p': dprintf(fd[1], "part %s\n", v1); break;
+ case 'n': dprintf(fd[1], "names #%s\n", chan); break;
+ case 'M': dprintf(fd[1], "privmsg nickserv :%s\n", v1); break;
+ case 'm': dprintf(fd[1], "privmsg %s :%s\n", v2, v1); break;
}
- } else dprintf(fd[1], "privmsg #%s :%s", chan, usrin);
+ } else dprintf(fd[1], "privmsg #%s :%s", chan, usrin);
}
}
- fprintf(stderr, "<< irc server connection closed\n");
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &save) < 0) return 2;
+ puts("<< connection closed");
}
return 0;
}