summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2017-01-18 22:16:55 +0100
committertomsmeding <tom.smeding@gmail.com>2017-01-18 22:16:55 +0100
commit6b260962eef4caa486ceee9a60b61c67269025c6 (patch)
treedacf3dfd87d440f998eab8e486996e12132aef01
Initial
-rw-r--r--.gitignore3
-rw-r--r--Makefile26
-rw-r--r--global.c16
-rw-r--r--global.h10
-rw-r--r--main.c90
-rw-r--r--memory.c11
-rw-r--r--memory.h16
-rw-r--r--showmenu.c53
-rw-r--r--showmenu.h5
-rw-r--r--tcp.c157
-rw-r--r--tcp.h31
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)));
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..1dcdc1f
--- /dev/null
+++ b/main.c
@@ -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,...);
diff --git a/tcp.c b/tcp.c
new file mode 100644
index 0000000..a254b99
--- /dev/null
+++ b/tcp.c
@@ -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;
+}
diff --git a/tcp.h b/tcp.h
new file mode 100644
index 0000000..a09732a
--- /dev/null
+++ b/tcp.h
@@ -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);