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!";
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,
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,
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,
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
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,
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,
79 char *mtpt="/mnt/web";
83 void docmd(Panel *, char *);
84 void doprev(Panel *, int, int);
86 void setcurrent(int, char *);
87 char *genwww(Panel *, int);
89 void dolink(Panel *, int, Rtext *);
103 static Www a[1+NWWW];
104 return &a[1+(index % NWWW)];
107 return wwwtop<NWWW ? wwwtop : NWWW;
110 void err(Display *, char *msg){
111 fprint(2, "err: %s (%r)\n", msg);
114 int subpanel(Panel *obj, Panel *subj){
116 if(obj==subj) return 1;
117 for(obj=obj->child;obj;obj=obj->next)
118 if(subpanel(obj, subj)) return 1;
122 * Make sure that the keyboard focus is on-screen, by adjusting it to
123 * be the cmd entry if necessary.
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
134 && subpanel(t->p, pl_kbfocus)) return;
139 void scrolltext(int dy)
143 s = plgetscroll(text);
147 if(s.pos.y > s.size.y)
149 plsetscroll(text, s);
150 pldraw(root, screen);
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);
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);
184 void killcohort(void){
186 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
187 postnote(PNGROUP, getpid(), "kill\n");
191 void dienow(void*, char*){
194 int mkmfile(char *stem, int mode){
196 char filename[NNAME];
201 sprint(home, "%s/lib", henv);
202 f=create(home, OREAD, DMDIR|0777);
204 sprint(home, "%s/lib/mothra", henv);
205 f=create(home, OREAD, DMDIR|0777);
210 strcpy(home, "/tmp");
212 snprint(filename, sizeof(filename), "%s/%s", home, stem);
213 f=create(filename, OWRITE, mode);
215 f=create(stem, OWRITE, mode);
218 void main(int argc, char *argv[]){
220 enum { Eplumb = 128 };
227 case 'd': debug++; break;
228 case 'v': verbose=1; break;
236 * so that we can stop all subprocesses with a note,
237 * and to isolate rendezvous from other processes
239 rfork(RFNOTEG|RFNAMEG|RFREND);
244 fprint(2, "Usage: %s [-d] [-m mtpt] [url]\n", argv[0]);
248 if(url==0 || url[0]=='\0')
251 case 1: url=argv[0]; break;
253 errfile=mkmfile("mothra.err", 0666);
258 logfile=mkmfile("mothra.log", 0666|DMAPPEND);
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");
267 plinit(screen->depth);
268 if(debug) notify(dienow);
270 hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
272 fprint(2, "%s: can't allocimage!\n", argv[0]);
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);
278 fprint(2, "%s: can't allocimage!\n", argv[0]);
281 bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
282 fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
285 strcpy(new->title, "See error message above");
286 plrtstr(&new->text, 0, 0, font, "See error message above", 0, 0);
290 unlockdisplay(display);
292 lockdisplay(display);
294 geturl(url, GET, 0, 1, 0);
296 if(logfile==-1) message("Can't open log file");
299 if(mouse.buttons==0 && current){
300 if(current->finished){
308 else if(current->changed){
314 unlockdisplay(display);
316 lockdisplay(display);
326 scrolltext(-text->size.y/4);
329 scrolltext(text->size.y/4);
335 plmouse(root, e.mouse);
340 geturl(pm->data, GET, 0, 1, 0);
346 void message(char *s, ...){
347 static char buf[1024];
351 out = buf + vsnprint(buf, sizeof(buf), s, args);
354 plinitlabel(msg, PACKN|FILLX, buf);
355 if(defdisplay) pldraw(msg, screen);
357 void htmlerror(char *name, int line, char *m, ...){
358 static char buf[1024];
363 out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
364 out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
367 fprint(2, "%s\n", buf);
370 void eresized(int new){
373 lockdisplay(display);
374 if(new && getwindow(display, Refnone) == -1) {
375 fprint(2, "getwindow: %r\n");
379 plinitlabel(curttl, PACKE|EXPAND, "---");
380 plinitlabel(cururl, PACKE|EXPAND, "---");
383 plinitlabel(curttl, PACKE|EXPAND, current->title);
384 plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
386 draw(screen, r, display->white, 0, ZP);
387 pldraw(root, screen);
388 unlockdisplay(display);
390 void *emalloc(int n){
394 fprint(2, "out of space\n");
399 void *emallocz(int n, int z){
407 char *genwww(Panel *, int index){
408 static char buf[1024];
414 snprint(buf, sizeof(buf), "%2d %s", i+1, www(i)->title);
419 esetcursor(current && current->alldone?0:&readingcurs);
422 * selected text should be a url.
423 * get the document, scroll to the given tag
425 void setcurrent(int index, char *tag){
431 if(new==current && (tag==0 || tag[0]==0)) return;
433 current->yoffs=plgetpostextview(text);
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);
441 for(tp=current->text;tp;tp=tp->next){
443 if(ap && ap->name && strcmp(ap->name, tag)==0){
444 current->yoffs=tp->topy;
449 plsetpostextview(text, current->yoffs);
451 flushimage(display, 1);
454 do ++s; while(*s==' ' || *s=='\t');
457 void save(Url *url, char *name){
460 ofd=create(name, OWRITE, 0666);
462 message("save: %s: %r", name);
465 esetcursor(&patientcurs);
466 ifd=urlopen(url, GET, 0);
469 message("save: %s: %r", selection->fullname);
472 switch(rfork(RFNOTEG|RFFDG|RFPROC|RFNOWAIT)){
474 message("Can't fork -- please wait");
475 esetcursor(&patientcurs);
476 while((n=read(ifd, buf, 4096))>0)
481 while((n=read(ifd, buf, 4096))>0)
483 if(n==-1) fprint(2, "save: %s: %r\n", url->fullname);
489 void screendump(char *name, int full){
492 fd=create(name, OWRITE|OTRUNC, 0666);
494 message("can't create %s", name);
498 writeimage(fd, screen, 0);
500 if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
501 message("can't allocate image");
505 draw(b, b->r, screen, 0, b->r.min);
506 writeimage(fd, b, 0);
513 * user typed a command.
515 void docmd(Panel *p, char *s){
517 while(*s==' ' || *s=='\t') s++;
519 * Non-command does a get on the url
521 if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
522 geturl(s, GET, 0, 1, 0);
525 message("Unknown command %s, type h for help", s);
531 geturl(selection->fullname, GET, 0, 1, 0);
533 message("no url selected");
535 else geturl(s, GET, 0, 1, 0);
539 if(*s == '\0' && selection)
540 geturl(selection->fullname, GET, 0, 0, 0);
545 message("Usage: W file");
553 message("Usage: w file");
562 s=strrchr(selection->fullname, '/');
565 if(s==0 || *s=='\0'){
566 message("Usage: s file");
573 draw(screen, screen->r, display->white, 0, ZP);
576 plinitentry(cmd, EXPAND, 0, "", docmd);
577 if(defdisplay) pldraw(cmd, screen);
579 void hiturl(int buttons, char *url, int map){
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;
587 * user selected from the list of available pages
589 void doprev(Panel *p, int buttons, int index){
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;
603 * Follow an html link
605 void dolink(Panel *p, int buttons, Rtext *word){
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);
620 hiturl(buttons, a->link, 0);
623 void filter(char *cmd, int fd){
624 flushimage(display, 1);
625 switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
627 message("Can't fork!");
633 execl("/bin/rc", "rc", "-c", cmd, 0);
634 message("Can't exec /bin/rc!");
641 void gettext(Www *w, int fd, int type){
642 flushimage(display, 1);
643 switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
645 message("Can't fork, please wait");
647 plrdhtml(w->url->fullname, fd, w);
649 plrdplain(w->url->fullname, fd, w);
653 plrdhtml(w->url->fullname, fd, w);
655 plrdplain(w->url->fullname, fd, w);
661 void freetext(Rtext *t){
666 for(; t!=0; t = t->next){
681 void popwin(char *cmd){
682 flushimage(display, 1);
683 switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
685 message("sorry, can't fork to %s", cmd);
688 execl("/bin/window", "window", "100 100 800 800", "rc", "-c", cmd, 0);
693 int readstr(char *buf, int nbuf, char *base, char *name)
698 snprint(path, sizeof path, "%s/%s", base, name);
699 if((fd = open(path, OREAD)) < 0){
701 memset(buf, 0, nbuf);
704 n = read(fd, buf, nbuf-1);
714 int urlopen(Url *url, int method, char *body){
715 int conn, ctlfd, fd, n;
718 snprint(buf, sizeof buf, "%s/clone", mtpt);
719 if((ctlfd = open(buf, ORDWR)) < 0)
721 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
728 if(url->basename[0]){
729 n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
730 write(ctlfd, buf, n);
732 n = snprint(buf, sizeof buf, "url %s", url->reltext);
733 if(write(ctlfd, buf, n) != n){
739 if(method == POST && body){
740 snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
741 if((fd = open(buf, OWRITE)) < 0)
744 if(write(fd, body, n) != n){
751 snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
752 if((fd = open(buf, OREAD)) < 0)
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");
759 snprint(buf, sizeof buf, "%s/%d", mtpt, conn);
760 readstr(buf, sizeof buf, buf, "contenttype");
761 url->type = content2type(buf, url->fullname);
767 int pipeline(char *cmd, int fd)
774 werrstr("pipeline for %s failed: %r", cmd);
787 execl("/bin/rc", "rc", "-c", cmd, 0);
796 * select the file at the given url
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;
808 void selurl(char *urlname){
810 seturl(&url, urlname, current?
811 current->url->fullname :
814 message("selected: %s", selection->fullname);
816 Url *copyurl(Url *u){
818 v=emalloc(sizeof(Url));
822 void freeurl(Url *u){
823 if(u!=&defurl && u!=&badurl)
828 * get the file at the given url
830 void geturl(char *urlname, int method, char *body, int cache, int map){
839 message("getting %s", selection->reltext);
840 esetcursor(&patientcurs);
842 if((fd=urlopen(selection, method, body)) < 0){
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){
854 message("Bad type %x in geturl", selection->type);
858 w = www(i = wwwtop++);
860 extern void freeform(void *p);
861 extern void freepix(void *p);
863 /* wait for the reader to finish the document */
864 while(!w->finished && !w->alldone){
865 unlockdisplay(display);
867 lockdisplay(display);
874 memset(w, 0, sizeof(*w));
877 w->url=copyurl(current->url);
879 w->url=copyurl(selection);
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);
892 filter("page -w", fd);
895 filter("/sys/lib/mothra/tiffview", fd);
898 filter("fb/xbm2pic|fb/9v", fd);
905 void updtext(Www *w){
908 for(t=w->text;t;t=t->next){
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);
921 Cursor confirmcursor={
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,
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,
935 esetcursor(&confirmcursor);
936 do down=emouse(); while(!down.buttons);
937 do up=emouse(); while(up.buttons);
939 return down.buttons==(1<<(b-1));
941 void snarf(Panel *p){
943 fd=create("/dev/snarf", OWRITE, 0666);
945 fprint(fd, "%s", selection->fullname);
949 void paste(Panel *p){
952 fd=open("/dev/snarf", OREAD);
953 strncpy(buf, plentryval(p), sizeof(buf));
955 n=read(fd, buf+len, 1023-len);
958 plinitentry(cmd, PACKE|EXPAND, 0, buf, docmd);
963 void hit3(int button, int item){
964 char name[100], *home;
973 current->yoffs=plgetpostextview(text);
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);
995 snprint(name, sizeof(name), "%s/lib/mothra/hit.html", home);
996 fd=open(name, OWRITE);
998 fd=create(name, OWRITE, 0666);
1000 message("can't open %s", name);
1003 fprint(fd, "<head><title>Hit List</title></head>\n");
1004 fprint(fd, "<body><h1>Hit list</h1>\n");
1007 fprint(fd, "<p><a href=\"%s\">%s</a>\n",
1008 selection->fullname, selection->fullname);
1012 home=getenv("home");
1014 message("no $home");
1017 snprint(name, sizeof(name), "file:%s/lib/mothra/hit.html", home);
1018 geturl(name, GET, 0, 1, 0);
1022 draw(screen, screen->r, display->white, 0, ZP);