]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mothra/mothra.c
mothra: remove ftp,gopher,file and http code and use /mnt/web instead
[plan9front.git] / sys / src / cmd / mothra / mothra.c
1 /*
2  * Trivial web browser
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <draw.h>
7 #include <event.h>
8 #include <keyboard.h>
9 #include <plumb.h>
10 #include <cursor.h>
11 #include <panel.h>
12 #include "mothra.h"
13 #include "rtext.h"
14 int verbose=0;          /* -v flag causes html errors to appear in error log */
15 int defdisplay=1;       /* is the default (initial) display visible? */
16 Panel *root;    /* the whole display */
17 Panel *alt;     /* the alternate display */
18 Panel *alttext; /* the alternate text window */
19 Panel *cmd;     /* command entry */
20 Panel *curttl;  /* label giving the title of the visible text */
21 Panel *cururl;  /* label giving the url of the visible text */
22 Panel *list;    /* list of previously acquired www pages */
23 Panel *msg;     /* message display */
24 Panel *menu3;   /* button 3 menu */
25 Mouse mouse;    /* current mouse data */
26 char mothra[] = "mothra!";
27 Url defurl={
28         "http://cat-v.org/",
29         "",
30         "http://cat-v.org/",
31         "",
32         "",
33         HTML,
34 };
35 Url badurl={
36         "",
37         "",
38         "No file loaded",
39         "",
40         "",
41         HTML,
42 };
43 Cursor patientcurs={
44         0, 0,
45         0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
46         0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
47         0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
48         0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
49
50         0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
51         0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
52         0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
53         0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
54 };
55 Cursor confirmcurs={
56         0, 0,
57         0x0F, 0xBF, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
58         0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
59         0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF,
60         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
61
62         0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
63         0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
64         0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
65         0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90
66 };
67 Cursor readingcurs={
68         -10, -3,
69         0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
70         0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0,
71         0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
72         0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00,
73
74         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
75         0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60,
76         0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
77         0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 };
79 char *mtpt="/mnt/web";
80 Www *current=0;
81 Url *selection=0;
82 int logfile;
83 void docmd(Panel *, char *);
84 void doprev(Panel *, int, int);
85 void selurl(char *);
86 void setcurrent(int, char *);
87 char *genwww(Panel *, int);
88 void updtext(Www *);
89 void dolink(Panel *, int, Rtext *);
90 void hit3(int, int);
91 char *buttons[]={
92         "alt display",
93         "snarf url",
94         "paste",
95         "save hit",
96         "hit list",
97         "exit",
98         0
99 };
100
101 int wwwtop=0;
102 Www *www(int index){
103         static Www a[1+NWWW];
104         return &a[1+(index % NWWW)];
105 }
106 int nwww(void){
107         return wwwtop<NWWW ? wwwtop : NWWW;
108 }
109
110 void err(Display *, char *msg){
111         fprint(2, "err: %s (%r)\n", msg);
112         abort();
113 }
114 int subpanel(Panel *obj, Panel *subj){
115         if(obj==0) return 0;
116         if(obj==subj) return 1;
117         for(obj=obj->child;obj;obj=obj->next)
118                 if(subpanel(obj, subj)) return 1;
119         return 0;
120 }
121 /*
122  * Make sure that the keyboard focus is on-screen, by adjusting it to
123  * be the cmd entry if necessary.
124  */
125 void adjkb(void){
126         Rtext *t;
127         int yoffs;
128         extern Panel *pl_kbfocus;       /* this is a secret panel library name */
129         yoffs=text->r.min.y-plgetpostextview(text);
130         for(t=current->text;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
131                 if(t->r.max.y+yoffs>text->r.max.y) break;
132                 if(t->r.min.y+yoffs>=text->r.min.y
133                 && t->b==0
134                 && subpanel(t->p, pl_kbfocus)) return;
135         }
136         plgrabkb(cmd);
137 }
138
139 void scrolltext(int dy)
140 {
141         Scroll s;
142
143         s = plgetscroll(text);
144         s.pos.y += dy;
145         if(s.pos.y < 0)
146                 s.pos.y = 0;
147         if(s.pos.y > s.size.y)
148                 s.pos.y = s.size.y;
149         plsetscroll(text, s);
150         pldraw(root, screen);
151 }
152
153 void mkpanels(void){
154         Panel *p, *bar;
155         menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
156         root=plpopup(root, EXPAND, 0, 0, menu3);
157                 p=plgroup(root, PACKN|FILLX);
158                         msg=pllabel(p, PACKN|FILLX, mothra);
159                         plplacelabel(msg, PLACEW);
160                         pllabel(p, PACKW, "Go:");
161                         cmd=plentry(p, PACKN|FILLX, 0, "", docmd);
162                 p=plgroup(root, PACKN|FILLX);
163                         bar=plscrollbar(p, PACKW);
164                         list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
165                         plscroll(list, 0, bar);
166                 p=plgroup(root, PACKN|FILLX);
167                         pllabel(p, PACKW, "Title:");
168                         curttl=pllabel(p, PACKE|EXPAND, "Initializing");
169                         plplacelabel(curttl, PLACEW);
170                 p=plgroup(root, PACKN|FILLX);
171                         pllabel(p, PACKW, "Url:");
172                         cururl=pllabel(p, PACKE|EXPAND, "---");
173                         plplacelabel(cururl, PLACEW);
174                 p=plgroup(root, PACKN|EXPAND);
175                         bar=plscrollbar(p, PACKW);
176                         text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
177                         plscroll(text, 0, bar);
178         plgrabkb(cmd);
179         alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
180                 bar=plscrollbar(alt, PACKW);
181                 alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
182                 plscroll(alttext, 0, bar);
183 }
184 void killcohort(void){
185         int i;
186         for(i=0;i!=3;i++){      /* It's a long way to the kitchen */
187                 postnote(PNGROUP, getpid(), "kill\n");
188                 sleep(1);
189         }
190 }
191 void dienow(void*, char*){
192         noted(NDFLT);
193 }
194 int mkmfile(char *stem, int mode){
195         char *henv;
196         char filename[NNAME];
197         int f;
198         if(home[0]=='\0'){
199                 henv=getenv("home");
200                 if(henv){
201                         sprint(home, "%s/lib", henv);
202                         f=create(home, OREAD, DMDIR|0777);
203                         if(f!=-1) close(f);
204                         sprint(home, "%s/lib/mothra", henv);
205                         f=create(home, OREAD, DMDIR|0777);
206                         if(f!=-1) close(f);
207                         free(henv);
208                 }
209                 else
210                         strcpy(home, "/tmp");
211         }
212         snprint(filename, sizeof(filename), "%s/%s", home, stem);
213         f=create(filename, OWRITE, mode);
214         if(f==-1)
215                 f=create(stem, OWRITE, mode);
216         return f;
217 }
218 void main(int argc, char *argv[]){
219         Event e;
220         enum { Eplumb = 128 };
221         Plumbmsg *pm;
222         Www *new;
223         char *url;
224         int errfile;
225         int i;
226         ARGBEGIN{
227         case 'd': debug++; break;
228         case 'v': verbose=1; break;
229         case 'm':
230                 if(mtpt = ARGF())
231                         break;
232         default:  goto Usage;
233         }ARGEND
234
235         /*
236          * so that we can stop all subprocesses with a note,
237          * and to isolate rendezvous from other processes
238          */
239         rfork(RFNOTEG|RFNAMEG|RFREND);
240         atexit(killcohort);
241         switch(argc){
242         default:
243         Usage:
244                 fprint(2, "Usage: %s [-d] [-m mtpt] [url]\n", argv[0]);
245                 exits("usage");
246         case 0:
247                 url=getenv("url");
248                 if(url==0 || url[0]=='\0')
249                         url=defurl.fullname;
250                 break;
251         case 1: url=argv[0]; break;
252         }
253         errfile=mkmfile("mothra.err", 0666);
254         if(errfile!=-1){
255                 dup(errfile, 2);
256                 close(errfile);
257         }
258         logfile=mkmfile("mothra.log", 0666|DMAPPEND);
259         
260         initdraw(err,0,"mothra");
261         display->locking = 1;
262         chrwidth=stringwidth(font, "0");
263         pltabsize(chrwidth, 8*chrwidth);
264         einit(Emouse|Ekeyboard);
265         eplumb(Eplumb, "web");
266         etimer(0, 1000);
267         plinit(screen->depth);
268         if(debug) notify(dienow);
269         getfonts();
270         hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
271         if(hrule==0){
272                 fprint(2, "%s: can't allocimage!\n", argv[0]);
273                 exits("no mem");
274         }
275         draw(hrule, Rect(0,1,1280,3), display->black, 0, ZP);
276         linespace=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
277         if(linespace==0){
278                 fprint(2, "%s: can't allocimage!\n", argv[0]);
279                 exits("no mem");
280         }
281         bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
282         fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
283         new = www(-1);
284         new->url=&badurl;
285         strcpy(new->title, "See error message above");
286         plrtstr(&new->text, 0, 0, font, "See error message above", 0, 0);
287         new->alldone=1;
288         mkpanels();
289
290         unlockdisplay(display);
291         eresized(0);
292         lockdisplay(display);
293
294         geturl(url, GET, 0, 1, 0);
295
296         if(logfile==-1) message("Can't open log file");
297         mouse.buttons=0;
298         for(;;){
299                 if(mouse.buttons==0 && current){
300                         if(current->finished){
301                                 updtext(current);
302                                 current->finished=0;
303                                 current->changed=0;
304                                 current->alldone=1;
305                                 message(mothra);
306                                 esetcursor(0);
307                         }
308                         else if(current->changed){
309                                 updtext(current);
310                                 current->changed=0;
311                         }
312                 }
313
314                 unlockdisplay(display);
315                 i=event(&e);
316                 lockdisplay(display);
317
318                 switch(i){
319                 case Ekeyboard:
320                         switch(e.kbdc){
321                         default:
322                                 adjkb();
323                                 plkeyboard(e.kbdc);
324                                 break;
325                         case Kup:
326                                 scrolltext(-text->size.y/4);
327                                 break;
328                         case Kdown:
329                                 scrolltext(text->size.y/4);
330                                 break;
331                         }
332                         break;
333                 case Emouse:
334                         mouse=e.mouse;
335                         plmouse(root, e.mouse);
336                         break;
337                 case Eplumb:
338                         pm=e.v;
339                         if(pm->ndata > 0)
340                                 geturl(pm->data, GET, 0, 1, 0);
341                         plumbfree(pm);
342                         break;
343                 }
344         }
345 }
346 void message(char *s, ...){
347         static char buf[1024];
348         char *out;
349         va_list args;
350         va_start(args, s);
351         out = buf + vsnprint(buf, sizeof(buf), s, args);
352         va_end(args);
353         *out='\0';
354         plinitlabel(msg, PACKN|FILLX, buf);
355         if(defdisplay) pldraw(msg, screen);
356 }
357 void htmlerror(char *name, int line, char *m, ...){
358         static char buf[1024];
359         char *out;
360         va_list args;
361         if(verbose){
362                 va_start(args, m);
363                 out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
364                 out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
365                 va_end(args);
366                 *out='\0';
367                 fprint(2, "%s\n", buf);
368         }
369 }
370 void eresized(int new){
371         Rectangle r;
372
373         lockdisplay(display);
374         if(new && getwindow(display, Refnone) == -1) {
375                 fprint(2, "getwindow: %r\n");
376                 exits("getwindow");
377         }
378         r=screen->r;
379         plinitlabel(curttl, PACKE|EXPAND, "---");
380         plinitlabel(cururl, PACKE|EXPAND, "---");
381         plpack(root, r);
382         if(current){
383                 plinitlabel(curttl, PACKE|EXPAND, current->title);
384                 plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
385         }
386         draw(screen, r, display->white, 0, ZP);
387         pldraw(root, screen);
388         unlockdisplay(display);
389 }
390 void *emalloc(int n){
391         void *v;
392         v=malloc(n);
393         if(v==0){
394                 fprint(2, "out of space\n");
395                 exits("no mem");
396         }
397         return v;
398 }
399 void *emallocz(int n, int z){
400         void *v;
401         v = emalloc(n);
402         if(z)
403                 memset(v, 0, n);
404         return v;
405 }
406
407 char *genwww(Panel *, int index){
408         static char buf[1024];
409         int i;
410
411         if(index >= nwww())
412                 return 0;
413         i = wwwtop-index-1;
414         snprint(buf, sizeof(buf), "%2d %s", i+1, www(i)->title);
415         return buf;
416 }
417
418 void donecurs(void){
419         esetcursor(current && current->alldone?0:&readingcurs);
420 }
421 /*
422  * selected text should be a url.
423  * get the document, scroll to the given tag
424  */
425 void setcurrent(int index, char *tag){
426         Www *new;
427         Rtext *tp;
428         Action *ap;
429         int i;
430         new=www(index);
431         if(new==current && (tag==0 || tag[0]==0)) return;
432         if(current)
433                 current->yoffs=plgetpostextview(text);
434         current=new;
435         plinitlabel(curttl, PACKE|EXPAND, current->title);
436         if(defdisplay) pldraw(curttl, screen);
437         plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
438         if(defdisplay) pldraw(cururl, screen);
439         plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
440         if(tag && tag[0]){
441                 for(tp=current->text;tp;tp=tp->next){
442                         ap=tp->user;
443                         if(ap && ap->name && strcmp(ap->name, tag)==0){
444                                 current->yoffs=tp->topy;
445                                 break;
446                         }
447                 }
448         }
449         plsetpostextview(text, current->yoffs);
450         donecurs();
451         flushimage(display, 1);
452 }
453 char *arg(char *s){
454         do ++s; while(*s==' ' || *s=='\t');
455         return s;
456 }
457 void save(Url *url, char *name){
458         int ofd, ifd, n;
459         char buf[4096];
460         ofd=create(name, OWRITE, 0666);
461         if(ofd==-1){
462                 message("save: %s: %r", name);
463                 return;
464         }
465         esetcursor(&patientcurs);
466         ifd=urlopen(url, GET, 0);
467         donecurs();
468         if(ifd==-1){
469                 message("save: %s: %r", selection->fullname);
470                 close(ofd);
471         }
472         switch(rfork(RFNOTEG|RFFDG|RFPROC|RFNOWAIT)){
473         case -1:
474                 message("Can't fork -- please wait");
475                 esetcursor(&patientcurs);
476                 while((n=read(ifd, buf, 4096))>0)
477                         write(ofd, buf, n);
478                 donecurs();
479                 break;
480         case 0:
481                 while((n=read(ifd, buf, 4096))>0)
482                         write(ofd, buf, n);
483                 if(n==-1) fprint(2, "save: %s: %r\n", url->fullname);
484                 _exits(0);
485         }
486         close(ifd);
487         close(ofd);
488 }
489 void screendump(char *name, int full){
490         Image *b;
491         int fd;
492         fd=create(name, OWRITE|OTRUNC, 0666);
493         if(fd==-1){
494                 message("can't create %s", name);
495                 return;
496         }
497         if(full){
498                 writeimage(fd, screen, 0);
499         } else {
500                 if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
501                         message("can't allocate image");
502                         close(fd);
503                         return;
504                 }
505                 draw(b, b->r, screen, 0, b->r.min);
506                 writeimage(fd, b, 0);
507                 freeimage(b);
508         }
509         close(fd);
510 }
511
512 /*
513  * user typed a command.
514  */
515 void docmd(Panel *p, char *s){
516         USED(p);
517         while(*s==' ' || *s=='\t') s++;
518         /*
519          * Non-command does a get on the url
520          */
521         if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
522                 geturl(s, GET, 0, 1, 0);
523         else switch(s[0]){
524         default:
525                 message("Unknown command %s, type h for help", s);
526                 break;
527         case 'g':
528                 s=arg(s);
529                 if(*s=='\0'){
530                         if(selection)
531                                 geturl(selection->fullname, GET, 0, 1, 0);
532                         else
533                                 message("no url selected");
534                 }
535                 else geturl(s, GET, 0, 1, 0);
536                 break;
537         case 'r':
538                 s = arg(s);
539                 if(*s == '\0' && selection)
540                         geturl(selection->fullname, GET, 0, 0, 0);
541                 break;
542         case 'W':
543                 s=arg(s);
544                 if(s=='\0'){
545                         message("Usage: W file");
546                         break;
547                 }
548                 screendump(s, 1);
549                 break;
550         case 'w':
551                 s=arg(s);
552                 if(s=='\0'){
553                         message("Usage: w file");
554                         break;
555                 }
556                 screendump(s, 0);
557                 break;
558         case 's':
559                 s=arg(s);
560                 if(*s=='\0'){
561                         if(selection){
562                                 s=strrchr(selection->fullname, '/');
563                                 if(s) s++;
564                         }
565                         if(s==0 || *s=='\0'){
566                                 message("Usage: s file");
567                                 break;
568                         }
569                 }
570                 save(selection, s);
571                 break;
572         case 'q':
573                 draw(screen, screen->r, display->white, 0, ZP);
574                 exits(0);
575         }
576         plinitentry(cmd, EXPAND, 0, "", docmd);
577         if(defdisplay) pldraw(cmd, screen);
578 }
579 void hiturl(int buttons, char *url, int map){
580         switch(buttons){
581         case 1: geturl(url, GET, 0, 1, map); break;
582         case 2: selurl(url); break;
583         case 4: message("Button 3 hit on url can't happen!"); break;
584         }
585 }
586 /*
587  * user selected from the list of available pages
588  */
589 void doprev(Panel *p, int buttons, int index){
590         int i;
591         USED(p);
592         if(index >= nwww())
593                 return;
594         i = wwwtop-index-1;
595         switch(buttons){
596         case 1: setcurrent(i, 0);       /* no break ... */
597         case 2: selurl(www(i)->url->fullname); break;
598         case 4: message("Button 3 hit on page can't happen!"); break;
599         }
600 }
601
602 /*
603  * Follow an html link
604  */
605 void dolink(Panel *p, int buttons, Rtext *word){
606         char mapurl[NNAME];
607         Action *a;
608         Point coord;
609         int yoffs;
610         USED(p);
611         a=word->user;
612         if(a && a->link){
613                 if(a->ismap){
614                         yoffs=plgetpostextview(p);
615                         coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
616                         snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
617                         hiturl(buttons, mapurl, 1);
618                 }
619                 else
620                         hiturl(buttons, a->link, 0);
621         }
622 }
623 void filter(char *cmd, int fd){
624         flushimage(display, 1);
625         switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
626         case -1:
627                 message("Can't fork!");
628                 break;
629         case 0:
630                 close(0);
631                 dup(fd, 0);
632                 close(fd);
633                 execl("/bin/rc", "rc", "-c", cmd, 0);
634                 message("Can't exec /bin/rc!");
635                 _exits(0);
636         default:
637                 break;
638         }
639         close(fd);
640 }
641 void gettext(Www *w, int fd, int type){
642         flushimage(display, 1);
643         switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
644         case -1:
645                 message("Can't fork, please wait");
646                 if(type==HTML)
647                         plrdhtml(w->url->fullname, fd, w);
648                 else
649                         plrdplain(w->url->fullname, fd, w);
650                 break;
651         case 0:
652                 if(type==HTML)
653                         plrdhtml(w->url->fullname, fd, w);
654                 else
655                         plrdplain(w->url->fullname, fd, w);
656                 _exits(0);
657         }
658         close(fd);
659 }
660
661 void freetext(Rtext *t){
662         Rtext *tt;
663         Action *a;
664
665         tt = t;
666         for(; t!=0; t = t->next){
667                 t->b=0;
668                 free(t->text);
669                 t->text=0;
670                 if(a = t->user){
671                         t->user=0;
672                         free(a->image);
673                         free(a->link);
674                         free(a->name);
675                         free(a);
676                 }
677         }
678         plrtfree(tt);
679 }
680
681 void popwin(char *cmd){
682         flushimage(display, 1);
683         switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
684         case -1:
685                 message("sorry, can't fork to %s", cmd);
686                 break;
687         case 0:
688                 execl("/bin/window", "window", "100 100 800 800", "rc", "-c", cmd, 0);
689                 _exits(0);
690         }
691 }
692
693 int readstr(char *buf, int nbuf, char *base, char *name)
694 {
695         char path[128];
696         int n, fd;
697
698         snprint(path, sizeof path, "%s/%s", base, name);
699         if((fd = open(path, OREAD)) < 0){
700         ErrOut:
701                 memset(buf, 0, nbuf);
702                 return 0;
703         }
704         n = read(fd, buf, nbuf-1);
705         close(fd);
706         if(n <= 0){
707                 close(fd);
708                 goto ErrOut;
709         }
710         buf[n] = 0;
711         return n;
712 }
713
714 int urlopen(Url *url, int method, char *body){
715         int conn, ctlfd, fd, n;
716         char buf[1024+1];
717
718         snprint(buf, sizeof buf, "%s/clone", mtpt);
719         if((ctlfd = open(buf, ORDWR)) < 0)
720                 return -1;
721         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
722                 close(ctlfd);
723                 return -1;
724         }
725         buf[n] = 0;
726         conn = atoi(buf);
727
728         if(url->basename[0]){
729                 n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
730                 write(ctlfd, buf, n);
731         }
732         n = snprint(buf, sizeof buf, "url %s", url->reltext);
733         if(write(ctlfd, buf, n) != n){
734         ErrOut:
735                 close(ctlfd);
736                 return -1;
737         }
738
739         if(method == POST && body){
740                 snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
741                 if((fd = open(buf, OWRITE)) < 0)
742                         goto ErrOut;
743                 n = strlen(body);
744                 if(write(fd, body, n) != n){
745                         close(fd);
746                         goto ErrOut;
747                 }
748                 close(fd);
749         }
750
751         snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
752         if((fd = open(buf, OREAD)) < 0)
753                 goto ErrOut;
754
755         snprint(buf, sizeof buf, "%s/%d/parsed", mtpt, conn);
756         readstr(url->fullname, sizeof(url->fullname), buf, "url");
757         readstr(url->tag, sizeof(url->tag), buf, "fragment");
758
759         snprint(buf, sizeof buf, "%s/%d", mtpt, conn);
760         readstr(buf, sizeof buf, buf, "contenttype");
761         url->type = content2type(buf, url->fullname);
762
763         close(ctlfd);
764         return fd;
765 }
766
767 int pipeline(char *cmd, int fd)
768 {
769         int pfd[2];
770
771         if(pipe(pfd)==-1){
772 Err:
773                 close(fd);
774                 werrstr("pipeline for %s failed: %r", cmd);
775                 return -1;
776         }
777         switch(fork()){
778         case -1:
779                 close(pfd[0]);
780                 close(pfd[1]);
781                 goto Err;
782         case 0:
783                 dup(fd, 0);
784                 dup(pfd[0], 1);
785                 close(pfd[0]);
786                 close(pfd[1]);
787                 execl("/bin/rc", "rc", "-c", cmd, 0);
788                 _exits(0);
789         }
790         close(pfd[0]);
791         close(fd);
792         return pfd[1];
793 }
794
795 /*
796  * select the file at the given url
797  */
798 void seturl(Url *url, char *urlname, char *base){
799         strncpy(url->reltext, urlname, sizeof(url->reltext));
800         strcpy(url->basename, base);
801         url->fullname[0] = 0;
802         url->charset[0] = 0;
803         url->tag[0] = 0;
804         url->type = 0;
805         url->map = 0;
806 }
807
808 void selurl(char *urlname){
809         static Url url;
810         seturl(&url, urlname, current?
811                 current->url->fullname :
812                 defurl.fullname);
813         selection=&url;
814         message("selected: %s", selection->fullname);
815 }
816 Url *copyurl(Url *u){
817         Url *v;
818         v=emalloc(sizeof(Url));
819         *v=*u;
820         return v;
821 }
822 void freeurl(Url *u){
823         if(u!=&defurl && u!=&badurl)
824                 free(u);
825 }
826
827 /*
828  * get the file at the given url
829  */
830 void geturl(char *urlname, int method, char *body, int cache, int map){
831         int i, fd;
832         char cmd[NNAME];
833         int pfd[2];
834         Www *w;
835
836         selurl(urlname);
837         selection->map=map;
838
839         message("getting %s", selection->reltext);
840         esetcursor(&patientcurs);
841         for(;;){
842                 if((fd=urlopen(selection, method, body)) < 0){
843                         message("%r");
844                         setcurrent(-1, 0);
845                         break;
846                 }
847                 message("getting %s", selection->fullname);
848                 if(selection->type&COMPRESS)
849                         fd=pipeline("/bin/uncompress", fd);
850                 else if(selection->type&GUNZIP)
851                         fd=pipeline("/bin/gunzip", fd);
852                 switch(selection->type&~COMPRESSION){
853                 default:
854                         message("Bad type %x in geturl", selection->type);
855                         break;
856                 case PLAIN:
857                 case HTML:
858                         w = www(i = wwwtop++);
859                         if(i >= NWWW){
860                                 extern void freeform(void *p);
861                                 extern void freepix(void *p);
862
863                                 /* wait for the reader to finish the document */
864                                 while(!w->finished && !w->alldone){
865                                         unlockdisplay(display);
866                                         sleep(10);
867                                         lockdisplay(display);
868                                 }
869
870                                 freetext(w->text);
871                                 freeform(w->form);
872                                 freepix(w->pix);
873                                 freeurl(w->url);
874                                 memset(w, 0, sizeof(*w));
875                         }
876                         if(selection->map)
877                                 w->url=copyurl(current->url);
878                         else
879                                 w->url=copyurl(selection);
880                         w->finished = 0;
881                         w->alldone = 0;
882                         gettext(w, fd, selection->type&~COMPRESSION);
883                         plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
884                         if(defdisplay) pldraw(list, screen);
885                         setcurrent(i, selection->tag);
886                         break;
887                 case POSTSCRIPT:
888                 case GIF:
889                 case JPEG:
890                 case PNG:
891                 case PDF:
892                         filter("page -w", fd);
893                         break;
894                 case TIFF:
895                         filter("/sys/lib/mothra/tiffview", fd);
896                         break;
897                 case XBM:
898                         filter("fb/xbm2pic|fb/9v", fd);
899                         break;
900                 }
901                 break;
902         }
903         donecurs();
904 }
905 void updtext(Www *w){
906         Rtext *t;
907         Action *a;
908         for(t=w->text;t;t=t->next){
909                 a=t->user;
910                 if(a){
911                         if(a->field)
912                                 mkfieldpanel(t);
913                         a->field=0;
914                 }
915         }
916         w->yoffs=plgetpostextview(text);
917         plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
918         plsetpostextview(text, w->yoffs);
919         pldraw(root, screen);
920 }
921 Cursor confirmcursor={
922         0, 0,
923         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
924         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
925         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
926         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
927
928         0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
929         0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
930         0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
931         0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
932 };
933 int confirm(int b){
934         Mouse down, up;
935         esetcursor(&confirmcursor);
936         do down=emouse(); while(!down.buttons);
937         do up=emouse(); while(up.buttons);
938         donecurs();
939         return down.buttons==(1<<(b-1));
940 }
941 void snarf(Panel *p){
942         int fd;
943         fd=create("/dev/snarf", OWRITE, 0666);
944         if(fd>=0){
945                 fprint(fd, "%s", selection->fullname);
946                 close(fd);
947         }
948 }
949 void paste(Panel *p){
950         char buf[1024];
951         int n, len, fd;
952         fd=open("/dev/snarf", OREAD);
953         strncpy(buf, plentryval(p), sizeof(buf));
954         len=strlen(buf);
955         n=read(fd, buf+len, 1023-len);
956         if(n>0){
957                 buf[len+n]='\0';
958                 plinitentry(cmd, PACKE|EXPAND, 0, buf, docmd);
959                 pldraw(cmd, screen);
960         }
961         close(fd);
962 }
963 void hit3(int button, int item){
964         char name[100], *home;
965         Panel *swap;
966         int fd;
967         USED(button);
968         switch(item){
969         case 0:
970                 swap=root;
971                 root=alt;
972                 alt=swap;
973                 current->yoffs=plgetpostextview(text);
974                 swap=text;
975                 text=alttext;
976                 alttext=swap;
977                 defdisplay=!defdisplay;
978                 plpack(root, screen->r);
979                 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
980                 plsetpostextview(text, current->yoffs);
981                 pldraw(root, screen);
982                 break;
983         case 1:
984                 snarf(cmd);
985                 break;
986         case 2:
987                 paste(cmd);
988                 break;
989         case 3:
990                 home=getenv("home");
991                 if(home==0){
992                         message("no $home");
993                         return;
994                 }
995                 snprint(name, sizeof(name), "%s/lib/mothra/hit.html", home);
996                 fd=open(name, OWRITE);
997                 if(fd==-1){
998                         fd=create(name, OWRITE, 0666);
999                         if(fd==-1){
1000                                 message("can't open %s", name);
1001                                 return;
1002                         }
1003                         fprint(fd, "<head><title>Hit List</title></head>\n");
1004                         fprint(fd, "<body><h1>Hit list</h1>\n");
1005                 }
1006                 seek(fd, 0, 2);
1007                 fprint(fd, "<p><a href=\"%s\">%s</a>\n",
1008                         selection->fullname, selection->fullname);
1009                 close(fd);
1010                 break;
1011         case 4:
1012                 home=getenv("home");
1013                 if(home==0){
1014                         message("no $home");
1015                         return;
1016                 }
1017                 snprint(name, sizeof(name), "file:%s/lib/mothra/hit.html", home);
1018                 geturl(name, GET, 0, 1, 0);
1019                 break;
1020         case 5:
1021                 if(confirm(3)){
1022                         draw(screen, screen->r, display->white, 0, ZP);
1023                         exits(0);
1024                 }
1025                 break;
1026         }
1027 }