aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2023-09-21 16:49:11 +0000
committerrodri <rgl@antares-labs.eu>2023-09-21 16:49:11 +0000
commit497acdd8860d629ba24ed9976fd47eeb743b74ab (patch)
tree7e8db0be276527ccc0b3d173290655653c8cdd64
parent33de2d046acfc7ee42fb71b1804b091f671b098a (diff)
downloadbattleship-497acdd8860d629ba24ed9976fd47eeb743b74ab.tar.gz
battleship-497acdd8860d629ba24ed9976fd47eeb743b74ab.tar.bz2
battleship-497acdd8860d629ba24ed9976fd47eeb743b74ab.zip
add parsecmd to the server.
modified it to allow for commands with the same name but different nargs. now each match list entry is preceded by an 'm', to simplify its processing. note that this breaks the client. the next commit will fix that.
-rw-r--r--btsd.c71
-rw-r--r--dat.h23
-rw-r--r--fns.h7
-rw-r--r--mkfile1
-rw-r--r--parse.c96
-rw-r--r--util.c6
6 files changed, 182 insertions, 22 deletions
diff --git a/btsd.c b/btsd.c
index b2f9196..fd6b003 100644
--- a/btsd.c
+++ b/btsd.c
@@ -8,6 +8,25 @@
#include "dat.h"
#include "fns.h"
+enum {
+ CMid,
+ CMplay,
+ CMlayout,
+ CMshoot,
+ CMgetmatches,
+ CMwatch,
+ CMleave,
+};
+Cmdtab clcmd[] = {
+ CMid, "id", 2,
+ CMplay, "play", 1,
+ CMlayout, "layout", 2,
+ CMshoot, "shoot", 2,
+ CMgetmatches, "watch", 1,
+ CMwatch, "watch", 2,
+ CMleave, "leave", 1,
+};
+
int debug;
Channel *playerq;
@@ -195,8 +214,10 @@ playerproc(void *arg)
{
Player *my;
Match *m;
- char *s, *f[2];
- int nf, mid;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ char *s;
+ int mid;
my = arg;
@@ -220,27 +241,30 @@ playerproc(void *arg)
if(debug)
fprint(2, "[%d] rcvd '%s'\n", getpid(), s);
+ cb = parsecmd(s, strlen(s));
+ ct = lookupcmd(cb, clcmd, nelem(clcmd));
+ if(ct == nil)
+ goto Nocmd;
+
if(my->name[0] == 0){
- nf = tokenize(s, f, nelem(f));
- if(nf == 2 && strcmp(f[0], "id") == 0 && strlen(f[1]) > 0)
- snprint(my->name, sizeof my->name, "%s", f[1]);
+ if(ct->index == CMid && strlen(cb->f[1]) > 0)
+ snprint(my->name, sizeof my->name, "%s", cb->f[1]);
else
chanprint(my->io.out, "id\n");
}else
switch(my->state){
case Waiting0:
- nf = tokenize(s, f, nelem(f));
- if(nf == 1 && strcmp(f[0], "play") == 0)
+ if(ct->index == CMplay)
sendp(playerq, my);
- else if(nf == 1 && strcmp(f[0], "watch") == 0){
+ else if(ct->index == CMgetmatches){
rlock(&theaterlk);
chanprint(my->io.out, "matches\n");
for(m = theater.next; m != &theater; m = m->next)
- chanprint(my->io.out, "%d %s %s\n", m->id, m->pl[0]->name, m->pl[1]->name);
+ chanprint(my->io.out, "m %d %s %s\n", m->id, m->pl[0]->name, m->pl[1]->name);
chanprint(my->io.out, "end\n");
runlock(&theaterlk);
- }else if(nf == 2 && strcmp(f[0], "watch") == 0){
- mid = strtoul(f[1], nil, 10);
+ }else if(ct->index == CMwatch){
+ mid = strtoul(cb->f[1], nil, 10);
m = getmatch(mid);
if(m == nil)
chanprint(my->io.out, "no such match\n");
@@ -249,14 +273,15 @@ playerproc(void *arg)
}
break;
case Watching:
- nf = tokenize(s, f, nelem(f));
- if(nf == 1 && strcmp(f[0], "leave") == 0)
+ if(ct->index == CMleave)
sendp(my->battle->ctl, newmsg(my, estrdup("leave seat")));
break;
default:
if(my->battle != nil)
sendp(my->battle->data, newmsg(my, estrdup(s)));
}
+Nocmd:
+ free(cb);
free(s);
break;
case CTL:
@@ -288,11 +313,11 @@ battleproc(void *arg)
{
Msg *msg;
Match *m;
+ Cmdbuf *cb;
+ Cmdtab *ct;
Player *p, *op;
Stands stands;
- char *f[2];
uint n0;
- int nf;
Point2 cell;
char *coords[5];
@@ -324,12 +349,15 @@ battleproc(void *arg)
p = msg->from;
op = p == m->pl[0]? m->pl[1]: m->pl[0];
- nf = tokenize(msg->body, f, nelem(f));
+ cb = parsecmd(msg->body, strlen(msg->body));
+ ct = lookupcmd(cb, clcmd, nelem(clcmd));
+ if(ct == nil)
+ goto Nocmd;
switch(p->state){
case Outlaying:
- if(nf == 2 && strcmp(f[0], "layout") == 0)
- if(gettokens(f[1], coords, nelem(coords), ",") == nelem(coords)){
+ if(ct->index == CMlayout)
+ if(gettokens(cb->f[1], coords, nelem(coords), ",") == nelem(coords)){
if(debug)
fprint(2, "rcvd layout from %s @ %s\n", p->name, p->nci->raddr);
for(i = 0; i < nelem(coords); i++){
@@ -355,8 +383,8 @@ battleproc(void *arg)
}
break;
case Playing:
- if(nf == 2 && strcmp(f[0], "shoot") == 0){
- cell = coords2cell(f[1]);
+ if(ct->index == CMshoot){
+ cell = coords2cell(cb->f[1]);
switch(gettile(op, cell)){
case Tship:
settile(op, cell, Thit);
@@ -389,7 +417,8 @@ Swapturn:
}
break;
}
-
+Nocmd:
+ free(cb);
freemsg(msg);
break;
case CTL:
diff --git a/dat.h b/dat.h
index 7828287..dad541d 100644
--- a/dat.h
+++ b/dat.h
@@ -129,9 +129,30 @@ struct Menulist
char *title;
Rectangle r, sr; /* content and scroll rects */
int high; /* [-1,nitems) where -1 is none */
+ int off; /* entry offset ∈ [0, nitems-Maxvisitems] */
void (*add)(Menulist*, int, char*);
void (*clear)(Menulist*);
- void (*update)(Menulist*, Mousectl*);
+ int (*update)(Menulist*, Mousectl*, Channel*);
void (*draw)(Menulist*, Image*);
};
+
+/*
+ * Kernel-style command parser
+ */
+typedef struct Cmdbuf Cmdbuf;
+typedef struct Cmdtab Cmdtab;
+
+struct Cmdbuf
+{
+ char *buf;
+ char **f;
+ int nf;
+};
+
+struct Cmdtab
+{
+ int index; /* used by client to switch on result */
+ char *cmd; /* command name */
+ int narg; /* expected #args; 0 ==> variadic */
+};
diff --git a/fns.h b/fns.h
index 4e5656f..5940cb0 100644
--- a/fns.h
+++ b/fns.h
@@ -22,9 +22,16 @@ int shiplen(int);
char *shipname(int);
char *statename(int);
int max(int, int);
+int min(int, int);
/*
* menulist
*/
Menulist *newmenulist(int, char*);
void delmenulist(Menulist*);
+
+/*
+ * parse
+ */
+Cmdbuf *parsecmd(char*, int);
+Cmdtab *lookupcmd(Cmdbuf*, Cmdtab*, int);
diff --git a/mkfile b/mkfile
index 9fb9a57..d7bb633 100644
--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,7 @@ TARG=\
OFILES=\
alloc.$O\
+ parse.$O\
util.$O\
menulist.$O\
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..30671c4
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,96 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+#include "dat.h"
+#include "fns.h"
+
+
+/*
+ * Generous estimate of number of fields, including terminal nil pointer
+ */
+static int
+ncmdfield(char *p, int n)
+{
+ int white, nwhite;
+ char *ep;
+ int nf;
+
+ if(p == nil)
+ return 1;
+
+ nf = 0;
+ ep = p+n;
+ white = 1; /* first text will start field */
+ while(p < ep){
+ nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */
+ if(white && !nwhite) /* beginning of field */
+ nf++;
+ white = nwhite;
+ }
+ return nf+1; /* +1 for nil */
+}
+
+/*
+ * parse a command written to a device
+ */
+Cmdbuf *
+parsecmd(char *p, int n)
+{
+ Cmdbuf *cb;
+ int nf;
+ char *sp;
+
+ nf = ncmdfield(p, n);
+
+ /* allocate Cmdbuf plus string pointers plus copy of string including \0 */
+ sp = emalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1);
+ cb = (Cmdbuf*)sp;
+ cb->f = (char**)(&cb[1]);
+ cb->buf = (char*)(&cb->f[nf]);
+
+ memmove(cb->buf, p, n);
+
+ /* dump new line and null terminate */
+ if(n > 0 && cb->buf[n-1] == '\n')
+ n--;
+ cb->buf[n] = '\0';
+
+ cb->nf = tokenize(cb->buf, cb->f, nf-1);
+ cb->f[cb->nf] = nil;
+
+ return cb;
+}
+
+/*
+ * Look up entry in table
+ */
+Cmdtab *
+lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
+{
+ int i;
+ Cmdtab *ct;
+
+ if(cb->nf == 0){
+ werrstr("empty command");
+ return nil;
+ }
+
+ for(ct = ctab, i=0; i<nctab; i++, ct++){
+ if(strcmp(ct->cmd, cb->f[0]) != 0)
+ continue;
+ if(ct->narg != 0 && ct->narg != cb->nf){
+// werrstr("wrong #args in command");
+// return nil;
+ /* allow the same cmd name for different commands */
+ continue;
+ }
+ return ct;
+ }
+
+ werrstr("unknown command");
+ return nil;
+}
diff --git a/util.c b/util.c
index 325bd3e..cdd1d52 100644
--- a/util.c
+++ b/util.c
@@ -147,3 +147,9 @@ max(int a, int b)
{
return a > b? a: b;
}
+
+int
+min(int a, int b)
+{
+ return a < b? a: b;
+}