diff options
| author | B. Watson <urchlay@slackware.uk> | 2026-02-11 13:26:57 -0500 |
|---|---|---|
| committer | B. Watson <urchlay@slackware.uk> | 2026-02-11 13:26:57 -0500 |
| commit | b7b86e3a26d4bfba9c49f0a24ec030a7b3ebd8a3 (patch) | |
| tree | d2cb7323c78a4983e2a498110a077aaf24d1f696 /src/irc.c | |
| download | fujinet-chat-b7b86e3a26d4bfba9c49f0a24ec030a7b3ebd8a3.tar.gz | |
initial commit
Diffstat (limited to 'src/irc.c')
| -rw-r--r-- | src/irc.c | 306 |
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 |
