commit 7c709434eccd461a3f3c522df6224adf4e50a8da from: rsc date: Fri Mar 18 19:30:22 2005 UTC new files commit - 72fd2f881907db5e367ffee1fcecb737c44a0090 commit + 7c709434eccd461a3f3c522df6224adf4e50a8da blob - /dev/null blob + cea426d0e5a2a7843406ea3f6f08a476608a4625 (mode 644) --- /dev/null +++ src/cmd/netfiles/COPYING @@ -0,0 +1,24 @@ + +Copyright (c) 2005 Russ Cox + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +These conditions shall not be whined about. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + blob - /dev/null blob + 7519da77fc68810a15d1f955a07c38fc03c904bf (mode 644) --- /dev/null +++ src/cmd/netfiles/acme.c @@ -0,0 +1,636 @@ +#include +#include +#include +#include <9pclient.h> +#include "acme.h" + +extern int *xxx; +static CFsys *acmefs; +Win *windows; +static Win *last; + +void +mountacme(void) +{ + if(acmefs == nil){ + acmefs = nsmount("acme", nil); + if(acmefs == nil) + sysfatal("cannot mount acme: %r"); + } +} + +Win* +newwin(void) +{ + Win *w; + CFid *fid; + char buf[100]; + int id, n; + + mountacme(); + fid = fsopen(acmefs, "new/ctl", ORDWR); + if(fid == nil) + sysfatal("open new/ctl: %r"); + n = fsread(fid, buf, sizeof buf-1); + if(n <= 0) + sysfatal("read new/ctl: %r"); + buf[n] = 0; + id = atoi(buf); + if(id == 0) + sysfatal("read new/ctl: malformed message: %s", buf); + + w = emalloc(sizeof *w); + w->id = id; + w->ctl = fid; + w->next = nil; + w->prev = last; + if(last) + last->next = w; + else + windows = w; + last = w; + return w; +} + +void +winclosefiles(Win *w) +{ + if(w->ctl){ + fsclose(w->ctl); + w->ctl = nil; + } + if(w->body){ + fsclose(w->body); + w->body = nil; + } + if(w->addr){ + fsclose(w->addr); + w->addr = nil; + } + if(w->tag){ + fsclose(w->tag); + w->tag = nil; + } + if(w->event){ + fsclose(w->event); + w->event = nil; + } + if(w->data){ + fsclose(w->data); + w->data = nil; + } + if(w->xdata){ + fsclose(w->xdata); + w->xdata = nil; + } +} + +void +winfree(Win *w) +{ + winclosefiles(w); + if(w->c){ + chanfree(w->c); + w->c = nil; + } + if(w->next) + w->next->prev = w->prev; + else + last = w->prev; + if(w->prev) + w->prev->next = w->next; + else + windows = w->next; + free(w); +} + +void +windeleteall(void) +{ + Win *w, *next; + + for(w=windows; w; w=next){ + next = w->next; + winctl(w, "delete"); + } +} + +static CFid* +wfid(Win *w, char *name) +{ + char buf[100]; + CFid **fid; + + if(strcmp(name, "ctl") == 0) + fid = &w->ctl; + else if(strcmp(name, "body") == 0) + fid = &w->body; + else if(strcmp(name, "addr") == 0) + fid = &w->addr; + else if(strcmp(name, "tag") == 0) + fid = &w->tag; + else if(strcmp(name, "event") == 0) + fid = &w->event; + else if(strcmp(name, "data") == 0) + fid = &w->data; + else if(strcmp(name, "xdata") == 0) + fid = &w->xdata; + else{ + fid = 0; + sysfatal("bad window file name %s", name); + } + + if(*fid == nil){ + snprint(buf, sizeof buf, "acme/%d/%s", w->id, name); + *fid = fsopen(acmefs, buf, ORDWR); + if(*fid == nil) + sysfatal("open %s: %r", buf); + } + return *fid; +} + +int +winopenfd(Win *w, char *name, int mode) +{ + char buf[100]; + + snprint(buf, sizeof buf, "%d/%s", w->id, name); + return fsopenfd(acmefs, buf, mode); +} + +int +winctl(Win *w, char *fmt, ...) +{ + char *s; + va_list arg; + CFid *fid; + int n; + + va_start(arg, fmt); + s = evsmprint(fmt, arg); + va_end(arg); + + fid = wfid(w, "ctl"); + n = fspwrite(fid, s, strlen(s), 0); + free(s); + return n; +} + +int +winname(Win *w, char *fmt, ...) +{ + char *s; + va_list arg; + int n; + + va_start(arg, fmt); + s = evsmprint(fmt, arg); + va_end(arg); + + n = winctl(w, "name %s\n", s); + free(s); + return n; +} + +int +winprint(Win *w, char *name, char *fmt, ...) +{ + char *s; + va_list arg; + int n; + + va_start(arg, fmt); + s = evsmprint(fmt, arg); + va_end(arg); + + n = fswrite(wfid(w, name), s, strlen(s)); + free(s); + return n; +} + +int +winaddr(Win *w, char *fmt, ...) +{ + char *s; + va_list arg; + int n; + + va_start(arg, fmt); + s = evsmprint(fmt, arg); + va_end(arg); + + n = fswrite(wfid(w, "addr"), s, strlen(s)); + free(s); + return n; +} + +int +winreadaddr(Win *w, uint *q1) +{ + char buf[40], *p; + uint q0; + int n; + + n = fspread(wfid(w, "addr"), buf, sizeof buf-1, 0); + if(n <= 0) + return -1; + buf[n] = 0; + q0 = strtoul(buf, &p, 10); + if(q1) + *q1 = strtoul(p, nil, 10); + return q0; +} + +int +winread(Win *w, char *file, void *a, int n) +{ + return fspread(wfid(w, file), a, n, 0); +} + +int +winwrite(Win *w, char *file, void *a, int n) +{ + return fswrite(wfid(w, file), a, n); +} + +char* +fsreadm(CFid *fid) +{ + char *buf; + int n, tot, m; + + m = 128; + buf = emalloc(m+1); + tot = 0; + while((n = fspread(fid, buf+tot, m-tot, tot)) > 0){ + tot += n; + if(tot >= m){ + m += 128; + buf = erealloc(buf, m+1); + } + } + if(n < 0){ + free(buf); + return nil; + } + buf[tot] = 0; + return buf; +} + +char* +winmread(Win *w, char *file) +{ + return fsreadm(wfid(w, file)); +} + +char* +winindex(void) +{ + CFid *fid; + char *s; + + mountacme(); + if((fid = fsopen(acmefs, "index", ORDWR)) == nil) + return nil; + s = fsreadm(fid); + fsclose(fid); + return s; +} + +int +winseek(Win *w, char *file, int n, int off) +{ + return fsseek(wfid(w, file), n, off); +} + +int +winwriteevent(Win *w, Event *e) +{ + char buf[100]; + + snprint(buf, sizeof buf, "%c%c%d %d \n", e->c1, e->c2, e->q0, e->q1); + return fswrite(wfid(w, "event"), buf, strlen(buf)); +} + +int +windel(Win *w, int sure) +{ + return winctl(w, sure ? "delete" : "del"); +} + +int +winfd(Win *w, char *name, int mode) +{ + char buf[100]; + + snprint(buf, sizeof buf, "acme/%d/%s", w->id, name); + return fsopenfd(acmefs, buf, mode); +} + +static void +error(Win *w, char *msg) +{ + if(msg == nil) + longjmp(w->jmp, 1); + fprint(2, "%s: win%d: %s\n", argv0, w->id, msg); + longjmp(w->jmp, 2); +} + +static int +getec(Win *w, CFid *efd) +{ + if(w->nbuf <= 0){ + w->nbuf = fsread(efd, w->buf, sizeof w->buf); + if(w->nbuf <= 0) + error(w, nil); + w->bufp = w->buf; + } + --w->nbuf; + return *w->bufp++; +} + +static int +geten(Win *w, CFid *efd) +{ + int n, c; + + n = 0; + while('0'<=(c=getec(w,efd)) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error(w, "event number syntax"); + return n; +} + +static int +geter(Win *w, CFid *efd, char *buf, int *nb) +{ + Rune r; + int n; + + r = getec(w, efd); + buf[0] = r; + n = 1; + if(r < Runeself) + goto Return; + while(!fullrune(buf, n)) + buf[n++] = getec(w, efd); + chartorune(&r, buf); + Return: + *nb = n; + return r; +} + +static void +gete(Win *w, CFid *efd, Event *e) +{ + int i, nb; + + e->c1 = getec(w, efd); + e->c2 = getec(w, efd); + e->q0 = geten(w, efd); + e->q1 = geten(w, efd); + e->flag = geten(w, efd); + e->nr = geten(w, efd); + if(e->nr > EVENTSIZE) + error(w, "event string too long"); + e->nb = 0; + for(i=0; inr; i++){ + /* e->r[i] = */ geter(w, efd, e->text+e->nb, &nb); + e->nb += nb; + } +/* e->r[e->nr] = 0; */ + e->text[e->nb] = 0; + if(getec(w, efd) != '\n') + error(w, "event syntax 2"); +} + +int +winreadevent(Win *w, Event *e) +{ + CFid *efd; + int r; + + if((r = setjmp(w->jmp)) != 0){ + if(r == 1) + return 0; + return -1; + } + efd = wfid(w, "event"); + gete(w, efd, e); + e->oq0 = e->q0; + e->oq1 = e->q1; + + /* expansion */ + if(e->flag&2){ + gete(w, efd, &w->e2); + if(e->q0==e->q1){ + w->e2.oq0 = e->q0; + w->e2.oq1 = e->q1; + w->e2.flag = e->flag; + *e = w->e2; + } + } + + /* chorded argument */ + if(e->flag&8){ + gete(w, efd, &w->e3); /* arg */ + gete(w, efd, &w->e4); /* location */ + strcpy(e->arg, w->e3.text); + strcpy(e->loc, w->e4.text); + } + + return 1; +} + +int +eventfmt(Fmt *fmt) +{ + Event *e; + + e = va_arg(fmt->args, Event*); + return fmtprint(fmt, "%c%c %d %d %d %d %q", e->c1, e->c2, e->q0, e->q1, e->flag, e->nr, e->text); +} + +void* +emalloc(uint n) +{ + void *v; + + v = mallocz(n, 1); + if(v == nil) + sysfatal("out of memory"); + return v; +} + +void* +erealloc(void *v, uint n) +{ + v = realloc(v, n); + if(v == nil) + sysfatal("out of memory"); + return v; +} + +char* +estrdup(char *s) +{ + if(s == nil) + return nil; + s = strdup(s); + if(s == nil) + sysfatal("out of memory"); + return s; +} + +char* +evsmprint(char *s, va_list v) +{ + s = vsmprint(s, v); + if(s == nil) + sysfatal("out of memory"); + return s; +} + +int +pipewinto(Win *w, char *name, int errto, char *cmd, ...) +{ + va_list arg; + char *p; + int fd[3], pid; + + va_start(arg, cmd); + p = evsmprint(cmd, arg); + va_end(arg); + fd[0] = winfd(w, name, OREAD); + fd[1] = dup(errto, -1); + fd[2] = dup(errto, -1); + pid = threadspawnl(fd, "rc", "rc", "-c", p, 0); + free(p); + return pid; +} + +int +pipetowin(Win *w, char *name, int errto, char *cmd, ...) +{ + va_list arg; + char *p; + int fd[3], pid, pfd[2]; + char buf[1024]; + int n; + + /* + * cannot use winfd here because of buffering caused + * by pipe. program might exit before final write to acme + * happens. so we might return before the final write. + * + * to avoid this, we tend the pipe ourselves. + */ + if(pipe(pfd) < 0) + sysfatal("pipe: %r"); + va_start(arg, cmd); + p = evsmprint(cmd, arg); + va_end(arg); + fd[0] = open("/dev/null", OREAD); + fd[1] = pfd[1]; + if(errto == 0) + fd[2] = dup(fd[1], -1); + else + fd[2] = dup(errto, -1); + pid = threadspawnl(fd, "rc", "rc", "-c", p, 0); + free(p); + while((n = read(pfd[0], buf, sizeof buf)) > 0) + winwrite(w, name, buf, n); + close(pfd[0]); + return pid; +} + +char* +sysrun(int errto, char *fmt, ...) +{ + static char buf[1024]; + char *cmd; + va_list arg; + int n, fd[3], p[2], tot, pid; + +#undef pipe + if(pipe(p) < 0) + sysfatal("pipe: %r"); + fd[0] = open("/dev/null", OREAD); + fd[1] = p[1]; + if(errto == 0) + fd[2] = dup(fd[1], -1); + else + fd[2] = dup(errto, -1); + + va_start(arg, fmt); + cmd = evsmprint(fmt, arg); + va_end(arg); + pid = threadspawnl(fd, "rc", "rc", "-c", cmd, 0); + + tot = 0; + while((n = read(p[0], buf+tot, sizeof buf-tot)) > 0) + tot += n; + close(p[0]); + twait(pid); + if(n < 0) + return nil; + free(cmd); + if(tot == sizeof buf) + tot--; + buf[tot] = 0; + while(tot > 0 && isspace(buf[tot-1])) + tot--; + buf[tot] = 0; + if(tot == 0){ + werrstr("no output"); + return nil; + } + return estrdup(buf); +} + +static void +eventreader(void *v) +{ + Event e[2]; + Win *w; + int i; + + w = v; + i = 0; + for(;;){ + if(winreadevent(w, &e[i]) <= 0) + break; + sendp(w->c, &e[i]); + i = 1-i; /* toggle */ + } + sendp(w->c, nil); + threadexits(nil); +} + +Channel* +wineventchan(Win *w) +{ + if(w->c == nil){ + w->c = chancreate(sizeof(Event*), 0); + threadcreate(eventreader, w, 32*1024); + } + return w->c; +} + +char* +wingetname(Win *w) +{ + int n; + char *p; + + n = winread(w, "tag", w->name, sizeof w->name-1); + if(n <= 0) + return nil; + w->name[n] = 0; + p = strchr(w->name, ' '); + if(p) + *p = 0; + return w->name; +} + blob - /dev/null blob + 50997e97a728135b44bee4666742ccf1cf1eefc7 (mode 644) --- /dev/null +++ src/cmd/netfiles/acme.h @@ -0,0 +1,82 @@ +typedef struct Event Event; +typedef struct Win Win; + +#define EVENTSIZE 256 +struct Event +{ + int c1; + int c2; + int oq0; + int oq1; + int q0; + int q1; + int flag; + int nb; + int nr; + char text[EVENTSIZE*UTFmax+1]; + char arg[EVENTSIZE*UTFmax+1]; + char loc[EVENTSIZE*UTFmax+1]; +}; + +struct Win +{ + int id; + CFid *ctl; + CFid *tag; + CFid *body; + CFid *addr; + CFid *event; + CFid *data; + CFid *xdata; + Channel *c; /* chan(Event) */ + Win *next; + Win *prev; + + /* events */ + int nbuf; + char name[1024]; + char buf[1024]; + char *bufp; + jmp_buf jmp; + Event e2; + Event e3; + Event e4; +}; + +Win *newwin(void); + +int eventfmt(Fmt*); +int pipewinto(Win *w, char *name, int, char *fmt, ...); +int pipetowin(Win *w, char *name, int, char *fmt, ...); +char *sysrun(int errto, char*, ...); +int winaddr(Win *w, char *fmt, ...); +int winctl(Win *w, char *fmt, ...); +int windel(Win *w, int sure); +int winfd(Win *w, char *name, int); +char *winmread(Win *w, char *file); +int winname(Win *w, char *fmt, ...); +int winprint(Win *w, char *name, char *fmt, ...); +int winread(Win *w, char *file, void *a, int n); +int winseek(Win *w, char *file, int n, int off); +int winreadaddr(Win *w, uint*); +int winreadevent(Win *w, Event *e); +int winwrite(Win *w, char *file, void *a, int n); +int winwriteevent(Win *w, Event *e); +int winopenfd(Win *w, char *name, int mode); +void windeleteall(void); +void winfree(Win *w); +void winclosefiles(Win *w); +Channel *wineventchan(Win *w); +char *winindex(void); +void mountacme(void); +char *wingetname(Win *w); + +void *erealloc(void*, uint); +void *emalloc(uint); +char *estrdup(char*); +char *evsmprint(char*, va_list); + +int twait(int); +void twaitinit(void); + +extern Win *windows; blob - /dev/null blob + 0b58e48b75300b27ef9e05bf6249089c8133bf36 (mode 644) --- /dev/null +++ src/cmd/netfiles/main.c @@ -0,0 +1,490 @@ +/* + * Remote file system editing client. + * Only talks to acme - external programs do all the hard work. + * + * If you add a plumbing rule: + +# /n/ paths go to simulator in acme +kind is text +data matches '[a-zA-Z0-9_\-./]+('$addr')?' +data matches '(/n/[a-zA-Z0-9_\-./]+)('$addr')?' +plumb to netfileedit +plumb client Netfiles + + * then plumbed paths starting with /n/ will find their way here. + * + * Perhaps on startup should look for windows named /n/ and attach to them? + * Or might that be too aggressive? + */ + +#include +#include +#include +#include <9pclient.h> +#include +#include "acme.h" + +char *root = "/n/"; + +void +usage(void) +{ + fprint(2, "usage: Netfiles\n"); + threadexitsall("usage"); +} + +extern int chatty9pclient; +int debug; +#define dprint if(debug)print +Win *mkwin(char*); +int do3(Win *w, char *arg); + +enum { + STACK = 128*1024, +}; + +enum { + Put, + Get, + Del, + Delete, + Debug, + XXX +}; + +char *cmds[] = { + "Put", + "Get", + "Del", + "Delete", + "Debug", + nil +}; + +typedef struct Arg Arg; +struct Arg +{ + char *file; + char *addr; + Channel *c; +}; + +Arg* +arg(char *file, char *addr, Channel *c) +{ + Arg *a; + + a = emalloc(sizeof *a); + a->file = estrdup(file); + a->addr = estrdup(addr); + a->c = c; + return a; +} + +/* + * return window id of a window named name or name/ + * assumes name is cleaned. + */ +int +nametowinid(char *name) +{ + char *index, *p, *next; + int len, n; + + index = winindex(); + n = -1; + len = strlen(name); + for(p=index; p && *p; p=next){ + if((next = strchr(p, '\n')) != nil) + *next = 0; + if(strlen(p) <= 5*12) + continue; + if(memcmp(p+5*12, name, len)!=0) + continue; + if(p[5*12+len]!=' ' && (p[5*12+len]!='/' || p[5*12+len+1]!=' ')) + continue; + n = atoi(p); + break; + } + free(index); + return n; +} + +/* + * look up window by name + */ +Win* +nametowin(char *name) +{ + int id; + Win *w; + + id = nametowinid(name); + if(id == -1) + return nil; + for(w=windows; w; w=w->next) + if(w->id == id) + return w; + return nil; +} + +/* + * look for s in list + */ +int +lookup(char *s, char **list) +{ + int i; + + for(i=0; list[i]; i++) + if(strcmp(list[i], s) == 0) + return i; + return -1; +} + +/* + * move to top of file + */ +void +wintop(Win *w) +{ + winaddr(w, "#0"); + winctl(w, "dot=addr"); + winctl(w, "show"); +} + +/* + * Expand the click further than acme usually does -- all non-white space is okay. + */ +char* +expandarg(Win *w, Event *e) +{ + if(e->c2 == 'l') + return estrdup(e->text); + dprint("expand %d %d %d %d\n", e->oq0, e->oq1, e->q0, e->q1); + if(e->oq0 == e->oq1 && e->q0 != e->q1) + winaddr(w, "#%ud+#1-/[^ \t\\n]*/,#%ud-#1+/[^ \t\\n]*/", e->q0, e->q1); + else + winaddr(w, "#%ud,#%ud", e->q0, e->q1); + return winmread(w, "xdata"); +} + +/* + * handle a plumbing message + */ +void +doplumb(void *vm) +{ + char *addr; + Plumbmsg *m; + Win *w; + + m = vm; + if(m->ndata >= 1024){ + fprint(2, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", + m->ndata, m->data); + plumbfree(m); + return; + } + + addr = plumblookup(m->attr, "addr"); + w = nametowin(m->data); + if(w == nil) + w = mkwin(m->data); + winaddr(w, "%s", addr); + winctl(w, "dot=addr"); + winctl(w, "show"); +// windecref(w); + plumbfree(m); +} + +/* + * dispatch messages from the plumber + */ +void +plumbthread(void *v) +{ + CFid *fid; + Plumbmsg *m; + + fid = plumbopenfid("netfileedit", OREAD); + if(fid == nil){ + fprint(2, "cannot open plumb/netfileedit: %r\n"); + return; + } + while((m = plumbrecvfid(fid)) != nil) + threadcreate(doplumb, m, STACK); + fsclose(fid); +} + +/* + * parse /n/system/path + */ +int +parsename(char *name, char **server, char **path) +{ + char *p, *nul; + + cleanname(name); + if(strncmp(name, "/n/", 3) != 0 && name[3] == 0) + return -1; + nul = nil; + if((p = strchr(name+3, '/')) == nil) + *path = estrdup("/"); + else{ + *path = estrdup(p); + *p = 0; + nul = p; + } + p = name+3; + if(p[0] == 0){ + free(*path); + *server = *path = nil; + if(nul) + *nul = '/'; + return -1; + } + *server = estrdup(p); + if(nul) + *nul = '/'; + return 0; +} + +/* + * shell out to find the type of a given file + */ +char* +filestat(char *server, char *path) +{ + return sysrun(2, "9 netstat %q %q", server, path); +} + +/* + * manage a single window + */ +void +filethread(void *v) +{ + char *arg, *name, *p, *server, *path, *type; + Arg *a; + Channel *c; + Event *e; + Win *w; + + a = v; + threadsetname("file %s", a->file); + w = newwin(); + winname(w, a->file); + winprint(w, "tag", "Get Put Look "); + c = wineventchan(w); + + goto caseGet; + + while((e=recvp(c)) != nil){ + if(e->c1!='K') + dprint("acme %E\n", e); + if(e->c1=='M') + switch(e->c2){ + case 'x': + case 'X': + switch(lookup(e->text, cmds)){ + caseGet: + case Get: + server = nil; + path = nil; + if(parsename(name=wingetname(w), &server, &path) < 0){ + fprint(2, "Netfiles: bad name %s\n", name); + goto out; + } + type = filestat(server, path); + if(type == nil) + type = estrdup(""); + if(strcmp(type, "file")==0 || strcmp(type, "directory")==0){ + winaddr(w, ","); + winprint(w, "data", "[reading...]"); + winaddr(w, ","); + if(strcmp(type, "file")==0) + twait(pipetowin(w, "data", 2, "9 netget %q %q", server, path)); + else + twait(pipetowin(w, "data", 2, "9 netget -d %q %q | winid=%d mc", server, path, w->id)); + cleanname(name); + if(strcmp(type, "directory")==0){ + p = name+strlen(name); + if(p[-1] != '/'){ + p[0] = '/'; + p[1] = 0; + } + } + winname(w, name); + wintop(w); + winctl(w, "clean"); + if(a && a->addr){ + winaddr(w, "%s", a->addr); + winctl(w, "dot=addr"); + winctl(w, "show"); + } + } + free(type); + out: + free(server); + free(path); + if(a){ + if(a->c){ + sendp(a->c, w); + a->c = nil; + } + free(a->file); + free(a->addr); + free(a); + a = nil; + } + break; + case Put: + server = nil; + path = nil; + if(parsename(name=wingetname(w), &server, &path) < 0){ + fprint(2, "Netfiles: bad name %s\n", name); + goto out; + } + if(twait(pipewinto(w, "body", 2, "9 netput %q %q", server, path)) >= 0){ + cleanname(name); + winname(w, name); + winctl(w, "clean"); + } + free(server); + free(path); + break; + case Del: + winctl(w, "del"); + break; + case Delete: + winctl(w, "delete"); + break; + case Debug: + debug = !debug; + break; + default: + winwriteevent(w, e); + break; + } + break; + case 'l': + case 'L': + arg = expandarg(w, e); + if(arg!=nil && do3(w, arg) < 0) + winwriteevent(w, e); + free(arg); + break; + } + } + winfree(w); +} + +/* + * handle a button 3 click + */ +int +do3(Win *w, char *text) +{ + char *addr, *name, *type, *server, *path, *p, *q; + static char lastfail[1000]; + + if(text[0] == '/') + name = estrdup(text); + else{ + p = wingetname(w); + q = strrchr(p, '/'); + *(q+1) = 0; + name = emalloc(strlen(p)+1+strlen(text)+1); + strcpy(name, p); + strcat(name, "/"); + strcat(name, text); + } + dprint("do3 %s => %s\n", text, name); + if((addr = strchr(name, ':')) != nil) + *addr++ = 0; + cleanname(name); + if(strcmp(name, lastfail) == 0){ + free(name); + return -1; + } + if(parsename(name, &server, &path) < 0){ + free(name); + return -1; + } + type = filestat(server, path); + free(server); + free(path); + if(strcmp(type, "file")==0 || strcmp(type, "directory")==0){ + w = nametowin(name); + if(w == nil) + w = mkwin(name); + winaddr(w, "%s", addr); + winctl(w, "dot=addr"); + winctl(w, "show"); + free(name); + free(type); + return 0; + } + /* + * remember last name that didn't exist so that + * only the first right-click is slow when searching for text. + */ + strecpy(lastfail, lastfail+sizeof lastfail, name); + free(name); + return -1; +} + +Win* +mkwin(char *name) +{ + Arg *a; + Channel *c; + Win *w; + + c = chancreate(sizeof(void*), 0); + a = arg(name, nil, c); + threadcreate(filethread, a, STACK); + w = recvp(c); + chanfree(c); + return w; +} + +void +loopthread(void *v) +{ + QLock lk; + + qlock(&lk); + qlock(&lk); +} + +void +threadmain(int argc, char **argv) +{ + ARGBEGIN{ + case '9': + chatty9pclient = 1; + break; + case 'D': + debug = 1; + break; + default: + usage(); + }ARGEND + + if(argc) + usage(); + + threadnotify(nil, 0); /* set up correct default handlers */ + + fmtinstall('E', eventfmt); + doquote = needsrcquote; + quotefmtinstall(); + + twaitinit(); + threadcreate(plumbthread, nil, STACK); + threadcreate(loopthread, nil, STACK); + threadexits(nil); +} + blob - /dev/null blob + 7b8a033fa344e03ef869c1faf372f7dad345fd63 (mode 644) --- /dev/null +++ src/cmd/netfiles/mkfile @@ -0,0 +1,28 @@ +<$PLAN9/src/mkhdr + +TARG=Netfiles + +OFILES=\ + acme.$O\ + main.$O\ + wait.$O\ + +HFILES=acme.h + +<$PLAN9/src/mkone + +XTARG=\ + netget\ + netput\ + netstat\ + +install:V: + for i in $XTARG; do + cp $i $BIN + done + +push:V: + tar cf - mkfile acme.c main.c wait.c acme.h netget netput netstat | + gzip >netfiles.tar.gz + scp netfiles.tar.gz swtch.com:www/swtch.com + blob - /dev/null blob + c621482d7fbd2de1aa5a30284f88b14f02bbcdd2 (mode 755) --- /dev/null +++ src/cmd/netfiles/netfileget @@ -0,0 +1,45 @@ +#!/usr/local/plan9/bin/rc + +f=getfile +if(~ $1 -d){ + f=getdir + shift +} + +if(! ~ $#* 2){ + echo 'usage: netget [-d] system path' >[1=2] + exit usage +} + +ns=`{namespace} +if(u test -S $ns/$1) + f=$f^9p + +t=/tmp/netget.$pid.$USER +fn sigexit { rm -f $t } + +fn getfile { + rm -f $t + if(! echo get $2 $t | sftp -b - $1 >/dev/null) + exit 1 + cat $t +} + +fn getfile9p { + if(! 9p read $1/$2) + exit 1 +} + +fn getdir { + if(! {echo cd $2; echo ls -l} | sftp -b - $1 | sed '1,2d; s/sftp> //g; /^$/d' >$t) + exit 1 + cat $t | awk '$NF == "." || $NF == ".." { next } {s = $NF; if($0 ~ /^d/) s = s "/"; print s}' +} + +fn getdir9p { + 9p ls -l $1/$2 | awk '{s=$NF; if($0 ~ /^d/) s=s"/"; print s}' +} + +$f $1 $2 +exit 0 + blob - /dev/null blob + baa3eb127bd90d006d9a0c3ceee54a45fb156569 (mode 755) --- /dev/null +++ src/cmd/netfiles/netfileput @@ -0,0 +1,27 @@ +#!/usr/local/plan9/bin/rc + +if(! ~ $#* 2){ + echo 'usage: netput system path' >[1=2] + exit usage +} + +f=putfile +ns=`{namespace} +if(u test -S $ns/$1) + f=$f^9p + +t=/tmp/netget.$pid.$USER +fn sigexit { rm -f $t } + +fn putfile{ + cat >$t + if(! echo put $t $2 | sftp -b - $1 >/dev/null) + exit 1 +} +fn putfile9p{ + if(! 9p write $1/$2) + exit 1 +} + +$f $1 $2 +exit 0 blob - /dev/null blob + 1d687e5153767c9d4631b8a2da7a258e4532305d (mode 755) --- /dev/null +++ src/cmd/netfiles/netfilestat @@ -0,0 +1,52 @@ +#!/usr/local/plan9/bin/rc + +if(! ~ $#* 2){ + echo usage: netisdir system path >[1=2] + exit usage +} + +f=dostat +ns=`{namespace} +if(u test -S $ns/$1) + f=$f^9p + +t=/tmp/netisdir.$pid.$USER +fn sigexit { rm -f $t } + +fn dostat { + { + echo !echo XXX connected + echo cd $2 + echo !echo XXX directory exists + } | sftp -b - $1 >$t >[2=1] + if(9 grep -s XXX.directory.exists $t){ + echo directory + exit 0 + } + if(9 grep -s 'is not a directory' $t){ + echo file + exit 0 + } + cat $t | sed 's/sftp> //g; /^$/d; /XXX/d; /^cd /d' >[1=2] + if(! 9 grep -s XXX.connected $t){ + echo connect failed + exit 0 + } + echo nonexistent + exit 0 +} + +fn dostat9p { + if(! 9p ls -ld $1/$2 >$t >[2]/dev/null){ + echo nonexistent + exit 0 + } + if(9 grep -s '^d' $t){ + echo directory + exit 0 + } + echo file + exit 0 +} + +$f $1 $2 blob - /dev/null blob + 6f31a29bb56a881e3cb7fc6b26b6958539b592c5 (mode 644) --- /dev/null +++ src/cmd/netfiles/wait.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include <9pclient.h> +#include "acme.h" + +extern int debug; + +#define dprint if(debug)print + +typedef struct Waitreq Waitreq; +struct Waitreq +{ + int pid; + Channel *c; +}; + +/* + * watch the exiting children + */ +Channel *twaitchan; /* chan(Waitreq) */ +void +waitthread(void *v) +{ + Alt a[3]; + Waitmsg *w, **wq; + Waitreq *rq, r; + int i, nrq, nwq; + + threadsetname("waitthread"); + a[0].c = threadwaitchan(); + a[0].v = &w; + a[0].op = CHANRCV; + a[1].c = twaitchan; + a[1].v = &r; + a[1].op = CHANRCV; + a[2].op = CHANEND; + + nrq = 0; + nwq = 0; + rq = nil; + wq = nil; + dprint("wait: start\n"); + for(;;){ + cont2:; + dprint("wait: alt\n"); + switch(alt(a)){ + case 0: + dprint("wait: pid %d exited\n", w->pid); + for(i=0; ipid){ + dprint("wait: match with rq chan %p\n", rq[i].c); + sendp(rq[i].c, w); + rq[i] = rq[--nrq]; + goto cont2; + } + } + if(i == nrq){ + dprint("wait: queueing waitmsg\n"); + wq = erealloc(wq, (nwq+1)*sizeof(wq[0])); + wq[nwq++] = w; + } + break; + + case 1: + dprint("wait: req for pid %d chan %p\n", r.pid, r.c); + for(i=0; ipid == r.pid){ + dprint("wait: match with waitmsg\n"); + sendp(r.c, w); + wq[i] = wq[--nwq]; + goto cont2; + } + } + if(i == nwq){ + dprint("wait: queueing req\n"); + rq = erealloc(rq, (nrq+1)*sizeof(rq[0])); + rq[nrq] = r; + dprint("wait: queueing req pid %d chan %p\n", rq[nrq].pid, rq[nrq].c); + nrq++; + } + break; + } + } +} + +Waitmsg* +twaitfor(int pid) +{ + Waitreq r; + Waitmsg *w; + + r.pid = pid; + r.c = chancreate(sizeof(Waitmsg*), 1); + send(twaitchan, &r); + w = recvp(r.c); + chanfree(r.c); + return w; +} + +int +twait(int pid) +{ + int x; + Waitmsg *w; + + w = twaitfor(pid); + x = w->msg[0] != 0 ? -1 : 0; + free(w); + return x; +} + +void +twaitinit(void) +{ + threadwaitchan(); /* allocate it before returning */ + twaitchan = chancreate(sizeof(Waitreq), 10); + threadcreate(waitthread, nil, 128*1024); +} +