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->link == nil && a->image == nil))
724 hiturl(buttons, a->image ? a->image : a->link, 0);
727 yoffs=plgetpostextview(p);
728 coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
729 snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
730 hiturl(buttons, mapurl, 1);
732 hiturl(buttons, a->link, 0);
736 void filter(char *cmd, int fd){
737 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
739 message("Can't fork!");
745 execl("/bin/rc", "rc", "-c", cmd, 0);
750 void gettext(Www *w, int fd, int type){
751 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
753 message("Can't fork, please wait");
757 plrdhtml(w->url->fullname, fd, w);
759 plrdplain(w->url->fullname, fd, w);
765 void freetext(Rtext *t){
770 for(; t!=0; t = t->next){
785 int pipeline(char *cmd, int fd)
792 werrstr("pipeline for %s failed: %r", cmd);
805 execl("/bin/rc", "rc", "-c", cmd, 0);
816 return url->fullname;
819 Url* selurl(char *urlname){
821 seturl(&url, urlname, current ? current->url->fullname : "");
823 message("selected: %s", urlstr(selection));
826 void seturl(Url *url, char *urlname, char *base){
827 nstrcpy(url->reltext, urlname, sizeof(url->reltext));
828 nstrcpy(url->basename, base, sizeof(url->basename));
829 url->fullname[0] = 0;
833 Url *copyurl(Url *u){
835 v=emalloc(sizeof(Url));
839 void freeurl(Url *u){
844 * get the file at the given url
846 void geturl(char *urlname, int post, int plumb, int map){
847 int i, fd, typ, pfd[2];
852 if(*urlname == '#' && post < 0){
860 message("getting %s", selection->reltext);
861 esetcursor(&patientcurs);
863 if((fd=urlget(selection, post)) < 0){
867 message("getting %s", selection->fullname);
868 if(mothmode && !plumb)
875 message("unknown file type");
879 snprint(cmd, sizeof(cmd), "%s", urltofile(selection));
880 if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){
887 fd = pipeline("/bin/uhtml", fd);
890 for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){
892 n += countpix(w->pix);
893 if(n >= NPIXMB*1024*1024)
896 w = www(i = wwwtop++);
898 /* wait for the reader to finish the document */
899 while(!w->finished && !w->alldone){
908 memset(w, 0, sizeof(*w));
911 w->url=copyurl(current->url);
913 w->url=copyurl(selection);
917 if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){
920 if(w->finished || w->alldone)
923 write(kickpipe[1], "C", 1);
927 plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
928 if(defdisplay) pldraw(list, screen);
929 setcurrent(i, selection->tag);
936 filter("page -w", fd);
943 void updtext(Www *w){
946 if(defdisplay && w->gottitle==0 && w->title[0]!='\0')
947 pldraw(list, screen);
948 for(t=w->text;t;t=t->next){
958 w->yoffs=plgetpostextview(text);
959 plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
960 plsetpostextview(text, w->yoffs);
961 pldraw(text, screen);
966 write(kickpipe[1], "F", 1);
970 mothon(Www *w, int on)
975 if(w==0 || mothmode==on)
978 message("moth mode!");
982 * insert or remove artificial links to the href for
983 * images that are also links
985 for(t=w->text;t;t=t->next){
987 if(a == nil || a->image == nil)
999 ap=emalloc(sizeof(Action));
1000 ap->link = strdup(a->link);
1001 plrtstr(&t->next, 0, 0, t->font, strdup("->"), PL_HOT, ap);
1013 void killpix(Www *w){
1016 if(w==0 || !w->finished && !w->alldone)
1018 for(t=w->text; t; t=t->next)
1025 void snarf(Panel *p){
1027 plputsnarf(urlstr(selection));
1032 void paste(Panel *p){
1036 void hit3(int button, int item){
1048 current->yoffs=plgetpostextview(text);
1052 defdisplay=!defdisplay;
1053 plpack(root, screen->r);
1055 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1056 plsetpostextview(text, current->yoffs);
1058 pldraw(root, screen);
1061 mothon(current, !mothmode);
1070 snprint(name, sizeof(name), "%s/hit.html", home);
1071 fd=open(name, OWRITE);
1073 fd=create(name, OWRITE, 0666);
1075 message("can't open %s", name);
1078 fprint(fd, "<html><head><title>Hit List</title></head>\n");
1079 fprint(fd, "<body><h1>Hit list</h1>\n");
1082 fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
1086 snprint(name, sizeof(name), "file:%s/hit.html", home);
1087 geturl(name, -1, 1, 0);