#include #include #include #include #include "irc.h" #include "screen.h" #include "edbox.h" #include "numerics.h" #ifdef __ATARI__ #include #include #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) { scr_create(channel, 1); txbuf_set_str("JOIN "); txbuf_append_str(channel); txbuf_append_str("\n"); txbuf_send(); joined = 1; } static void do_pong(void) { scr_print(SCR_SERVER, "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 char do_chantext(void) { char s; s = scr_getbyname(msg_dest); scr_putc(s, '<'); scr_print(s, msg_src); if(!s) { /* if we don't have a window for it */ scr_putc(s, '/'); scr_print(s, msg_dest); } scr_putc(s, '>'); return s; } static char do_private(void) { char s; scr_putc(SCR_PRIV, '*'); scr_print(SCR_PRIV, msg_src); scr_putc(SCR_PRIV, '*'); return s; } static void do_privmsg(void) { char s; if(*msg_dest == '#') s = do_chantext(); else s = do_private(); scr_print(s, msg_text); /* text ends with an EOL, don't print another */ } /* 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) { scr_print(SCR_SERVER, msg_src); scr_putc(SCR_SERVER, ' '); } scr_print(SCR_SERVER, msg_cmd); for(; arg < msg_argcount; arg++) { scr_putc(SCR_SERVER, ' '); scr_print(SCR_SERVER, msg_args[arg]); } if(msg_text) { scr_putc(SCR_SERVER, ' '); scr_print(SCR_SERVER, msg_text); } } static void do_numeric(void) { char s; 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: s = scr_getbyname(msg_args[1]); scr_print(s, "Topic for "); scr_print(s, msg_args[1]); scr_print(s, ": "); scr_print(s, msg_text); break; case RPL_TOPICWHOTIME: s = scr_getbyname(msg_args[1]); scr_print(s, "Topic set by: "); scr_print(s, msg_args[1]); scr_putc(s, '\n'); break; default: do_catchall(1); break; } } static void invalid_msg(char type) { scr_print(SCR_SERVER, "??? unknown, type "); scr_putc(SCR_SERVER, type); scr_putc(SCR_SERVER, '\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++; } } /* TODO: there needs to be a scr_printnum() */ void print_errnum(void) { extern unsigned char err; char tmp[10]; scr_print_current("Error #"); itoa(err, tmp, 10); scr_print_current(tmp); scr_print_current(", press any key...\n"); } #ifdef __ATARI__ int irc_read(void) { if(!trip) return 1; err = nstatus(url); if(err == 136) { scr_print_current("Disconnected, press any key...\n"); cgetc(); return 0; } else if(err != 1) { print_errnum(); 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) { scr_print_current("READ ERROR: "); print_errnum(); 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(); } static void start_keystroke(void) { char i, s; i = cgetc(); if(i >= '1' && i <= '7') { s = i - '1'; if(scr_status[s] != SCR_UNUSED) scr_display(s); } } static void keystroke(void) { if(GTIA_READ.consol == 6) { /* start pressed */ start_keystroke(); } else { edbox_keystroke(); } } /* only exits on error (e.g. connection closed, which might be via /QUIT). */ void irc_loop(void) { while(1) { if(!irc_read()) return; if(OS.ch != 0xff) if(joined) 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