aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
+}