]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mothra/mothra.c
mothra: correct spelling
[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 'a':
528                 s = arg(s);
529                 if(*s=='\0' && selection)
530                         hit3(3, 0);
531                 break;
532         case 'g':
533                 s = arg(s);
534                 if(*s=='\0'){
535                         if(selection)
536                                 geturl(selection->fullname, GET, 0, 1, 0);
537                         else
538                                 message("no url selected");
539                 }
540                 else geturl(s, GET, 0, 1, 0);
541                 break;
542         case 'j':
543                 s = arg(s);
544                 if(*s)
545                         doprev(nil, 1, wwwtop-atoi(s));
546                 else
547                         message("Usage: j index");
548                 break;
549         case 'r':
550                 s = arg(s);
551                 if(*s=='\0' && selection)
552                         geturl(selection->fullname, GET, 0, 0, 0);
553                 break;
554         case 'W':
555                 s = arg(s);
556                 if(s=='\0'){
557                         message("Usage: W file");
558                         break;
559                 }
560                 screendump(s, 1);
561                 break;
562         case 'w':
563                 s = arg(s);
564                 if(s=='\0'){
565                         message("Usage: w file");
566                         break;
567                 }
568                 screendump(s, 0);
569                 break;
570         case 's':
571                 s = arg(s);
572                 if(*s=='\0'){
573                         if(selection){
574                                 s=strrchr(selection->fullname, '/');
575                                 if(s) s++;
576                         }
577                         if(s==0 || *s=='\0'){
578                                 message("Usage: s file");
579                                 break;
580                         }
581                 }
582                 save(selection, s);
583                 break;
584         case 'q':
585                 draw(screen, screen->r, display->white, 0, ZP);
586                 exits(0);
587         }
588         plinitentry(cmd, EXPAND, 0, "", docmd);
589         if(defdisplay) pldraw(cmd, screen);
590 }
591 void hiturl(int buttons, char *url, int map){
592         switch(buttons){
593         case 1: geturl(url, GET, 0, 1, map); break;
594         case 2: selurl(url); break;
595         case 4: message("Button 3 hit on url can't happen!"); break;
596         }
597 }
598 /*
599  * user selected from the list of available pages
600  */
601 void doprev(Panel *p, int buttons, int index){
602         int i;
603         USED(p);
604         if(index < 0 || index >= nwww())
605                 return;
606         i = wwwtop-index-1;
607         switch(buttons){
608         case 1: setcurrent(i, 0);       /* no break ... */
609         case 2: selurl(www(i)->url->fullname); break;
610         case 4: message("Button 3 hit on page can't happen!"); break;
611         }
612 }
613
614 /*
615  * Follow an html link
616  */
617 void dolink(Panel *p, int buttons, Rtext *word){
618         char mapurl[NNAME];
619         Action *a;
620         Point coord;
621         int yoffs;
622         USED(p);
623         a=word->user;
624         if(a && a->link){
625                 if(a->ismap){
626                         yoffs=plgetpostextview(p);
627                         coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
628                         snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
629                         hiturl(buttons, mapurl, 1);
630                 }
631                 else
632                         hiturl(buttons, a->link, 0);
633         }
634 }
635 void filter(char *cmd, int fd){
636         flushimage(display, 1);
637         switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
638         case -1:
639                 message("Can't fork!");
640                 break;
641         case 0:
642                 close(0);
643                 dup(fd, 0);
644                 close(fd);
645                 execl("/bin/rc", "rc", "-c", cmd, 0);
646                 message("Can't exec /bin/rc!");
647                 _exits(0);
648         default:
649                 break;
650         }
651         close(fd);
652 }
653 void gettext(Www *w, int fd, int type){
654         flushimage(display, 1);
655         switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
656         case -1:
657                 message("Can't fork, please wait");
658                 if(type==HTML)
659                         plrdhtml(w->url->fullname, fd, w);
660                 else
661                         plrdplain(w->url->fullname, fd, w);
662                 break;
663         case 0:
664                 if(type==HTML)
665                         plrdhtml(w->url->fullname, fd, w);
666                 else
667                         plrdplain(w->url->fullname, fd, w);
668                 _exits(0);
669         }
670         close(fd);
671 }
672
673 void freetext(Rtext *t){
674         Rtext *tt;
675         Action *a;
676
677         tt = t;
678         for(; t!=0; t = t->next){
679                 t->b=0;
680                 free(t->text);
681                 t->text=0;
682                 if(a = t->user){
683                         t->user=0;
684                         free(a->image);
685                         free(a->link);
686                         free(a->name);
687                         free(a);
688                 }
689         }
690         plrtfree(tt);
691 }
692
693 void popwin(char *cmd){
694         flushimage(display, 1);
695         switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
696         case -1:
697                 message("sorry, can't fork to %s", cmd);
698                 break;
699         case 0:
700                 execl("/bin/window", "window", "100 100 800 800", "rc", "-c", cmd, 0);
701                 _exits(0);
702         }
703 }
704
705 int readstr(char *buf, int nbuf, char *base, char *name)
706 {
707         char path[128];
708         int n, fd;
709
710         snprint(path, sizeof path, "%s/%s", base, name);
711         if((fd = open(path, OREAD)) < 0){
712         ErrOut:
713                 memset(buf, 0, nbuf);
714                 return 0;
715         }
716         n = read(fd, buf, nbuf-1);
717         close(fd);
718         if(n <= 0){
719                 close(fd);
720                 goto ErrOut;
721         }
722         buf[n] = 0;
723         return n;
724 }
725
726 int fileurlopen(Url *url){
727         char *rel, *base, *x;
728         int fd;
729
730         rel = base = nil;
731         if(cistrncmp(url->basename, "file:", 5) == 0)
732                 base = url->basename+5;
733         if(cistrncmp(url->reltext, "file:", 5) == 0)
734                 rel = url->reltext+5;
735         if(rel == nil && base == nil)
736                 return -1;
737         if(rel == nil)
738                 rel = url->reltext;
739         if(base && base[0] == '/' && rel[0] != '/'){
740                 if(x = strrchr(base, '/'))
741                         *x = 0;
742                 snprint(url->fullname, sizeof(url->fullname), "%s/%s", base, rel);
743                 if(x)   *x = '/';
744         }else
745                 snprint(url->fullname, sizeof(url->fullname), "%s", rel);
746         url->tag[0] = 0;
747         if(x = strrchr(url->fullname, '#')){
748                 *x++ = 0;
749                 strncpy(url->tag, x, sizeof(url->tag));
750         }
751         fd = open(cleanname(url->fullname), OREAD);
752         if(fd < 0)
753                 return -1;
754         memset(url->fullname, 0, sizeof(url->fullname));
755         strcpy(url->fullname, "file:");
756         fd2path(fd, url->fullname+5, sizeof(url->fullname)-6);
757         url->type = content2type("application/octet-stream", url->fullname);
758         return fd;
759 }
760
761 int urlopen(Url *url, int method, char *body){
762         int conn, ctlfd, fd, n;
763         char buf[1024+1], *p;
764
765         if(debug) fprint(2, "urlopen %s (%s)\n", url->reltext, url->basename); 
766
767         if(method == GET)
768                 if((fd = fileurlopen(url)) >= 0)
769                         return fd;
770
771         snprint(buf, sizeof buf, "%s/clone", mtpt);
772         if((ctlfd = open(buf, ORDWR)) < 0)
773                 return -1;
774         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
775                 close(ctlfd);
776                 return -1;
777         }
778         buf[n] = 0;
779         conn = atoi(buf);
780
781         if(url->basename[0]){
782                 n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
783                 write(ctlfd, buf, n);
784         }
785         n = snprint(buf, sizeof buf, "url %s", url->reltext);
786         if(write(ctlfd, buf, n) != n){
787         ErrOut:
788                 close(ctlfd);
789                 return -1;
790         }
791
792         if(method == POST && body){
793                 snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
794                 if((fd = open(buf, OWRITE)) < 0)
795                         goto ErrOut;
796                 n = strlen(body);
797                 if(write(fd, body, n) != n){
798                         close(fd);
799                         goto ErrOut;
800                 }
801                 close(fd);
802         }
803
804         snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
805         if((fd = open(buf, OREAD)) < 0)
806                 goto ErrOut;
807
808         snprint(buf, sizeof buf, "%s/%d/parsed", mtpt, conn);
809         readstr(url->fullname, sizeof(url->fullname), buf, "url");
810         readstr(url->tag, sizeof(url->tag), buf, "fragment");
811
812         snprint(buf, sizeof buf, "%s/%d", mtpt, conn);
813         readstr(buf, sizeof buf, buf, "contenttype");
814         url->charset[0] = 0;
815         if(p = cistrstr(buf, "charset=")){
816                 p += 8;
817                 strncpy(url->charset, p, sizeof(url->charset));
818                 if(p = strchr(url->charset, ';'))
819                         *p = 0;
820         }
821         if(p = strchr(buf, ';'))
822                 *p = 0;
823         url->type = content2type(buf, url->fullname);
824
825         close(ctlfd);
826         return fd;
827 }
828
829 int pipeline(char *cmd, int fd)
830 {
831         int pfd[2];
832
833         if(pipe(pfd)==-1){
834 Err:
835                 close(fd);
836                 werrstr("pipeline for %s failed: %r", cmd);
837                 return -1;
838         }
839         switch(fork()){
840         case -1:
841                 close(pfd[0]);
842                 close(pfd[1]);
843                 goto Err;
844         case 0:
845                 dup(fd, 0);
846                 dup(pfd[0], 1);
847                 close(pfd[0]);
848                 close(pfd[1]);
849                 execl("/bin/rc", "rc", "-c", cmd, 0);
850                 _exits(0);
851         }
852         close(pfd[0]);
853         close(fd);
854         return pfd[1];
855 }
856
857 /*
858  * select the file at the given url
859  */
860 void selurl(char *urlname){
861         static Url url;
862         seturl(&url, urlname, current?
863                 current->url->fullname :
864                 defurl.fullname);
865         selection=&url;
866         message("selected: %s", selection->fullname[0] ? selection->fullname : selection->reltext);
867 }
868 void seturl(Url *url, char *urlname, char *base){
869         strncpy(url->reltext, urlname, sizeof(url->reltext));
870         strncpy(url->basename, base, sizeof(url->basename));
871         url->fullname[0] = 0;
872         url->charset[0] = 0;
873         url->tag[0] = 0;
874         url->type = 0;
875         url->map = 0;
876 }
877 Url *copyurl(Url *u){
878         Url *v;
879         v=emalloc(sizeof(Url));
880         *v=*u;
881         return v;
882 }
883 void freeurl(Url *u){
884         if(u!=&defurl && u!=&badurl)
885                 free(u);
886 }
887
888 /*
889  * get the file at the given url
890  */
891 void geturl(char *urlname, int method, char *body, int cache, int map){
892         int i, fd;
893         char cmd[NNAME];
894         int pfd[2];
895         Www *w;
896
897         selurl(urlname);
898         selection->map=map;
899
900         message("getting %s", selection->reltext);
901         esetcursor(&patientcurs);
902         for(;;){
903                 if((fd=urlopen(selection, method, body)) < 0){
904                         message("%r");
905                         setcurrent(-1, 0);
906                         break;
907                 }
908                 message("getting %s", selection->fullname);
909                 if(selection->type&COMPRESS)
910                         fd=pipeline("/bin/uncompress", fd);
911                 else if(selection->type&GUNZIP)
912                         fd=pipeline("/bin/gunzip", fd);
913                 snprint(cmd, sizeof(cmd), selection->charset[0] ?
914                         "/bin/uhtml -c %s" : "/bin/uhtml", selection->charset);
915                 fd = pipeline(cmd, fd);
916                 switch(selection->type&~COMPRESSION){
917                 default:
918                         message("Bad type %x in geturl", selection->type);
919                         break;
920                 case HTML:
921                 case PLAIN:
922                         w = www(i = wwwtop++);
923                         if(i >= NWWW){
924                                 extern void freeform(void *p);
925                                 extern void freepix(void *p);
926
927                                 /* wait for the reader to finish the document */
928                                 while(!w->finished && !w->alldone){
929                                         unlockdisplay(display);
930                                         sleep(10);
931                                         lockdisplay(display);
932                                 }
933
934                                 freetext(w->text);
935                                 freeform(w->form);
936                                 freepix(w->pix);
937                                 freeurl(w->url);
938                                 memset(w, 0, sizeof(*w));
939                         }
940                         if(selection->map)
941                                 w->url=copyurl(current->url);
942                         else
943                                 w->url=copyurl(selection);
944                         w->finished = 0;
945                         w->alldone = 0;
946                         gettext(w, fd, selection->type&~COMPRESSION);
947                         plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
948                         if(defdisplay) pldraw(list, screen);
949                         setcurrent(i, selection->tag);
950                         break;
951                 case POSTSCRIPT:
952                 case GIF:
953                 case JPEG:
954                 case PNG:
955                 case PDF:
956                         filter("page -w", fd);
957                         break;
958                 case TIFF:
959                         filter("/sys/lib/mothra/tiffview", fd);
960                         break;
961                 case XBM:
962                         filter("fb/xbm2pic|fb/9v", fd);
963                         break;
964                 }
965                 break;
966         }
967         donecurs();
968 }
969 void updtext(Www *w){
970         Rtext *t;
971         Action *a;
972         for(t=w->text;t;t=t->next){
973                 a=t->user;
974                 if(a){
975                         if(a->field)
976                                 mkfieldpanel(t);
977                         a->field=0;
978                 }
979         }
980         w->yoffs=plgetpostextview(text);
981         plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
982         plsetpostextview(text, w->yoffs);
983         pldraw(root, screen);
984 }
985 Cursor confirmcursor={
986         0, 0,
987         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
988         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
989         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
990         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
991
992         0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
993         0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
994         0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
995         0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
996 };
997 int confirm(int b){
998         Mouse down, up;
999         esetcursor(&confirmcursor);
1000         do down=emouse(); while(!down.buttons);
1001         do up=emouse(); while(up.buttons);
1002         donecurs();
1003         return down.buttons==(1<<(b-1));
1004 }
1005 void snarf(Panel *p){
1006         int fd;
1007         fd=create("/dev/snarf", OWRITE, 0666);
1008         if(fd>=0){
1009                 fprint(fd, "%s", selection->fullname[0] ? selection->fullname : selection->reltext);
1010                 close(fd);
1011         }
1012 }
1013 void paste(Panel *p){
1014         char buf[1024];
1015         int n, len, fd;
1016         fd=open("/dev/snarf", OREAD);
1017         strncpy(buf, plentryval(p), sizeof(buf));
1018         len=strlen(buf);
1019         n=read(fd, buf+len, sizeof(buf)-len-1);
1020         if(n>0){
1021                 buf[len+n]='\0';
1022                 plinitentry(cmd, PACKE|EXPAND, 0, buf, docmd);
1023                 pldraw(cmd, screen);
1024         }
1025         close(fd);
1026 }
1027 void hit3(int button, int item){
1028         char name[NNAME];
1029         Panel *swap;
1030         int fd;
1031         USED(button);
1032         switch(item){
1033         case 0:
1034                 swap=root;
1035                 root=alt;
1036                 alt=swap;
1037                 current->yoffs=plgetpostextview(text);
1038                 swap=text;
1039                 text=alttext;
1040                 alttext=swap;
1041                 defdisplay=!defdisplay;
1042                 plpack(root, screen->r);
1043                 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1044                 plsetpostextview(text, current->yoffs);
1045                 pldraw(root, screen);
1046                 break;
1047         case 1:
1048                 snarf(cmd);
1049                 break;
1050         case 2:
1051                 paste(cmd);
1052                 break;
1053         case 3:
1054                 snprint(name, sizeof(name), "%s/hit.html", home);
1055                 fd=open(name, OWRITE);
1056                 if(fd==-1){
1057                         fd=create(name, OWRITE, 0666);
1058                         if(fd==-1){
1059                                 message("can't open %s", name);
1060                                 return;
1061                         }
1062                         fprint(fd, "<head><title>Hit List</title></head>\n");
1063                         fprint(fd, "<body><h1>Hit list</h1>\n");
1064                 }
1065                 seek(fd, 0, 2);
1066                 fprint(fd, "<p><a href=\"%s\">%s</a>\n",
1067                         selection->fullname, selection->fullname);
1068                 close(fd);
1069                 break;
1070         case 4:
1071                 snprint(name, sizeof(name), "file:%s/hit.html", home);
1072                 geturl(name, GET, 0, 1, 0);
1073                 break;
1074         case 5:
1075                 if(confirm(3)){
1076                         draw(screen, screen->r, display->white, 0, ZP);
1077                         exits(0);
1078                 }
1079                 break;
1080         }
1081 }