shithub: guifs

ref: 7c6a945996a1d5510ff1412320ac7d07a0f82851
dir: /main.c/

View raw version
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <draw.h>

#include "guifs.h"

#define Eexist	"file does not exist"
#define Enodir	"not a directory"
#define Eperm	"permission denied"
#define Eoffset	"can't write to this file at non-zero offset"
#define Ebadctl	"bad ctl message"

char *username;

enum {
	Qdir,
	Qclone,
	Qevent,
	Qtype,
	Qprops,

	Qprop,
};

enum {
	Fclone,
	Fevent,
	Ftype,
	Fprops,
	Fmax,
};

GuiElement *root;

#define QID_TYPE(q)	((q.path) & 0xFF)
#define QID_PROP(q)	(((q.path) >> 8) & 0xFF)

void *
emalloc(ulong size)
{
	void *p = mallocz(size, 1);
	if(!p)
		sysfatal("malloc failed");
	return p;
}

void *
erealloc(void *p, ulong size)
{
	p = realloc(p, size);
	if(!p)
		sysfatal("realloc failed");
	return p;
}

Qid
mkqid(int type)
{
	static int id = 0;

	Qid q;
	q.vers = 0;
	q.path = (type & 0xFFFF) | (id << 16);
	id++;
	switch(type){
	case Qdir:
	case Qprops:
		q.type = QTDIR;
		break;
	case Qclone:
	case Qevent:
	case Qtype:
		q.type = QTFILE;
		break;
	}
	return q;
}

Qid
mkpropqid(int proptag)
{
	return mkqid(Qprop | ((proptag & 0xFF) << 8));
}

void
settype(GuiElement *g, int type)
{
	GuiSpec spec = guispecs[type];
	free(g->props);
	g->type = type;

	g->nprops = spec.nprops;
	g->props = emalloc(sizeof(Prop) * spec.nprops);
	for(int i = 0; i < spec.nprops; i++){
		int tag = spec.proptags[i];
		g->props[i].tag = tag;
		g->props[i].val = propspecs[tag].def();
		g->props[i].qid = mkpropqid(tag);
	}

	updategui(0); /* redraw everything */
}

GuiElement *
newgui(GuiElement *parent)
{
	GuiElement *g = emalloc(sizeof(GuiElement));
	memset(g, 0, sizeof(GuiElement));
	g->parent = parent;
	g->qid = mkqid(Qdir);
	g->qclone = mkqid(Qclone);
	g->qevent = mkqid(Qevent);
	g->qtype = mkqid(Qtype);
	g->qprops = mkqid(Qprops);

	if(parent){
		g->id = parent->nchildren;
		parent->nchildren++;
		parent->children = erealloc(parent->children, parent->nchildren * sizeof(GuiElement *));
		parent->children[g->id] = g;
	}

	settype(g, Gcontainer);

	return g;
}

GuiElement *
findchild(GuiElement *g, char *name)
{
	char *r;
	uvlong id = strtoull(name, &r, 10);
	if(*r != 0){
		return nil;
	}

	if(id < g->nchildren)
		return g->children[id];

	return nil;
}

void
fsattach(Req *r)
{
	if(root == nil){
		GuiElement *g = newgui(nil);
		root = g;
		settype(g, Gcontainer);
	}

	r->fid->aux = root;
	r->fid->qid = root->qid;
	r->ofcall.qid = r->fid->qid;

	respond(r, nil);
}

char *
fswalk1(Fid *fid, char *name, Qid *qid)
{
	GuiElement *g = fid->aux;
	GuiElement *child;

	switch(QID_TYPE(fid->qid)){
	case Qdir:
		if(strcmp(name, "..") == 0){
			if(g->parent == nil) // Root element
				*qid = g->qid;
			else{
				fid->aux = g->parent;
				*qid = g->parent->qid;
			}
			return nil;
		}else if(strcmp(name, "clone") == 0){
			*qid = g->qclone;
			return nil;
		}else if(strcmp(name, "event") == 0){
			*qid = g->qevent;
			return nil;
		}else if(strcmp(name, "type") == 0){
			*qid = g->qtype;
			return nil;
		}else if(strcmp(name, "props") == 0){
			*qid = g->qprops;
			return nil;
		}else if(child = findchild(g, name)){
			fid->aux = child;
			*qid = child->qid;
			return nil;
		}
		return Eexist;
	case Qprops:
		if(strcmp(name, "..") == 0){
			*qid = g->qid;
			return nil;
		}
		for(int i = 0; i < g->nprops; i++){
			PropSpec spec = propspecs[g->props[i].tag];
			if(strcmp(name, spec.name) == 0){
				*qid = g->props[i].qid;
				return nil;
			}
		}
		return Eexist;
	default:
		return Enodir;
	}
}

char *
fsclone(Fid *old, Fid *new)
{
	new->aux = old->aux;
	return nil;
}

