ref: 7c6a945996a1d5510ff1412320ac7d07a0f82851
dir: /main.c/
#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);
}