shithub: todofs

Download patch

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)