ref: 6dc0bc759e48e1fc98cd5faf7237c128dd8eb914
parent: 567dc43094a9a507f605d55cfb629bb49af856b9
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Jun 25 09:26:35 EDT 2024
adds global IDs everywhere, adds task creation and nextid config field, adds README
--- /dev/null
+++ b/README
@@ -1,0 +1,55 @@
+todo filesystem
+
+* this is an experiment. *
+* It's not the best, both design and implementation. *
+
+
+Usage:
+
+todofs path/to/dir
+
+the todo-directory must contain at least one file called `index`:
+
+ key=config nextid=1
+
+It will be filled with metadata when creating tasks and statuses.
+
+
+Console interface (ctl file):
+
+addstatus <name> - create a new status
+dump - write data back to disk. Shouldn't be needed.
+
+
+Task interaction:
+
+Imagine the following scenario:
+
+ /mnt/todo/StatusA/1-First_Task
+ /mnt/todo/StatusB/2-Second_Task
+ ; cd /mnt/todo
+
+To move task 1 to StatusB, use create:
+
+ ; touch StatusB/1
+
+To edit a task, just open/read/write the task file. This will be forwarded to files-on-disk automatically.
+
+To rename a task:
+
+ ; mv StatusB/2-Second_Task 'StatusB/My other task'
+ ; ls StatusB/2*
+ 2-My_other_task
+
+To create a new task, create it:
+
+ ; touch 'StatusA/New Task'
+ ; ls StatusA
+ 3-New_Task
+
+To change assignee/group:
+
+ ; chgrp -o alice StatusA/3-New_Task
+ ; chgrp engineering StatusA/3-New_Task
+ ; ls -l StatusA
+ ... alice engineering ... 3-New_Task
--- a/todofs.c
+++ b/todofs.c
@@ -60,6 +60,20 @@
return (num << 2) | type & 3;
}
+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;
@@ -67,7 +81,6 @@
typedef struct Task Task;
struct Task {
Task* next;
- char hash[HASHLEN];
char *title;
ulong id;
Dir *dir;
@@ -84,7 +97,7 @@
};
Status *statuses = nil;
-ulong maxtask = 0;
+ulong nextid;
Task* getgtask(ulong id, Status **status);
@@ -103,6 +116,11 @@
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);
}
@@ -110,10 +128,10 @@
Bprint(bout, "\n");
tid = 0;
- while (tid <= maxtask) {
+ while (tid < nextid) {
t = getgtask(tid, &s);
if (t) {
- Bprint(bout, "key=task id=\"%s\" status=\"%s\"", t->hash, s->name);
+ Bprint(bout, "key=task id=\"%s\" status=\"%s\"", ultostr(t->id), s->name);
if (t->cassignee) {
Bprint(bout, " assignee=\"%s\"", t->dir->uid);
}
@@ -205,14 +223,17 @@
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 && (strcmp(t->hash, name) != 0)) {
+ while (t && l != t->id) {
t = t->next;
i++;
}
@@ -349,9 +370,9 @@
if (*c == ' ' || *c == '\t')
*c = '_';
}
- t->dir->name = smprint("%s-%s", t->hash, buf);
+ t->dir->name = smprint("%s-%s", ultostr(t->id), buf);
} else {
- t->dir->name = strdup(t->hash);
+ t->dir->name = smprint("%s", ultostr(t->id));
}
t->cfname = 1;
return 1;
@@ -360,22 +381,15 @@
static int
updatetask(Task *t, char *name, char *assignee, char *group, char *title)
{
- char path[256];
+ freetask(t);
- snprint(path, sizeof(path), "%s/%s", srcdir, name);
- snprint(t->hash, sizeof(t->hash), "%s", name);
+ t->dir = dirstat(name);
- freetask(t);
+ t->dir->uid = strdup(assignee ? assignee : "na");
+ t->cassignee = 1;
- t->dir = dirstat(path);
- if (assignee) {
- t->dir->uid = strdup(assignee);
- t->cassignee = 1;
- }
- if (group) {
- t->dir->gid = strdup(group);
- t->cgroup = 1;
- }
+ t->dir->gid = strdup(group ? group : "ng");
+ t->cgroup = 1;
return settasktitle(t, title);
}
@@ -440,7 +454,14 @@
if (ndbchanged(index))
ndbreopen(index);
- id = 0;
+ 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"))) {
@@ -447,14 +468,40 @@
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);
- id++;
} else
sysfatal("invalid index");
}
- maxtask = id;
}
+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)
{
@@ -560,7 +607,6 @@
taskread(Req *r)
{
Task *t;
- char path[256];
Biobuf *bin;
long n;
@@ -570,8 +616,7 @@
return 0;
}
- snprint(path, sizeof(path), "%s/%s", srcdir, t->hash);
- bin = Bopen(path, OREAD);
+ bin = Bopen(ultostr(t->id), OREAD);
if (!bin)
return 0;
@@ -623,7 +668,6 @@
taskwrite(Req *r)
{
Task *t;
- char path[256];
Biobuf *bout;
long n;
@@ -633,9 +677,7 @@
return 0;
}
- snprint(path, sizeof(path), "%s/%s", srcdir, t->hash);
-
- bout = Bopen(path, OWRITE);
+ bout = Bopen(ultostr(t->id), OWRITE);
if (!bout)
return 0;
@@ -744,9 +786,10 @@
void
fscreate(Req *r)
{
- Status *s;
+ Status *s, *s2;
Task *t;
int tid, sid;
+ ulong id;
switch (qidtype(r->fid->qid.path)) {
case Qstatus:
@@ -755,13 +798,16 @@
respond(r, "status not found");
return;
}
- t = getstask(r->ifcall.name, &sid, &tid);
+ id = strtoid(r->ifcall.name);
+ fprint(2, "id of task: %ulx\n", id);
+ t = getgtask(id, &s2);
if (t) {
+ /* move existing task */
if (sid == qidnum(r->fid->qid.path)) {
respond(r, "task already has status");
return;
}
- if (!taskmv(t, getnstatus(sid), s)) {
+ if (!taskmv(t, s2, s)) {
responderror(r);
return;
}
@@ -768,9 +814,19 @@
r->fid->qid = (Qid){mkqid(Qtask, t->id), 0, 0};
r->ofcall.qid = r->fid->qid;
} else {
- // TODO: create new task
- respond(r, "not implemented yet");
- return;
+ /* 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, 0};
+ r->ofcall.qid = r->fid->qid;
}
savedata();
break;
@@ -904,6 +960,9 @@
case 'm':
mtpt = EARGF(usage());
break;
+ case '9':
+ chatty9p++;
+ break;
default:
usage();
}ARGEND;
@@ -915,7 +974,10 @@
srcdir = *argv;
- idxfile = smprint("%s/index", srcdir);
+ if (chdir(srcdir))
+ sysfatal("unable to chdir: %r");
+
+ idxfile = "index";
assert(idxfile);
index = ndbopen(idxfile);
if (!index)