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);
529 if(*s=='\0' && selection)
536 geturl(selection->fullname, GET, 0, 1, 0);
538 message("no url selected");
540 else geturl(s, GET, 0, 1, 0);
545 doprev(nil, 1, wwwtop-atoi(s));
547 message("Usage: j index");
551 if(*s=='\0' && selection)
552 geturl(selection->fullname, GET, 0, 0, 0);
557 message("Usage: W file");
565 message("Usage: w file");
574 s=strrchr(selection->fullname, '/');
577 if(s==0 || *s=='\0'){
578 message("Usage: s file");
585 draw(screen, screen->r, display->white, 0, ZP);
588 plinitentry(cmd, EXPAND, 0, "", docmd);
589 if(defdisplay) pldraw(cmd, screen);
591 void hiturl(int buttons, char *url, int map){
593 case 1: geturl(url, GET, 0, 1, map); break;
594 case 2: selurl(url); break;
595 case 4: message("Button 3 hit on url can't happen!"); break;
599 * user selected from the list of available pages
601 void doprev(Panel *p, int buttons, int index){
604 if(index < 0 || index >= nwww())
608 case 1: setcurrent(i, 0); /* no break ... */
609 case 2: selurl(www(i)->url->fullname); break;
610 case 4: message("Button 3 hit on page can't happen!"); break;
615 * Follow an html link
617 void dolink(Panel *p, int buttons, Rtext *word){
626 yoffs=plgetpostextview(p);
627 coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
628 snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
629 hiturl(buttons, mapurl, 1);
632 hiturl(buttons, a->link, 0);
635 void filter(char *cmd, int fd){
636 flushimage(display, 1);
637 switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
639 message("Can't fork!");
645 execl("/bin/rc", "rc", "-c", cmd, 0);
646 message("Can't exec /bin/rc!");
653 void gettext(Www *w, int fd, int type){
654 flushimage(display, 1);
655 switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
657 message("Can't fork, please wait");
659 plrdhtml(w->url->fullname, fd, w);
661 plrdplain(w->url->fullname, fd, w);
665 plrdhtml(w->url->fullname, fd, w);
667 plrdplain(w->url->fullname, fd, w);
673 void freetext(Rtext *t){
678 for(; t!=0; t = t->next){
693 void popwin(char *cmd){
694 flushimage(display, 1);
695 switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
697 message("sorry, can't fork to %s", cmd);
700 execl("/bin/window", "window", "100 100 800 800", "rc", "-c", cmd, 0);
705 int readstr(char *buf, int nbuf, char *base, char *name)
710 snprint(path, sizeof path, "%s/%s", base, name);
711 if((fd = open(path, OREAD)) < 0){
713 memset(buf, 0, nbuf);
716 n = read(fd, buf, nbuf-1);
726 int fileurlopen(Url *url){
727 char *rel, *base, *x;
731 if(cistrncmp(url->basename, "file:", 5) == 0)
732 base = url->basename+5;
733 if(cistrncmp(url->reltext, "file:", 5) == 0)
734 rel = url->reltext+5;
735 if(rel == nil && base == nil)
739 if(base && base[0] == '/' && rel[0] != '/'){
740 if(x = strrchr(base, '/'))
742 snprint(url->fullname, sizeof(url->fullname), "%s/%s", base, rel);
745 snprint(url->fullname, sizeof(url->fullname), "%s", rel);
747 if(x = strrchr(url->fullname, '#')){
749 strncpy(url->tag, x, sizeof(url->tag));
751 fd = open(cleanname(url->fullname), OREAD);
754 memset(url->fullname, 0, sizeof(url->fullname));
755 strcpy(url->fullname, "file:");
756 fd2path(fd, url->fullname+5, sizeof(url->fullname)-6);
757 url->type = content2type("application/octet-stream", url->fullname);
761 int urlopen(Url *url, int method, char *body){
762 int conn, ctlfd, fd, n;
763 char buf[1024+1], *p;
765 if(debug) fprint(2, "urlopen %s (%s)\n", url->reltext, url->basename);
768 if((fd = fileurlopen(url)) >= 0)
771 snprint(buf, sizeof buf, "%s/clone", mtpt);
772 if((ctlfd = open(buf, ORDWR)) < 0)
774 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
781 if(url->basename[0]){
782 n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
783 write(ctlfd, buf, n);
785 n = snprint(buf, sizeof buf, "url %s", url->reltext);
786 if(write(ctlfd, buf, n) != n){
792 if(method == POST && body){
793 snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
794 if((fd = open(buf, OWRITE)) < 0)
797 if(write(fd, body, n) != n){
804 snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
805 if((fd = open(buf, OREAD)) < 0)
808 snprint(buf, sizeof buf, "%s/%d/parsed", mtpt, conn);
809 readstr(url->fullname, sizeof(url->fullname), buf, "url");
810 readstr(url->tag, sizeof(url->tag), buf, "fragment");
812 snprint(buf, sizeof buf, "%s/%d", mtpt, conn);
813 readstr(buf, sizeof buf, buf, "contenttype");
815 if(p = cistrstr(buf, "charset=")){
817 strncpy(url->charset, p, sizeof(url->charset));
818 if(p = strchr(url->charset, ';'))
821 if(p = strchr(buf, ';'))
823 url->type = content2type(buf, url->fullname);
829 int pipeline(char *cmd, int fd)
836 werrstr("pipeline for %s failed: %r", cmd);
849 execl("/bin/rc", "rc", "-c", cmd, 0);
858 * select the file at the given url
860 void selurl(char *urlname){
862 seturl(&url, urlname, current?
863 current->url->fullname :
866 message("selected: %s", selection->fullname[0] ? selection->fullname : selection->reltext);
868 void seturl(Url *url, char *urlname, char *base){
869 strncpy(url->reltext, urlname, sizeof(url->reltext));
870 strncpy(url->basename, base, sizeof(url->basename));
871 url->fullname[0] = 0;
877 Url *copyurl(Url *u){
879 v=emalloc(sizeof(Url));
883 void freeurl(Url *u){
884 if(u!=&defurl && u!=&badurl)
889 * get the file at the given url
891 void geturl(char *urlname, int method, char *body, int cache, int map){
900 message("getting %s", selection->reltext);
901 esetcursor(&patientcurs);
903 if((fd=urlopen(selection, method, body)) < 0){
908 message("getting %s", selection->fullname);
909 if(selection->type&COMPRESS)
910 fd=pipeline("/bin/uncompress", fd);
911 else if(selection->type&GUNZIP)
912 fd=pipeline("/bin/gunzip", fd);
913 snprint(cmd, sizeof(cmd), selection->charset[0] ?
914 "/bin/uhtml -c %s" : "/bin/uhtml", selection->charset);
915 fd = pipeline(cmd, fd);
916 switch(selection->type&~COMPRESSION){
918 message("Bad type %x in geturl", selection->type);
922 w = www(i = wwwtop++);
924 extern void freeform(void *p);
925 extern void freepix(void *p);
927 /* wait for the reader to finish the document */
928 while(!w->finished && !w->alldone){
929 unlockdisplay(display);
931 lockdisplay(display);
938 memset(w, 0, sizeof(*w));
941 w->url=copyurl(current->url);
943 w->url=copyurl(selection);
946 gettext(w, fd, selection->type&~COMPRESSION);
947 plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
948 if(defdisplay) pldraw(list, screen);
949 setcurrent(i, selection->tag);
956 filter("page -w", fd);
959 filter("/sys/lib/mothra/tiffview", fd);
962 filter("fb/xbm2pic|fb/9v", fd);
969 void updtext(Www *w){
972 for(t=w->text;t;t=t->next){
980 w->yoffs=plgetpostextview(text);
981 plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
982 plsetpostextview(text, w->yoffs);
983 pldraw(root, screen);
985 Cursor confirmcursor={
987 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
988 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
989 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
990 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
992 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
993 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
994 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
995 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
999 esetcursor(&confirmcursor);
1000 do down=emouse(); while(!down.buttons);
1001 do up=emouse(); while(up.buttons);
1003 return down.buttons==(1<<(b-1));
1005 void snarf(Panel *p){
1007 fd=create("/dev/snarf", OWRITE, 0666);
1009 fprint(fd, "%s", selection->fullname[0] ? selection->fullname : selection->reltext);
1013 void paste(Panel *p){
1016 fd=open("/dev/snarf", OREAD);
1017 strncpy(buf, plentryval(p), sizeof(buf));
1019 n=read(fd, buf+len, sizeof(buf)-len-1);
1022 plinitentry(cmd, PACKE|EXPAND, 0, buf, docmd);
1023 pldraw(cmd, screen);
1027 void hit3(int button, int item){
1037 current->yoffs=plgetpostextview(text);
1041 defdisplay=!defdisplay;
1042 plpack(root, screen->r);
1043 plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
1044 plsetpostextview(text, current->yoffs);
1045 pldraw(root, screen);
1054 snprint(name, sizeof(name), "%s/hit.html", home);
1055 fd=open(name, OWRITE);
1057 fd=create(name, OWRITE, 0666);
1059 message("can't open %s", name);
1062 fprint(fd, "<head><title>Hit List</title></head>\n");
1063 fprint(fd, "<body><h1>Hit list</h1>\n");
1066 fprint(fd, "<p><a href=\"%s\">%s</a>\n",
1067 selection->fullname, selection->fullname);
1071 snprint(name, sizeof(name), "file:%s/hit.html", home);
1072 geturl(name, GET, 0, 1, 0);
1076 draw(screen, screen->r, display->white, 0, ZP);