diff options
author | tomsmeding <tom.smeding@gmail.com> | 2017-01-18 22:16:55 +0100 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2017-01-18 22:16:55 +0100 |
commit | 6b260962eef4caa486ceee9a60b61c67269025c6 (patch) | |
tree | dacf3dfd87d440f998eab8e486996e12132aef01 |
Initial
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | global.c | 16 | ||||
-rw-r--r-- | global.h | 10 | ||||
-rw-r--r-- | main.c | 90 | ||||
-rw-r--r-- | memory.c | 11 | ||||
-rw-r--r-- | memory.h | 16 | ||||
-rw-r--r-- | showmenu.c | 53 | ||||
-rw-r--r-- | showmenu.h | 5 | ||||
-rw-r--r-- | tcp.c | 157 | ||||
-rw-r--r-- | tcp.h | 31 |
11 files changed, 418 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a62f14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.dSYM +regexbattle diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..563919e --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CC = gcc +CFLAGS = -Wall -Wextra -Wwrite-strings -Wconversion -Wno-sign-conversion \ + -std=c11 -g -fwrapv -I$(TERMIO_PREFIX)/include +LDFLAGS = -L$(TERMIO_PREFIX)/lib -ltermio + +TERMIO_PREFIX = $(HOME)/prefix + +BIN = regexbattle + +obj_files = $(patsubst %.c,%.o,$(wildcard *.c)) + +.PHONY: all clean remake + +all: $(BIN) + +clean: + rm -rf $(BIN) *.o *.dSYM + +remake: clean + make all + +$(BIN): $(obj_files) + $(CC) -o $@ $^ $(LDFLAGS) + +%.o: %.c *.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/global.c b/global.c new file mode 100644 index 0000000..27f522c --- /dev/null +++ b/global.c @@ -0,0 +1,16 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include "global.h" + +__attribute__((noreturn, format (printf, 1, 2))) +void throw(const char *format,...){ + char *buf; + va_list ap; + va_start(ap,format); + vasprintf(&buf,format,ap); + va_end(ap); + fprintf(stderr,"THROW: %s\n",buf); + exit(1); +} diff --git a/global.h b/global.h new file mode 100644 index 0000000..61d8cee --- /dev/null +++ b/global.h @@ -0,0 +1,10 @@ +#pragma once + +#include <stdint.h> +#include <inttypes.h> + +typedef int64_t i64; +typedef uint64_t u64; + + +void throw(const char *format,...) __attribute__((noreturn, format (printf, 1, 2))); @@ -0,0 +1,90 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <stdarg.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <termio.h> +#include "memory.h" +#include "showmenu.h" +#include "tcp.h" + +#define HOSTNAME "localhost" +#define PORT 1423 + +#define GAMENAME "regexbattle" + + +static char *die_message=NULL; + +__attribute__((noreturn, format (printf,1,2))) +static void die(const char *format,...){ + va_list ap; + va_start(ap,format); + char *buf; + int ret=vasprintf(&buf,format,ap); + va_end(ap); + if(ret<0){ + die_message=strdup("Double error roll"); + } else { + die_message=buf; + } + exit(1); +} + +static void exit_cleanup(void){ + endkeyboard(); + endscreen(); + if(die_message!=NULL){ + printf("%s\n",die_message); + } +} + +int main(void){ + int sock=tcp_connect(HOSTNAME,PORT); + if(sock==-1){ + printf("Could not connect to %s:%d\n",HOSTNAME,PORT); + return 1; + } + char *recvbuf=NULL; + i64 recvbufsz=0; + tcp_send_line(sock,"ping"); + tcp_read_line(sock,&recvbuf,&recvbufsz); + if(strcmp(recvbuf,"pong")!=0){ + printf("Protocol error; different server running on %s:%d?\n",HOSTNAME,PORT); + return 1; + } + + initscreen(); + initkeyboard(false); + installCLhandler(true); + atexit(exit_cleanup); + + clearscreen(); + + while(true){ + i64 sel=showmenu("REGEXBATTLE", + "List public rooms","Create a room","Join a room", + "Quit",NULL); + switch(sel){ + case 0:{ + tcp_send_line(sock,"room_list " GAMENAME); + TcpList *list=tcp_read_list(sock,"room_list"); + if(list==NULL)die("Protocol error: receiving list"); + Size termsize=gettermsize(); + fillrect(0,7,termsize.w,termsize.h-7,' '); + moveto(0,7); + for(i64 i=0;i<list->nitems&&i+7<termsize.h;i++){ + tprintf("Room: %s\n",list->items[i]); + } + break; + } + + case 1: break; + case 2: break; + case 3: return 0; + } + } +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..6f8253f --- /dev/null +++ b/memory.c @@ -0,0 +1,11 @@ +#include <stdio.h> +#include "memory.h" + + +void* check_after_allocation(const char *funcname, void *ptr){ + if(ptr == NULL){ + perror(funcname); + exit(1); + } + return ptr; +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..6fcb7e1 --- /dev/null +++ b/memory.h @@ -0,0 +1,16 @@ +#pragma once + +#include <stdlib.h> +#include <string.h> + + +#define malloc(num, type) \ + ((type*)check_after_allocation("malloc", malloc((num)*sizeof(type)))) +#define calloc(num, type) \ + ((type*)check_after_allocation("calloc", calloc((num), sizeof(type)))) +#define realloc(ptr, num, type) \ + ((type*)check_after_allocation("realloc", realloc(ptr, (num)*sizeof(type)))) +#define strdup(str) \ + ((char*)check_after_allocation("strdup", strdup(str))) + +void* check_after_allocation(const char *funcname, void *ptr); diff --git a/showmenu.c b/showmenu.c new file mode 100644 index 0000000..2e1bfbd --- /dev/null +++ b/showmenu.c @@ -0,0 +1,53 @@ +#include <stdarg.h> +#include <termio.h> +#include "memory.h" +#include "showmenu.h" + +static int menu_index; + +static void menu_callback(int index){ + menu_index=index; +} + +i64 showmenu(const char *title,...){ + va_list ap,ap2; + va_start(ap,title); + va_copy(ap2,ap); + i64 nargs=0; + while(va_arg(ap2,const char*)!=NULL)nargs++; + va_end(ap2); + + moveto(2,0); + tprintf("=== %s ===",title); + + Menudata menudata; + menudata.nitems=(int)nargs; + menudata.items=malloc(nargs,Menuitem); + for(i64 i=0;i<nargs;i++){ + menudata.items[i].text=strdup(va_arg(ap,const char*)); + menudata.items[i].hotkey='\0'; + menudata.items[i].func=menu_callback; + } + va_end(ap); + + menu_index=0; + + Menuwidget *mw=menu_make(2,2,&menudata); + while(true){ + redraw(); + int key=tgetkey(); + if(key<=0)return -1; + bool selected=false; + switch(menu_handlekey(mw,key)){ + case MENUKEY_IGNORED: bel(); break; + case MENUKEY_CALLED: selected=true; break; + case MENUKEY_HANDLED: + default: + break; + } + if(selected)break; + } + + menu_destroy(mw); + return menu_index; +}
\ No newline at end of file diff --git a/showmenu.h b/showmenu.h new file mode 100644 index 0000000..118151f --- /dev/null +++ b/showmenu.h @@ -0,0 +1,5 @@ +#pragma once + +#include "global.h" + +i64 showmenu(const char *title,...); @@ -0,0 +1,157 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <netdb.h> +#include <sys/socket.h> +#include <errno.h> +#include "global.h" +#include "memory.h" +#include "tcp.h" + +i64 tcp_read_line(int sock,char **buf,i64 *bufsz){ + if(*bufsz==0||*buf==NULL){ + *bufsz=512; + *buf=malloc(*bufsz,char); + } + i64 len=0; + while(true){ + if(len==*bufsz-1){ + *bufsz*=2; + *buf=realloc(*buf,*bufsz,char); + } + i64 ret=recv(sock,*buf+len,1,0); + if(ret<=0)return -1; + if((*buf)[len]=='\n'){ + (*buf)[len]='\0'; + return len; + } + len++; + } +} + +i64 sock_read_data(int sock,char *buf,i64 length){ + i64 got=0; + while(got<length){ + i64 ret=recv(sock,buf+got,length-got,0); + if(ret<=0)return -1; + got+=ret; + } + return 0; +} + +i64 tcp_send_data(int sock,const char *buf,i64 length){ + i64 sent=0; + while(sent<length){ + i64 ret=send(sock,buf+sent,length-sent,0); + if(ret==-1){ + if(errno==EINTR)continue; + return -1; + } + sent+=ret; + } + return 0; +} + +i64 tcp_send_str(int sock,const char *str){ + return tcp_send_data(sock,str,strlen(str)); +} + +i64 tcp_send_line(int sock,const char *str){ + if(tcp_send_str(sock,str)==-1)return -1; + return tcp_send_str(sock,"\n"); +} + +__attribute__((format (printf,2,3))) +i64 sock_send_line_f(int sock,const char *format,...){ + va_list ap; + va_start(ap,format); + char *buf; + vasprintf(&buf,format,ap); + va_end(ap); + if(buf==NULL)throw("vasprintf: allocation failure"); + i64 ret=tcp_send_line(sock,buf); + free(buf); + return ret; +} + + +i64 tcp_read_ok(int sock,const char *tag){ + char *buf=NULL; + i64 bufsz=0; + i64 ret=tcp_read_line(sock,&buf,&bufsz); + if(ret==-1){ + if(buf!=NULL)free(buf); + return -1; + } + + char *walker=buf; + char *ok=strsep(&walker," "); + char *realtag=strsep(&walker," "); + bool success=walker==NULL&&strcmp(ok,"ok")==0&&strcmp(realtag,tag)==0; + free(buf); + return success?0:-1; +} + +TcpList* tcp_read_list(int sock,const char *tag){ + char *buf=NULL; + i64 bufsz=0; + i64 ret=tcp_read_line(sock,&buf,&bufsz); + if(ret==-1){ + if(buf!=NULL)free(buf); + return NULL; + } + + char *walker=buf; + char *word1=strsep(&walker," "); + char *word2=strsep(&walker," "); + char *word3=strsep(&walker," "); + char *argstart=walker; + i64 nitems=0; + while((strsep(&walker," "))!=NULL)nitems++; + if(word1==NULL||word2==NULL||word3==NULL||strcmp(word1,"list")!=0||strcmp(word2,tag)!=0){ + free(buf); + return NULL; + } + char *endp; + i64 word3i=strtol(word3,&endp,10); + if(word3[0]=='\0'||*endp!='\0'||word3i!=nitems){ + free(buf); + return NULL; + } + + TcpList *list=malloc(1,TcpList); + list->nitems=nitems; + list->items=malloc(nitems,char*); + walker=argstart; + for(i64 i=0;i<nitems;i++){ + list->items[i]=strdup(strsep(&walker," ")); + } + + return list; +} + + +static const char* itoa(int n){ + static char buf[64]; + sprintf(buf,"%d",n); + return buf; +} + + +int tcp_connect(const char *hostname,int port){ + struct addrinfo hints,*res; + memset(&hints,0,sizeof(hints)); + hints.ai_family=AF_UNSPEC; + hints.ai_socktype=SOCK_STREAM; + int ret=getaddrinfo(hostname,itoa(port),&hints,&res); + if(ret!=0){ + return -1; + } + + int sock=socket(res->ai_family,res->ai_socktype,res->ai_protocol); + if(sock==-1)return -1; + if(connect(sock,res->ai_addr,res->ai_addrlen)==-1)return -1; + return sock; +} @@ -0,0 +1,31 @@ +#pragma once + +#include "global.h" + +//Unless specified otherwise, functions return -1 on error or socket closure. + +//If *bufsz==0 || *buf==NULL, allocates buf newly. +//Returns line length; reallocates buf if needed. +i64 tcp_read_line(int sock,char **buf,i64 *bufsz); + +i64 tcp_read_data(int sock,char *buf,i64 length); + +i64 tcp_send_data(int sock,const char *buf,i64 length); +i64 tcp_send_str(int sock,const char *str); +i64 tcp_send_line(int sock,const char *str); +i64 tcp_send_line_f(int sock,const char *format,...) __attribute__((format (printf,2,3))); + + +i64 tcp_read_ok(int sock,const char *tag); + +typedef struct TcpList{ + i64 nitems; + char **items; +} TcpList; + +//Returns NULL on error instead of -1. +TcpList* tcp_read_list(int sock,const char *tag); + + +//Returns the socket. +int tcp_connect(const char *hostname,int port); |