#include #include #include #include #include "irc.h" #include "numerics.h" #ifdef __ATARI__ #include #include #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); } /* numerics call this with arg==1, since arg 0 is always our nick. other commands call with arg==0 to print everything. */ static void do_catchall(int arg) { if(msg_src) { ui_print(msg_src); ui_putchar(' '); } ui_print(msg_cmd); for(; arg < msg_argcount; arg++) { ui_putchar(' '); ui_print(msg_args[arg]); } if(msg_text) { ui_putchar(' '); ui_print(msg_text); } } static void do_numeric(void) { unsigned int num = atoi(msg_cmd); switch(num) { /* don't print these, but they do trigger an action */ case RPL_MOTDSTART: case RPL_ENDOFMOTD: if(!joined) join_channel(); break; case RPL_TOPIC: ui_print("Topic for "); ui_print(msg_args[1]); ui_print(": "); ui_print(msg_text); break; case RPL_TOPICWHOTIME: ui_print("Topic set by: "); ui_print(msg_args[1]); ui_putchar('\n'); break; default: do_catchall(1); break; } } 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(0); } ui_end_msg(); #else { int i; printf("src: %s\n", msg_src ? msg_src : ""); printf("cmd: %s\n", msg_cmd ? msg_cmd : ""); 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 : ""); } #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__ int 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