new files

This commit is contained in:
rsc 2005-03-18 19:30:22 +00:00
parent 72fd2f8819
commit 7c709434ec
9 changed files with 1504 additions and 0 deletions

24
src/cmd/netfiles/COPYING Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2005 Russ Cox <rsc@swtch.com>
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.

636
src/cmd/netfiles/acme.c Normal file
View file

@ -0,0 +1,636 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#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; i<e->nr; 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;
}

82
src/cmd/netfiles/acme.h Normal file
View file

@ -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;

490
src/cmd/netfiles/main.c Normal file
View file

@ -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 <u.h>
#include <libc.h>
#include <thread.h>
#include <9pclient.h>
#include <plumb.h>
#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);
}

28
src/cmd/netfiles/mkfile Normal file
View file

@ -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

45
src/cmd/netfiles/netfileget Executable file
View file

@ -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

27
src/cmd/netfiles/netfileput Executable file
View file

@ -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

52
src/cmd/netfiles/netfilestat Executable file
View file

@ -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

120
src/cmd/netfiles/wait.c Normal file
View file

@ -0,0 +1,120 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#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; i<nrq; i++){
if(rq[i].pid == w->pid){
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; i<nwq; i++){
if(w->pid == 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);
}