2 * Remote file system editing client.
3 * Only talks to acme - external programs do all the hard work.
5 * If you add a plumbing rule:
7 # /n/ paths go to simulator in acme
9 data matches '[a-zA-Z0-9_\-./]+('$addr')?'
10 data matches '(/n/[a-zA-Z0-9_\-./]+)('$addr')?'
14 * then plumbed paths starting with /n/ will find their way here.
16 * Perhaps on startup should look for windows named /n/ and attach to them?
17 * Or might that be too aggressive?
32 fprint(2, "usage: Netfiles\n");
33 threadexitsall("usage");
36 extern int chatty9pclient;
38 #define dprint if(debug>1)print
39 #define cprint if(debug)print
41 int do3(Win *w, char *arg);
71 typedef struct Arg Arg;
80 arg(char *file, char *addr, Channel *c)
84 a = emalloc(sizeof *a);
85 a->file = estrdup(file);
86 a->addr = estrdup(addr);
96 for(w=windows; w; w=w->next)
103 * return Win* of a window named name or name/
104 * assumes name is cleaned.
107 nametowin(char *name)
109 char *index, *p, *next;
115 for(p=index; p && *p; p=next){
116 if((next = strchr(p, '\n')) != nil)
118 if(strlen(p) <= 5*12)
120 if(memcmp(p+5*12, name, len)!=0)
122 if(p[5*12+len]!=' ' && (p[5*12+len]!='/' || p[5*12+len+1]!=' '))
125 if((w = winbyid(n)) != nil){
139 lookup(char *s, char **list)
143 for(i=0; list[i]; i++)
144 if(strcmp(list[i], s) == 0)
150 * move to top of file
156 winctl(w, "dot=addr");
161 isdot(Win *w, uint xq0, uint xq1)
165 winctl(w, "addr=dot");
166 q0 = winreadaddr(w, &q1);
167 return xq0==q0 && xq1==q1;
171 * Expand the click further than acme usually does -- all non-white space is okay.
174 expandarg(Win *w, Event *e)
178 if(e->c2 == 'l') /* in tag - no choice but to accept acme's expansion */
179 return estrdup(e->text);
181 winctl(w, "addr=dot");
183 q0 = winreadaddr(w, &q1);
184 cprint("acme expanded %d-%d into %d-%d (dot %d-%d)\n",
185 e->oq0, e->oq1, e->q0, e->q1, q0, q1);
187 if(e->oq0 == e->oq1 && e->q0 != e->q1 && !isdot(w, e->q0, e->q1)){
188 winaddr(w, "#%ud+#1-/[^ \t\\n]*/,#%ud-#1+/[^ \t\\n]*/", e->q0, e->q1);
189 q0 = winreadaddr(w, &q1);
190 cprint("\tre-expand to %d-%d\n", q0, q1);
192 winaddr(w, "#%ud,#%ud", e->q0, e->q1);
193 return winmread(w, "xdata");
197 * handle a plumbing message
207 if(m->ndata >= 1024){
208 fprint(2, "insanely long file name (%d bytes) in plumb message (%.32s...)\n",
214 addr = plumblookup(m->attr, "addr");
215 w = nametowin(m->data);
218 winaddr(w, "%s", addr);
219 winctl(w, "dot=addr");
226 * dispatch messages from the plumber
234 threadsetname("plumbthread");
235 fid = plumbopenfid("netfileedit", OREAD);
237 fprint(2, "cannot open plumb/netfileedit: %r\n");
240 while((m = plumbrecvfid(fid)) != nil)
241 threadcreate(doplumb, m, STACK);
246 * parse /n/system/path
249 parsename(char *name, char **server, char **path)
254 if(strncmp(name, "/n/", 3) != 0 && name[3] == 0)
257 if((p = strchr(name+3, '/')) == nil)
258 *path = estrdup("/");
267 *server = *path = nil;
272 *server = estrdup(p);
279 * shell out to find the type of a given file
282 filestat(char *server, char *path)
291 if(cache.server && strcmp(server, cache.server) == 0)
292 if(cache.path && strcmp(path, cache.path) == 0){
293 type = estrdup(cache.type);
294 cprint("9 netfilestat %q %q => %s (cached)\n", server, path, type);
298 type = sysrun(2, "9 netfilestat %q %q", server, path);
303 cache.server = estrdup(server);
304 cache.path = estrdup(path);
305 cache.type = estrdup(type);
307 cprint("9 netfilestat %q %q => %s\n", server, path, type);
312 * manage a single window
317 char *arg, *name, *p, *server, *path, *type;
324 threadsetname("file %s", a->file);
327 winprint(w, "tag", "Get Put Look ");
332 while((e=recvp(c)) != nil){
334 dprint("acme %E\n", e);
339 switch(lookup(e->text, cmds)){
344 if(parsename(name=wingetname(w), &server, &path) < 0){
345 fprint(2, "Netfiles: bad name %s\n", name);
348 type = filestat(server, path);
351 if(strcmp(type, "file")==0 || strcmp(type, "directory")==0){
353 winprint(w, "data", "[reading...]");
355 cprint("9 netfileget %s%q %q\n",
356 strcmp(type, "file") == 0 ? "" : "-d", server, path);
357 if(strcmp(type, "file")==0)
358 twait(pipetowin(w, "data", 2, "9 netfileget %q %q", server, path));
360 twait(pipetowin(w, "data", 2, "9 netfileget -d %q %q | winid=%d mc", server, path, w->id));
362 if(strcmp(type, "directory")==0){
363 p = name+strlen(name);
373 winaddr(w, "%s", a->addr);
374 winctl(w, "dot=addr");
396 if(parsename(name=wingetname(w), &server, &path) < 0){
397 fprint(2, "Netfiles: bad name %s\n", name);
400 cprint("9 netfileput %q %q\n", server, path);
401 if(twait(pipewinto(w, "body", 2, "9 netfileput %q %q", server, path)) >= 0){
417 print("Netfiles debug %s\n", debugstr[debug]);
426 arg = expandarg(w, e);
427 if(arg!=nil && do3(w, arg) < 0)
437 * handle a button 3 click
440 do3(Win *w, char *text)
442 char *addr, *name, *type, *server, *path, *p, *q;
443 static char lastfail[1000];
447 name = estrdup(text);
454 name = emalloc(strlen(p)+1+strlen(text)+1);
460 cprint("button3 %s %s => %s\n", p, text, name);
461 if((addr = strchr(name, ':')) != nil)
464 cprint("b3 \t=> name=%s addr=%s\n", name, addr);
465 if(strcmp(name, lastfail) == 0){
466 cprint("b3 \t=> nonexistent (cached)\n");
470 if(parsename(name, &server, &path) < 0){
471 cprint("b3 \t=> parsename failed\n");
475 type = filestat(server, path);
478 if(strcmp(type, "file")==0 || strcmp(type, "directory")==0){
482 cprint("b3 \t=> creating new window %d\n", w->id);
484 cprint("b3 \t=> reusing window %d\n", w->id);
486 winaddr(w, "%s", addr);
487 winctl(w, "dot=addr");
495 * remember last name that didn't exist so that
496 * only the first right-click is slow when searching for text.
498 cprint("b3 caching %s => type %s\n", name, type);
499 strecpy(lastfail, lastfail+sizeof lastfail, name);
512 c = chancreate(sizeof(void*), 0);
513 a = arg(name, nil, c);
514 threadcreate(filethread, a, STACK);
525 threadsetname("loopthread");
531 threadmain(int argc, char **argv)
547 cprint("netfiles starting\n");
549 threadnotify(nil, 0); /* set up correct default handlers */
551 fmtinstall('E', eventfmt);
552 doquote = needsrcquote;
556 threadcreate(plumbthread, nil, STACK);
557 threadcreate(loopthread, nil, STACK);