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 *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 char mothra[] = "mothra!";
28 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
29 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
30 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
31 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
33 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
34 0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
35 0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
36 0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
40 0x0F, 0xBF, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
41 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
42 0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF,
43 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
45 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
46 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
47 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
48 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90
52 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
53 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0,
54 0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
55 0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
58 0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60,
59 0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
60 0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 {0x00, 0x00, 0x60, 0x06, 0xf8, 0x1f, 0xfc, 0x3f,
65 0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe,
66 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8,
67 0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x00, 0x00, },
68 {0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x58, 0x1a,
69 0x5c, 0x3a, 0x64, 0x26, 0x27, 0xe4, 0x37, 0xec,
70 0x37, 0xec, 0x17, 0xe8, 0x1b, 0xd8, 0x0e, 0x70,
71 0x0c, 0x30, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, }
79 void docmd(Panel *, char *);
80 void doprev(Panel *, int, int);
82 void setcurrent(int, char *);
83 char *genwww(Panel *, int);
85 void dolink(Panel *, int, Rtext *);
87 void mothon(Www *, int);
103 return &a[index % NWWW];
106 return wwwtop<NWWW ? wwwtop : NWWW;
109 int subpanel(Panel *obj, Panel *subj){
111 if(obj==subj) return 1;
112 for(obj=obj->child;obj;obj=obj->next)
113 if(subpanel(obj, subj)) return 1;
117 * Make sure that the keyboard focus is on-screen, by adjusting it to
118 * be the cmd entry if necessary.
124 yoffs=text->r.min.y-plgetpostextview(text);
125 for(t=current->text;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
126 if(t->r.max.y+yoffs>=text->r.min.y
127 && t->r.min.y+yoffs<text->r.max.y
129 && subpanel(t->p, plkbfocus))
137 void scrolltext(int dy, int whence)
141 s = plgetscroll(text);
150 s.pos.y = s.size.y+dy;
155 if(s.pos.y > s.size.y)
157 plsetscroll(text, s);
162 menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
163 root=plpopup(root, EXPAND, 0, 0, menu3);
164 p=plgroup(root, PACKN|FILLX);
165 msg=pllabel(p, PACKN|FILLX, mothra);
166 plplacelabel(msg, PLACEW);
167 pllabel(p, PACKW, "Go:");
168 cmd=plentry(p, PACKN|FILLX, 0, "", docmd);
169 p=plgroup(root, PACKN|FILLX);
170 bar=plscrollbar(p, PACKW);
171 list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
172 plscroll(list, 0, bar);
173 p=plgroup(root, PACKN|FILLX);
174 pllabel(p, PACKW, "Url:");
175 cururl=pllabel(p, PACKE|EXPAND, "---");
176 plplacelabel(cururl, PLACEW);
177 p=plgroup(root, PACKN|EXPAND);
178 bar=plscrollbar(p, PACKW|USERFL);
179 text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
180 plscroll(text, 0, bar);
182 alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
183 bar=plscrollbar(alt, PACKW|USERFL);
184 alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
185 plscroll(alttext, 0, bar);
188 void killcohort(void){
190 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
191 postnote(PNGROUP, cohort, "kill\n");
195 void catch(void*, char*){
198 void dienow(void*, char*){
201 int mkmfile(char *stem, int mode){
203 char filename[NNAME];
208 sprint(home, "%s/lib", henv);
209 f=create(home, OREAD, DMDIR|0777);
211 sprint(home, "%s/lib/mothra", henv);
212 f=create(home, OREAD, DMDIR|0777);
217 strcpy(home, "/tmp");
219 snprint(filename, sizeof(filename), "%s/%s", home, stem);
220 f=create(filename, OWRITE, mode);
222 f=create(stem, OWRITE, mode);
227 if(current && current->alldone==0)
228 esetcursor(&readingcurs);
230 esetcursor(&mothcurs);
235 void drawlock(int dolock){
239 lockdisplay(display);
242 unlockdisplay(display);
246 void scrollto(char *tag);
247 extern char *mtpt; /* url */
249 void main(int argc, char *argv[]){
251 enum { Eplumb = 128, Ekick = 256 };
260 fmtinstall('U', Ufmt);
263 case 'd': debug=1; break;
264 case 'v': verbose=1; break;
272 * so that we can stop all subprocesses with a note,
273 * and to isolate rendezvous from other processes
275 if(cohort=rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){
287 fprint(2, "Usage: %s [-d] [-m mtpt] [url]\n", argv[0]);
292 case 1: url=argv[0]; break;
294 errfile=mkmfile("mothra.err", 0666);
299 if(initdraw(0, 0, mothra) < 0)
300 sysfatal("initdraw: %r");
301 display->locking = 1;
302 chrwidth=stringwidth(font, "0");
303 pltabsize(chrwidth, 8*chrwidth);
304 einit(Emouse|Ekeyboard);
305 eplumb(Eplumb, "web");
306 if(pipe(kickpipe) < 0)
307 sysfatal("pipe: %r");
308 estart(Ekick, kickpipe[0], 256);
309 plinit(screen->depth);
310 if(debug) notify(dienow);
312 hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
314 sysfatal("can't allocimage!");
315 draw(hrule, Rect(0,1,1280,3), display->black, 0, ZP);
316 linespace=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
318 sysfatal("can't allocimage!");
319 bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
320 fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
323 unlockdisplay(display);
328 geturl(url, -1, 1, 0);
332 if(mouse.buttons==0 && current){
333 if(current->finished){
335 if(current->url->tag[0])
336 scrollto(current->url->tag);
345 flushimage(display, 1);
352 if(mouse.buttons==0 && current && current->changed){
353 if(!current->finished)
368 scrolltext(-text->size.y/4, 1);
371 scrolltext(-text->size.y/2, 1);
374 scrolltext(text->size.y/4, 1);
377 scrolltext(text->size.y/2, 1);
380 scrolltext(-text->size.y, 2);
386 if(mouse.buttons & (8|16)){
387 if(mouse.buttons & 8)
388 scrolltext(-text->size.y/24, 1);
390 scrolltext(text->size.y/24, 1);
393 plmouse(root, &mouse);
398 geturl(pm->data, -1, 1, 0);
404 Cursor confirmcursor={
406 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
407 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
408 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
409 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
411 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
412 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
413 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
414 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
418 esetcursor(&confirmcursor);
419 do down=emouse(); while(!down.buttons);
420 do up=emouse(); while(up.buttons);
422 return down.buttons==(1<<(b-1));
424 void message(char *s, ...){
425 static char buf[1024];
429 out = buf + vsnprint(buf, sizeof(buf), s, args);
432 plinitlabel(msg, PACKN|FILLX, buf);
433 if(defdisplay) pldraw(msg, screen);
435 void htmlerror(char *name, int line, char *m, ...){
436 static char buf[1024];
441 out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
442 out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
445 fprint(2, "%s\n", buf);
448 void eresized(int new){
452 if(new && getwindow(display, Refnone) == -1) {
453 fprint(2, "getwindow: %r\n");
459 draw(screen, r, display->white, 0, ZP);
460 pldraw(root, screen);
461 flushimage(display, 1);
464 void *emalloc(int n){
468 sysfatal("out of memory");
470 setmalloctag(v, getcallerpc(&n));
473 void nstrcpy(char *to, char *from, int len){
474 strncpy(to, from, len);
478 char *genwww(Panel *, int index){
479 static char buf[1024];
487 if(w->title[0]!='\0')
489 snprint(buf, sizeof(buf), "%2d %s", i+1, w->title);
493 void scrollto(char *tag){
496 if(current == nil || text == nil)
499 for(tp=current->text;tp;tp=tp->next){
501 if(ap && ap->name && strcmp(ap->name, tag)==0){
502 current->yoffs=tp->topy;
507 plsetpostextview(text, current->yoffs);
508 flushimage(display, 1);
512 * selected text should be a url.
514 void setcurrent(int index, char *tag){
518 if(new==current && (tag==0 || tag[0]==0)) return;
520 current->yoffs=plgetpostextview(text);
522 plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
523 if(defdisplay) pldraw(cururl, screen);
524 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
526 if((i = open("/dev/label", OWRITE)) >= 0){
527 fprint(i, "%s %s", mothra, current->url->fullname);
533 do ++s; while(*s==' ' || *s=='\t');
536 void save(int ifd, char *name){
540 message("save: %s: %r", name);
543 ofd=create(name, OWRITE, 0666);
545 message("save: %s: %r", name);
548 switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){
550 message("Can't fork: %r");
558 snprint(buf, sizeof(buf),
559 "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name);
560 execl("/bin/rc", "rc", "-c", buf, nil);
567 void screendump(char *name, int full){
570 fd=create(name, OWRITE, 0666);
572 message("can't create %s", name);
576 writeimage(fd, screen, 0);
578 if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
579 message("can't allocate image");
583 draw(b, b->r, screen, 0, b->r.min);
584 writeimage(fd, b, 0);
591 * convert a url into a local file name.
593 char *urltofile(Url *url){
597 if(url->fullname[0] || url->reltext[0])
601 if(slash = strrchr(name, '/'))
609 * user typed a command.
611 void docmd(Panel *p, char *s){
616 while(*s==' ' || *s=='\t') s++;
618 * Non-command does a get on the url
620 if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
622 else switch(c = s[0]){
624 message("Unknown command %s", s);
628 if(*s=='\0' && selection)
636 s = urlstr(selection);
638 message("no url selected");
645 doprev(nil, 1, wwwtop-atoi(s));
647 message("Usage: j index");
650 mothon(current, !mothmode);
658 if(s==0 || *s=='\0'){
659 snprint(buf, sizeof(buf), "dump.bit");
660 if(eenter("Screendump to", buf, sizeof(buf), &mouse) <= 0)
664 screendump(s, c == 'W');
669 message("no url selected");
672 if(s==0 || *s=='\0'){
673 snprint(buf, sizeof(buf), "%s", urltofile(selection));
674 if(eenter("Save to", buf, sizeof(buf), &mouse) <= 0)
678 save(urlget(selection, -1), s);
683 plinitentry(cmd, EXPAND, 0, "", docmd);
684 if(defdisplay) pldraw(cmd, screen);
687 void hiturl(int buttons, char *url, int map){
689 case 1: geturl(url, -1, 0, map); break;
690 case 2: selurl(url); break;
691 case 4: message("Button 3 hit on url can't happen!"); break;
696 * user selected from the list of available pages
698 void doprev(Panel *p, int buttons, int index){
701 if(index < 0 || index >= nwww())
705 case 1: setcurrent(i, 0); /* no break ... */
706 case 2: selurl(www(i)->url->fullname); break;
707 case 4: message("Button 3 hit on page can't happen!"); break;
712 * Follow an html link
714 void dolink(Panel *p, int buttons, Rtext *word){
715 char *file, mapurl[NNAME];
721 if(a == nil || a->image == nil && a->link == nil)
724 hiturl(buttons, a->image ? a->image : a->link, 0);
726 yoffs=plgetpostextview(p);
727 coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
728 snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
729 hiturl(buttons, mapurl, 1);
731 hiturl(buttons, a->link ? a->link : a->image, 0);
734 void filter(char *cmd, int fd){
735 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
737 message("Can't fork!");
743 execl("/bin/rc", "rc", "-c", cmd, 0);
748 void gettext(Www *w, int fd, int type){
749 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
751 message("Can't fork, please wait");
755 plrdhtml(w->url->fullname, fd, w);
757 plrdplain(w->url->fullname, fd, w);
763 void freetext(Rtext *t){
768 for(; t!=0; t = t->next){
783 int pipeline(char *cmd, int fd)
790 werrstr("pipeline for %s failed: %r", cmd);
803 execl("/bin/rc", "rc", "-c", cmd, 0);
814 return url->fullname;
817 Url* selurl(char *urlname){
819 seturl(&url, urlname, current ? current->url->fullname : "");
821 message("selected: %s", urlstr(selection));
824 void seturl(Url *url, char *urlname, char *base){
825 nstrcpy(url->reltext, urlname, sizeof(url->reltext));
826 nstrcpy(url->basename, base, sizeof(url->basename));
827 url->fullname[0] = 0;
831 Url *copyurl(Url *u){
833 v=emalloc(sizeof(Url));
837 void freeurl(Url *u){
842 * get the file at the given url
844 void geturl(char *urlname, int post, int plumb, int map){
845 int i, fd, typ, pfd[2];
850 if(*urlname == '#' && post < 0){
858 message("getting %s", selection->reltext);
859 esetcursor(&patientcurs);
861 if((fd=urlget(selection, post)) < 0){
865 message("getting %s", selection->fullname);
866 if(mothmode && !plumb)
873 message("unknown file type");
877 snprint(cmd, sizeof(cmd), "%s", urltofile(selection));
878 if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){
885 fd = pipeline("/bin/uhtml", fd);
888 for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){
890 n += countpix(w->pix);
891 if(n >= NPIXMB*1024*1024)
894 w = www(i = wwwtop++);
896 /* wait for the reader to finish the document */
897 while(!w->finished && !w->alldone){
906 memset(w, 0, sizeof(*w));
909 w->url=copyurl(current->url);
911 w->url=copyurl(selection);
915 if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){
918 if(w->finished || w->alldone)
921 write(kickpipe[1], "C", 1);
925 plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
926 if(defdisplay) pldraw(list, screen);
927 setcurrent(i, selection->tag);
934 filter("page -w", fd);
941 void updtext(Www *w){
944 if(defdisplay && w->gottitle==0 && w->title[0]!='\0')
945 pldraw(list, screen);
946 for(t=w->text;t;t=t->next){
956 w->yoffs=plgetpostextview(text);
957 plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
958 plsetpostextview(text, w->yoffs);
959 pldraw(text, screen);
964 write(kickpipe[1], "F", 1);
968 mothon(Www *w, int on)
973 if(w==0 || mothmode==on)
976 message("moth mode!");
980 * insert or remove artificial links to the href for
981 * images that are also links
983 for(t=w->text;t;t=t->next){
985 if(a == nil || a->image == nil || a->link == nil)
990 ap=emalloc(sizeof(Action));
991 ap->link = strdup(a->link);
992 plrtstr(&t->next, 0, 0, t->font, strdup("->"), PL_HOT, ap);
1004 void killpix(Www *w){
1007 if(w==0 || !w->finished && !w->alldone)
1009 for(t=w->text; t; t=t->next)
1016 void snarf(Panel *p){
1018 plputsnarf(urlstr(selection));
1019 /* non-ops if nothing selected */
1023 void paste(Panel *p){
1027 void hit3(int button, int item){
1039 current->yoffs=plgetpostextview(text);
1043 defdisplay=!defdisplay;
1044 plpack(root, screen->r);
1046 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1047 plsetpostextview(text, current->yoffs);
1049 pldraw(root, screen);
1052 mothon(current, !mothmode);
1061 snprint(name, sizeof(name), "%s/hit.html", home);
1062 fd=open(name, OWRITE);
1064 fd=create(name, OWRITE, 0666);
1066 message("can't open %s", name);
1069 fprint(fd, "<html><head><title>Hit List</title></head>\n");
1070 fprint(fd, "<body><h1>Hit list</h1>\n");
1073 fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
1077 snprint(name, sizeof(name), "file:%s/hit.html", home);
1078 geturl(name, -1, 1, 0);