aboutsummaryrefslogtreecommitdiff
path: root/src/irc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc.c')
-rw-r--r--src/irc.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/irc.c b/src/irc.c
new file mode 100644
index 0000000..735ca2f
--- /dev/null
+++ b/src/irc.c
@@ -0,0 +1,306 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "irc.h"
+
+#ifdef __ATARI__
+#include <atari.h>
+#include <conio.h>
+#include "conio.h"
+#include "nio.h"
+#else
+#define CH_EOL '|'
+unsigned char rx_buf[MAX_IRC_MSG_LEN]; // RX buffer.
+unsigned short bw=0; // # of bytes waiting.
+#endif
+
+#define MAX_MSG 512
+
+char *msg_src, *msg_cmd, *msg_dest, *msg_text;
+char *msg_args[MAX_MSG_ARGS];
+int msg_argcount;
+
+static char msgbuf[MAX_MSG] = { 0 };
+static char *msg; /* with source removed */
+static int msgbuf_len = 0, msg_len = 0;
+
+static int joined = 0;
+
+#ifdef __ATARI__
+static void join_channel(void) {
+ ui_print("Joining channel...\n");
+ txbuf_set_str("JOIN ");
+ txbuf_append_str(channel);
+ txbuf_append_str("\n");
+ txbuf_send();
+ joined = 1;
+}
+
+static void do_pong(void) {
+ ui_putchar(CH_EOL);
+ ui_print("PING/PONG\n"); /* make hiding this a preference, or just ditch it */
+ txbuf_set_str("PONG ");
+ txbuf_append_str(msg_args[0]);
+ txbuf_send();
+}
+
+static void do_privmsg(void) {
+ static char chan;
+
+ chan = (*msg_dest == '#');
+
+ if(chan) {
+ ui_putchar('<');
+ } else {
+ ui_putchar('*');
+ }
+
+ ui_print(msg_src);
+
+ if(chan) {
+ ui_putchar('>');
+ } else {
+ ui_putchar('*');
+ }
+
+ ui_putchar(' ');
+ ui_print(msg_text);
+}
+
+static void do_catchall(void) {
+ int i;
+ if(msg_src) {
+ ui_print(msg_src);
+ ui_putchar(' ');
+ }
+ ui_print(msg_cmd);
+ for(i = 0; i < msg_argcount; i++) {
+ ui_putchar(' ');
+ ui_print(msg_args[i]);
+ }
+ if(msg_text) {
+ ui_putchar(' ');
+ ui_print(msg_text);
+ }
+}
+
+static void do_numeric(void) {
+ do_catchall();
+
+ /* RPL_ENDOFMOTD or RPL_NOMOTD */
+ if(!joined && (streq(msg_cmd, "372") || streq(msg_cmd, "422"))) {
+ join_channel();
+ }
+}
+
+static void invalid_msg(char type) {
+ ui_print("??? unknown, type ");
+ ui_putchar(type);
+ ui_putchar('\n');
+}
+#else
+static void do_pong(void) { }
+static void invalid_msg(char type) {
+ printf("??? unknown, type %c\n", type);
+}
+#endif
+
+/* msgbuf contains a complete message from the server, whose
+ length is msgbuf_len. the last character *must* be CH_EOL,
+ and the last argument ends with CH_EOL. */
+static void parse_msg(void) {
+ char *p;
+
+#ifndef __ATARI__
+ printf("\ngot message:\n");
+ for(msg = msgbuf; *msg != CH_EOL; msg++)
+ putchar(*msg);
+ putchar('\n');
+ putchar('\n');
+#endif
+
+ msg_cmd = msg_text = msg_src = msg_dest = 0;
+ msg = msgbuf;
+
+ /* ignore empty message */
+ if(*msg == CH_EOL) return;
+
+ /* if there's a final multiword arg... */
+ /* FIXME: channel names can have colons, which breaks this... */
+ p = strstr(msg + 1, " :"); /* +1 to skip leading colon in msg source */
+ if(p) {
+ msg_text = p + 2;
+ *p = 0;
+ }
+
+ /* first token is either the source (with a :) or a command (without) */
+ p = strtok(msg, " ");
+ if(!p) {
+ invalid_msg('1');
+ return;
+ }
+
+ if(*p == ':') {
+ msg_src = p; /* generally :irc.example.com or :nick!user@host */
+ msg_cmd = strtok(0, " ");
+ } else {
+ msg_src = 0; /* no source supplied */
+ msg_cmd = p;
+ }
+
+ if(!msg_cmd) {
+ invalid_msg('2');
+ return;
+ }
+
+ /* special case for ping, treat as 1 arg, even if it has space and no : */
+ if(streq_i(msg_cmd, "PING")) {
+ msg_argcount = 1;
+ msg_args[0] = msg_cmd + 6;
+ do_pong();
+ return;
+ } else {
+ for(msg_argcount = 0; msg_argcount < MAX_MSG_ARGS; msg_argcount++) {
+ p = strtok(0, " ");
+ if(p) {
+ msg_args[msg_argcount] = p;
+ } else {
+ break;
+ }
+ }
+ }
+ if(msg_argcount) msg_dest = msg_args[0];
+
+ if(msg_src) {
+ if((p = strstr(msg_src, "!"))) {
+ msg_src++;
+ *p = '\0';
+ } else {
+ msg_src = 0;
+ }
+ }
+
+#ifdef __ATARI__
+ OS.crsinh = 1;
+ ui_start_msg();
+ if(streq_i(msg_cmd, "PRIVMSG")) {
+ do_privmsg();
+ } else if(isdigit(msg_cmd[0])) {
+ do_numeric();
+ } else {
+ do_catchall();
+ }
+ ui_end_msg();
+#else
+ {
+ int i;
+ printf("src: %s\n", msg_src ? msg_src : "<none>");
+ printf("cmd: %s\n", msg_cmd ? msg_cmd : "<none>");
+ printf("args: %d\n", msg_argcount);
+ for(i = 0; i < msg_argcount; i++)
+ printf(" %d: %s\n", i, msg_args[i]);
+ printf("text: %s\n", msg_text ? msg_text : "<none>");
+ }
+#endif
+}
+
+static void irc_parse(void) {
+ int i;
+ char *p = rx_buf;
+
+#ifndef __ATARI__
+ printf("irc_parse() called, bw == %d\n", bw);
+#endif
+
+ for(i = 0; i < bw; i++) {
+ msgbuf[msgbuf_len] = *p;
+ if(*p == CH_EOL) {
+ msgbuf[msgbuf_len + 1] = '\0';
+ parse_msg();
+ msgbuf_len = 0;
+ } else {
+ msgbuf_len++;
+ }
+ p++;
+ }
+}
+
+#ifdef __ATARI__
+bool irc_read(void) {
+ if(!trip) return 1;
+
+ err = nstatus(url);
+
+ if(err == 136) {
+ ui_print("Disconnected, press any key...\n");
+ cgetc();
+ return 0;
+ } else if(err != 1) {
+ print_error(err);
+ return 0;
+ }
+
+ // Get # of bytes waiting, no more than size of rx_buf
+ bw = OS.dvstat[1] * 256 + OS.dvstat[0];
+
+ if(bw > sizeof(rx_buf))
+ bw = sizeof(rx_buf);
+
+ if(bw > 0) {
+ err = nread(url, rx_buf, bw);
+ if(err != 1) {
+ ui_print("READ ERROR: ");
+ print_error(err);
+ return 0;
+ }
+
+ trip = 0;
+ PIA.pactl |= 1; // Flag interrupt as serviced, ready for next one.
+
+ irc_parse();
+ }
+
+ return 1;
+}
+
+/* modern.ircdocs.horse say to do this IMMEDIATELY upon TCP
+ connection, without waiting for anything from the server. */
+void irc_register(void) {
+ txbuf_init();
+ txbuf_append_str("USER ");
+ txbuf_append_str(usernick); /* local (UNIX) username, just use the nick */
+ txbuf_append_str(" 0 * :FujiNetChat User\n"); /* "real" name (make it a pref?) */
+ txbuf_send();
+
+ txbuf_init();
+ txbuf_append_str("NICK ");
+ txbuf_append_str(usernick);
+ txbuf_append_str("\n");
+ txbuf_send();
+}
+
+/* only exits on error (e.g. connection closed, which might be via /QUIT). */
+void irc_loop(void) {
+ while(1) {
+ if(!irc_read()) return;
+
+ if(kbhit())
+ if(joined)
+ ui_keystroke();
+ else join_channel();
+ }
+}
+
+#else // !defined(__ATARI__)
+/* parsetest */
+int main(int argc, char **argv) {
+ strcpy((char *)rx_buf, argv[1]);
+ bw = strlen(rx_buf);
+ irc_parse();
+ /*
+ */
+ return 0;
+}
+#endif