#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include <draw.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "trafficfs.h"
/*
* we generate a three-level directory structure:
* root/
* trafficfs (our own root)
* ctl (toc of generated files; generate files)
* toc (table of contents of nr files)
* 21/ (group of levels with same minimal solution move count)
* 0 (first level in this group)
* 1 (second level in this group)
* ...
* 22/
* 0 (first level in this group)
* 1 (second level in this group)
* ...
* ...
* foo (generated file)
* bar (generated file)
*/
typedef struct Aux Aux;
struct Aux {
int doff;
int gnr;
int gidx;
int lnr;
};
enum {
Qroot = 0,
Qdir,
Qctl,
Qtoc,
Qgen,
Qgrp,
Qlvl,
};
#define PATH(qid,grp,lvl) (((lvl)<<16)|(((grp)&0xFF)<<8)|((qid)&0xFF))
#define QDIR(qid) ((int)((qid)&0xFF))
#define GRP(qid) (((qid)>>8) &0xFF)
#define LVL(qid) ((qid)>>16)
int debug;
ulong starttime;
/* representation of levels */
Directory *directory;
/* stuff used to on-the-fly generate the combined level files */
int *choicelist; /* array from which we choose problems */
int maxchoicelist; /* max nr of entries in choicelist (without subtracting seen levels) */
Seen *dirseen; /* keeps track of levels that user has seen */
Generated *generated; /* on-the-fly generated files of levels */
int ngenerated; /* number of items in generated */
void printlevel(char *s, Level *l);
void chooseproblems(Generated *g);
void*
emalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
abort();
sysfatal("malloc %lud fails\n", sz);
}
setmalloctag(v, getcallerpc(&sz));
memset(v, 0, sz);
return v;
}
void*
erealloc(void *p, ulong sz)
{
void *v;
v = realloc(p, sz);
if(v == nil)
sysfatal("realloc %lud fails\n", sz);
setrealloctag(v, getcallerpc(&p));
return v;
}
static Point
lin2grouplvl(int n)
{
Group *gp;
Level *lp;
lp = directory->level + n;
for (gp=directory->group; gp<directory->group+directory->ngroup; gp++) {
if (lp >= gp->base && lp < gp->beyond)
return Pt(gp->moves, lp-gp->base);
}
return Pt(0,0);
}
int
index(int n)
{
if (n < directory->base)
return -1;
if (n > directory->beyond)
return -1;
return n - directory->base;
}
int
isseen(uchar *seen, int n)
{
int i, r;
i = n / 8;
r = n % 8;
return (seen[i] & 1<<r) == (1<<r);
}
void
setseen(uchar *seen, int n)
{
int i, r;
i = n / 8;
r = n % 8;
seen[i] |= 1<<r;
}
void
unsetseen(uchar *seen, int n)
{
int i, r;
i = n / 8;
r = n % 8;
seen[i] &= ~(1<<r);
if(debug) fprint(2, "unsetseen %d i=%d r=%d\n", n, i, r);
}
void
dirsetseen(Seen *s, int n)
{
Point p;
int i;
p = lin2grouplvl(n);
i = index(p.x);
if (debug) fprint(2, "setseenlin %d -> %d (%d), %d\n", n, p.x, i, p.y);
if (!isseen(s->seen, n)) {
setseen(s->seen, n);
s->group[i].nseen++;
}
}
void
resetseen(Seen *s, int base, int beyond)
{
int baselvl, beyondlvl, count, nextlvl, i;
baselvl = directory->group[base - directory->base].base - directory->level;
beyondlvl = directory->group[beyond - directory->base - 1].beyond - directory->level;
if (debug) fprint(2, "resetseen base %d beyond %d baselvl %d beyondlvl %d\n", base, beyond, baselvl, beyondlvl);
/* first bits in byte containing also bits for previous level */
for(i=baselvl; i<beyondlvl && i%8 != 0; i++) {
unsetseen(s->seen, i);
if (debug) fprint(2, "resetseen %d beyondlvl=%d\n", i, beyondlvl);
}
nextlvl = i;
count = beyondlvl - nextlvl;
/* complete bytes containing only bits for this level */
if (count/8 > 0) {
if (debug) fprint(2, "resetseen off %d count %d\n", nextlvl/8, count/8);
memset(s->seen+(nextlvl/8), 0, count/8);
nextlvl += (count/8)*8;
}
/* last bits in byte containing also bits for next level */
for(i=nextlvl; i<beyondlvl; i++) {
unsetseen(s->seen, i);
if (debug) fprint(2, "resetseen %d nextlvl=%d beyondlvl=%d\n", i, nextlvl, beyondlvl);
}
for (i=base - directory->base; i < beyond - directory->base; i++)
s->group[i].nseen = 0;
}
static void
fsattach(Req *r)
{
r->fid->qid = (Qid){Qroot, 0, QTDIR};
r->ofcall.qid = r->fid->qid;
r->fid->aux = emalloc(sizeof(Aux));
respond(r, nil);
}
static char*
fsclone(Fid *old, Fid *new)
{
Aux *na;
na = emalloc(sizeof(Aux));
*na = *((Aux*)old->aux);
new->aux = na;
return nil;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
int i, n;
char *p;
Aux *a;
a = fid->aux;
switch(QDIR(fid->qid.path)) {
case Qroot:
if(strcmp(name, "..") == 0) {
*qid = (Qid){Qroot, 0, QTDIR};
return nil;
}
if(strcmp(name, "trafficfs") == 0) {
*qid = (Qid){Qdir, 0, QTDIR};
return nil;
}
return "file not found";
case Qdir:
if(strcmp(name, "..") == 0) {
*qid = (Qid){Qroot, 0, QTDIR};
return nil;
}
if(strcmp(name, "toc") == 0) {
*qid = (Qid){Qtoc, 0, QTFILE};
return nil;
}
if(strcmp(name, "ctl") == 0) {
*qid = (Qid){Qctl, 0, QTFILE};
return nil;
}
/* group directories */
n = strtoul(name, &p, 0);
if('0'<=name[0] && name[0]<='9' && *p=='\0'){
for(i=0; i<directory->ngroup; i++)
if(directory->group[i].moves == n) {
*qid = (Qid){PATH(Qgrp,i,0), 0, QTDIR};
a->gnr = n;
a->gidx = i;
return nil;
}
}
/* generated files */
for (i=0; i < ngenerated; i++) {
if(strcmp(generated[i].name, name) ==0) {
*qid = (Qid){PATH(Qgen,i,0), 0, QTFILE};
return nil;
}
}
return "file not found";
case Qgrp:
if(strcmp(name, "..") == 0) {
*qid = (Qid){Qdir, 0, QTDIR};
return nil;
}
/* level files */
n = strtoul(name, &p, 0);
if('0'<=name[0] && name[0]<='9' && *p=='\0'){
if (n >= 0 && n < directory->group[a->gidx].count) {
*qid = (Qid){PATH(Qlvl,a->gidx,n), 0, QTFILE};
a->lnr = n;
return nil;
}
}
return "file not found";
default: /* bug: lib9p could handle this */
return "walk in non-directory";
}
}
static void
fsopen(Req *r)
{
int omode;
omode = r->ifcall.mode;
if(omode == OREAD)
respond(r, nil);
else if ((omode&~OTRUNC) == OWRITE && QDIR(r->fid->qid.path) == Qctl)
respond(r, nil);
else
respond(r, "permission denied");
}
int
fillstat(ulong qid, Dir *d)
{
char buf[32];
memset(d, 0, sizeof(Dir));
d->uid = estrdup9p("rushhour");
d->gid = estrdup9p("rushhour");
d->muid = estrdup9p("");
d->qid = (Qid){qid, 0, 0};
d->atime = d->mtime = starttime;
switch(QDIR(qid)){
case Qroot:
d->name = estrdup9p("/");
d->qid.type = QTDIR;
d->mode = DMDIR|0555;
break;
case Qdir:
d->name = estrdup9p("trafficfs");
d->qid.type = QTDIR;
d->mode = DMDIR|0555;
break;
case Qctl:
d->name = estrdup9p("ctl");
d->qid.type = QTFILE;
d->mode = 0666;
break;
case Qtoc:
d->name = estrdup9p("toc");
d->qid.type = QTFILE;
d->mode = 0444;
break;
case Qgen:
d->name = estrdup9p(generated[GRP(qid)].name);
d->qid.type = QTFILE;
d->mode = 0444;
d->mtime = generated[GRP(qid)].mtime;
d->length = generated[GRP(qid)].ndata;
break;
case Qgrp:
sprint(buf, "%d", directory->group[GRP(qid)].moves);
d->name = estrdup9p(buf);
d->qid.type = QTDIR;
d->mode = DMDIR|0555;
break;
case Qlvl:
sprint(buf, "%uld", LVL(qid));
d->name = estrdup9p(buf);
d->qid.type = QTFILE;
d->mode = 0444;
break;
default:
/* should not happen */
return 0;
break;
}
return 1;
}
static void
fsstat(Req *r)
{
fillstat((ulong)r->fid->qid.path, &r->d);
respond(r, nil);
}
static int
genroot(int n, Dir *dir, void *aux)
{
USED(aux);
if (n==0)
return fillstat(PATH(Qdir,0, 0), dir);
else
return -1;
}
static int
gendir(int n, Dir *dir, void *aux)
{
USED(aux);
if (n==0)
return fillstat(PATH(Qctl,0, 0), dir);
else if (n == 1)
return fillstat(PATH(Qtoc,0, 0), dir);
else if (n < ngenerated+1+1)
return fillstat(PATH(Qgen,n-1-1, 0), dir);
else if (n < 1+1+ngenerated+directory->ngroup)
return fillstat(PATH(Qgrp,n-1-1-ngenerated, 0), dir);
else
return -1;
}
static int
gengrp(int n, Dir *dir, void *aux)
{
Aux *a;
a = aux;
if (n < directory->group[a->gidx].count)
return fillstat(PATH(Qlvl,a->gidx,n), dir);
else
return -1;
}
static void
fsread(Req *r)
{
int j, m, cum;
Fid *fid;
vlong offset;
char s[1024];
Aux *a;
Generated *g;
Level *lp;
fid = r->fid;
offset = r->ifcall.offset;
a = fid->aux;
switch(QDIR(fid->qid.path)) {
case Qroot:
dirread9p(r, genroot, a);
respond(r, nil);
break;
case Qdir:
dirread9p(r, gendir, a);
respond(r, nil);
break;
case Qgrp:
dirread9p(r, gengrp, a);
respond(r, nil);
break;
case Qlvl:
/* XXX make sure that s is big enough */
sprint(s, "; moves %d level %d\n", a->gnr, a->lnr);
sprint(s+strlen(s), ":%d/%d\n", a->gnr, a->lnr);
lp = directory->group[a->gidx].base + a->lnr;
printlevel(s, lp);
readstr(r, s);
respond(r, nil);
break;
case Qctl:
/* XXX make sure that s is big enough */
sprint(s,"");
for(g=&generated[0]; g < &generated[ngenerated]; g++)
sprint(s+strlen(s), "%s %d of %d (%d-%d) in [%d,%d) (requested %d in [%d,%d)) \n", g->name, g->count, g->avail, g->total, g->seen, g->min, g->max, g->req.count, g->req.min, g->req.max);
s[strlen(s)] = '\0';
readstr(r, s);
respond(r, nil);
break;
case Qtoc:
m = directory->ngroup;
sprint(s,"");
cum=0;
for(j=0; j<m; j++) {
cum += directory->group[j].count;
sprint(s+strlen(s), "%d %d %d %d\n", directory->group[j].moves, directory->group[j].count, cum, dirseen->group[j].nseen);
}
s[strlen(s)] = '\0';
readstr(r, s);
respond(r, nil);
break;
case Qgen:
g = &generated[GRP(fid->qid.path)];
readbuf(r, g->data, g->ndata);
respond(r, nil);
break;
default:
/* should not happen */
break;
}
}
static int
parsectl(Req *r, char *data)
{
int ok, i, j, n, w;
char *lines[25];
char *words[25];
int min, max, count;
char *name, *p;
Generated *gp;
GrpSeen *sgp;
ok = 1;
n = gettokens(data, lines, 25, "\r\n");
for (j=0; j<n; j++) {
w = gettokens(lines[j], words, 25, "\t ");
if (w == 0)
continue;
if (strcmp(words[0], "gen") == 0 && w == 5) {
name = words[1]; /* To Fix: check name does not contain '/' */
if (strchr(name, '/') != nil) {
respond(r, "name should not contain /");
return 0;
}
for (p=name; *p != '\0' && isdigit(*p); p++)
;
if (p == '\0') {
respond(r, "name should not contain only digits");
return 0;
}
min = atoi(words[2]);
max = atoi(words[3]);
count = atoi(words[4]);
if (min < 0 || max < 0 || count < 0){
respond(r, "gen numbers should be positive");
return 0;
}
for(gp=generated; gp<generated+ngenerated; gp++){
if (strcmp(gp->name, name) == 0)
break;
}
if (gp == generated+ngenerated) {
generated = erealloc(generated, sizeof(Generated)*(ngenerated+1));
ngenerated += 1;
gp = generated+ngenerated-1;
gp->name = estrdup9p(name);
gp->data = nil;
gp->ndata = 0;
}
gp->req.min = min;
gp->req.max = max;
gp->req.count = count;
chooseproblems(gp);
} else if (strcmp(words[0], "reset") == 0 && w == 2) {
name = words[1];
for(gp=generated; gp<generated+ngenerated; gp++){
if (strcmp(gp->name, name) == 0) {
resetseen(dirseen, gp->min, gp->max);
break;
}
}
if (gp == generated+ngenerated) {
respond(r, "no such name");
return 0;
}
} else if (strcmp(words[0], "reset") == 0 && w == 1) {
memset(dirseen->seen, 0, (directory->nlevel/8)+1);
for(sgp=dirseen->group; sgp<dirseen->group+dirseen->ngroup; sgp++)
sgp->nseen = 0;
} else {
ok = 0;
for(i=0; i<w; i++)
fprint(2, "error:w[%d]=(%s) (strlen=%ld) %x\n", i, words[i], strlen(words[i]), *(words[i]));
}
}
if (!ok)
respond(r, "parse error");
return ok;
}
static void
fswrite(Req *r)
{
Fid *fid;
long count;
vlong offset;
static char *data = nil;
static int ndata = 0;
fid = r->fid;
count = r->ifcall.count;
offset = r->ifcall.offset;
switch(QDIR(fid->qid.path)) {
case Qctl:
/* if (offset == 0) { */
if (ndata < count+1){
data = erealloc(data, count+1);
ndata = count+1;
}
memcpy(data, r->ifcall.data, count);
data[count] = '\0';
if (!parsectl(r, data))
return;
/* } */
r->ofcall.count = count;
respond(r, nil);
break;
default:
respond(r, "permission denied");
break;
}
}
static void
fsdestroyfid(Fid *fid)
{
Aux *a;
a = fid->aux;
if(a == nil)
return;
free(a);
}
Srv fs = {
.attach= fsattach,
.destroyfid= fsdestroyfid,
.clone= fsclone,
.walk1= fswalk1,
.open= fsopen,
.read= fsread,
.write= fswrite,
.stat= fsstat,
};
int
levelscount(int min, int max)
{
int base, beyond, res;
if (min == max)
return 0;
if (min < directory->base)
min = directory->base;
if (min > directory->beyond)
min = directory->beyond;
if (max < directory->base)
max = directory->base;
if (max > directory->beyond)
max = directory->beyond;
if (min == max)
return 0;
base = min - directory->base;
beyond = max - directory->base;
if (beyond < 1)
return 0;
res = directory->group[beyond-1].beyond - directory->group[base].base;
// fprint(2, "levelscount min %d max %d base %d beyond %d res %d\n", min, max, base, beyond, res);
return res;
}
int
buildchoicelist(int cum, int min, int max, Generated *g, Seen*s)
{
int i, j, lvl, off;
int base, beyond;
maxchoicelist = cum;
free(choicelist);
choicelist = emalloc(sizeof(int)*maxchoicelist);
base = min - directory->base;
beyond = max - directory->base;
USED(beyond);
off = directory->group[base].base - directory->level;
lvl = off;
j = 0;
for(i=0; i<cum; i++) {
if (!isseen(s->seen, lvl)) {
choicelist[j] = lvl;
j++;
} else
if (debug) fprint(2, "buildchoicelist(%d, %d, %d) seen %d\n", cum, min, max, lvl);
lvl++;
}
g->avail = j;
g->total = lvl - off;
g->seen = g->total - g->avail ;
return j;
}
int
intcmp(void *pa, void *pb)
{
int *a, *b;
a = pa;
b = pb;
return (*a - *b);
}
/*
* we generate g->count random numbers for
* the levels in [g->min, g->max)
* we may generated duplicate random numbers.
* we just throw out duplicates, but thus may
* end up with < g->count numbers
*/
void
chooseproblems(Generated *g)
{
int i, *levels, n, cum, prev, v, r, k;
Point p;
char *lbuf, *lp;
uchar *seen;
Level *lvlp;
g->mtime =time(0);
g->min = g->req.min;
g->max = g->req.max;
if (g->min < directory->base)
g->min = directory->base;
if (g->max > directory->beyond)
g->max = directory->beyond;
if (g->min > g->max)
g->min = g->max;
cum = levelscount(g->min, g->max);
g->avail = buildchoicelist(cum, g->min, g->max, g, dirseen);
if (g->avail > g->req.count)
n = g->req.count;
else
n = g->avail;
levels = emalloc(sizeof(int) * n);
lbuf = emalloc(sizeof(char)*n*150); /* be sure 150 is enough */
if (debug) fprint(2, "levelscount %d %d -> %d ; n = %d\n", g->min, g->max, g->avail, n);
if (g->avail > g->req.count) {
seen = emalloc((g->avail/8)+1);
for(i=0; i<n; i++) {
r = nrand(g->avail);
v = choicelist[r];
k = 0;
while (isseen(seen, r) && k < g->avail){
r = nrand(g->avail);
v = choicelist[r];
k++;
}
// if (k > 0) fprint(2, "rand i=%d r=%d v=%d k=%d\n", i, r, v,k);
if (debug) fprint(2, "rand i=%d r=%d v=%d k=%d\n", i, r, v,k);
setseen(seen, r);
levels[i] = v;
dirsetseen(dirseen, v);
}
free(seen);
qsort(levels, n, sizeof(int), intcmp);
} else {
for(i=0; i<n; i++) {
v = choicelist[i];
levels[i] = v;
dirsetseen(dirseen, v);
}
}
lp = lbuf;
prev = -1;
g->count = 0;
for(i=0; i<n; i++) {
if (prev == levels[i])
continue; /* throw out duplicate */
prev = levels[i];
g->count ++;
p = lin2grouplvl(levels[i]);
sprint(lp, "; moves %d level %d\n", p.x, p.y);
sprint(lp+strlen(lp), ":%d/%d\n", p.x, p.y);
lvlp = directory->level + levels[i];
printlevel(lp, lvlp);
lp += strlen(lp);
}
free(levels);
free(g->data);
g->data = lbuf;
g->ndata = lp - lbuf;
}
Seen*
seeninit(Directory *d)
{
Seen* s;
s = emalloc(sizeof(Seen));
s->ngroup = d->ngroup;
s->group = emalloc(sizeof(GrpSeen)*s->ngroup);
s->seen = emalloc((d->nlevel/8)+1);
return s;
}
int
readint(Biobuf *bp)
{
int i, n;
uchar b;
n = 0;
for (i=0; i < 4; i++) {
Bread(bp, &b, 1);
n+= (b << (i*8));
}
return n;
}
Directory*
readlevels(Biobuf *bp)
{
Directory *d;
Group *gp;
Level *lp;
int lvl, *off, *op, noff;
char ignored[10];
d = emalloc(sizeof(Directory));
d->base = readint(bp) - 1; /* should be 21-1 */
d->beyond = readint(bp); /* should be 52 */
d->ngroup = d->beyond - d->base;
d->group = emalloc(sizeof(Group)*d->ngroup);
noff = d->ngroup + 1; /* +1 for offset to end of file */
off = emalloc(sizeof(int)*noff);
lvl = d->base;
op = off;
for (gp=d->group; gp<d->group+d->ngroup; gp++) {
gp->moves = lvl++;
*(op++) = readint(bp);
}
*op = readint(bp); /* one more time for offset to end of file */
d->nlevel = (off[d->ngroup] - off[0])/8; /* 8 bytes per level */
d->level = emalloc(sizeof(Level)*d->nlevel);
op = off;
lp = d->level;
for (gp=d->group; gp<d->group+d->ngroup; gp++) {
if (debug) fprint(2, "Group %d\n", gp->moves);
gp->base = lp;
gp->count = (*(op+1) - *op)/8;
gp->beyond = lp + gp->count;
for (; lp<gp->beyond; lp++) {
Bread(bp, lp->row, 3);
Bread(bp, ignored, 1);
Bread(bp, lp->col, 3);
Bread(bp, ignored, 1);
}
op++;
}
free(off);
return d;
}
void
setboard(char **board, Point p, int l, int orient, int v)
{
int i;
Point dir[] = {
[OCol] Pt(1,0),
[ORow] Pt(0,1),
};
if (orient == OCol)
p = Pt(p.y,p.x);
for (i=0; i<l; i++) {
board[1+p.x][1+p.y] = v;
p = addpt(p, dir[orient]);
}
}
void
decstrip(char **board, int i, int b, int orient, char *car, char *truck)
{
switch(b){
case 0:
break;
case 1:
case 2:
case 3:
case 4:
case 5:
setboard(board, Pt(i, b-1), 2, orient, *car);
(*car)++;
break;
case 6:
case 7:
case 8:
case 9:
setboard(board, Pt(i, b-6), 3, orient, *truck);
(*truck)++;
break;
case 10:
case 11:
case 12:
setboard(board, Pt(i, 0), 2, orient, *car);
(*car)++;
setboard(board, Pt(i, b-8), 2, orient, *car);
(*car)++;
break;
case 13:
case 14:
setboard(board, Pt(i, 1), 2, orient, *car);
(*car)++;
setboard(board, Pt(i, b-10), 2, orient, *car);
(*car)++;
break;
case 15:
setboard(board, Pt(i, 2), 2, orient, *car);
(*car)++;
setboard(board, Pt(i, 4), 2, orient, *car);
(*car)++;
break;
}
}
void
printlevel(char *s, Level *l)
{
/*
* unfortunately, the following gives
* brd[rownr, colnr]
* which is opposite to convention (of brd[x][y])
*/
char *brd[] = {
"########",
"#......#",
"#......#",
"#......@",
"#......#",
"#......#",
"#......#",
"########",
};
char *board[8]; /* same as nr of rows of brd */
char car = 'a';
char truck = 'o';
char us = 'x';
int i;
int bl[6];
/*
* brd is only initialized once, not for for every call;
* if we change brd in-place we will be confronted
* with these changes in next call of this function
*/
for(i=0; i<8; i++)
board[i] = estrdup9p(brd[i]);
bl[0] = l->row[0] & 0x0F;
bl[1] = (l->row[0] >> 4) & 0x0F;
bl[2] = l->row[1] & 0x0F;
bl[3] = (l->row[1] >> 4) & 0x0F;
bl[4] = l->row[2] & 0x0F;
bl[5] = (l->row[2] >> 4) & 0x0F;
for (i=0; i< 6; i++)
if (i == 2)
decstrip(board, i, bl[i], ORow, &us, &truck);
else
decstrip(board, i, bl[i], ORow, &car, &truck);
bl[0] = l->col[0] & 0x0F;
bl[1] = (l->col[0] >> 4) & 0x0F;
bl[2] = l->col[1] & 0x0F;
bl[3] = (l->col[1] >> 4) & 0x0F;
bl[4] = l->col[2] & 0x0F;
bl[5] = (l->col[2] >> 4) & 0x0F;
for (i=0; i< 6; i++)
decstrip(board, i, bl[i], OCol, &car, &truck);
for (i=0; i<8; i++)
sprint(s+strlen(s), "%s\n", board[i]);
sprint(s+strlen(s), "\n");
for(i=0; i<8; i++)
free(board[i]);
}
void
usage(void)
{
fprint(2, "Usage: %s [-m mntpnt] [-s srv] [levelfile]\n", argv0);
exits("usage");
}
void
main(int argc, char**argv)
{
Biobuf *bp;
char *mtpt, *srvpost, *srvname, *srvfile, *levelfile;
mtpt = "/mnt";
srvpost = nil;
srvname = nil;
levelfile = "/sys/games/lib/rushhour/levels/ttraffic.levels";
debug = 0;
ARGBEGIN{
case 'd':
debug++;
break;
case 'D':
chatty9p++;
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
srvpost = EARGF(usage());
break;
default:
usage();
}ARGEND
if (mtpt != nil && *mtpt == 0)
mtpt = nil;
if (argc > 1)
usage();
if (argc == 1)
levelfile = argv[0];
starttime = time(0);
srand(time(0));
bp = Bopen(levelfile, OREAD);
if (bp == nil)
sysfatal("cannot open levelfile: %r");
directory = readlevels(bp);
Bterm(bp);
dirseen = seeninit(directory);
ngenerated = 0;
generated = nil;
if(srvpost){
srvname = smprint("trafficfs.%s", srvpost);
srvfile = smprint("/srv/%s", srvname);
remove(srvfile);
free(srvfile);
}
postmountsrv(&fs, srvname, mtpt, MBEFORE);
}
|