#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" static const Style normal_style={9,9,false,false}; static const Style bold_style={9,9,true,false}; static const Style command_style={3,9,false,false}; static const Style server_style={6,9,false,false}; static const Style error_style={1,9,true,false}; static i64 uniqid(void){ static i64 id=1; return id++; } static ssize_t send_all(int socket,const void *buffer,i64 length){ ssize_t cursor=0; while(cursorai_family,res->ai_socktype,res->ai_protocol); if(sock!=-1){ if(connect(sock,res->ai_addr,res->ai_addrlen)==0){ break; } } close(sock); sock=-1; current=current->ai_next; } freeaddrinfo(res); return sock; } static bool termio_needs_reset=false; static void termio_reset(void){ if(!termio_needs_reset)return; endkeyboard(); endscreen(); termio_needs_reset=false; } static void termio_init(void){ initscreen(); initkeyboard(false); installCLhandler(true); atexit(termio_reset); termio_needs_reset=true; } static i64 bar_width=11; static char *user_buffer=NULL; static i64 user_bufsz=0; static i64 user_buflen=0; static int server_sock=-1; enum log_type{ LOG_MESSAGE, LOG_SERVER, LOG_COMMAND, LOG_ERROR, }; static void emit_log_line(enum log_type type,const char *head,const char *line){ i64 headlen=strlen(head); if(headlen+1>bar_width){ bar_width=headlen+1; } Size termsize=gettermsize(); assert(termsize.w>bar_width); i64 linelen=strlen(line); i64 nlines=1,cursor=termsize.w-bar_width; while(cursor "); setstyle(&normal_style); fillrect(2,termsize.h-1,termsize.w-2,1,' '); tprintf("%s",user_buffer); redraw(); } static char *server_user=NULL; static char *server_room=NULL; static void send_message(const char *msg){ if(server_user==NULL){ emit_log_line(LOG_ERROR,"--","Cannot send messages while not logged in"); return; } if(server_room==NULL){ emit_log_line(LOG_ERROR,"--","Cannot send messages while not in a room"); return; } } static void cmd_register(const char **args){ emit_log_line_f(LOG_COMMAND,"##","/register %s %s",args[0],args[1]); } static void cmd_login(const char **args){ emit_log_line_f(LOG_COMMAND,"##","Login called with %s and %s",args[0],args[1]); } static void cmd_quit(const char **args){ close(server_sock); exit(0); } struct cmd_list_item{ const char *name; int nargs; bool longlast; void (*func)(const char **args); }; static struct cmd_list_item cmd_list[]={ {"register",2,true,cmd_register}, {"login",2,true,cmd_login}, {"quit",0,false,cmd_quit}, }; #define NCOMMANDS ((i64)(sizeof(cmd_list)/sizeof(cmd_list[0]))) static void execute_command(char *line){ if(line[0]=='/'){ send_message(line); return; } i64 linelen=strlen(line); char *sepp=memchr(line,' ',linelen); if(sepp==NULL)sepp=line+linelen; *sepp='\0'; i64 cmdlen=sepp-line; i64 cmdi; for(cmdi=0;cmdilinelen){ emit_log_line_f(LOG_ERROR,"--","Too few parameters to command '%s'",line); return; } if(i==nargs-1&&cmd_list[cmdi].longlast){ sepp=line+linelen; } else { sepp=memchr(line+cursor,' ',linelen-cursor); if(sepp==NULL)sepp=line+linelen; } *sepp='\0'; args[i]=line+cursor; cursor=sepp-line+1; while(line[cursor]==' ')cursor++; } if(sepp-line0){ user_buflen--; user_buffer[user_buflen]='\0'; redraw_prompt(); } else { bel(); } break; case KEY_CTRL+'U': user_buflen=0; user_buffer[0]='\0'; redraw_prompt(); break; default: if(key>=32&&key<=126){ if(user_buflen==user_bufsz-1){ user_bufsz*=2; user_buffer=realloc(user_buffer,user_bufsz,char); } user_buffer[user_buflen++]=key; user_buffer[user_buflen]='\0'; redraw_prompt(); } else { bel(); } break; } return false; } // Returns whether socket was closed static bool handle_server(void){ static i64 bufsz=256,buflen=0; static char *buffer=NULL; if(buffer==NULL){ buffer=malloc(bufsz,char); } if(bufsz-buflen<128){ bufsz*=2; buffer=realloc(buffer,bufsz,char); } ssize_t nr; while(true){ nr=read(server_sock,buffer+buflen,bufsz-buflen); if(nr<0){ if(errno==EINTR)continue; if(errno==ECONNRESET){ buflen=0; return true; } perror("read"); exit(1); } if(nr==0){ buflen=0; return true; } buflen+=nr; break; } char *lfp=memchr(buffer,'\n',buflen); if(lfp==NULL)return false; *lfp='\0'; emit_log_line(LOG_SERVER,">>",buffer); i64 linelen=lfp-buffer; memmove(buffer,buffer+linelen+1,buflen-linelen-1); buflen-=linelen+1; return false; } int main(int argc,char **argv){ const char *hostname="tomsmeding.com"; int port=29536; if(argc>=2){ hostname=argv[1]; if(argc>=3){ port=strtol(argv[2],NULL,10); if(argc>=4){ fprintf(stderr,"Usage: %s [host [port]]\n",argv[0]); return 1; } } } server_sock=connect_server(hostname,port); if(server_sock==-1){ fprintf(stderr,"Could not connect to %s port %d\n",hostname,port); return 1; } termio_init(); clearscreen(); moveto(0,0); setstyle(&bold_style); tprintf("TOMSG CLIENT"); user_bufsz=256; user_buflen=0; user_buffer=malloc(user_bufsz,char); redraw_prompt(); while(true){ fd_set inset; FD_ZERO(&inset); FD_SET(STDIN_FILENO,&inset); FD_SET(server_sock,&inset); int ret=select(server_sock+1,&inset,NULL,NULL,NULL); if(ret==-1){ if(errno==EINTR)continue; perror("select"); return 1; } if(FD_ISSET(STDIN_FILENO,&inset)){ if(handle_stdin())break; } if(FD_ISSET(server_sock,&inset)){ if(handle_server())break; } } close(server_sock); termio_reset(); }