16 int verbose=0; /* -v flag causes html errors to be written to file-descriptor 2 */
17 int killimgs=0; /* should mothra kill images? */
18 int defdisplay=1; /* is the default (initial) display visible? */
19 int visxbar=0; /* horizontal scrollbar visible? */
20 int topxbar=0; /* horizontal scrollbar at top? */
21 Panel *root; /* the whole display */
22 Panel *alt; /* the alternate display */
23 Panel *alttext; /* the alternate text window */
24 Panel *cmd; /* command entry */
25 Panel *cururl; /* label giving the url of the visible text */
26 Panel *list; /* list of previously acquired www pages */
27 Panel *msg; /* message display */
28 Panel *menu3; /* button 3 menu */
29 char mothra[] = "mothra!";
32 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
33 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
34 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
35 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
37 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
38 0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
39 0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
40 0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
42 Cursor confirmcursor={
44 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
46 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
47 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
49 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
50 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
51 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
52 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
56 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
57 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0,
58 0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
59 0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
62 0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60,
63 0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
64 0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 {0x00, 0x00, 0x60, 0x06, 0xf8, 0x1f, 0xfc, 0x3f,
69 0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe,
70 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8,
71 0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x00, 0x00, },
72 {0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x58, 0x1a,
73 0x5c, 0x3a, 0x64, 0x26, 0x27, 0xe4, 0x37, 0xec,
74 0x37, 0xec, 0x17, 0xe8, 0x1b, 0xd8, 0x0e, 0x70,
75 0x0c, 0x30, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, }
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 *);
91 void mothon(Www *, int);
108 return &a[index % NWWW];
111 return wwwtop<NWWW ? wwwtop : NWWW;
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.
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.min.y
132 && t->r.min.y+yoffs<text->r.max.y
134 && subpanel(t->p, plkbfocus))
142 void scrolltext(int dy, int whence)
146 s = plgetscroll(text);
155 s.pos.y = s.size.y+dy;
158 if(s.pos.y > s.size.y)
162 plsetscroll(text, s);
165 void sidescroll(int dx, int whence)
169 s = plgetscroll(text);
178 s.pos.x = s.size.x+dx;
181 if(s.pos.x > s.size.x - text->size.x + 5)
182 s.pos.x = s.size.x - text->size.x + 5;
185 plsetscroll(text, s);
189 Panel *p, *xbar, *ybar, *swap;
198 menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
199 root=plpopup(root, EXPAND, 0, 0, menu3);
200 p=plgroup(root, PACKN|FILLX);
201 msg=pllabel(p, PACKN|FILLX, mothra);
202 plplacelabel(msg, PLACEW);
203 pllabel(p, PACKW, "Go:");
204 cmd=plentry(p, PACKN|FILLX, 0, "", docmd);
205 p=plgroup(root, PACKN|FILLX);
206 ybar=plscrollbar(p, PACKW);
207 list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
208 plscroll(list, 0, ybar);
209 p=plgroup(root, PACKN|FILLX);
210 pllabel(p, PACKW, "Url:");
211 cururl=pllabel(p, PACKE|EXPAND, "---");
212 plplacelabel(cururl, PLACEW);
213 p=plgroup(root, PACKN|EXPAND);
214 ybar=plscrollbar(p, PACKW|USERFL);
215 xbar=plscrollbar(p, xflags);
216 text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
217 plscroll(text, xbar, ybar);
219 alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
220 ybar=plscrollbar(alt, PACKW|USERFL);
221 xbar=plscrollbar(alt, xflags);
222 alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
223 plscroll(alttext, xbar, ybar);
234 void killcohort(void){
236 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
237 postnote(PNGROUP, cohort, "kill\n");
241 void catch(void*, char*){
244 void dienow(void*, char*){
249 static char *home; /* where to put files */
256 tmp = smprint("%s/lib", henv);
257 f=create(tmp, OREAD, DMDIR|0777);
261 home = smprint("%s/lib/mothra", henv);
262 f=create(home, OREAD, DMDIR|0777);
267 home = strdup("/tmp");
273 if(current && current->alldone==0)
274 esetcursor(&readingcurs);
276 esetcursor(&mothcurs);
281 void drawlock(int dolock){
285 lockdisplay(display);
288 unlockdisplay(display);
292 void scrollto(char *tag);
295 extern char *mtpt; /* url */
297 void main(int argc, char *argv[]){
299 enum { Eplumb = 128, Ekick = 256 };
305 fmtinstall('U', Ufmt);
308 case 'd': debug=1; break;
309 case 'v': verbose=1; break;
310 case 'k': killimgs=1; break;
314 case 'a': defdisplay=0; break;
319 * so that we can stop all subprocesses with a note,
320 * and to isolate rendezvous from other processes
322 if(cohort=rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){
334 fprint(2, "Usage: %s [-dvak] [-m mtpt] [url]\n", argv0);
339 case 1: url=argv[0]; break;
341 if(initdraw(0, 0, mothra) < 0)
342 sysfatal("initdraw: %r");
343 display->locking = 1;
344 chrwidth=stringwidth(font, "0");
345 pltabsize(chrwidth, 8*chrwidth);
346 einit(Emouse|Ekeyboard);
347 eplumb(Eplumb, "web");
348 if(pipe(kickpipe) < 0)
349 sysfatal("pipe: %r");
350 estart(Ekick, kickpipe[0], 256);
351 plinit(screen->depth);
352 if(debug) notify(dienow);
354 hrule=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
356 sysfatal("can't allocimage!");
357 draw(hrule, Rect(0,1,1,3), display->black, 0, ZP);
358 linespace=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
360 sysfatal("can't allocimage!");
361 bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
362 fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
364 unlockdisplay(display);
369 geturl(url, -1, 1, 0);
373 if(mouse.buttons==0 && current){
374 if(current->finished){
376 if(current->url->tag[0])
377 scrollto(current->url->tag);
392 if(mouse.buttons==0 && current && current->changed){
393 if(!current->finished)
408 scrolltext(-text->size.y/4, 1);
411 scrolltext(-text->size.y/2, 1);
414 scrolltext(text->size.y/4, 1);
417 scrolltext(text->size.y/2, 1);
420 scrolltext(-text->size.y, 2);
426 sidescroll(text->size.x/4, 1);
429 sidescroll(-text->size.x/4, 1);
435 if(mouse.buttons & (8|16) && ptinrect(mouse.xy, text->r)){
436 if(mouse.buttons & 8)
437 scrolltext(text->r.min.y - mouse.xy.y, 1);
439 scrolltext(mouse.xy.y - text->r.min.y, 1);
442 plmouse(root, &mouse);
447 geturl(pm->data, -1, 1, 0);
455 esetcursor(&confirmcursor);
456 do down=emouse(); while(!down.buttons);
457 do up=emouse(); while(up.buttons);
459 return down.buttons==(1<<(b-1));
461 void message(char *s, ...){
462 static char buf[1024];
466 out = buf + vsnprint(buf, sizeof(buf), s, args);
469 plinitlabel(msg, PACKN|FILLX, buf);
470 if(defdisplay) pldraw(msg, screen);
472 void htmlerror(char *name, int line, char *m, ...){
473 static char buf[1024];
478 out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
479 out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
482 fprint(2, "%s\n", buf);
485 void eresized(int new){
489 if(new && getwindow(display, Refnone) == -1) {
490 fprint(2, "getwindow: %r\n");
496 pldraw(cmd, screen); /* put cmd box on screen for alt display */
497 pldraw(root, screen);
498 flushimage(display, 1);
501 void *emalloc(int n){
505 sysfatal("out of memory");
507 setmalloctag(v, getcallerpc(&n));
510 void nstrcpy(char *to, char *from, int len){
511 strncpy(to, from, len);
515 char *genwww(Panel *, int index){
516 static char buf[1024];
526 if(w->title[0]!='\0'){
528 snprint(buf, sizeof(buf), "%2d %s", i+1, w->title);
530 snprint(buf, sizeof(buf), "%2d %s", i+1, urlstr(w->url));
534 void scrollto(char *tag){
537 if(current == nil || text == nil)
540 for(tp=current->text;tp;tp=tp->next){
542 if(ap && ap->name && strcmp(ap->name, tag)==0){
543 current->yoffs=tp->topy;
548 plsetpostextview(text, current->yoffs);
552 * selected text should be a url.
554 void setcurrent(int index, char *tag){
558 if(new==current && (tag==0 || tag[0]==0)) return;
560 current->yoffs=plgetpostextview(text);
562 plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
563 if(defdisplay) pldraw(cururl, screen);
564 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
566 if((i = open("/dev/label", OWRITE)) >= 0){
567 fprint(i, "%s %s", mothra, current->url->fullname);
573 do ++s; while(*s==' ' || *s=='\t');
576 void save(int ifd, char *name){
580 message("save: %s: %r", name);
583 ofd=create(name, OWRITE, 0666);
585 message("save: %s: %r", name);
588 switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){
590 message("Can't fork: %r");
598 snprint(buf, sizeof(buf),
599 "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name);
600 execl("/bin/rc", "rc", "-c", buf, nil);
607 void screendump(char *name, int full){
610 fd=create(name, OWRITE, 0666);
612 message("can't create %s", name);
616 writeimage(fd, screen, 0);
618 if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
619 message("can't allocate image");
623 draw(b, b->r, screen, 0, b->r.min);
624 writeimage(fd, b, 0);
631 * convert a url into a local file name.
633 char *urltofile(Url *url){
638 if(name == nil || name[0] == 0)
640 if(slash = strrchr(name, '/'))
648 * user typed a command.
650 void docmd(Panel *p, char *s){
655 while(*s==' ' || *s=='\t') s++;
657 * Non-command does a get on the url
659 if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
661 else switch(c = s[0]){
663 message("Unknown command %s", s);
667 if(*s=='\0' && selection)
675 s = urlstr(selection);
677 message("no url selected");
684 doprev(nil, 1, wwwtop-atoi(s));
686 message("Usage: j index");
689 mothon(current, !mothmode);
692 killimgs = !killimgs;
699 if(s==0 || *s=='\0'){
700 snprint(buf, sizeof(buf), "dump.bit");
701 if(eenter("Screendump to", buf, sizeof(buf), &mouse) <= 0)
705 screendump(s, c == 'W');
710 message("no url selected");
713 if(s==0 || *s=='\0'){
714 snprint(buf, sizeof(buf), "%s", urltofile(selection));
715 if(eenter("Save to", buf, sizeof(buf), &mouse) <= 0)
719 save(urlget(selection, -1), s);
724 plinitentry(cmd, EXPAND, 0, "", docmd);
725 pldraw(root, screen);
728 void regerror(char *msg)
730 werrstr("regerror: %s", msg);
734 static char last[256];
740 if(current == nil || current->text == nil || text == nil)
742 strncpy(buf, last, sizeof(buf)-1);
743 if(eenter("Search for", buf, sizeof(buf), &mouse) <= 0)
745 strncpy(last, buf, sizeof(buf)-1);
751 for(tp=current->text;tp;tp=tp->next)
752 if(tp->flags & PL_SEL)
757 tp->flags &= ~PL_SEL;
761 tp->flags &= ~PL_SEL;
762 if(tp->text && *tp->text)
763 if(regexec(re, tp->text, nil, 0)){
765 plsetpostextview(text, tp->topy);
775 void hiturl(int buttons, char *url, int map){
777 case 1: geturl(url, -1, 0, map); break;
778 case 2: selurl(url); break;
779 case 4: message("Button 3 hit on url can't happen!"); break;
784 * user selected from the list of available pages
786 void doprev(Panel *p, int buttons, int index){
789 if(index < 0 || index >= nwww())
793 case 1: setcurrent(i, 0); /* no break ... */
794 case 2: selurl(www(i)->url->fullname); break;
795 case 4: message("Button 3 hit on page can't happen!"); break;
800 * Follow an html link
802 void dolink(Panel *p, int buttons, Rtext *word){
806 if(a == nil || (a->link == nil && a->image == nil))
809 hiturl(buttons, a->image ? a->image : a->link, 0);
816 yoffs=plgetpostextview(p);
817 coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
818 snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
819 hiturl(buttons, mapurl, 1);
821 hiturl(buttons, a->link, 0);
825 void filter(int fd, char *cmd){
826 switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){
828 message("Can't fork!");
831 dupfds(fd, 1, 2, -1);
832 execl("/bin/rc", "rc", "-c", cmd, nil);
837 void gettext(Www *w, int fd, int type){
838 switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
840 message("Can't fork, please wait");
844 plrdhtml(w->url->fullname, fd, w, killimgs);
846 plrdplain(w->url->fullname, fd, w);
852 void freetext(Rtext *t){
857 for(; t!=0; t = t->next){
880 for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++)
885 if((fd = open("/fd", OREAD)) < 0)
886 sysfatal("open: %r");
887 n = dirreadall(fd, &dir);
889 if(strstr(dir[i].name, "ctl"))
891 fd = atoi(dir[i].name);
898 int pipeline(int fd, char *fmt, ...)
900 char buf[80], *argv[4];
905 vsnprint(buf, sizeof buf, fmt, arg);
911 werrstr("pipeline for %s failed: %r", buf);
914 switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
920 dupfds(fd, pfd[1], 2, -1);
925 exec("/bin/rc", argv);
936 return url->fullname;
939 Url *copyurl(Url *u){
941 v=emalloc(sizeof(Url));
943 v->reltext = strdup(u->reltext);
944 v->basename = strdup(u->basename);
947 void freeurl(Url *u){
952 void seturl(Url *url, char *urlname, char *base){
953 url->reltext = strdup(urlname);
954 url->basename = strdup(base);
955 url->fullname[0] = 0;
959 Url* selurl(char *urlname){
963 selection=emalloc(sizeof(Url));
964 seturl(selection, urlname, current ? current->url->fullname : "");
965 if(last) freeurl(last);
966 message("selected: %s", urlstr(selection));
967 plgrabkb(cmd); /* for snarf */
972 * get the file at the given url
974 void geturl(char *urlname, int post, int plumb, int map){
980 if(*urlname == '#' && post < 0){
988 message("getting %s", urlstr(selection));
989 esetcursor(&patientcurs);
991 if((fd=urlget(selection, post)) < 0){
995 message("getting %s", selection->fullname);
996 if(mothmode && !plumb)
1003 message("unknown file type");
1007 snprint(cmd, sizeof(cmd), "%s", urltofile(selection));
1008 if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){
1015 fd = pipeline(fd, "exec uhtml");
1018 for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){
1020 n += countpix(w->pix);
1021 if(n >= NPIXMB*1024*1024)
1024 w = www(i = wwwtop++);
1026 /* wait for the reader to finish the document */
1027 while(!w->finished && !w->alldone){
1036 memset(w, 0, sizeof(*w));
1039 w->url=copyurl(current->url);
1041 w->url=copyurl(selection);
1044 gettext(w, fd, typ);
1045 if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){
1048 if(w->finished || w->alldone)
1051 write(kickpipe[1], "C", 1);
1055 plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
1056 if(defdisplay) pldraw(list, screen);
1057 setcurrent(i, selection->tag);
1064 filter(fd, "exec page -w");
1071 void updtext(Www *w){
1074 if(defdisplay && w->gottitle==0 && w->title[0]!='\0')
1075 pldraw(list, screen);
1076 for(t=w->text;t;t=t->next){
1086 w->yoffs=plgetpostextview(text);
1087 plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
1088 plsetpostextview(text, w->yoffs);
1089 pldraw(text, screen);
1092 void finish(Www *w){
1094 write(kickpipe[1], "F", 1);
1098 mothon(Www *w, int on)
1103 if(w==0 || mothmode==on)
1106 message("moth mode!");
1110 * insert or remove artificial links to the href for
1111 * images that are also links
1113 for(t=w->text;t;t=t->next){
1115 if(a == nil || a->image == nil)
1121 t->flags &= ~PL_HOT;
1127 ap=emalloc(sizeof(Action));
1128 ap->link = strdup(a->link);
1129 plrtstr(&t->next, 0, 0, 0, t->font, strdup("->"), PL_HOT, ap);
1143 void killpix(Www *w){
1146 if(w==0 || !w->finished && !w->alldone)
1148 for(t=w->text; t; t=t->next)
1155 void snarf(Panel *p){
1158 plputsnarf(urlstr(selection));
1161 message("no url selected");
1165 void paste(Panel *p){
1169 void hit3(int button, int item){
1180 current->yoffs=plgetpostextview(text);
1184 defdisplay=!defdisplay;
1185 plpack(root, screen->r);
1187 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1188 plsetpostextview(text, current->yoffs);
1190 pldraw(root, screen);
1193 mothon(current, !mothmode);
1206 message("no url selected");
1209 snprint(name, sizeof(name), "%s/hit.html", mkhome());
1210 fd=open(name, OWRITE);
1212 fd=create(name, OWRITE, 0666);
1214 message("can't open %s", name);
1217 fprint(fd, "<html><head><title>Hit List</title></head>\n");
1218 fprint(fd, "<body><h1>Hit list</h1>\n");
1221 fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
1225 snprint(name, sizeof(name), "file:%s/hit.html", mkhome());
1226 geturl(name, -1, 1, 0);