/*
* Trivial web browser
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>
#include <plumb.h>
#include <cursor.h>
#include <panel.h>
#include <regexp.h>
#include "mothra.h"
#include "rtext.h"
int debug=0;
int verbose=0; /* -v flag causes html errors to be written to file-descriptor 2 */
int killimgs=0; /* should mothra kill images? */
int defdisplay=1; /* is the default (initial) display visible? */
int visxbar=0; /* horizontal scrollbar visible? */
int topxbar=0; /* horizontal scrollbar at top? */
Panel *root; /* the whole display */
Panel *alt; /* the alternate display */
Panel *alttext; /* the alternate text window */
Panel *cmd; /* command entry */
Panel *cururl; /* label giving the url of the visible text */
Panel *list; /* list of previously acquired www pages */
Panel *msg; /* message display */
Panel *menu3; /* button 3 menu */
char mothra[] = "mothra!";
Cursor patientcurs={
0, 0,
0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
};
Cursor confirmcursor={
0, 0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
};
Cursor readingcurs={
-10, -3,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0,
0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60,
0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
Cursor mothcurs={
{-7, -7},
{0x00, 0x00, 0x60, 0x06, 0xf8, 0x1f, 0xfc, 0x3f,
0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe,
0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8,
0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x00, 0x00, },
{0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x58, 0x1a,
0x5c, 0x3a, 0x64, 0x26, 0x27, 0xe4, 0x37, 0xec,
0x37, 0xec, 0x17, 0xe8, 0x1b, 0xd8, 0x0e, 0x70,
0x0c, 0x30, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, }
};
Www *current=0;
Url *selection=0;
int mothmode;
int kickpipe[2];
void docmd(Panel *, char *);
void doprev(Panel *, int, int);
char *urlstr(Url *);
void setcurrent(int, char *);
char *genwww(Panel *, int);
void updtext(Www *);
void dolink(Panel *, int, Rtext *);
void hit3(int, int);
void mothon(Www *, int);
void killpix(Www *w);
char *buttons[]={
"alt display",
"moth mode",
"snarf",
"paste",
"plumb",
"search",
"save hit",
"hit list",
"exit",
0
};
int wwwtop=0;
Www *www(int index){
static Www a[NWWW];
return &a[index % NWWW];
}
int nwww(void){
return wwwtop<NWWW ? wwwtop : NWWW;
}
int subpanel(Panel *obj, Panel *subj){
if(obj==0) return 0;
if(obj==subj) return 1;
for(obj=obj->child;obj;obj=obj->next)
if(subpanel(obj, subj)) return 1;
return 0;
}
/*
* Make sure that the keyboard focus is on-screen, by adjusting it to
* be the cmd entry if necessary.
*/
int adjkb(void){
Rtext *t;
int yoffs;
if(current){
yoffs=text->r.min.y-plgetpostextview(text);
for(t=current->text;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
if(t->r.max.y+yoffs>=text->r.min.y
&& t->r.min.y+yoffs<text->r.max.y
&& t->b==0
&& subpanel(t->p, plkbfocus))
return 1;
}
}
plgrabkb(cmd);
return 0;
}
void scrolltext(int dy, int whence)
{
Scroll s;
s = plgetscroll(text);
switch(whence){
case 0:
s.pos.y = dy;
break;
case 1:
s.pos.y += dy;
break;
case 2:
s.pos.y = s.size.y+dy;
break;
}
if(s.pos.y > s.size.y)
s.pos.y = s.size.y;
if(s.pos.y < 0)
s.pos.y = 0;
plsetscroll(text, s);
}
void sidescroll(int dx, int whence)
{
Scroll s;
s = plgetscroll(text);
switch(whence){
case 0:
s.pos.x = dx;
break;
case 1:
s.pos.x += dx;
break;
case 2:
s.pos.x = s.size.x+dx;
break;
}
if(s.pos.x > s.size.x - text->size.x + 5)
s.pos.x = s.size.x - text->size.x + 5;
if(s.pos.x < 0)
s.pos.x = 0;
plsetscroll(text, s);
}
void mkpanels(void){
Panel *p, *xbar, *ybar, *swap;
int xflags;
if(topxbar)
xflags=PACKN|USERFL;
else
xflags=PACKS|USERFL;
if(!visxbar)
xflags|=IGNORE;
menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
root=plpopup(root, EXPAND, 0, 0, menu3);
p=plgroup(root, PACKN|FILLX);
msg=pllabel(p, PACKN|FILLX, mothra);
plplacelabel(msg, PLACEW);
pllabel(p, PACKW, "Go:");
cmd=plentry(p, PACKN|FILLX, 0, "", docmd);
p=plgroup(root, PACKN|FILLX);
ybar=plscrollbar(p, PACKW);
list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
plscroll(list, 0, ybar);
p=plgroup(root, PACKN|FILLX);
pllabel(p, PACKW, "Url:");
cururl=pllabel(p, PACKE|EXPAND, "---");
plplacelabel(cururl, PLACEW);
p=plgroup(root, PACKN|EXPAND);
ybar=plscrollbar(p, PACKW|USERFL);
xbar=plscrollbar(p, xflags);
text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
plscroll(text, xbar, ybar);
plgrabkb(cmd);
alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
ybar=plscrollbar(alt, PACKW|USERFL);
xbar=plscrollbar(alt, xflags);
alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
plscroll(alttext, xbar, ybar);
if(!defdisplay){
swap=root;
root=alt;
alt=swap;
swap=text;
text=alttext;
alttext=swap;
}
}
int cohort = -1;
void killcohort(void){
int i;
for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
postnote(PNGROUP, cohort, "kill\n");
sleep(1);
}
}
void catch(void*, char*){
noted(NCONT);
}
void dienow(void*, char*){
noted(NDFLT);
}
char* mkhome(void){
static char *home; /* where to put files */
char *henv, *tmp;
int f;
if(home == nil){
henv=getenv("home");
if(henv){
tmp = smprint("%s/lib", henv);
f=create(tmp, OREAD, DMDIR|0777);
if(f!=-1) close(f);
free(tmp);
home = smprint("%s/lib/mothra", henv);
f=create(home, OREAD, DMDIR|0777);
if(f!=-1) close(f);
free(henv);
}
else
home = strdup("/tmp");
}
return home;
}
void donecurs(void){
if(current && current->alldone==0)
esetcursor(&readingcurs);
else if(mothmode)
esetcursor(&mothcurs);
else
esetcursor(0);
}
void drawlock(int dolock){
static int ref = 0;
if(dolock){
if(ref++ == 0)
lockdisplay(display);
} else {
if(--ref == 0)
unlockdisplay(display);
}
}
void scrollto(char *tag);
void search(void);
extern char *mtpt; /* url */
void main(int argc, char *argv[]){
Event e;
enum { Eplumb = 128, Ekick = 256 };
Plumbmsg *pm;
char *url;
int i;
quotefmtinstall();
fmtinstall('U', Ufmt);
ARGBEGIN{
case 'd': debug=1; break;
case 'v': verbose=1; break;
case 'k': killimgs=1; break;
case 'm':
if(mtpt = ARGF())
break;
case 'a': defdisplay=0; break;
default: goto Usage;
}ARGEND
/*
* so that we can stop all subprocesses with a note,
* and to isolate rendezvous from other processes
*/
if(cohort=rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){
atexit(killcohort);
notify(catch);
waitpid();
exits(0);
}
cohort = getpid();
atexit(killcohort);
switch(argc){
default:
Usage:
fprint(2, "usage: %s [-dvak] [-m mtpt] [url]\n", argv0);
exits("usage");
case 0:
url=getenv("url");
break;
case 1: url=argv[0]; break;
}
if(initdraw(0, 0, mothra) < 0)
sysfatal("initdraw: %r");
display->locking = 1;
chrwidth=stringwidth(font, "0");
pltabsize(chrwidth, 8*chrwidth);
einit(Emouse|Ekeyboard);
eplumb(Eplumb, "web");
if(pipe(kickpipe) < 0)
sysfatal("pipe: %r");
estart(Ekick, kickpipe[0], 256);
plinit();
if(debug) notify(dienow);
getfonts();
hrule=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
if(hrule==0)
sysfatal("can't allocimage!");
draw(hrule, Rect(0,1,1,3), display->black, 0, ZP);
linespace=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
if(linespace==0)
sysfatal("can't allocimage!");
bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
mkpanels();
unlockdisplay(display);
eresized(0);
drawlock(1);
if(url && url[0])
geturl(url, -1, 1, 0);
mouse.buttons=0;
for(;;){
if(mouse.buttons==0 && current){
if(current->finished){
updtext(current);
if(current->url->tag[0])
scrollto(current->url->tag);
current->finished=0;
current->changed=0;
current->alldone=1;
message(mothra);
donecurs();
}
}
drawlock(0);
i=event(&e);
drawlock(1);
switch(i){
case Ekick:
if(mouse.buttons==0 && current && current->changed){
if(!current->finished)
updtext(current);
current->changed=0;
}
break;
case Ekeyboard:
switch(e.kbdc){
default:
adjkb();
plkeyboard(e.kbdc);
break;
case Khome:
scrolltext(0, 0);
break;
case Kup:
scrolltext(-text->size.y/4, 1);
break;
case Kpgup:
scrolltext(-text->size.y/2, 1);
break;
case Kdown:
scrolltext(text->size.y/4, 1);
break;
case Kpgdown:
scrolltext(text->size.y/2, 1);
break;
case Kend:
scrolltext(-text->size.y, 2);
break;
case Kack:
search();
break;
case Kright:
sidescroll(text->size.x/4, 1);
break;
case Kleft:
sidescroll(-text->size.x/4, 1);
break;
}
break;
case Emouse:
mouse=e.mouse;
if(mouse.buttons & (8|16) && ptinrect(mouse.xy, text->r)){
if(mouse.buttons & 8)
scrolltext(text->r.min.y - mouse.xy.y, 1);
else
scrolltext(mouse.xy.y - text->r.min.y, 1);
break;
}
plmouse(root, &mouse);
break;
case Eplumb:
pm=e.v;
if(pm->ndata > 0)
geturl(pm->data, -1, 1, 0);
plumbfree(pm);
break;
}
}
}
int confirm(int b){
Mouse down, up;
esetcursor(&confirmcursor);
do down=emouse(); while(!down.buttons);
do up=emouse(); while(up.buttons);
donecurs();
return down.buttons==(1<<(b-1));
}
void message(char *s, ...){
static char buf[1024];
char *out;
va_list args;
va_start(args, s);
out = buf + vsnprint(buf, sizeof(buf), s, args);
va_end(args);
*out='\0';
plinitlabel(msg, PACKN|FILLX, buf);
if(defdisplay) pldraw(msg, screen);
}
void htmlerror(char *name, int line, char *m, ...){
static char buf[1024];
char *out;
va_list args;
if(verbose){
va_start(args, m);
out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line);
out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args);
va_end(args);
*out='\0';
fprint(2, "%s\n", buf);
}
}
void eresized(int new){
Rectangle r;
drawlock(1);
if(new && getwindow(display, Refnone) == -1) {
fprint(2, "getwindow: %r\n");
exits("getwindow");
}
r=screen->r;
plpack(root, r);
plpack(alt, r);
pldraw(cmd, screen); /* put cmd box on screen for alt display */
pldraw(root, screen);
flushimage(display, 1);
drawlock(0);
}
void *emalloc(int n){
void *v;
v=malloc(n);
if(v==0)
sysfatal("out of memory");
memset(v, 0, n);
setmalloctag(v, getcallerpc(&n));
return v;
}
void nstrcpy(char *to, char *from, int len){
strncpy(to, from, len);
to[len-1] = 0;
}
char *genwww(Panel *, int index){
static char buf[1024];
Www *w;
int i;
if(index >= nwww())
return 0;
i = wwwtop-index-1;
w = www(i);
if(!w->url)
return 0;
if(w->title[0]!='\0'){
w->gottitle=1;
snprint(buf, sizeof(buf), "%2d %s", i+1, w->title);
} else
snprint(buf, sizeof(buf), "%2d %s", i+1, urlstr(w->url));
return buf;
}
void scrollto(char *tag){
Rtext *tp;
Action *ap;
if(current == nil || text == nil)
return;
if(tag && tag[0]){
for(tp=current->text;tp;tp=tp->next){
ap=tp->user;
if(ap && ap->name && strcmp(ap->name, tag)==0){
current->yoffs=tp->topy;
break;
}
}
}
plsetpostextview(text, current->yoffs);
}
/*
* selected text should be a url.
*/
void setcurrent(int index, char *tag){
Www *new;
int i;
new=www(index);
if(new==current && (tag==0 || tag[0]==0)) return;
if(current)
current->yoffs=plgetpostextview(text);
current=new;
plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
if(defdisplay) pldraw(cururl, screen);
plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
scrollto(tag);
if((i = open("/dev/label", OWRITE)) >= 0){
fprint(i, "%s %s", mothra, current->url->fullname);
close(i);
}
donecurs();
}
char *arg(char *s){
do ++s; while(*s==' ' || *s=='\t');
return s;
}
void save(int ifd, char *name){
char buf[NNAME+64];
int ofd;
if(ifd < 0){
message("save: %s: %r", name);
return;
}
ofd=create(name, OWRITE, 0666);
if(ofd < 0){
message("save: %s: %r", name);
return;
}
switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){
case -1:
message("Can't fork: %r");
break;
case 0:
dup(ifd, 0);
close(ifd);
dup(ofd, 1);
close(ofd);
snprint(buf, sizeof(buf),
"{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name);
execl("/bin/rc", "rc", "-c", buf, nil);
exits("exec");
}
close(ifd);
close(ofd);
donecurs();
}
void screendump(char *name, int full){
Image *b;
int fd;
fd=create(name, OWRITE, 0666);
if(fd==-1){
message("can't create %s", name);
return;
}
if(full){
writeimage(fd, screen, 0);
} else {
if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){
message("can't allocate image");
close(fd);
return;
}
draw(b, b->r, screen, 0, b->r.min);
writeimage(fd, b, 0);
freeimage(b);
}
close(fd);
}
/*
* convert a url into a local file name.
*/
char *urltofile(Url *url){
char *name, *slash;
if(url == nil)
return nil;
name = urlstr(url);
if(name == nil || name[0] == 0)
name = "/";
if(slash = strrchr(name, '/'))
name = slash+1;
if(name[0] == 0)
name = "index";
return name;
}
/*
* user typed a command.
*/
void docmd(Panel *p, char *s){
char buf[NNAME];
int c;
USED(p);
while(*s==' ' || *s=='\t') s++;
/*
* Non-command does a get on the url
*/
if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
geturl(s, -1, 0, 0);
else switch(c = s[0]){
default:
message("Unknown command %s", s);
break;
case 'a':
s = arg(s);
if(*s=='\0' && selection)
hit3(3, 0);
break;
case 'g':
s = arg(s);
if(*s=='\0'){
case 'r':
if(selection)
s = urlstr(selection);
else
message("no url selected");
}
geturl(s, -1, 0, 0);
break;
case 'j':
s = arg(s);
if(*s)
doprev(nil, 1, wwwtop-atoi(s));
else
message("Usage: j index");
break;
case 'm':
mothon(current, !mothmode);
break;
case 'k':
killimgs = !killimgs;
if (killimgs)
killpix(current);
break;
case 'w':
case 'W':
s = arg(s);
if(s==0 || *s=='\0'){
snprint(buf, sizeof(buf), "dump.bit");
if(eenter("Screendump to", buf, sizeof(buf), &mouse) <= 0)
break;
s = buf;
}
screendump(s, c == 'W');
break;
case 's':
s = arg(s);
if(!selection){
message("no url selected");
break;
}
if(s==0 || *s=='\0'){
snprint(buf, sizeof(buf), "%s", urltofile(selection));
if(eenter("Save to", buf, sizeof(buf), &mouse) <= 0)
break;
s = buf;
}
save(urlget(selection, -1), s);
break;
case 'q':
exits(0);
}
plinitentry(cmd, EXPAND, 0, "", docmd);
pldraw(root, screen);
}
void regerror(char *msg)
{
werrstr("regerror: %s", msg);
}
void search(void){
static char last[256];
char buf[256];
Reprog *re;
Rtext *tp;
for(;;){
if(current == nil || current->text == nil || text == nil)
return;
strncpy(buf, last, sizeof(buf)-1);
if(eenter("Search for", buf, sizeof(buf), &mouse) <= 0)
return;
strncpy(last, buf, sizeof(buf)-1);
re = regcompnl(buf);
if(re == nil){
message("%r");
continue;
}
for(tp=current->text;tp;tp=tp->next)
if(tp->flags & PL_SEL)
break;
if(tp == nil)
tp = current->text;
else {
tp->flags &= ~PL_SEL;
tp = tp->next;
}
while(tp != nil){
tp->flags &= ~PL_SEL;
if(tp->text && *tp->text)
if(regexec(re, tp->text, nil, 0)){
tp->flags |= PL_SEL;
plsetpostextview(text, tp->topy);
break;
}
tp = tp->next;
}
free(re);
updtext(current);
}
}
void hiturl(int buttons, char *url, int map){
switch(buttons){
case 1: geturl(url, -1, 0, map); break;
case 2: selurl(url); break;
case 4: message("Button 3 hit on url can't happen!"); break;
}
}
/*
* user selected from the list of available pages
*/
void doprev(Panel *p, int buttons, int index){
int i;
USED(p);
if(index < 0 || index >= nwww())
return;
i = wwwtop-index-1;
switch(buttons){
case 1: setcurrent(i, 0); /* no break ... */
case 2: selurl(www(i)->url->fullname); break;
case 4: message("Button 3 hit on page can't happen!"); break;
}
}
/*
* Follow an html link
*/
void dolink(Panel *p, int buttons, Rtext *word){
Action *a;
a=word->user;
if(a == nil || (a->link == nil && a->image == nil))
return;
if(mothmode)
hiturl(buttons, a->image ? a->image : a->link, 0);
else if(a->link){
if(a->ismap){
char mapurl[NNAME];
Point coord;
int yoffs;
yoffs=plgetpostextview(p);
coord=subpt(subpt(mouse.xy, word->r.min), p->r.min);
snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs);
hiturl(buttons, mapurl, 1);
} else
hiturl(buttons, a->link, 0);
}
}
void filter(int fd, char *cmd){
switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){
case -1:
message("Can't fork!");
break;
case 0:
dupfds(fd, 1, 2, -1);
execl("/bin/rc", "rc", "-c", cmd, nil);
_exits(0);
}
close(fd);
}
void gettext(Www *w, int fd, int type){
switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
case -1:
message("Can't fork, please wait");
break;
case 0:
if(type==HTML)
plrdhtml(w->url->fullname, fd, w, killimgs);
else
plrdplain(w->url->fullname, fd, w);
_exits(0);
}
close(fd);
}
void freetext(Rtext *t){
Rtext *tt;
Action *a;
tt = t;
for(; t!=0; t = t->next){
t->b=0;
free(t->text);
t->text=0;
if(a = t->user){
t->user=0;
free(a->image);
free(a->link);
free(a->name);
free(a);
}
}
plrtfree(tt);
}
void
dupfds(int fd, ...)
{
int mfd, n, i;
va_list arg;
Dir *dir;
va_start(arg, fd);
for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++)
if(fd != mfd)
if(dup(fd, mfd) < 0)
sysfatal("dup: %r");
va_end(arg);
if((fd = open("/fd", OREAD)) < 0)
sysfatal("open: %r");
n = dirreadall(fd, &dir);
for(i=0; i<n; i++){
if(strstr(dir[i].name, "ctl"))
continue;
fd = atoi(dir[i].name);
if(fd >= mfd)
close(fd);
}
free(dir);
}
int pipeline(int fd, char *fmt, ...)
{
char buf[80], *argv[4];
va_list arg;
int pfd[2];
va_start(arg, fmt);
vsnprint(buf, sizeof buf, fmt, arg);
va_end(arg);
if(pipe(pfd) < 0){
Err:
close(fd);
werrstr("pipeline for %s failed: %r", buf);
return -1;
}
switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
case -1:
close(pfd[0]);
close(pfd[1]);
goto Err;
case 0:
dupfds(fd, pfd[1], 2, -1);
argv[0] = "rc";
argv[1] = "-c";
argv[2] = buf;
argv[3] = nil;
exec("/bin/rc", argv);
_exits(0);
}
close(fd);
close(pfd[1]);
return pfd[0];
}
char*
urlstr(Url *url){
if(url->fullname[0])
return url->fullname;
return url->reltext;
}
Url *copyurl(Url *u){
Url *v;
v=emalloc(sizeof(Url));
*v=*u;
v->reltext = strdup(u->reltext);
v->basename = strdup(u->basename);
return v;
}
void freeurl(Url *u){
free(u->reltext);
free(u->basename);
free(u);
}
void seturl(Url *url, char *urlname, char *base){
url->reltext = strdup(urlname);
url->basename = strdup(base);
url->fullname[0] = 0;
url->tag[0] = 0;
url->map = 0;
}
Url* selurl(char *urlname){
Url *last;
last=selection;
selection=emalloc(sizeof(Url));
seturl(selection, urlname, current ? current->url->fullname : "");
if(last) freeurl(last);
message("selected: %s", urlstr(selection));
plgrabkb(cmd); /* for snarf */
return selection;
}
/*
* get the file at the given url
*/
void geturl(char *urlname, int post, int plumb, int map){
int i, fd, typ;
char cmd[NNAME];
ulong n;
Www *w;
if(*urlname == '#' && post < 0){
scrollto(urlname+1);
return;
}
selurl(urlname);
selection->map=map;
message("getting %s", urlstr(selection));
esetcursor(&patientcurs);
for(;;){
if((fd=urlget(selection, post)) < 0){
message("%r");
break;
}
message("getting %s", selection->fullname);
if(mothmode && !plumb)
typ = -1;
else
typ = snooptype(fd);
switch(typ){
default:
if(plumb){
message("unknown file type");
close(fd);
break;
}
snprint(cmd, sizeof(cmd), "%s", urltofile(selection));
if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){
close(fd);
break;
}
save(fd, cmd);
break;
case HTML:
fd = pipeline(fd, "exec uhtml");
case PLAIN:
n=0;
for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){
w = www(i);
n += countpix(w->pix);
if(n >= NPIXMB*1024*1024)
killpix(w);
}
w = www(i = wwwtop++);
if(i >= NWWW){
/* wait for the reader to finish the document */
while(!w->finished && !w->alldone){
drawlock(0);
sleep(10);
drawlock(1);
}
freetext(w->text);
freeform(w->form);
freepix(w->pix);
freeurl(w->url);
memset(w, 0, sizeof(*w));
}
if(selection->map)
w->url=copyurl(current->url);
else
w->url=copyurl(selection);
w->finished = 0;
w->alldone = 0;
gettext(w, fd, typ);
if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){
for(;;){
sleep(1000);
if(w->finished || w->alldone)
break;
if(w->changed)
write(kickpipe[1], "C", 1);
}
_exits(0);
}
plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
if(defdisplay) pldraw(list, screen);
setcurrent(i, selection->tag);
break;
case GIF:
case JPEG:
case PNG:
case BMP:
case PAGE:
filter(fd, "exec page -w");
break;
}
break;
}
donecurs();
}
void updtext(Www *w){
Rtext *t;
Action *a;
if(defdisplay && w->gottitle==0 && w->title[0]!='\0')
pldraw(list, screen);
for(t=w->text;t;t=t->next){
a=t->user;
if(a){
if(a->field)
mkfieldpanel(t);
a->field=0;
}
}
if(w != current)
return;
w->yoffs=plgetpostextview(text);
plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink);
plsetpostextview(text, w->yoffs);
pldraw(text, screen);
}
void finish(Www *w){
w->finished = 1;
write(kickpipe[1], "F", 1);
}
void
mothon(Www *w, int on)
{
Rtext *t, *x;
Action *a, *ap;
if(w==0 || mothmode==on)
return;
if(mothmode = on)
message("moth mode!");
else
message(mothra);
/*
* insert or remove artificial links to the href for
* images that are also links
*/
for(t=w->text;t;t=t->next){
a=t->user;
if(a == nil || a->image == nil)
continue;
if(a->link == nil){
if(on)
t->flags |= PL_HOT;
else
t->flags &= ~PL_HOT;
continue;
}
x = t->next;
if(on){
t->next = nil;
ap=emalloc(sizeof(Action));
ap->link = strdup(a->link);
plrtstr(&t->next, 0, 0, 0, t->font, strdup("->"), PL_HOT, ap);
t->next->next = x;
} else {
if(x) {
t->next = x->next;
x->next = nil;
freetext(x);
}
}
}
updtext(w);
donecurs();
}
void killpix(Www *w){
Rtext *t;
if(w==0 || !w->finished && !w->alldone)
return;
for(t=w->text; t; t=t->next)
if(t->b && t->user)
t->b=0;
freepix(w->pix);
w->pix=0;
updtext(w);
}
void snarf(Panel *p){
if(p==0 || p==cmd){
if(selection){
plputsnarf(urlstr(selection));
plsnarf(text);
}else
message("no url selected");
}else
plsnarf(p);
}
void paste(Panel *p){
if(p==0) p=cmd;
plpaste(p);
}
void hit3(int button, int item){
char buf[1024];
char name[NNAME];
char *s;
Panel *swap;
int fd;
USED(button);
switch(item){
case 0:
swap=root;
root=alt;
alt=swap;
if(current)
current->yoffs=plgetpostextview(text);
swap=text;
text=alttext;
alttext=swap;
defdisplay=!defdisplay;
plpack(root, screen->r);
if(current){
plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
plsetpostextview(text, current->yoffs);
}
pldraw(root, screen);
break;
case 1:
mothon(current, !mothmode);
break;
case 2:
snarf(plkbfocus);
break;
case 3:
paste(plkbfocus);
break;
case 4:
if(plkbfocus==nil || plkbfocus==cmd){
if(text==nil || text->snarf==nil || selection==nil)
return;
if((s=text->snarf(text))==nil)
s=smprint("%s", urlstr(selection));
}else
if((s=plkbfocus->snarf(plkbfocus))==nil)
return;
if((fd=plumbopen("send", OWRITE))<0){
message("can't plumb");
free(s);
return;
}
plumbsendtext(fd, "mothra", nil, getwd(buf, sizeof buf), s);
close(fd);
free(s);
break;
case 5:
search();
break;
case 6:
if(!selection){
message("no url selected");
break;
}
snprint(name, sizeof(name), "%s/hit.html", mkhome());
fd=open(name, OWRITE);
if(fd==-1){
fd=create(name, OWRITE, 0666);
if(fd==-1){
message("can't open %s", name);
return;
}
fprint(fd, "<html><head><title>Hit List</title></head>\n");
fprint(fd, "<body><h1>Hit list</h1>\n");
}
seek(fd, 0, 2);
fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
close(fd);
break;
case 7:
snprint(name, sizeof(name), "file:%s/hit.html", mkhome());
geturl(name, -1, 1, 0);
break;
case 8:
if(confirm(3))
exits(0);
break;
}
}