void
fsopen(Req *r)
{
	GuiElement *g = r->fid->aux;

	switch(QID_TYPE(r->fid->qid)){
	case Qdir:
	case Qevent:
	case Qclone:
	case Qprops:
		if(r->ifcall.mode != OREAD){
			respond(r, Eperm);
			return;
		}
		break;
	}

	if(QID_TYPE(r->fid->qid) == Qclone){
		/* Create a new child gui element */
		GuiElement *child = newgui(g);

		assert(r->fid->qid.vers == child->id);

		/* Update qid version, so a read reports the correct child id */
		assert(memcmp(&r->fid->qid, &g->qclone, sizeof(Qid)) == 0);
		g->qclone.vers++;
		r->fid->qid = g->qclone;
		r->ofcall.qid = g->qclone;
	}

	respond(r, nil);
}

void
fsstat(Req *r)
{
	r->d.qid = r->fid->qid;
	r->d.uid = estrdup9p(username);
	r->d.gid = estrdup9p(username);
	r->d.muid = estrdup9p(username);
	switch(QID_TYPE(r->fid->qid)){
	case Qdir:
		r->d.name = estrdup9p("/");
		r->d.mode = 0555|DMDIR;
		break;
	case Qprops:
		r->d.name = estrdup9p("/");
		r->d.mode = 0555|DMDIR;
		break;
	}

	respond(r, nil);
}

int
dirtreegen(int n, Dir *d, void *aux)
{
	GuiElement *g = aux;

	d->uid = estrdup9p(username);
	d->gid = estrdup9p(username);
	d->muid = estrdup9p(username);

	if(n < Fmax){
		d->length = 0;

		switch(n){
		case Fclone:
			d->mode = 0444;
			d->name = estrdup9p("clone");
			d->qid = g->qclone;
			break;
		case Fevent:
			d->mode = 0444;
			d->name = estrdup9p("event");
			d->qid = g->qevent;
			break;
		case Ftype:
			d->mode = 0666;
			d->name = estrdup9p("type");
			d->qid = g->qtype;
			break;
		case Fprops:
			d->mode = 0555|DMDIR;
			d->name = estrdup9p("props");
			d->qid = g->qprops;
			break;
		}
		return 0;
	}else
		n -= Fmax;

	if(g && n < g->nchildren){
		GuiElement *child = g->children[n];

		d->mode = DMDIR|0555;
		d->qid = child->qid;

		char buf[64];
		snprint(buf, sizeof(buf), "%d", child->id);
		d->name = estrdup9p(buf);
		return 0;
	}

	return -1;
}

int
proptreegen(int n, Dir *d, void *aux)
{
	GuiElement *g = aux;

	d->uid = estrdup9p(username);
	d->gid = estrdup9p(username);
	d->muid = estrdup9p(username);

	if(n >= g->nprops)
		return -1;

	PropSpec spec = propspecs[g->props[n].tag];
	d->mode = 0666;
	d->name = estrdup9p(spec.name);
	d->qid = g->props[n].qid;
	return 0;
}

void
fsread(Req *r)
{
	GuiElement *g = r->fid->aux;
	char buf[256];

	switch(QID_TYPE(r->fid->qid)){
	case Qdir:
		dirread9p(r, dirtreegen, g);
		break;
	case Qclone:
		snprint(buf, sizeof(buf), "%uld\n", r->fid->qid.vers-1);
		readstr(r, buf);
		break;
	case Qevent:
		/* in another thread, wait for events on a channel
		 * and call readstr on each of them individually.
		 */
		readstr(r, "eveeent\n");
		break;
	case Qtype:
		snprint(buf, sizeof(buf), "%s\n", guispecs[g->type].name);
		readstr(r, buf);
		break;
	case Qprops:
		dirread9p(r, proptreegen, g);
		break;
	case Qprop:
		{
			int tag = QID_PROP(r->fid->qid);
			PropSpec spec = propspecs[tag];
			PropVal val = getprop(g, tag);
			char *str = spec.print(val);
			readstr(r, str);
			free(str);
		}
		break;
	}
	respond(r, nil);
}

void
fswrite(Req *r)
{
	GuiElement *g = r->fid->aux;
	char *err = nil;
	
	switch(QID_TYPE(r->fid->qid)){
	case Qtype:
		err = "Can't switch type";
		break;
	case Qprop:
		{
			int tag = QID_PROP(r->fid->qid);
			PropSpec spec = propspecs[tag];
			PropVal val;

			char *buf = emalloc(r->ifcall.count + 1);
			buf[r->ifcall.count] = 0;
			memcpy(buf, r->ifcall.data, r->ifcall.count);
			err = spec.parse(buf, &val);
			if(err == nil)
				setprop(g, tag, val);
			free(buf);
		}
	}

	respond(r, err);
}

Srv fs = {
	.attach = fsattach,
	.walk1 = fswalk1,
	.clone = fsclone,
	.open = fsopen,
	.stat = fsstat,
	.read = fsread,
	.write = fswrite,
};

void
usage(void)
{
	fprint(2, "usage: %s [-D] [-m mountpoint] [-s srvname] \n", argv0);
	exits("usage");
}

void
threadmain(int argc, char **argv)
{
	char *mtpt = "/mnt/gui";
	char *srvname = nil;
	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 's':
		srvname = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;

	if(argc > 1)
		usage();

	username = getuser();
	initgraphics();
	threadpostmountsrv(&fs, srvname, mtpt, MREPL);
	exits(nil);
}