14 int debug=0; /* -d flag causes debug messages to appear in mothra.err */
15 int verbose=0; /* -v flag causes html errors to appear in mothra.err */
16 int defdisplay=1; /* is the default (initial) display visible? */
17 Panel *root; /* the whole display */
18 Panel *alt; /* the alternate display */
19 Panel *alttext; /* the alternate text window */
20 Panel *cmd; /* command entry */
21 Panel *curttl; /* label giving the title of the visible text */
22 Panel *cururl; /* label giving the url of the visible text */
23 Panel *list; /* list of previously acquired www pages */
24 Panel *msg; /* message display */
25 Panel *menu3; /* button 3 menu */
26 char mothra[] = "mothra!";
29 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
30 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
31 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
32 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
34 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
35 0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
36 0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
37 0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
41 0x0F, 0xBF, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
42 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
43 0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF,
44 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
46 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
47 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
48 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
49 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90
53 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
54 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0,
55 0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
56 0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
59 0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60,
60 0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
61 0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 {0x00, 0x00, 0x60, 0x06, 0xf8, 0x1f, 0xfc, 0x3f,
66 0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe,
67 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8,
68 0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x00, 0x00, },
69 {0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x58, 0x1a,
70 0x5c, 0x3a, 0x64, 0x26, 0x27, 0xe4, 0x37, 0xec,
71 0x37, 0xec, 0x17, 0xe8, 0x1b, 0xd8, 0x0e, 0x70,
72 0x0c, 0x30, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, }
80 void docmd(Panel *, char *);
81 void doprev(Panel *, int, int);
83 void setcurrent(int, char *);
84 char *genwww(Panel *, int);
86 void dolink(Panel *, int, Rtext *);
88 void mothon(Www *, int);
104 return &a[index % NWWW];
107 return wwwtop<NWWW ? wwwtop : NWWW;
110 int subpanel(Panel *obj, Panel *subj){
112 if(obj==subj) return 1;
113 for(obj=obj->child;obj;obj=obj->next)
114 if(subpanel(obj, subj)) return 1;
118 * Make sure that the keyboard focus is on-screen, by adjusting it to
119 * be the cmd entry if necessary.
125 yoffs=text->r.min.y-plgetpostextview(text);
126 for(t=current->text;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
127 if(t->r.max.y+yoffs>=text->r.min.y
128 && t->r.min.y+yoffs<text->r.max.y
130 && subpanel(t->p, plkbfocus))
138 void scrolltext(int dy, int whence)
142 s = plgetscroll(text);
151 s.pos.y = s.size.y+dy;
156 if(s.pos.y > s.size.y)
158 plsetscroll(text, s);
163 menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
164 root=plpopup(root, EXPAND, 0, 0, menu3);
165 p=plgroup(root, PACKN|FILLX);
166 msg=pllabel(p, PACKN|FILLX, mothra);
167 plplacelabel(msg, PLACEW);
168 pllabel(p, PACKW, "Go:");
169 cmd=plentry(p, PACKN|FILLX, 0, "", docmd);
170 p=plgroup(root, PACKN|FILLX);
171 bar=plscrollbar(p, PACKW);
172 list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
173 plscroll(list, 0, bar);
174 p=plgroup(root, PACKN|FILLX);
175 pllabel(p, PACKW, "Title:");
176 curttl=pllabel(p, PACKE|EXPAND, "---");
177 plplacelabel(curttl, PLACEW);
178 p=plgroup(root, PACKN|FILLX);
179 pllabel(p, PACKW, "Url:");
180 cururl=pllabel(p, PACKE|EXPAND, "---");
181 plplacelabel(cururl, PLACEW);
182 p=plgroup(root, PACKN|EXPAND);
183 bar=plscrollbar(p, PACKW|USERFL);
184 text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
185 plscroll(text, 0, bar);
187 alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
188 bar=plscrollbar(alt, PACKW|USERFL);
189 alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
190 plscroll(alttext, 0, bar);
193 void killcohort(void){
195 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
196 postnote(PNGROUP, cohort, "kill\n");
200 void catch(void*, char*){
203 void dienow(void*, char*){
206 int mkmfile(char *stem, int mode){
208 char filename[NNAME];
213 sprint(home, "%s/lib", henv);
214 f=create(home, OREAD, DMDIR|0777);
216 sprint(home, "%s/lib/mothra", henv);
217 f=create(home, OREAD, DMDIR|0777);
222 strcpy(home, "/tmp");
224 snprint(filename, sizeof(filename), "%s/%s", home, stem);
225 f=create(filename, OWRITE, mode);
227 f=create(stem, OWRITE, mode);
232 if(current && current->alldone==0)
233 esetcursor(&readingcurs);
235 esetcursor(&mothcurs);
240 void drawlock(int dolock){
244 lockdisplay(display);
247 unlockdisplay(display);
251 void scrollto(char *tag);
252 extern char *mtpt; /* url */
254 void main(int argc, char *argv[]){
256 enum { Eplumb = 128, Ekick = 256 };
265 fmtinstall('U', Ufmt);
268 case 'd': debug=1; break;
269 case 'v': verbose=1; break;
277 * so that we can stop all subprocesses with a note,
278 * and to isolate rendezvous from other processes
280 if(cohort=rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){
292 fprint(2, "Usage: %s [-d] [-m mtpt] [url]\n", argv[0]);
297 case 1: url=argv[0]; break;
299 errfile=mkmfile("mothra.err", 0666);
304 if(initdraw(0, 0, mothra) < 0)
305 sysfatal("initdraw: %r");
306 display->locking = 1;
307 chrwidth=stringwidth(font, "0");
308 pltabsize(chrwidth, 8*chrwidth);
309 einit(Emouse|Ekeyboard);
310 eplumb(Eplumb, "web");
311 if(pipe(kickpipe) < 0)
312 sysfatal("pipe: %r");
313 estart(Ekick, kickpipe[0], 256);
314 plinit(screen->depth);
315 if(debug) notify(dienow);
317 hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
319 sysfatal("can't allocimage!");
320 draw(hrule, Rect(0,1,1280,3), display->black, 0, ZP);
321 linespace=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
323 sysfatal("can't allocimage!");
324 bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
325 fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
328 unlockdisplay(display);
333 geturl(url, -1, 1, 0);
337 if(mouse.buttons==0 && current){
338 if(current->finished){
340 if(current->url->tag[0])
341 scrollto(current->url->tag);
350 flushimage(display, 1);
357 if(mouse.buttons==0 && current && current->changed){
358 if(!current->finished)
373 scrolltext(-text->size.y/4, 1);
376 scrolltext(-text->size.y/2, 1);
379 scrolltext(text->size.y/4, 1);
382 scrolltext(text->size.y/2, 1);
385 scrolltext(-text->size.y, 2);
391 if(mouse.buttons & (8|16)){
392 if(mouse.buttons & 8)
393 scrolltext(-text->size.y/24, 1);
395 scrolltext(text->size.y/24, 1);
398 plmouse(root, &mouse);
403 geturl(pm->data, -1, 1, 0);
409 Cursor confirmcursor={
411 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
412 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
413 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
414 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
416 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
417 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
418 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
419 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
423 esetcursor(&confirmcursor);
424 do down=emouse(); while(!down.buttons);
425 do up=emouse(); while(up.buttons);
427 return down.buttons==(1<<(b-1));
429 void message(char *s, ...){
430 static char buf[1024];
434 out = buf + vsnprint(buf, sizeof(buf), s, args);
437 plinitlabel(msg, PACKN|FILLX, buf);
438 if(defdisplay) pldraw(msg, screen);
440 void htmlerror(char *name, int line, char *m, ...){
441 static char buf[1024];
446 out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
447 out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
450 fprint(2, "%s\n", buf);
453 void eresized(int new){
457 if(new && getwindow(display, Refnone) == -1) {
458 fprint(2, "getwindow: %r\n");
464 draw(screen, r, display->white, 0, ZP);
465 pldraw(root, screen);
466 flushimage(display, 1);
469 void *emalloc(int n){
473 sysfatal("out of memory");
475 setmalloctag(v, getcallerpc(&n));
478 void nstrcpy(char *to, char *from, int len){
479 strncpy(to, from, len);
483 char *genwww(Panel *, int index){
484 static char buf[1024];
490 snprint(buf, sizeof(buf), "%2d %s", i+1, www(i)->title);
494 void scrollto(char *tag){
497 if(current == nil || text == nil)
500 for(tp=current->text;tp;tp=tp->next){
502 if(ap && ap->name && strcmp(ap->name, tag)==0){
503 current->yoffs=tp->topy;
508 plsetpostextview(text, current->yoffs);
509 flushimage(display, 1);
513 * selected text should be a url.
515 void setcurrent(int index, char *tag){
519 if(new==current && (tag==0 || tag[0]==0)) return;
521 current->yoffs=plgetpostextview(text);
523 plinitlabel(curttl, PACKE|EXPAND, current->title);
524 if(defdisplay) pldraw(curttl, screen);
525 plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
526 if(defdisplay) pldraw(cururl, screen);
527 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
529 if((i = open("/dev/label", OWRITE)) >= 0){
530 fprint(i, "%s %s", mothra, current->url->fullname);
536 do ++s; while(*s==' ' || *s=='\t');
539 void save(int ifd, char *name){
543 message("save: %s: %r", name);
546 ofd=create(name, OWRITE, 0666);
548 message("save: %s: %r", name);
551 switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){
553 message("Can't fork: %r");
561 snprint(buf, sizeof(buf),
562 "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name);
563 execl("/bin/rc", "rc", "-c", buf, nil);
570 void screendump(char *name, int full){
573 fd=create(name, OWRITE, 0666);
575 message("can't create %s", name);
579 writeimage(fd, screen, 0);
581 if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
582 message("can't allocate image");
586 draw(b, b->r, screen, 0, b->r.min);
587 writeimage(fd, b, 0);
594 * convert a url into a local file name.
596 char *urltofile(Url *url){
600 if(url->fullname[0] || url->reltext[0])
604 if(slash = strrchr(name, '/'))
612 * user typed a command.
614 void docmd(Panel *p, char *s){
619 while(*s==' ' || *s=='\t') s++;
621 * Non-command does a get on the url
623 if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
625 else switch(c = s[0]){
627 message("Unknown command %s", s);
631 if(*s=='\0' && selection)
639 s = urlstr(selection);
641 message("no url selected");
648 doprev(nil, 1, wwwtop-atoi(s));
650 message("Usage: j index");
653 mothon(current, !mothmode);
661 if(s==0 || *s=='\0'){
662 snprint(buf, sizeof(buf), "dump.bit");
663 if(eenter("Screendump to", buf, sizeof(buf), &mouse) <= 0)
667 screendump(s, c == 'W');
672 message("no url selected");
675 if(s==0 || *s=='\0'){
676 snprint(buf, sizeof(buf), "%s", urltofile(selection));
677 if(eenter("Save to", buf, sizeof(buf), &mouse) <= 0)
681 save(urlget(selection, -1), s);
686 plinitentry(cmd, EXPAND, 0, "", docmd);
687 if(defdisplay) pldraw(cmd, screen);
690 void hiturl(int buttons, char *url, int map){
692 case 1: geturl(url, -1, 0, map); break;
693 case 2: selurl(url); break;
694 case 4: message("Button 3 hit on url can't happen!"); break;
699 * user selected from the list of available pages
701 void doprev(Panel *p, int buttons, int index){
704 if(index < 0 || index >= nwww())
708 case 1: setcurrent(i, 0); /* no break ... */
709 case 2: selurl(www(i)->url->fullname); break;
710 case 4: message("Button 3 hit on page can't happen!"); break;
715 * Follow an html link
717 void dolink(Panel *p, int buttons, Rtext *word){
718 char *file, mapurl[NNAME];
724 if(a == nil || a->image == nil && a->link == nil)
727 hiturl(buttons, a->image ? a->image : a->link, 0);
729 yoffs=plgetpostextview(p);
730 coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
731 snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
732 hiturl(buttons, mapurl, 1);
734 hiturl(buttons, a->link ? a->link : a->image, 0);
737 void filter(char *cmd, int fd){
738 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
740 message("Can't fork!");
746 execl("/bin/rc", "rc", "-c", cmd, 0);
751 void gettext(Www *w, int fd, int type){
752 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
754 message("Can't fork, please wait");
758 plrdhtml(w->url->fullname, fd, w);
760 plrdplain(w->url->fullname, fd, w);
766 void freetext(Rtext *t){
771 for(; t!=0; t = t->next){
786 int pipeline(char *cmd, int fd)
793 werrstr("pipeline for %s failed: %r", cmd);
806 execl("/bin/rc", "rc", "-c", cmd, 0);
817 return url->fullname;
820 Url* selurl(char *urlname){
822 seturl(&url, urlname, current ? current->url->fullname : "");
824 message("selected: %s", urlstr(selection));
827 void seturl(Url *url, char *urlname, char *base){
828 nstrcpy(url->reltext, urlname, sizeof(url->reltext));
829 nstrcpy(url->basename, base, sizeof(url->basename));
830 url->fullname[0] = 0;
834 Url *copyurl(Url *u){
836 v=emalloc(sizeof(Url));
840 void freeurl(Url *u){
845 * get the file at the given url
847 void geturl(char *urlname, int post, int plumb, int map){
848 int i, fd, typ, pfd[2];
853 if(*urlname == '#' && post < 0){
861 message("getting %s", selection->reltext);
862 esetcursor(&patientcurs);
864 if((fd=urlget(selection, post)) < 0){
868 message("getting %s", selection->fullname);
869 if(mothmode && !plumb)
876 message("unknown file type");
880 snprint(cmd, sizeof(cmd), "%s", urltofile(selection));
881 if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){
888 fd = pipeline("/bin/uhtml", fd);
891 for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){
893 n += countpix(w->pix);
894 if(n >= NPIXMB*1024*1024)
897 w = www(i = wwwtop++);
899 /* wait for the reader to finish the document */
900 while(!w->finished && !w->alldone){
909 memset(w, 0, sizeof(*w));
912 w->url=copyurl(current->url);
914 w->url=copyurl(selection);
918 if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){
921 if(w->finished || w->alldone)
924 write(kickpipe[1], "C", 1);
928 plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
929 if(defdisplay) pldraw(list, screen);
930 setcurrent(i, selection->tag);
937 filter("page -w", fd);
944 void updtext(Www *w){
947 for(t=w->text;t;t=t->next){
957 w->yoffs=plgetpostextview(text);
958 plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
959 plsetpostextview(text, w->yoffs);
960 pldraw(root, screen);
965 write(kickpipe[1], "F", 1);
969 mothon(Www *w, int on)
974 if(w==0 || mothmode==on)
977 message("moth mode!");
981 * insert or remove artificial links to the href for
982 * images that are also links
984 for(t=w->text;t;t=t->next){
986 if(a == nil || a->image == nil || a->link == nil)
991 ap=emalloc(sizeof(Action));
992 ap->link = strdup(a->link);
993 plrtstr(&t->next, 0, 0, t->font, strdup("->"), PL_HOT, ap);
1005 void killpix(Www *w){
1008 if(w==0 || !w->finished && !w->alldone)
1010 for(t=w->text; t; t=t->next)
1017 void snarf(Panel *p){
1019 plputsnarf(urlstr(selection));
1020 /* non-ops if nothing selected */
1024 void paste(Panel *p){
1028 void hit3(int button, int item){
1040 current->yoffs=plgetpostextview(text);
1044 defdisplay=!defdisplay;
1045 plpack(root, screen->r);
1047 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1048 plsetpostextview(text, current->yoffs);
1050 pldraw(root, screen);
1053 mothon(current, !mothmode);
1062 snprint(name, sizeof(name), "%s/hit.html", home);
1063 fd=open(name, OWRITE);
1065 fd=create(name, OWRITE, 0666);
1067 message("can't open %s", name);
1070 fprint(fd, "<html><head><title>Hit List</title></head>\n");
1071 fprint(fd, "<body><h1>Hit list</h1>\n");
1074 fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
1078 snprint(name, sizeof(name), "file:%s/hit.html", home);
1079 geturl(name, -1, 1, 0);