ref: efe80883a260ebb4396edd73dd707a72a0ccc222
dir: /todofs.c/
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <bio.h>
#include <ndb.h>
/* owner = assignee */
/* group = optional group */
/* filename = UID_short_description_based_on_title */
/* file contents = plain read from source file UID */
/* source directory contents:
index - index file, ndb format
UID1
UID2
UID3
*/
void
usage(void)
{
fprint(2, "usage: %s [-s srvname] [-m mountpoint] directory\n", argv0);
exits("usage");
}
#define HASHLEN 10
enum {
Qdir,
Qctl,
Qstatus,
Qtask,
Qtitle,
Qdata,
Qassignee,
Qgroup,
};
char *qnames[] = {
[Qdir] "/",
[Qctl] "ctl",
[Qstatus] nil,
[Qtask] nil,
[Qtitle] "title",
[Qdata] "data",
[Qassignee] "assignee",
[Qgroup] "group",
};
int
qidtype(ulong path)
{
// -----0000
return path & 15;
}
ulong
qidnum(ulong path)
{
// 000000----
return path >> 4;
}
ulong
mkqid(int type, int num)
{
return (num << 4) | type & 15;
}
static char*
ultostr(ulong n)
{
char buf[32];
snprint(buf, sizeof(buf), "%0ulx", n);
return buf;
}
static ulong
strtoid(char *s)
{
return strtoul(s, nil, 16);
}
char *srcdir;
char *idxfile;
Ndb *index = nil;
typedef struct Task Task;
struct Task {
Task* next;
char *title;
char *dataname;
char *ext;
ulong id;
Dir *dir;
int cassignee;
int cgroup;
int cfname;
};
typedef struct Status Status;
struct Status {
Status *next;
char *name;
Task *tasks;
};
Status *statuses = nil;
ulong nextid;
Task* getgtask(ulong id, Status **status);
int
savedata(void)
{
Biobuf *bout;
Status *s;
ulong tid;
Task *t;
ndbclose(index);
index = nil;
bout = Bopen(idxfile, OWRITE);
if (!bout)
goto Errout;
Bprint(bout, "key=config "
"nextid=\"%s\"\n"
"\n",
ultostr(nextid));
for (s = statuses; s; s = s->next) {
Bprint(bout, "key=status name=\"%s\"\n", s->name);
}
Bprint(bout, "\n");
tid = 0;
while (tid < nextid) {
t = getgtask(tid, &s);
if (t) {
Bprint(bout, "key=task id=\"%s\" status=\"%s\"", ultostr(t->id), s->name);
if (t->cassignee && strcmp(t->dir->uid, "na") != 0) {
Bprint(bout, " assignee=\"%s\"", t->dir->uid);
}
if (t->cgroup && strcmp(t->dir->gid, "ng") != 0) {
Bprint(bout, " group=\"%s\"", t->dir->gid);
}
if (t->title) {
Bprint(bout, " title=\"%s\"", t->title);
}
Bprint(bout, "\n");
}
tid++;
}
Bterm(bout);
index = ndbopen(idxfile);
return 1;
Errout:
index = ndbopen(idxfile);
return 0;
}
static char*
taskfile(Task *t, char *ext)
{
if (ext) {
return smprint("%s%s", ultostr(t->id), ext);
} else {
if (t->ext)
return smprint("%s%s", ultostr(t->id), t->ext);
else
return smprint("%s", ultostr(t->id));
}
}
Status*
getstatus(char *name, int *id)
{
Status *s;
int i;
if (!statuses)
return nil;
i = 0;
s = statuses;
while (s && (strcmp(s->name, name) != 0)) {
i++;
s = s->next;
}
if (id)
*id = i;
return s; /* found or nil */
}
Status*
getnstatus(int id)
{
Status *s;
int i;
i = 0;
s = statuses;
while (i != id && s->next) {
i++;
s = s->next;
}
return i == id ? s : nil;
}
static int
getsid(Status *s)
{
int i = 0;
for (Status *n = statuses; n; n = n->next) {
if (s == n)
return i;
i++;
}
return -1;
}
int
addstatus(char *name)
{
Status *s;
if (!statuses) {
statuses = mallocz(sizeof(Status), 1);
statuses->name = strdup(name);
return 1;
}
if (getstatus(name, nil)) {
werrstr("status '%s' already exists", name);
return 0;
}
for (s = statuses; s->next; s = s->next)
;
s->next = mallocz(sizeof(Status), 1);
s = s->next;
s->name = strdup(name);
return 1;
}
Task*
gettask(char *status, char *name, int *task)
{
Status *s;
Task *t;
int i;
ulong l;
s = getstatus(status, nil);
if (!s)
return nil;
l = strtoul(name, nil, 16);
t = s->tasks;
i = 0;
while (t && l != t->id) {
t = t->next;
i++;
}
if (task)
*task = i;
return t; /* found or nil */
}
Task*
getstask(char *name, int *status, int *task)
{
Status *s;
Task *t;
int i, j;
i = 0;
for (s = statuses; s; s = s->next, i++) {
t = gettask(s->name, name, &j);
if (t) {
if (status)
*status = i;
if (task)
*task = j;
return t;
}
}
return nil;
}
Task*
getftask(char *fname, int *status, int *task)
{
Task *t;
Status *s;
int i, j;
i = 0;
for (s = statuses; s; s = s->next, i++) {
t = s->tasks;
j = 0;
while (t) {
if (strcmp(t->dir->name, fname) == 0) {
if (status)
*status = i;
if (task)
*task = t->id;
return t;
}
t = t->next;
j++;
}
}
return nil;
}
Task*
getgtask(ulong id, Status **status)
{
Task *t;
Status *s;
for (s = statuses; s; s = s->next) {
t = s->tasks;
while (t) {
if (t->id == id) {
if (status)
*status = s;
return t;
}
t = t->next;
}
}
werrstr("task not found");
return nil;
}
Task*
getntask(char *status, int id)
{
Status *s;
int i;
Task *t;
s = getstatus(status, nil);
if (!s)
return nil;
i = 0;
t = s->tasks;
while (i != id && t->next) {
i++;
t = t->next;
}
return i == id ? t : nil;
}
void
freetask(Task *t)
{
if (t->cassignee)
free(t->dir->uid);
if (t->cgroup)
free(t->dir->gid);
if (t->cfname)
free(t->dir->name);
if (t->title)
free(t->title);
if (t->dataname)
free(t->dataname);
if (t->ext)
free(t->ext);
free(t->dir);
t->cassignee = 0;
t->cgroup = 0;
t->cfname = 0;
t->title = nil;
t->dir = nil;
t->dataname = nil;
t->ext = nil;
}
static void
settaskassignee(Task *t, char *assignee)
{
assert(t);
if (t->cassignee)
free(t->dir->uid);
t->dir->uid = strdup(assignee && assignee[0] ? assignee : "na");
t->cassignee = 1;
}
static void
settaskgroup(Task *t, char *group)
{
assert(t);
if (t->cgroup)
free(t->dir->uid);
t->dir->gid = strdup(group && group[0] ? group : "ng");
t->cgroup = 1;
}
static int
settasktitle(Task *t, char *title)
{
char buf[64], *c;
if (t->title)
free(t->title);
if (t->cfname && t->dir->name)
free(t->dir->name);
if (title && title[0]) {
t->title = strdup(title);
snprint(buf, sizeof(buf), "%s", title);
for (c = buf; *c; c++) {
if (*c == ' ' || *c == '\t')
*c = '_';
}
t->dir->name = smprint("%s-%s", ultostr(t->id), buf);
} else {
t->title = nil;
t->dir->name = smprint("%s", ultostr(t->id));
}
t->cfname = 1;
return 1;
}
static int
settaskextension(Task *t, char *name)
{
Dir null, *dir;
char *fromfile, *tofile;
char *ext;
ext = strchr(name, '.');
fromfile = taskfile(t, nil);
tofile = taskfile(t, ext && ext[1] ? ext : "");
dir = dirstat(tofile);
if (dir) {
fprint(2, "%s exists: remove\n", tofile);
if (remove(tofile) < 0) {
free(dir);
free(fromfile);
free(tofile);
return 0;
}
free(dir);
}
nulldir(&null);
null.name = tofile;
if (!dirwstat(fromfile, &null) < 0)
goto Errout;
free(fromfile);
free(tofile);
free(dir);
return 1;
Errout:
free(fromfile);
free(tofile);
free(dir);
return 0;
}
static int
updatetask(Task *t, char *name, char *assignee, char *group, char *title)
{
char buf[32];
char *ext;
Dir *dir;
int fd;
int n, i;
freetask(t);
fd = open(".", OREAD);
if (fd < 0)
sysfatal("unable to open dir: %r");
ext = nil;
i = 0;
while (n = dirread(fd, &dir)) {
for (i = 0; i < n; i++) {
if (strcmp(dir[i].name, "..") == 0)
continue;
ext = strchr(dir[i].name, '.');
if (ext) {
snprint(buf, sizeof(buf), "%s.", name);
if (strncmp(buf, dir[i].name, strlen(buf)) == 0) {
goto Break;
}
} else {
if (strcmp(name, dir[i].name) == 0)
goto Break;
}
}
free(dir);
}
Break:
close(fd);
if (!dir) {
werrstr("task not found: %s", name);
return 0;
}
t->dir = dirstat(dir[i].name);
free(dir);
if (!ext) {
t->dataname = strdup(qnames[Qdata]);
} else {
t->dataname = smprint("%s%s", qnames[Qdata], ext);
}
t->ext = ext ? strdup(ext) : nil;
settaskassignee(t, assignee);
settaskgroup(t, group);
return settasktitle(t, title);
}
int
addtask(char *status, char *name, char *assignee, char *group, char *title, ulong id)
{
Status *s;
Task *t;
s = getstatus(status, nil);
if (!s) {
werrstr("status %s not found", status);
return 0;
}
t = getstask(name, nil, nil);
if (t) {
t->id = id;
return updatetask(t, name, assignee, group, title);
}
if (!s->tasks) {
s->tasks = mallocz(sizeof(Task), 1);
t = s->tasks;
} else {
for (t = s->tasks; t->next; t = t->next)
;
t->next = mallocz(sizeof(Task), 1);
t = t->next;
}
t->id = id;
return updatetask(t, name, assignee, group, title);
}
void
readstatuses(void)
{
Ndbtuple *tuple, *val;
Ndbs s;
if (ndbchanged(index))
ndbreopen(index);
for (tuple = ndbsearch(index, &s, "key", "status"); tuple != nil; tuple = ndbsnext(&s, "key", "status")) {
if (val = ndbfindattr(tuple, s.t, "name")) {
addstatus(val->val);
} else
sysfatal("invalid index file");
}
}
static void
readtasks(void)
{
Ndbtuple *tuple, *val, *sval;
Ndbtuple *assignee, *group, *tval;
Ndbs ns;
ulong id;
if (ndbchanged(index))
ndbreopen(index);
tuple = ndbsearch(index, &ns, "key", "config");
if (!tuple)
sysfatal("bad index: missing config");
val = ndbfindattr(tuple, ns.t, "nextid");
if (!val)
sysfatal("bad config: missing nextid");
nextid = strtoul(val->val, nil, 16);
for (tuple = ndbsearch(index, &ns, "key", "task"); tuple != nil; tuple = ndbsnext(&ns, "key", "task")) {
if ((val = ndbfindattr(tuple, ns.t, "id")) &&
(sval = ndbfindattr(tuple, ns.t, "status"))) {
assignee = ndbfindattr(tuple, ns.t, "assignee");
group = ndbfindattr(tuple, ns.t, "group");
tval = ndbfindattr(tuple, ns.t, "title");
id = strtoul(val->val, nil, 16);
addtask(sval->val, val->val, assignee ? assignee->val : nil, group ? group->val : nil, tval ? tval->val : nil, id);
} else
sysfatal("invalid index");
}
}
static ulong
newtask(char *name, char *status)
{
int fd;
char *id;
ulong newid;
readstatuses();
readtasks();
id = ultostr(nextid);
fd = create(id, OREAD, 0666);
if (fd < 0) {
werrstr("unable to open task file '%s'", name);
return 0;
}
close(fd);
if (!addtask(status, id, nil, nil, name, nextid)) {
werrstr("cannot add task: %r");
return 0;
}
newid = nextid++;
return savedata() ? newid : 0;
}
void
fsopen(Req *r)
{
respond(r, nil);
}
static char*
statusname(int id)
{
Status *s = getnstatus(id);
return s ? strdup(s->name) : nil;
}
static void
filltaskstat(Dir *d, Task *t)
{
d->uid = estrdup9p(t->dir->uid);
d->gid = estrdup9p(t->dir->gid);
d->atime = t->dir->atime;
d->mtime = t->dir->mtime;
}
static void
fillstat(Dir *d, uvlong path)
{
int type = 0;
Task *t;
// memset(d, 0, sizeof(Dir));
d->uid = estrdup9p("todo");
d->gid = estrdup9p("todo");
switch (qidtype(path)) {
case Qdir:
case Qstatus:
case Qtask:
type = QTDIR;
break;
case Qctl:
type = 0;
break;
}
d->qid = (Qid){path, 0, type};
d->atime = d->mtime = 0;
d->length = 0;
if (qidtype(path) == Qtask) {
d->length = 999;
}
t = getgtask(qidnum(path), nil);
switch (qidtype(path)) {
case Qdir:
d->name = estrdup9p(qnames[Qdir]);
d->mode = DMDIR|0555;
break;
case Qstatus:
d->name = statusname(qidnum(path));
d->mode = DMDIR|0555;
break;
case Qtask:
d->name = estrdup9p(t->dir->name);
d->mode = DMDIR|0555;
case Qtitle:
d->name = estrdup9p(qnames[Qtitle]);
d->mode = 0666;
goto Commontask;
case Qdata:
d->name = estrdup9p(t->dataname);
d->mode = 0666;
goto Commontask;
case Qassignee:
d->name = estrdup9p(qnames[Qassignee]);
d->mode = 0666;
goto Commontask;
case Qgroup:
d->name = estrdup9p(qnames[Qgroup]);
d->mode = 0666;
/* falls through */
Commontask:
filltaskstat(d, t);
break;
case Qctl:
d->name = estrdup9p(qnames[Qctl]);
d->mode = 0666;
break;
}
}
static int
rootgen(int i, Dir *d, void *aux)
{
Status *s;
USED(aux);
switch (i) {
case 0: /* ctl */
fillstat(d, mkqid(Qctl, 0));
return 0;
default: /* statuses */
i -= 1;
s = getnstatus(i);
if (!s)
return -1;
fillstat(d, mkqid(Qstatus, i));
return 0;
}
}
static int
statusgen(int i, Dir *d, void *aux)
{
Status *s = (Status*)aux;
Task *t;
t = getntask(s->name, i);
if (!t)
return -1;
d->name = strdup(t->dir->name);
d->qid = (Qid){mkqid(Qtask, t->id), 0, QTDIR};
d->mode = DMDIR|0555;
d->atime = t->dir->atime;
d->mtime = t->dir->mtime;
d->length = t->dir->length;
d->uid = strdup(t->dir->uid);
d->gid = strdup(t->dir->gid);
return 0;
}
static int
taskgen(int i, Dir *d, void *aux)
{
Task *t = (Task*)aux;
switch (i + Qtitle) {
case Qtitle:
d->name = strdup(qnames[Qtitle]);
d->qid = (Qid){mkqid(Qtitle, t->id), 0, 0};
d->length = t->title ? strlen(t->title) : 0;
break;
case Qdata:
d->name = strdup(t->dataname);
d->qid = (Qid){mkqid(Qdata, t->id), 0, 0};
d->length = t->dir->length;
break;
case Qassignee:
d->name = strdup(qnames[Qassignee]);
d->qid = (Qid){mkqid(Qassignee, t->id), 0, 0};
d->length = t->dir->uid ? strlen(t->dir->uid) : 0;
break;
case Qgroup:
d->name = strdup(qnames[Qgroup]);
d->qid = (Qid){mkqid(Qgroup, t->id), 0, 0};
d->length = t->dir->gid ? strlen(t->dir->gid) : 0;
break;
default:
return -1;
}
d->mode = 0666;
filltaskstat(d, t);
return 0;
}
int
taskread(Req *r)
{
Task *t;
char *f;
Biobuf *bin;
long n;
t = getgtask(qidnum(r->fid->qid.path), nil);
if (!t) {
werrstr("task not found");
return 0;
}
f = t->ext ? smprint("%s%s", ultostr(t->id), t->ext) : strdup(ultostr(t->id));
bin = Bopen(f, OREAD);
free(f);
if (!bin)
return 0;
Bseek(bin, r->ifcall.offset, 0);
n = Bread(bin, r->ofcall.data, r->ifcall.count);
if (n < 0) {
Bterm(bin);
return 0;
}
r->ofcall.count = n;
Bterm(bin);
return 1;
}
void
fsread(Req *r)
{
Status *s;
Task *t;
ulong qnum = qidnum(r->fid->qid.path);
switch (qidtype(r->fid->qid.path)) {
case Qdir:
readstatuses();
dirread9p(r, rootgen, nil);
respond(r, nil);
break;
case Qstatus:
readstatuses();
s = getnstatus(qnum);
readtasks();
dirread9p(r, statusgen, s);
respond(r, nil);
break;
case Qtask:
readtasks();
t = getgtask(qnum, nil);
if (!t) {
respond(r, "task not found");
return;
}
dirread9p(r, taskgen, t);
respond(r, nil);
break;
case Qtitle:
readtasks();
t = getgtask(qnum, nil);
if (!t) {
respond(r, "task not found");
return;
}
if (t->title)
readstr(r, t->title);
respond(r, nil);
break;
case Qdata:
if (!taskread(r))
responderror(r);
else
respond(r, nil);
break;
case Qassignee:
readtasks();
t = getgtask(qnum, nil);
if (!t) {
respond(r, "task not found");
return;
}
if (t->cassignee)
readstr(r, t->dir->uid);
respond(r, nil);
break;
case Qgroup:
readtasks();
t = getgtask(qnum, nil);
if (!t) {
respond(r, "task not found");
return;
}
if (t->cgroup)
readstr(r, t->dir->gid);
respond(r, nil);
break;
case Qctl:
respond(r, nil);
break;
default:
respond(r, "error");
}
}
int
taskwrite(Req *r)
{
Task *t;
Biobuf *bout;
long n;
t = getgtask(qidnum(r->fid->qid.path), nil);
if (!t) {
werrstr("task not found");
return 0;
}
bout = Bopen(ultostr(t->id), OWRITE);
if (!bout)
return 0;
Bseek(bout, r->ifcall.offset, 0);
n = Bwrite(bout, r->ifcall.data, r->ifcall.count);
if (n < 0) {
Bterm(bout);
return 0;
}
r->ofcall.count = n;
Bterm(bout);
return 1;
}
int
ctlwrite(Req *r)
{
char str[256];
char *args[5];
int n;
memset(str, 0, sizeof(str));
memcpy(str, r->ifcall.data, r->ifcall.count);
n = tokenize(str, args, 5);
if (n < 1)
return 1;
if (strcmp(args[0], "addstatus") == 0) {
if (n != 2)
goto Addstatuserr;
if (args[1] && *args[1]) {
if (addstatus(args[1]))
return savedata();
return 1;
}
Addstatuserr:
werrstr("usage: addstatus status");
return 0;
}
if (strcmp(args[0], "dump") == 0) {
if (n != 1) {
werrstr("usage: dump");
return 0;
}
readtasks();
return savedata();
}
werrstr("invalid command: '%s'", args[0]);
return 0;
}
static int
taskwritefield(Task *t, int field, Req *r)
{
char *s;
if (r->ifcall.offset != 0) {
werrstr("write offset not 0");
return 0;
}
if (r->ifcall.count == 0) {
switch(field) {
case Qtitle:
settasktitle(t, nil);
break;
case Qassignee:
settaskassignee(t, nil);
break;
case Qgroup:
settaskgroup(t, nil);
break;
}
return 1;
}
s = r->ifcall.data;
r->ifcall.data[r->ifcall.count-1] = 0; // last byte 0
for (int i = 0; i < r->ifcall.count; i++) {
if (s[i] == '\n') {
s[i] = 0;
break;
}
}
switch (field) {
case Qtitle:
settasktitle(t, s);
break;
case Qassignee:
settaskassignee(t, s);
break;
case Qgroup:
settaskgroup(t, s);
break;
default:
sysfatal("cannot happen");
}
return savedata();
}
void
fswrite(Req *r)
{
Task *t;
ulong qnum;
int qtype;
qnum = qidnum(r->fid->qid.path);
qtype = qidtype(r->fid->qid.path);
switch (qtype) {
case Qctl:
if (!ctlwrite(r))
responderror(r);
else
respond(r, nil);
break;
case Qtitle:
case Qassignee:
case Qgroup:
t = getgtask(qnum, nil);
if (!t) {
responderror(r);
break;
}
if (!taskwritefield(t, qtype, r)) {
responderror(r);
break;
}
r->ofcall.count = r->ifcall.count;
respond(r, nil);
break;
case Qdata:
if (!taskwrite(r))
responderror(r);
else
respond(r, nil);
break;
case Qtask:
case Qdir:
case Qstatus:
respond(r, nil);
break;
default:
respond(r, "error");
}
}
int
taskmv(Task *t, Status *from, Status *to)
{
Task *prev;
if (from->tasks == t) {
from->tasks = t->next;
goto Chain;
}
for (prev = from->tasks; prev->next && prev->next != t; prev = prev->next)
;
prev->next = t->next;
Chain:
t->next = nil;
if (!to->tasks) {
to->tasks = t;
return 1;
}
for (prev = to->tasks; prev->next; prev = prev->next)
;
prev->next = t;
return 1;
}
void
fscreate(Req *r)
{
Status *s, *sfrom;
Task *t;
int sid;
ulong id, qnum;
qnum = qidnum(r->fid->qid.path);
switch (qidtype(r->fid->qid.path)) {
case Qstatus:
s = getnstatus(qnum);
if (!s) {
respond(r, "status not found");
return;
}
id = strtoid(r->ifcall.name);
t = getgtask(id, &sfrom);
sid = getsid(sfrom);
if (t) {
/* move existing task */
if (sid == qnum) {
respond(r, "task already has status");
return;
}
if (!taskmv(t, sfrom, s)) {
responderror(r);
return;
}
r->fid->qid = (Qid){mkqid(Qtask, t->id), 0, QTDIR};
r->ofcall.qid = r->fid->qid;
} else {
/* create new task */
id = newtask(r->ifcall.name, s->name);
if (!id) {
responderror(r);
return;
}
t = getgtask(id, nil);
if (!t) {
respond(r, "error creating new task");
return;
}
r->fid->qid = (Qid){mkqid(Qtask, t->id), 0, QTDIR};
r->ofcall.qid = r->fid->qid;
}
savedata();
break;
default:
respond(r, "not allowed");
return;
}
respond(r, nil);
}
static void
fsattach(Req *r)
{
r->ofcall.qid = (Qid){Qdir, 0, QTDIR};
r->fid->qid = r->ofcall.qid;
r->fid->aux = 0;
readstatuses();
readtasks();
respond(r, nil);
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
int isdotdot;
Status *s;
Task *t;
int sid, tid;
ulong qnum;
isdotdot = strcmp(name, "..") == 0;
qnum = qidnum(fid->qid.path);
switch (qidtype(fid->qid.path)) {
case Qdir:
if (isdotdot) {
*qid = fid->qid;
return nil;
}
if (strcmp(name, qnames[Qctl]) == 0) {
*qid = (Qid){mkqid(Qctl, 0), 0, 0};
return nil;
}
s = getstatus(name, &sid);
if (!s)
return "file not found";
*qid = (Qid){mkqid(Qstatus, sid), 0, QTDIR};
return nil;
case Qstatus:
if (isdotdot) {
*qid = (Qid){mkqid(Qdir, 0), 0, QTDIR};
return nil;
}
readstatuses();
readtasks();
if (t = getftask(name, &sid, &tid)) {
if (qnum != sid) {
return "task has a different status";
}
*qid = (Qid){mkqid(Qtask, t->id), 0, QTDIR};
return nil;
}
return "task not found";
case Qtask:
readstatuses();
readtasks();
t = getgtask(qnum, &s);
if (!t) {
return "task not found";
}
sid = getsid(s);
if (isdotdot) {
*qid = (Qid){mkqid(Qstatus, sid), 0, QTDIR};
return nil;
}
if (strcmp(name, qnames[Qtitle]) == 0) {
*qid = (Qid){mkqid(Qtitle, qnum), 0, 0};
return nil;
}
if (strcmp(name, qnames[Qassignee]) == 0) {
*qid = (Qid){mkqid(Qassignee, qnum), 0, 0};
return nil;
}
if (strcmp(name, qnames[Qgroup]) == 0) {
*qid = (Qid){mkqid(Qgroup, qnum), 0, 0};
return nil;
}
if (strcmp(name, t->dataname) == 0) {
*qid = (Qid){mkqid(Qdata, qnum), 0, 0};
return nil;
}
}
return "error";
}
static void
fsstat(Req *r)
{
fillstat(&r->d, r->fid->qid.path);
respond(r, nil);
}
static void
fswstat(Req *r)
{
Task *t;
ulong qnum;
int save = 0;
qnum = qidnum(r->fid->qid.path);
switch (qidtype(r->fid->qid.path)) {
case Qtask:
readstatuses();
readtasks();
t = getgtask(qnum, nil);
if (!t) {
respond(r, "invalid task");
return;
}
if (r->d.uid && r->d.uid[0]) {
if (t->cassignee && t->dir->uid)
free(t->dir->uid);
t->dir->uid = strdup(r->d.uid);
t->cassignee = 1;
save++;
}
if (r->d.gid && r->d.gid[0]) {
if (t->cgroup && t->dir->gid)
free(t->dir->gid);
t->dir->gid = strdup(r->d.gid);
t->cgroup = 1;
save++;
}
if (r->d.name && r->d.name[0]) {
settasktitle(t, r->d.name);
save++;
}
if (save)
savedata();
break;
case Qdata:
readstatuses();
readtasks();
t = getgtask(qnum, nil);
if (!t) {
responderror(r);
return;
}
if (!r->d.name || !r->d.name[0]) {
respond(r, "invalid operation");
return;
}
if (!settaskextension(t, r->d.name)) {
responderror(r);
return;
}
break;
default:
respond(r, "invalid operation");
return;
}
respond(r, nil);
}
Srv fs = {
.attach = fsattach,
.open = fsopen,
.read = fsread,
.write = fswrite,
.create = fscreate,
.walk1 = fswalk1,
.stat = fsstat,
.wstat = fswstat,
};
void
main(int argc, char **argv)
{
char *srvname = nil;
char *mtpt = "/mnt/todo";
int gen = 0;
int fd;
ARGBEGIN{
case 's':
srvname = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 'c':
gen++;
break;
case '9':
chatty9p++;
break;
default:
usage();
}ARGEND;
if (argc != 1)
usage();
quotefmtinstall();
srcdir = *argv;
if (chdir(srcdir))
sysfatal("unable to chdir: %r");
idxfile = "index";
if (gen) {
fd = create(idxfile, OWRITE, 0666);
if (fd < 0)
sysfatal("unable to create index file: %r");
fprint(fd, "key=config nextid=\"1\"\n");
close(fd);
exits(0);
}
assert(idxfile);
index = ndbopen(idxfile);
if (!index)
sysfatal("unable to open index file: %r");
postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
exits(0);
}