add mothra

front
cinap_lenrek 2011-09-04 19:16:30 +02:00
parent f6e73a6a22
commit 6842f87125
54 changed files with 9240 additions and 0 deletions

68
sys/src/cmd/mothra/auth.c Normal file
View File

@ -0,0 +1,68 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <bio.h>
#include "mothra.h"
static int
basicauth(char *arg, char *str, int n)
{
int i;
char *p;
char buf[1024];
Biobuf *b;
if(strncmp(arg, "realm=", 6) == 0)
arg += 6;
if(*arg == '"'){
arg++;
for(p = arg; *p && *p != '"'; p++);
*p = 0;
} else {
for(p = arg; *p && *p != ' ' && *p != '\t'; p++);
*p = 0;
}
p = getenv("home");
if(p == 0){
werrstr("$home not set");
return -1;
}
snprint(buf, sizeof(buf), "%s/lib/mothra/insecurity", p);
b = Bopen(buf, OREAD);
if(b == 0){
werrstr("www password file %s: %r", buf);
return -1;
}
i = strlen(arg);
while(p = Brdline(b, '\n'))
if(strncmp(arg, p, i) == 0 && p[i] == '\t')
break;
if(p == 0){
Bterm(b);
werrstr("no basic password for domain `%s'", arg);
return -1;
}
p[Blinelen(b)-1] = 0;
for(p += i; *p == '\t'; p++);
if (enc64(buf, sizeof buf, (uchar*)p, strlen(p)) < 0) {
Bterm(b);
werrstr("password too long: %s", p);
return -1;
}
snprint(str, n, "Authorization: Basic %s\r\n", buf);
return 0;
}
int
auth(Url *url, char *str, int n)
{
if(cistrcmp(url->authtype, "basic") == 0)
return basicauth(url->autharg, str, n);
werrstr("unknown auth method %s", url->authtype);
return -1;
}

View File

@ -0,0 +1,29 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <ctype.h>
#include "mothra.h"
int cistrcmp(char *s1, char *s2){
int c1, c2;
for(; *s1; s1++, s2++){
c1 = isupper(*s1) ? tolower(*s1) : *s1;
c2 = isupper(*s2) ? tolower(*s2) : *s2;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
return 0;
}
int cistrncmp(char *s1, char *s2, int n){
int c1, c2;
for(; *s1 && n!=0; s1++, s2++, --n){
c1 = isupper(*s1) ? tolower(*s1) : *s1;
c2 = isupper(*s2) ? tolower(*s2) : *s2;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
return 0;
}

View File

@ -0,0 +1,188 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <ctype.h>
#include "mothra.h"
enum{
IP=1, /* url can contain //ipaddress[:port] */
REL=2, /* fill in ip address & root of name from current, if necessary */
SSL=4, /* use SSL/TLS encryption */
};
Scheme scheme[]={
"http:", HTTP, IP|REL, 80,
"https:", HTTP, IP|REL|SSL, 443,
"ftp:", FTP, IP|REL, 21,
"file:", FILE, REL, 0,
"telnet:", TELNET, IP, 0,
"mailto:", MAILTO, 0, 0,
"gopher:", GOPHER, IP, 70,
0, HTTP, IP|REL, 80,
};
int endaddr(int c){
return c=='/' || c==':' || c=='?' || c=='#' || c=='\0';
}
/*
* Remove ., mu/.. and empty components from path names.
* Empty last components of urls are significant, and
* therefore preserved.
*/
void urlcanon(char *name){
char *s, *t;
char **comp, **p, **q;
int rooted;
rooted=name[0]=='/';
/*
* Break the name into a list of components
*/
comp=emalloc((strlen(name)+2)*sizeof(char *));
p=comp;
*p++=name;
for(s=name;;s++){
if(*s=='/'){
*p++=s+1;
*s='\0';
}
else if(*s=='\0' || *s=='?')
break;
}
*p=0;
/*
* go through the component list, deleting components that are empty (except
* the last component) or ., and any .. and its non-.. predecessor.
*/
p=q=comp;
while(*p){
if(strcmp(*p, "")==0 && p[1]!=0
|| strcmp(*p, ".")==0)
p++;
else if(strcmp(*p, "..")==0 && q!=comp && strcmp(q[-1], "..")!=0){
--q;
p++;
}
else
*q++=*p++;
}
*q=0;
/*
* rebuild the path name
*/
s=name;
if(rooted) *s++='/';
for(p=comp;*p;p++){
t=*p;
while(*t) *s++=*t++;
if(p[1]!=0) *s++='/';
}
*s='\0';
free(comp);
}
/*
* True url parsing is a nightmare.
* This assumes that there are two basic syntaxes
* for url's -- with and without an ip address.
* If the type identifier or the ip address and port number
* or the relative address is missing from urlname or is empty,
* it is copied from cur.
*/
void crackurl(Url *url, char *urlname, Url *cur){
char *relp, *tagp, *httpname;
int len;
Scheme *up;
char buf[30];
/*
* The following lines `fix' the most egregious urlname syntax errors
*/
while(*urlname==' ' || *urlname=='\t' || *urlname=='\n') urlname++;
relp=strchr(urlname, '\n');
if(relp) *relp='\0';
/*
* In emulation of Netscape, attach a free "http://"
* to names beginning with "www.".
*/
if(strncmp(urlname, "www.", 4)==0){
httpname=emalloc(strlen(urlname)+8);
strcpy(httpname, "http://");
strcat(httpname, urlname);
crackurl(url, httpname, cur);
free(httpname);
return;
}
url->port=cur->port;
strncpy(url->ipaddr, cur->ipaddr, sizeof(url->ipaddr));
strncpy(url->reltext, cur->reltext, sizeof(url->reltext));
if(strchr(urlname, ':')==0){
up=cur->scheme;
if(up==0){
up=&scheme[0];
cur->scheme=up;
}
}
else{
for(up=scheme;up->name;up++){
len=strlen(up->name);
if(strncmp(urlname, up->name, len)==0){
urlname+=len;
break;
}
}
if(up->name==0) up=&scheme[0]; /* default to http: */
}
url->access=up->type;
url->scheme=up;
if(up!=cur->scheme)
url->reltext[0]='\0';
if(up->flags&IP && strncmp(urlname, "//", 2)==0){
urlname+=2;
for(relp=urlname;!endaddr(*relp);relp++);
len=relp-urlname;
strncpy(url->ipaddr, urlname, len);
url->ipaddr[len]='\0';
urlname=relp;
if(*urlname==':'){
urlname++;
url->port=atoi(urlname);
while(!endaddr(*urlname)) urlname++;
}
else
url->port=up->port;
if(*urlname=='\0') urlname="/";
}
url->ssl = up->flags&SSL;
tagp=strchr(urlname, '#');
if(tagp){
*tagp='\0';
strncpy(url->tag, tagp+1, sizeof(url->tag));
}
else
url->tag[0]='\0';
if(!(up->flags&REL) || *urlname=='/')
strncpy(url->reltext, urlname, sizeof(url->reltext));
else if(urlname[0]){
relp=strrchr(url->reltext, '/');
if(relp==0)
strncpy(url->reltext, urlname, sizeof(url->reltext));
else
strcpy(relp+1, urlname);
}
urlcanon(url->reltext);
if(tagp) *tagp='#';
/*
* The following mess of strcpys and strcats
* can't be changed to a few sprints because
* urls are not necessarily composed of legal utf
*/
strcpy(url->fullname, up->name);
if(up->flags&IP){
strncat(url->fullname, "//", sizeof(url->fullname));
strncat(url->fullname, url->ipaddr, sizeof(url->fullname));
if(url->port!=up->port){
snprint(buf, sizeof(buf), ":%d", url->port);
strncat(url->fullname, buf, sizeof(url->fullname));
}
}
strcat(url->fullname, url->reltext);
url->map=0;
}

48
sys/src/cmd/mothra/file.c Normal file
View File

@ -0,0 +1,48 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
/*
* fd is the result of a successful open(name, OREAD),
* where name is the name of a directory. We convert
* this into an html page containing links to the files
* in the directory.
*/
int dir2html(char *name, int fd){
int p[2], first;
Dir *dir;
int i, n;
if(pipe(p)==-1){
close(fd);
return -1;
}
switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
case -1:
close(fd);
return -1;
case 0:
close(p[1]);
fprint(p[0], "<head>\n");
fprint(p[0], "<title>Directory %s</title>\n", name);
fprint(p[0], "</head>\n");
fprint(p[0], "<body>\n");
fprint(p[0], "<h1>%s</h1>\n", name);
fprint(p[0], "<ul>\n");
first=1;
while((n = dirread(fd, &dir)) > 0) {
for (i = 0; i < n; i++)
fprint(p[0], "<li><a href=\"%s/%s\">%s%s</a>\n", name, dir[i].name, dir[i].name,
dir[i].mode&DMDIR?"/":"");
free(dir);
}
fprint(p[0], "</ul>\n");
fprint(p[0], "</body>\n");
_exits(0);
default:
close(fd);
close(p[0]);
return p[1];
}
}

View File

@ -0,0 +1,106 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <ctype.h>
#include "mothra.h"
typedef struct Kind Kind;
struct Kind{
char *name;
int kind;
};
int klook(char *s, Kind *k){
while(k->name && cistrcmp(k->name, s)!=0)
k++;
return k->kind;
}
Kind suffix[]={
".html", HTML,
".htm", HTML,
"/", HTML,
".gif", GIF,
".jpe", JPEG,
".jpg", JPEG,
".jpeg", JPEG,
".png", PNG,
".pic", PIC,
".au", AUDIO,
".tif", TIFF,
".tiff", TIFF,
".xbm", XBM,
".txt", PLAIN,
".text", PLAIN,
".ai", POSTSCRIPT,
".eps", POSTSCRIPT,
".ps", POSTSCRIPT,
".pdf", PDF,
".zip", ZIP,
0, HTML
};
int suflook(char *s, int len, Kind *k){
int l;
while(k->name){
l=strlen(k->name);
if(l<=len && cistrcmp(k->name, s+len-l)==0) return k->kind;
k++;
}
return k->kind;
}
int suffix2type(char *name){
int len, kind, restore;
char *s;
len=strlen(name);
if(len>=2 && cistrcmp(name+len-2, ".Z")==0){
kind=COMPRESS;
len-=2;
}
else if(len>=3 && cistrcmp(name+len-3, ".gz")==0){
kind=GUNZIP;
len-=3;
}
else
kind=0;
restore=name[len];
name[len]='\0';
for(s=name+len;s!=name && *s!='.';--s);
kind|=suflook(name, len, suffix);
name[len]=restore;
return kind;
}
Kind content[]={
"text/html", HTML,
"text/x-html", HTML,
"application/html", HTML,
"application/x-html", HTML,
"text/plain", PLAIN,
"image/gif", GIF,
"image/jpeg", JPEG,
"image/pjpeg", JPEG,
"image/png", PNG,
"image/tiff", TIFF,
"image/x-xbitmap", XBM,
"image/x-bitmap", XBM,
"image/xbitmap", XBM,
"application/postscript", POSTSCRIPT,
"application/pdf", PDF,
"application/octet-stream", SUFFIX,
"application/zip", ZIP,
0, HTML
};
int content2type(char *s, char *name){
int type;
type=klook(s, content);
if(type==SUFFIX) type=suffix2type(name);
return type;
}
Kind encoding[]={
"x-compress", COMPRESS,
"compress", COMPRESS,
"x-gzip", GUNZIP,
"gzip", GUNZIP,
0, 0
};
int encoding2type(char *s){
return klook(s, encoding);
}

661
sys/src/cmd/mothra/forms.c Normal file
View File

@ -0,0 +1,661 @@
/*
* type=image is treated like submit
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
#include "html.h"
typedef struct Field Field;
typedef struct Option Option;
struct Form{
int method;
Url *action;
Field *fields, *efields;
Form *next;
};
struct Field{
Field *next;
Form *form;
char *name;
char *value;
int checked;
int size; /* should be a point, but that feature is deprecated */
int maxlength;
int type;
int rows, cols;
Option *options;
int multiple;
int state; /* is the button marked? */
Panel *p;
Panel *pulldown;
Panel *textwin;
};
/*
* Field types
*/
enum{
TYPEIN=1,
CHECK,
PASSWD,
RADIO,
SUBMIT,
RESET,
SELECT,
TEXTWIN,
HIDDEN,
INDEX,
};
struct Option{
int selected;
int def;
char label[NLABEL+1];
char *value;
Option *next;
};
void h_checkinput(Panel *, int, int);
void h_radioinput(Panel *, int, int);
void h_submitinput(Panel *, int);
void h_submittype(Panel *, char *);
void h_submitindex(Panel *, char *);
void h_resetinput(Panel *, int);
void h_select(Panel *, int, int);
void h_cut(Panel *, int);
void h_paste(Panel *, int);
void h_snarf(Panel *, int);
void h_edit(Panel *);
char *selgen(Panel *, int);
char *nullgen(Panel *, int);
Field *newfield(Form *form){
Field *f;
f=emallocz(sizeof(Field), 1);
if(form->efields==0)
form->fields=f;
else
form->efields->next=f;
form->efields=f;
f->next=0;
f->form=form;
return f;
}
/*
* Called by rdhtml on seeing a forms-related tag
*/
void rdform(Hglob *g){
char *s;
Field *f;
Option *o, **op;
Form *form;
switch(g->tag){
default:
fprint(2, "Bad tag <%s> in rdform (Can't happen!)\n", g->token);
return;
case Tag_form:
if(g->form){
htmlerror(g->name, g->lineno, "nested forms illegal\n");
break;
}
g->form=emallocz(sizeof(Form), 1);
g->form->action=emalloc(sizeof(Url));
s=pl_getattr(g->attr, "action");
if(s==0)
*g->form->action=*g->dst->url;
else
crackurl(g->form->action, s, g->dst->base);
s=pl_getattr(g->attr, "method");
if(s==0)
g->form->method=GET;
else if(cistrcmp(s, "post")==0)
g->form->method=POST;
else{
if(cistrcmp(s, "get")!=0)
htmlerror(g->name, g->lineno,
"unknown form method %s\n", s);
g->form->method=GET;
}
g->form->fields=0;
g->form->next = g->dst->form;
g->dst->form = g->form;
break;
case Tag_input:
if(g->form==0){
BadTag:
htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
tag[g->tag].name);
break;
}
f=newfield(g->form);
s=pl_getattr(g->attr, "name");
if(s==0)
f->name=0;
else
f->name=strdup(s);
s=pl_getattr(g->attr, "value");
if(s==0)
f->value=strdup("");
else
f->value=strdup(s);
f->checked=pl_hasattr(g->attr, "checked");
s=pl_getattr(g->attr, "size");
if(s==0)
f->size=20;
else
f->size=atoi(s);
s=pl_getattr(g->attr, "maxlength");
if(s==0)
f->maxlength=0x3fffffff;
else
f->maxlength=atoi(s);
s=pl_getattr(g->attr, "type");
/* bug -- password treated as text */
if(s==0 || cistrcmp(s, "text")==0 || cistrcmp(s, "password")==0 || cistrcmp(s, "int")==0){
s=pl_getattr(g->attr, "name");
if(s!=0 && strcmp(s, "isindex")==0)
f->type=INDEX;
else
f->type=TYPEIN;
/*
* If there's exactly one attribute, use its value as the name,
* regardless of the attribute name. This makes
* http://linus.att.com/ias/puborder.html work.
*/
if(s==0){
if(g->attr[0].name && g->attr[1].name==0)
f->name=strdup(g->attr[0].value);
else
f->name=strdup("no-name");
}
}
else if(cistrcmp(s, "checkbox")==0)
f->type=CHECK;
else if(cistrcmp(s, "radio")==0)
f->type=RADIO;
else if(cistrcmp(s, "submit")==0)
f->type=SUBMIT;
else if(cistrcmp(s, "image")==0){
/* presotto's egregious hack to make image submits do something */
if(f->name){
free(f->name);
f->name=0;
}
f->type=SUBMIT;
} else if(cistrcmp(s, "reset")==0)
f->type=RESET;
else if(cistrcmp(s, "hidden")==0)
f->type=HIDDEN;
else{
htmlerror(g->name, g->lineno, "bad field type %s, ignored", s);
break;
}
if((f->type==CHECK || f->type==RADIO) && !pl_hasattr(g->attr, "value")){
free(f->value);
f->value=strdup("on");
}
if(f->type!=HIDDEN)
pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
break;
case Tag_select:
if(g->form==0) goto BadTag;
f=newfield(g->form);
s=pl_getattr(g->attr, "name");
if(s==0){
f->name=strdup("select");
htmlerror(g->name, g->lineno, "select has no name=\n");
}
else
f->name=strdup(s);
s=pl_getattr(g->attr, "size");
if(s==0) f->size=4;
else{
f->size=atoi(s);
if(f->size<=0) f->size=1;
}
f->multiple=pl_hasattr(g->attr, "multiple");
f->type=SELECT;
f->options=0;
g->text=g->token;
g->tp=g->text;
g->etext=g->text;
break;
case Tag_option:
if(g->form==0) goto BadTag;
f=g->form->efields;
o=emallocz(sizeof(Option), 1);
for(op=&f->options;*op;op=&(*op)->next);
*op=o;
o->next=0;
g->text=o->label;
g->tp=o->label;
g->etext=o->label+NLABEL;
memset(o->label, 0, NLABEL+1);
*g->tp++=' ';
o->def=pl_hasattr(g->attr, "selected");
o->selected=o->def;
s=pl_getattr(g->attr, "value");
if(s==0)
o->value=o->label+1;
else
o->value=strdup(s);
break;
case Tag_textarea:
if(g->form==0) goto BadTag;
f=newfield(g->form);
s=pl_getattr(g->attr, "name");
if(s==0){
f->name=strdup("enter text");
htmlerror(g->name, g->lineno, "select has no name=\n");
}
else
f->name=strdup(s);
s=pl_getattr(g->attr, "rows");
f->rows=s?atoi(s):8;
s=pl_getattr(g->attr, "cols");
f->cols=s?atoi(s):30;
f->type=TEXTWIN;
/* suck up initial text */
pl_htmloutput(g, g->nsp, f->name, f);
break;
case Tag_isindex:
/*
* Make up a form with one tag, of type INDEX
* I have seen a page with <ISINDEX PROMPT="Enter a title here ">,
* which is nonstandard and not handled here.
*/
form=emalloc(sizeof(Form));
form->fields=0;
form->efields=0;
form->action=emalloc(sizeof(Url));
s=pl_getattr(g->attr, "action");
if(s==0)
*form->action=*g->dst->url;
else
crackurl(form->action, s, g->dst->base);
form->method=GET;
form->fields=0;
f=newfield(form);
f->name=0;
f->value=strdup("");
f->size=20;
f->maxlength=0x3fffffff;
f->type=INDEX;
pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
break;
}
}
/*
* Called by rdhtml on seeing a forms-related end tag
*/
void endform(Hglob *g){
switch(g->tag){
case Tag_form:
g->form=0;
break;
case Tag_select:
if(g->form==0)
htmlerror(g->name, g->lineno, "</select> not in form, ignored\n");
else
pl_htmloutput(g, g->nsp, g->form->efields->name,g->form->efields);
break;
case Tag_textarea:
break;
}
}
char *nullgen(Panel *, int ){
return 0;
}
char *selgen(Panel *p, int index){
Option *a;
Field *f;
f=p->userp;
if(f==0) return 0;
for(a=f->options;index!=0 && a!=0;--index,a=a->next);
if(a==0) return 0;
a->label[0]=a->selected?'*':' ';
return a->label;
}
char *seloption(Field *f){
Option *a;
for(a=f->options;a!=0;a=a->next)
if(a->selected)
return a->label+1;
return f->name;
}
void mkfieldpanel(Rtext *t){
Action *a;
Panel *win, *scrl, *menu, *pop, *button;
Field *f;
if((a = t->user) == nil)
return;
if((f = a->field) == nil)
return;
f->p=0;
switch(f->type){
case TYPEIN:
f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
break;
case CHECK:
f->p=plcheckbutton(0, 0, "", h_checkinput);
f->state=f->checked;
plsetbutton(f->p, f->checked);
break;
case RADIO:
f->p=plradiobutton(0, 0, "", h_radioinput);
f->state=f->checked;
plsetbutton(f->p, f->checked);
break;
case SUBMIT:
f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);
break;
case RESET:
f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);
break;
case SELECT:
f->pulldown=plgroup(0,0);
scrl=plscrollbar(f->pulldown, PACKW|FILLY);
win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
win->userp=f;
plinitlist(win, PACKN, selgen, f->size, h_select);
plscroll(win, 0, scrl);
plpack(f->pulldown, Rect(0,0,1024,1024));
f->p=plpulldown(0, FIXEDX, seloption(f), f->pulldown, PACKS);
f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
break;
case TEXTWIN:
menu=plgroup(0,0);
f->p=plframe(0,0);
pllabel(f->p, PACKN|FILLX, f->name);
scrl=plscrollbar(f->p, PACKW|FILLY);
pop=plpopup(f->p, PACKN|FILLX, 0, menu, 0);
f->textwin=pledit(pop, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height),
0, 0, h_edit);
f->textwin->userp=f;
button=plbutton(menu, PACKN|FILLX, "cut", h_cut);
button->userp=f->textwin;
button=plbutton(menu, PACKN|FILLX, "paste", h_paste);
button->userp=f->textwin;
button=plbutton(menu, PACKN|FILLX, "snarf", h_snarf);
button->userp=f->textwin;
plscroll(f->textwin, 0, scrl);
break;
case INDEX:
f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submitindex);
break;
}
if(f->p){
f->p->userp=f;
free(t->text);
t->text=0;
t->p=f->p;
t->hot=1;
}
}
void h_checkinput(Panel *p, int, int v){
((Field *)p->userp)->state=v;
}
void h_radioinput(Panel *p, int, int v){
Field *f, *me;
me=p->userp;
me->state=v;
if(v){
for(f=me->form->fields;f;f=f->next)
if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){
plsetbutton(f->p, 0);
f->state=0;
pldraw(f->p, screen);
}
}
}
void h_select(Panel *p, int, int index){
Option *a;
Field *f;
f=p->userp;
if(f==0) return;
if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;
for(a=f->options;index!=0 && a!=0;--index,a=a->next);
if(a==0) return;
a->selected=!a->selected;
plinitpulldown(f->p, FIXEDX, seloption(f), f->pulldown, PACKS);
pldraw(f->p, screen);
}
void h_resetinput(Panel *p, int){
Field *f;
Option *o;
for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){
case TYPEIN:
case PASSWD:
plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);
break;
case CHECK:
case RADIO:
f->state=f->checked;
plsetbutton(f->p, f->checked);
break;
case SELECT:
for(o=f->options;o;o=o->next)
o->selected=o->def;
break;
}
pldraw(text, screen);
}
void h_edit(Panel *p){
plgrabkb(p);
}
Rune *snarfbuf=0;
int nsnarfbuf=0;
void h_snarf(Panel *p, int){
int s0, s1;
Rune *text;
p=p->userp;
plegetsel(p, &s0, &s1);
if(s0==s1) return;
text=pleget(p);
if(snarfbuf) free(snarfbuf);
nsnarfbuf=s1-s0;
snarfbuf=malloc(nsnarfbuf*sizeof(Rune));
if(snarfbuf==0){
fprint(2, "No mem\n");
exits("no mem");
}
memmove(snarfbuf, text+s0, nsnarfbuf*sizeof(Rune));
}
void h_cut(Panel *p, int b){
h_snarf(p, b);
plepaste(p->userp, 0, 0);
}
void h_paste(Panel *p, int){
plepaste(p->userp, snarfbuf, nsnarfbuf);
}
int ulen(char *s){
int len;
len=0;
for(;*s;s++){
if(strchr("/$-_@.!*'(), ", *s)
|| 'a'<=*s && *s<='z'
|| 'A'<=*s && *s<='Z'
|| '0'<=*s && *s<='9')
len++;
else
len+=3;
}
return len;
}
int hexdigit(int v){
return 0<=v && v<=9?'0'+v:'A'+v-10;
}
char *ucpy(char *buf, char *s){
for(;*s;s++){
if(strchr("/$-_@.!*'(),", *s)
|| 'a'<=*s && *s<='z'
|| 'A'<=*s && *s<='Z'
|| '0'<=*s && *s<='9')
*buf++=*s;
else if(*s==' ')
*buf++='+';
else{
*buf++='%';
*buf++=hexdigit((*s>>4)&15);
*buf++=hexdigit(*s&15);
}
}
*buf='\0';
return buf;
}
char *runetou(char *buf, Rune r){
char rbuf[2];
if(r<=255){
rbuf[0]=r;
rbuf[1]='\0';
buf=ucpy(buf, rbuf);
}
return buf;
}
/*
* If there's exactly one button with type=text, then
* a CR in the button is supposed to submit the form.
*/
void h_submittype(Panel *p, char *){
int ntype;
Field *f;
ntype=0;
for(f=((Field *)p->userp)->form->fields;f;f=f->next) if(f->type==TYPEIN) ntype++;
if(ntype==1) h_submitinput(p, 0);
}
void h_submitindex(Panel *p, char *){
h_submitinput(p, 0);
}
void h_submitinput(Panel *p, int){
Form *form;
int size, nrune;
char *buf, *bufp, sep;
Rune *rp;
Field *f;
Option *o;
form=((Field *)p->userp)->form;
if(form->method==GET) size=ulen(form->action->fullname)+1;
else size=1;
for(f=form->fields;f;f=f->next) switch(f->type){
case TYPEIN:
case PASSWD:
size+=ulen(f->name)+1+ulen(plentryval(f->p))+1;
break;
case INDEX:
size+=ulen(plentryval(f->p))+1;
break;
case CHECK:
case RADIO:
if(!f->state) break;
case HIDDEN:
size+=ulen(f->name)+1+ulen(f->value)+1;
break;
case SELECT:
for(o=f->options;o;o=o->next)
if(o->selected)
size+=ulen(f->name)+1+ulen(o->value)+1;
break;
case TEXTWIN:
size+=ulen(f->name)+1+plelen(f->textwin)*3+1;
break;
}
buf=emalloc(size);
if(form->method==GET){
strcpy(buf, form->action->fullname);
sep='?';
}
else{
buf[0]='\0';
sep=0;
}
bufp=buf+strlen(buf);
if(form->method==GET && bufp!=buf && bufp[-1]=='?') *--bufp='\0'; /* spurious ? */
for(f=form->fields;f;f=f->next) switch(f->type){
case TYPEIN:
case PASSWD:
if(sep) *bufp++=sep;
sep='&';
bufp=ucpy(bufp, f->name);
*bufp++='=';
bufp=ucpy(bufp, plentryval(f->p));
break;
case INDEX:
if(sep) *bufp++=sep;
sep='&';
bufp=ucpy(bufp, plentryval(f->p));
break;
case CHECK:
case RADIO:
if(!f->state) break;
case HIDDEN:
if(sep) *bufp++=sep;
sep='&';
bufp=ucpy(bufp, f->name);
*bufp++='=';
bufp=ucpy(bufp, f->value);
break;
case SELECT:
for(o=f->options;o;o=o->next)
if(o->selected){
if(sep) *bufp++=sep;
sep='&';
bufp=ucpy(bufp, f->name);
*bufp++='=';
bufp=ucpy(bufp, o->value);
}
break;
case TEXTWIN:
if(sep) *bufp++=sep;
sep='&';
bufp=ucpy(bufp, f->name);
*bufp++='=';
rp=pleget(f->textwin);
for(nrune=plelen(f->textwin);nrune!=0;--nrune)
bufp=runetou(bufp, *rp++);
*bufp='\0';
break;
}
if(form->method==GET){
fprint(2, "GET %s\n", buf);
geturl(buf, GET, 0, 0, 0);
}
else{
fprint(2, "POST %s: %s\n", form->action->fullname, buf);
geturl(form->action->fullname, POST, buf, 0, 0);
}
free(buf);
}
void freeform(void *p)
{
Form *form;
Field *f;
Option *o;
while(form = p){
p = form->next;
free(form->action);
while(f = form->fields){
form->fields = f->next;
if(f->p!=0)
plfree(f->p);
free(f->name);
free(f->value);
while(o = f->options){
f->options = o->next;
if(o->value != o->label+1)
free(o->value);
free(o);
}
free(f);
}
free(form);
}
}

428
sys/src/cmd/mothra/ftp.c Normal file
View File

@ -0,0 +1,428 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <bio.h>
#include <ndb.h>
#include <ctype.h>
#include <ip.h>
#include "mothra.h"
enum
{
/* return codes */
Extra= 1,
Success= 2,
Incomplete= 3,
TempFail= 4,
PermFail= 5,
NAMELEN= 28,
Nnetdir= 3*NAMELEN, /* max length of network directory paths */
Ndialstr= 64, /* max length of dial strings */
};
typedef struct Ftp Ftp;
struct Ftp
{
char net[Nnetdir];
Biobuf *ftpctl;
Url *u;
};
static int ftpdebug;
/*
* read from biobuf turning cr/nl into nl
*/
char*
getcrnl(Biobuf *b)
{
char *p, *ep;
p = Brdline(b, '\n');
if(p == nil)
return nil;
ep = p + Blinelen(b) - 1;
if(*(ep-1) == '\r')
ep--;
*ep = 0;
return p;
}
char*
readfile(char *file, char *buf, int len)
{
int n, fd;
fd = open(file, OREAD);
if(fd < 0)
return nil;
n = read(fd, buf, len-1);
close(fd);
if(n <= 0)
return nil;
buf[n] = 0;
return buf;
}
char*
sysname(void)
{
static char sys[Ndbvlen];
char *p;
p = readfile("/dev/sysname", sys, sizeof(sys));
if(p == nil)
return "unknown";
return p;
}
char*
domainname(void)
{
static char domain[Ndbvlen];
Ndbtuple *t;
if(*domain)
return domain;
t = csgetval(0, "sys", sysname(), "dom", domain);
if(t){
ndbfree(t);
return domain;
} else
return sysname();
}
static int
sendrequest(Biobuf *b, char *fmt, ...)
{
char buf[2*1024], *s;
va_list args;
va_start(args, fmt);
s = buf + vsnprint(buf, (sizeof(buf)-4) / sizeof(*buf), fmt, args);
va_end(args);
*s++ = '\r';
*s++ = '\n';
if(write(Bfildes(b), buf, s - buf) != s - buf)
return -1;
if(ftpdebug)
write(2, buf, s - buf);
return 0;
}
static int
getreply(Biobuf *b, char *msg, int len)
{
char *line;
int rv;
int i, n;
while(line = getcrnl(b)){
/* add line to message buffer, strip off \r */
n = Blinelen(b);
if(ftpdebug)
write(2, line, n);
if(n > len - 1)
i = len - 1;
else
i = n;
if(i > 0){
memmove(msg, line, i);
msg += i;
len -= i;
*msg = 0;
}
/* stop if not a continuation */
rv = atoi(line);
if(rv >= 100 && rv < 600 && (n == 4 || (n > 4 && line[3] == ' ')))
return rv/100;
}
return -1;
}
int
terminateftp(Ftp *d)
{
if(d->ftpctl){
close(Bfildes(d->ftpctl));
Bterm(d->ftpctl);
free(d->ftpctl);
d->ftpctl = nil;
}
free(d);
return -1;
}
Biobuf*
hello(Ftp *d)
{
int fd;
char *p;
Biobuf *b;
char msg[1024];
char ndir[Nnetdir];
snprint(msg, sizeof msg, "tcp!%s!%d", d->u->ipaddr, d->u->port);
fd = dial(msg, 0, ndir, 0);
if(fd < 0){
d->ftpctl = nil;
return nil;
}
b = emalloc(sizeof(Biobuf));
Binit(b, fd, OREAD);
d->ftpctl = b;
/* remember network for the data connections */
p = strrchr(ndir, '/');
if(p == 0){
fprint(2, "dial is out of date\n");
return nil;
}
*p = 0;
strcpy(d->net, ndir);
/* wait for hello from other side */
if(getreply(b, msg, sizeof(msg)) != Success){
fprint(2, "instead of hello: %s\n", msg);
return nil;
}
return b;
}
int
logon(Ftp *d)
{
char msg[1024];
/* login anonymous */
sendrequest(d->ftpctl, "USER anonymous");
switch(getreply(d->ftpctl, msg, sizeof(msg))){
case Success:
return 0;
case Incomplete:
break; /* need password */
default:
fprint(2, "login failed: %s\n", msg);
werrstr(msg);
return -1;
}
/* send user id as password */
sprint(msg, "%s@", getuser());
sendrequest(d->ftpctl, "PASS %s", msg);
if(getreply(d->ftpctl, msg, sizeof(msg)) != Success){
fprint(2, "login failed: %s\n", msg);
werrstr(msg);
return -1;
}
return 0;
}
int
xfertype(Ftp *d, char *t)
{
char msg[1024];
sendrequest(d->ftpctl, "TYPE %s", t);
if(getreply(d->ftpctl, msg, sizeof(msg)) != Success){
fprint(2, "can't set type %s: %s\n", t, msg);
werrstr(msg);
return -1;
}
return 0;
}
int
passive(Ftp *d)
{
char msg[1024];
char dialstr[Ndialstr];
char *f[6];
char *p;
int fd;
sendrequest(d->ftpctl, "PASV");
if(getreply(d->ftpctl, msg, sizeof(msg)) != Success)
return -1;
/* get address and port number from reply, this is AI */
p = strchr(msg, '(');
if(p == nil){
for(p = msg+3; *p; p++)
if(isdigit(*p))
break;
} else
p++;
if(getfields(p, f, 6, 0, ",") < 6){
fprint(2, "passive mode protocol botch: %s\n", msg);
werrstr("ftp protocol botch");
return -1;
}
snprint(dialstr, sizeof(dialstr), "%s!%s.%s.%s.%s!%d", d->net,
f[0], f[1], f[2], f[3],
((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
/* open data connection */
fd = dial(dialstr, 0, 0, 0);
if(fd < 0){
fprint(2, "passive mode connect to %s failed: %r\n", dialstr);
return -1;
}
/* tell remote to send a file */
sendrequest(d->ftpctl, "RETR %s", d->u->reltext);
if(getreply(d->ftpctl, msg, sizeof(msg)) != Extra){
fprint(2, "passive mode retrieve failed: %s\n", msg);
werrstr(msg);
return -1;
}
return fd;
}
int
active(Ftp *d)
{
char msg[1024];
char buf[Ndialstr];
char netdir[Nnetdir];
char newdir[Nnetdir];
uchar ipaddr[4];
int dfd, cfd, listenfd;
char *p;
int port;
/* get a channel to listen on, let kernel pick the port number */
sprint(buf, "%s!*!0", d->net);
listenfd = announce(buf, netdir);
if(listenfd < 0){
fprint(2, "can't listen for ftp callback: %r\n", buf);
return -1;
}
/* get the local address and port number */
sprint(newdir, "%s/local", netdir);
readfile(newdir, buf, sizeof buf);
p = strchr(buf, '!')+1;
parseip(ipaddr, buf);
port = atoi(p);
/* tell remote side address and port*/
sendrequest(d->ftpctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2],
ipaddr[3], port>>8, port&0xff);
if(getreply(d->ftpctl, msg, sizeof(msg)) != Success){
close(listenfd);
werrstr("ftp protocol botch");
fprint(2, "active mode connect failed %s\n", msg);
return -1;
}
/* tell remote to send a file */
sendrequest(d->ftpctl, "RETR %s", d->u->reltext);
if(getreply(d->ftpctl, msg, sizeof(msg)) != Extra){
close(listenfd);
fprint(2, "active mode connect failed: %s\n", msg);
werrstr(msg);
return -1;
}
/* wait for a new call */
cfd = listen(netdir, newdir);
close(listenfd);
if(cfd < 0){
fprint(2, "active mode connect failed: %r\n");
return -1;
}
/* open the data connection and close the control connection */
dfd = accept(cfd, newdir);
close(cfd);
if(dfd < 0){
fprint(2, "active mode connect failed: %r\n");
werrstr("ftp protocol botch");
return -1;
}
return dfd;
}
/*
* Given a url, return a file descriptor on which caller can
* read an ftp document.
* The caller is responsible for processing redirection loops.
*/
int
ftp(Url *url)
{
int n;
int data;
Ftp *d;
int pfd[2];
char buf[2048];
if(url->type == 0)
url->type = PLAIN;
d = (Ftp*)emalloc(sizeof(Ftp));
d->u = url;
d->ftpctl = nil;
if(hello(d) == nil)
return terminateftp(d);
if(logon(d) < 0)
return terminateftp(d);
switch(url->type){
case PLAIN:
case HTML:
if(xfertype(d, "A") < 0)
return terminateftp(d);
break;
default:
if(xfertype(d, "I") < 0)
return terminateftp(d);
break;
}
/* first try passive mode, then active */
data = passive(d);
if(data < 0){
if(d->ftpctl == nil)
return -1;
data = active(d);
if(data < 0)
return -1;
}
if(pipe(pfd) < 0)
return -1;
switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
case -1:
werrstr("Can't fork");
close(pfd[0]);
close(pfd[1]);
return terminateftp(d);
case 0:
close(pfd[0]);
while((n=read(data, buf, sizeof(buf)))>0)
write(pfd[1], buf, n);
if(n<0)
fprint(2, "ftp: %s: %r\n", url->fullname);
_exits(0);
default:
close(pfd[1]);
close(data);
terminateftp(d);
return pfd[0];
}
return -1;
}

126
sys/src/cmd/mothra/getpix.c Normal file
View File

@ -0,0 +1,126 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
typedef struct Pix Pix;
struct Pix{
Pix *next;
Image *b;
int width;
int height;
char name[NNAME];
};
char *pixcmd[]={
[GIF] "gif -9t",
[JPEG] "jpg -9t",
[PNG] "png -9t",
[PIC] "fb/3to1 /lib/fb/cmap/rgbv",
[TIFF] "/sys/lib/mothra/tiffcvt",
[XBM] "fb/xbm2pic",
};
void storebitmap(Rtext *t, Image *b){
t->b=b;
free(t->text);
t->text=0;
}
void getimage(Rtext *t, Www *w){
int pfd[2];
Action *ap;
Url url;
Image *b;
int fd;
char err[512];
Pix *p;
ap=t->user;
crackurl(&url, ap->image, w->base);
for(p=w->pix;p!=nil; p=p->next)
if(strcmp(ap->image, p->name)==0 && ap->width==p->width && ap->height==p->height){
storebitmap(t, p->b);
free(ap->image);
ap->image=0;
w->changed=1;
return;
}
fd=urlopen(&url, GET, 0);
if(fd==-1){
Err:
snprint(err, sizeof(err), "[%s: %r]", url.fullname);
free(t->text);
t->text=strdup(err);
free(ap->image);
ap->image=0;
w->changed=1;
close(fd);
return;
}
if(url.type!=GIF
&& url.type!=JPEG
&& url.type!=PNG
&& url.type!=PIC
&& url.type!=TIFF
&& url.type!=XBM){
werrstr("unknown image type");
goto Err;
}
if((fd = pipeline(pixcmd[url.type], fd)) < 0)
goto Err;
if(ap->width>0 || ap->height>0){
char buf[80];
char *p;
p = buf;
p += sprint(p, "resize");
if(ap->width>0)
p += sprint(p, " -x %d", ap->width);
if(ap->height>0)
p += sprint(p, " -y %d", ap->height);
if((fd = pipeline(buf, fd)) < 0)
goto Err;
}
b=readimage(display, fd, 1);
if(b==0){
werrstr("can't read image");
goto Err;
}
close(fd);
p = emallocz(sizeof(Pix), 1);
strncpy(p->name, ap->image, sizeof(p->name));
p->b=b;
p->width=ap->width;
p->height=ap->height;
p->next=w->pix;
w->pix=p;
storebitmap(t, b);
free(ap->image);
ap->image=0;
w->changed=1;
}
void getpix(Rtext *t, Www *w){
Action *ap;
for(;t!=0;t=t->next){
ap=t->user;
if(ap && ap->image)
getimage(t, w);
}
}
void freepix(void *p)
{
Pix *x, *xx;
xx = p;
while(x = xx){
xx = x->next;
freeimage(x->b);
free(x);
}
}

View File

@ -0,0 +1,38 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
void httpheader(Url *, char *);
/*
* Given a url, return a file descriptor on which caller can
* read a gopher document.
*/
int gopher(Url *url){
int pfd[2];
char port[30];
if(pipe(pfd)==-1) return -1;
switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
case -1:
close(pfd[0]);
close(pfd[1]);
return -1;
case 0:
dup(pfd[1], 1);
close(pfd[0]);
close(pfd[1]);
sprint(port, "%d", url->port);
execl("/bin/aux/gopher2html",
"gopher2html", url->ipaddr, port, url->reltext+1, 0);
fprint(2, "Can't exec aux/gopher2html!\n");
print("<head><title>Mothra error</title></head>\n");
print("<body><h1>Mothra error</h1>\n");
print("Can't exec aux/gopher2html!</body>\n");
exits("no exec");
default:
close(pfd[1]);
url->type=HTML;
return pfd[0];
}
}

View File

@ -0,0 +1,230 @@
/*
* Reads gopher output from a TCP port, outputs
* html on standard output.
* Usage: gopher2html gopher-string
* where gopher-string is the string sent to
* the gopher server to get the document.
*
* Gopher protocol is described in rfc1436
*/
#include <u.h>
#include <libc.h>
char *cmd;
int ifd;
void errexit(char *s, ...){
static char buf[1024];
char *out;
va_list args;
va_start(args, s);
out = doprint(buf, buf+sizeof(buf), s, args);
va_end(args);
*out='\0';
print("<head><title>%s error</title></head>\n", cmd);
print("<body><h1>%s error</h1>\n", cmd);
print("%s</body>\n", buf);
exits("gopher error");
}
void wtext(char *buf, char *ebuf){
char *bp;
for(bp=buf;bp!=ebuf;bp++){
if(*bp=='<' || *bp=='>' || *bp=='&' || *bp=='"'){
if(bp!=buf) write(1, buf, bp-buf);
buf=bp+1;
switch(*bp){
case '<': print("&lt;"); break;
case '>': print("&gt;"); break;
case '&': print("&amp;"); break;
case '"': print("&quot;"); break;
}
}
}
if(bp!=buf) write(1, buf, bp-buf);
}
void savefile(char *name, char *type){
int fd, n;
char save[30], buf[1024];
for(n=1;;n++){
if(n==100) errexit("can't save binary file %s: %r", name);
sprint(save, "gopher.save.%d", n);
fd=create(save, OWRITE, 0444);
if(fd!=-1) break;
}
print("<head><title>%s</title></head\n", name);
print("<body><h1>%s</h1><p>\n", name);
print("Saving %s file %s in <tt>%s</tt>...\n", type, name, save);
while((n=read(ifd, buf, sizeof buf))>0) write(fd, buf, n);
close(fd);
print("done</body>\n");
}
void copyfile(char *title){
char buf[1024];
int n;
print("<head><title>%s</title></head>\n", title);
print("<body><h1>%s</h1><pre>\n", title);
while((n=read(ifd, buf, sizeof buf))>0) wtext(buf, buf+n);
print("</pre></body>\n");
}
/*
* A directory entry contains
* type name selector host port
* all tab separated, except type and name (type is one character)
*/
char ibuf[1024], *ibp, *eibuf;
#define EOF (-1)
int get(void){
int n;
Again:
if(ibp==eibuf){
n=read(ifd, ibuf, sizeof(ibuf));
if(n<=0) return EOF;
eibuf=ibuf+n;
ibp=ibuf;
}
if(*ibp=='\r'){
ibp++;
goto Again;
}
return *ibp++&255;
}
char *escape(char *in){
static char out[516];
char *op, *eop;
eop=out+512;
op=out;
for(;*in;in++){
if(op<eop){
if(strchr("/$-_@.&!*'(),", *in)
|| 'a'<=*in && *in<='z'
|| 'A'<=*in && *in<='Z'
|| '0'<=*in && *in<='9')
*op++=*in;
else{
sprint(op, "%%%.2X", *in&255);
op+=3;
}
}
}
*op='\0';
return out;
}
void copydir(char *title){
int type, c;
char name[513], *ename;
char selector[513];
char host[513];
char port[513];
char *bp;
print("<head><title>%s</title></head>\n", title);
print("<body><h1>%s</h1><ul>\n", title);
for(;;){
type=get();
if(type==EOF || type=='.') break;
bp=name;
while((c=get())!=EOF && c!='\t') if(bp!=&name[512]) *bp++=c;
ename=bp;
bp=selector;
while((c=get())!=EOF && c!='\t') if(bp!=&selector[512]) *bp++=c;
*bp='\0';
bp=host;
while((c=get())!=EOF && c!='\t') if(bp!=&host[512]) *bp++=c;
*bp='\0';
bp=port;
while((c=get())!=EOF && c!='\t' && c!='\n') if(bp!=&port[512]) *bp++=c;
while(c!=EOF && c!='\n') c=get();
*bp='\0';
switch(type){
case '3':
print("<li>");
wtext(name, ename);
break;
case '7':
print("<li><isindex action=\"gopher://%s:%s/%c%s\">",
host, port, type, escape(selector));
wtext(name, ename);
break;
default:
print("<li><a href=\"gopher://%s:%s/%c%s\">",
host, port, type, escape(selector));
wtext(name, ename);
print("</a>\n");
break;
}
}
print("</ul></body>\n");
}
int hexdigit(int c){
if('0'<=c && c<='9') return c-'0';
if('a'<=c && c<='f') return c-'a'+10;
if('A'<=c && c<='F') return c-'A'+10;
return -1;
}
void unescape(char *s){
char *t;
int hi, lo;
t=s;
while(*s){
if(*s=='%'
&& (hi=hexdigit(s[1]))>=0
&& (lo=hexdigit(s[2]))>=0){
*t++=hi*16+lo;
s+=3;
}
else *t++=*s++;
}
*t='\0';
}
void main(int argc, char *argv[]){
char dialstr[1024];
char *name;
cmd=argv[0];
if(argc!=4) errexit("Usage: %s host port selector", argv[0]);
sprint(dialstr, "tcp!%s!%s", argv[1], argv[2]);
ifd=dial(dialstr, 0, 0, 0);
if(ifd==-1) errexit("can't call %s:%s", argv[1], argv[2]);
unescape(argv[3]);
switch(argv[3][0]){
case '/':
fprint(ifd, "\r\n");
copydir(argv[3]);
break;
case '\0':
fprint(ifd, "\r\n");
copydir(argv[1]);
break;
case '7': /* index query */
name=strchr(argv[3], '?');
if(name!=0){
if(name==argv[3]+1){
argv[3][1]=argv[3][0];
argv[3]++;
}
else
*name='\t';
name++;
}
else
name=argv[3];
fprint(ifd, "%s\r\n", argv[3]+1);
copydir(name);
break;
default:
fprint(ifd, "%s\r\n", argv[3]+1);
name=strrchr(argv[3], '/');
if(name==0) name=argv[3];
else name++;
switch(argv[3][0]){
default: errexit("sorry, can't handle %s (type %c)",
argv[3]+1, argv[3][0]);
case '0': copyfile(name); break;
case '1': copydir(name); break;
case '4': savefile(name, "Macintosh BINHEX"); break;
case '5': savefile(name, "DOS binary"); break;
case '6': savefile(name, "uuencoded"); break;
case '9': savefile(name, "binary"); break;
case 'g': savefile(name, "GIF"); break;
case 'I': savefile(name, "some sort of image"); break;
}
break;
}
exits(0);
}

View File

@ -0,0 +1,78 @@
<html><head><title>Mothra help</title>
</head>
<body>
<H1>Mothra Help</H1>
<p>
Mothra is a World-wide Web browser. Its display looks like this:
<p><img src="file:display.pic" alt="[mothra display]">
<p>The display's regions, from top to bottom, are:
<ul>
<li>Error messages and other information.
<li>A text input window in which <a href="#commands">commands</a> can be typed.
<li>A scrollable list of titles of previously visited documents, with the most recent first.
Pointing at one of these lines with mouse button 1 revisits the document.
<li>The title of the currently-displayed document.
<li>The URL of the currently-displayed document.
<li>The scrollable document display. Underlined text and
images surrounded by boxes may be pointed at with button 1 to
visit the files that they refer to. Files that are not
HTML documents (for example images or mailto: urls) cause
<i>9v</i> or <i>mail</i> to pop up in a new 8&#189; window.
</ul>
<h4>Mouse Action</H4>
<p>Pointing with button
2 instead of button 1 selects a url without following it;
the url will be displayed in the selection: area and commands
will refer to the url, but it will not be drawn in the document display.
Button 3 pops up a command menu that contains
<ul>
<li><b>alt display</b><br>switches to (or from) the alternate display, which shows only
the scrollable document display area. This might be useful when running mothra
in a small window.
<li><b>snarf url</b><br>copies the selected url into the snarf buffer.
<li><b>paste</b><br>appends the snarf buffer to the command window.
<li><b>inline pix</b><br>turn off/on loading of inline images. Image maps cannot be disabled.
<li><b>fix cmap</b><br>reload the default plan 9 colormap
<li><b>save hit</b><br>appends the selected url to file:$home/lib/hit.html
<li><b>hit list</b><br>displays file:$home/lib/hit.html
<li><b>exit</b>
</ul>
<a name="#commands"><h4>Commands</h4></a>
<p>The commands you can type are:
<ul>
<li>g [url]<br>get the page with the given url (default, the selection.)
<li>r [url]<br>refresh the display if the URL changes.
Otherwise, you will probably see a cached version.
<li>s file<br>save the current page in the given file.
<li>w file<br>write a bitmap image of the document display area in the given file.
<li>q<br>exit.
<li>?<br>get help.
<li>h<br>get help.
</ul>
<p>
<h4>Configuration</h4>
Mothra gets configuration information from the environment.
<ul>
<li>$url<br>The default <i>url</i> displayed when mothra starts.
A <i>url</i> given on the command line overrides this.
The default is <b>/sys/lib/mothra/start.html</b>
<li>$httpproxy<br>The network address of an http proxy server,
in the format expected by dial(2). If $httpproxy is not set
or is null, no proxy server is used.
</ul>
<h4>Command line</h4>
If the mothra command has an argument, it is the name of a <i>url</i> to visit
instead of the startup page. Giving mothra the <b>-i</b> flag disables loading
of inline images. The <b>inline pix</b> menu item will reset this option.
<h4>Files</h4>
Mothra creates several files in $home/lib/mothra.
<ul>
<li>mothra.log<br>a list of all the url's visited
<li>mothra.err<br>a log of error messages, mostly uninteresting
<li>hit.html<br>the hit list used by the <b>save hit</b>
and <b>hit list</b> commands. Since <b>save hit</b> only
adds new urls to the end of this file, it is safe to edit it
to add annotation or sort the saved urls.
</ul>
</body>
</html>

201
sys/src/cmd/mothra/html.h Normal file
View File

@ -0,0 +1,201 @@
/*
* Parameters
*/
#define NSTACK 100 /* html grammar is not recursive, so 30 or so should do */
#define NHBUF 8192 /* Input buffer size */
#define NPEEKC 3 /* Maximum lookahead */
#define NTOKEN 1024 /* Maximum token length */
#define NATTR 512 /* Maximum number of attributes of a tag */
typedef struct Pair Pair;
typedef struct Tag Tag;
typedef struct Stack Stack;
typedef struct Hglob Hglob;
typedef struct Form Form;
typedef struct Entity Entity;
struct Pair{
char *name;
char *value;
};
struct Entity{
char *name;
Rune value;
};
struct Tag{
char *name;
int action;
};
struct Stack{
int tag; /* html tag being processed */
int pre; /* in preformatted text? */
int font; /* typeface */
int size; /* point size of text */
int margin; /* left margin position */
int indent; /* extra indent at paragraph start */
int number; /* paragraph number */
int ismap; /* flag of <img> */
int width; /* size of image */
int height;
int table; /* depth of table nesting */
char image[NNAME]; /* arg of <img> */
char link[NNAME]; /* arg of <a href=...> */
char name[NNAME]; /* arg of <a name=...> */
};
/*
* Globals -- these are packed up into a struct that gets passed around
* so that multiple parsers can run concurrently
*/
struct Hglob{
char *tp; /* pointer in text buffer */
char *name; /* input file name */
int hfd; /* input file descriptor */
char hbuf[NHBUF]; /* input buffer */
char *hbufp; /* next character in buffer */
char *ehbuf; /* end of good characters in buffer */
int heof; /* end of file flag */
int peekc[NPEEKC]; /* characters to re-read */
int npeekc; /* # of characters to re-read */
char token[NTOKEN]; /* if token type is TEXT */
Pair attr[NATTR]; /* tag attribute/value pairs */
int nsp; /* # of white-space characters before TEXT token */
int spacc; /* place to accumulate more spaces */
/* if negative, won't accumulate! */
int tag; /* if token type is TAG or END */
Stack stack[NSTACK]; /* parse stack */
Stack *state; /* parse stack pointer */
int lineno; /* input line number */
int linebrk; /* flag set if we require a line-break in output */
int para; /* flag set if we need an indent at the break */
char *text; /* text buffer */
char *etext; /* end of text buffer */
Form *form; /* data for form under construction */
Www *dst; /* where the text goes */
char charset[NNAME];
};
/*
* Token types
*/
enum{
TAG=1,
ENDTAG,
TEXT,
};
/*
* Magic characters corresponding to
* literal < followed by / ! or alpha,
* literal > and
* end of file
*/
#define STAG 65536
#define ETAG 65537
#define EOF -1
/*
* fonts
*/
enum{
ROMAN,
ITALIC,
BOLD,
CWIDTH,
};
/*
* font sizes
*/
enum{
SMALL,
NORMAL,
LARGE,
ENORMOUS,
};
/*
* Token names for the html parser.
* Tag_end corresponds to </end> tags.
* Tag_text tags text not in a tag.
* Those two must follow the others.
*/
enum{
Tag_comment,
Tag_a,
Tag_address,
Tag_b,
Tag_base,
Tag_blockquot,
Tag_body,
Tag_br,
Tag_center,
Tag_cite,
Tag_code,
Tag_dd,
Tag_dfn,
Tag_dir,
Tag_dl,
Tag_dt,
Tag_em,
Tag_font,
Tag_form,
Tag_h1,
Tag_h2,
Tag_h3,
Tag_h4,
Tag_h5,
Tag_h6,
Tag_head,
Tag_hr,
Tag_html,
Tag_i,
Tag_img,
Tag_input,
Tag_isindex,
Tag_kbd,
Tag_key,
Tag_li,
Tag_link,
Tag_listing,
Tag_menu,
Tag_meta,
Tag_nextid,
Tag_ol,
Tag_option,
Tag_p,
Tag_plaintext,
Tag_pre,
Tag_samp,
Tag_select,
Tag_strong,
Tag_textarea,
Tag_title,
Tag_tt,
Tag_u,
Tag_ul,
Tag_var,
Tag_xmp,
Tag_frame, /* rm 5.8.97 */
Tag_table, /* rm 3.8.00 */
Tag_td,
Tag_tr,
Tag_script,
Tag_style,
Tag_end, /* also used to indicate unrecognized start tag */
Tag_text,
};
enum{
NTAG=Tag_end,
END=1, /* tag must have a matching end tag */
NOEND, /* tag must not have a matching end tag */
OPTEND, /* tag may have a matching end tag */
ERR, /* tag must not occur */
};
Tag tag[];
Entity pl_entity[];
int pl_entities;
void rdform(Hglob *);
void endform(Hglob *);
char *pl_getattr(Pair *, char *);
int pl_hasattr(Pair *, char *);
void pl_htmloutput(Hglob *, int, char *, Field *);

View File

@ -0,0 +1,340 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
#include "html.h"
Tag tag[]={
[Tag_comment] "!--", NOEND,
[Tag_a] "a", END,
[Tag_address] "address", END,
[Tag_b] "b", END,
[Tag_base] "base", NOEND,
[Tag_blockquot] "blockquote", END,
[Tag_body] "body", END, /* OPTEND */
[Tag_br] "br", NOEND,
[Tag_center] "center", END,
[Tag_cite] "cite", END,
[Tag_code] "code", END,
[Tag_dd] "dd", NOEND, /* OPTEND */
[Tag_dfn] "dfn", END,
[Tag_dir] "dir", END,
[Tag_dl] "dl", END,
[Tag_dt] "dt", NOEND, /* OPTEND */
[Tag_em] "em", END,
[Tag_font] "font", END,
[Tag_form] "form", END,
[Tag_h1] "h1", END,
[Tag_h2] "h2", END,
[Tag_h3] "h3", END,
[Tag_h4] "h4", END,
[Tag_h5] "h5", END,
[Tag_h6] "h6", END,
[Tag_head] "head", END, /* OPTEND */
[Tag_hr] "hr", NOEND,
[Tag_html] "html", END, /* OPTEND */
[Tag_i] "i", END,
[Tag_input] "input", NOEND,
[Tag_img] "img", NOEND,
[Tag_isindex] "isindex", NOEND,
[Tag_kbd] "kbd", END,
[Tag_key] "key", END,
[Tag_li] "li", NOEND, /* OPTEND */
[Tag_link] "link", NOEND,
[Tag_listing] "listing", END,
[Tag_menu] "menu", END,
[Tag_meta] "meta", NOEND,
[Tag_nextid] "nextid", NOEND,
[Tag_ol] "ol", END,
[Tag_option] "option", NOEND, /* OPTEND */
[Tag_p] "p", NOEND, /* OPTEND */
[Tag_plaintext] "plaintext", NOEND,
[Tag_pre] "pre", END,
[Tag_samp] "samp", END,
[Tag_script] "script", END,
[Tag_style] "style", END,
[Tag_select] "select", END,
[Tag_strong] "strong", END,
[Tag_table] "table", END,
[Tag_td] "td", END,
[Tag_textarea] "textarea", END,
[Tag_title] "title", END,
[Tag_tr] "tr", END,
[Tag_tt] "tt", END,
[Tag_u] "u", END,
[Tag_ul] "ul", END,
[Tag_var] "var", END,
[Tag_xmp] "xmp", END,
[Tag_frame] "frame", NOEND,
[Tag_end] 0, ERR,
};
Entity pl_entity[]={
"AElig", L'Æ',
"Aacute", L'Á',
"Acirc", L'Â',
"Agrave", L'À',
"Alpha", L'Α',
"Aring", L'Å',
"Atilde", L'Ã',
"Auml", L'Ä',
"Beta", L'Β',
"Ccedil", L'Ç',
"Chi", L'Χ',
"Dagger", L'',
"Delta", L'Δ',
"ETH", L'Ð',
"Eacute", L'É',
"Ecirc", L'Ê',
"Egrave", L'È',
"Epsilon", L'Ε',
"Eta", L'Η',
"Euml", L'Ë',
"Gamma", L'Γ',
"Iacute", L'Í',
"Icirc", L'Î',
"Igrave", L'Ì',
"Iota", L'Ι',
"Iuml", L'Ï',
"Kappa", L'Κ',
"Lambda", L'Λ',
"Mu", L'Μ',
"Ntilde", L'Ñ',
"Nu", L'Ν',
"OElig", L'Œ',
"Oacute", L'Ó',
"Ocirc", L'Ô',
"Ograve", L'Ò',
"Omega", L'Ω',
"Omicron", L'Ο',
"Oslash", L'Ø',
"Otilde", L'Õ',
"Ouml", L'Ö',
"Phi", L'Φ',
"Pi", L'Π',
"Prime", L'',
"Psi", L'Ψ',
"Rho", L'Ρ',
"Scaron", L'Š',
"Sigma", L'Σ',
"THORN", L'Þ',
"Tau", L'Τ',
"Theta", L'Θ',
"Uacute", L'Ú',
"Ucirc", L'Û',
"Ugrave", L'Ù',
"Upsilon", L'Υ',
"Uuml", L'Ü',
"Xi", L'Ξ',
"Yacute", L'Ý',
"Yuml", L'Ÿ',
"Zeta", L'Ζ',
"aacute", L'á',
"acirc", L'â',
"acute", L'´',
"aelig", L'æ',
"agrave", L'à',
"alefsym", L'',
"alpha", L'α',
"amp", L'&',
"and", L'',
"ang", L'',
"aring", L'å',
"asymp", L'',
"atilde", L'ã',
"auml", L'ä',
"bdquo", L'',
"beta", L'β',
"brvbar", L'¦',
"bull", L'',
"cap", L'',
"ccedil", L'ç',
"cdots", L'',
"cedil", L'¸',
"cent", L'¢',
"chi", L'χ',
"circ", L'ˆ',
"clubs", L'',
"cong", L'',
"copy", L'©',
"crarr", L'',
"cup", L'',
"curren", L'¤',
"dArr", L'',
"dagger", L'',
"darr", L'',
"ddots", L'',
"deg", L'°',
"delta", L'δ',
"diams", L'',
"divide", L'÷',
"eacute", L'é',
"ecirc", L'ê',
"egrave", L'è',
"emdash", L'',
"empty", L'',
"emsp", L'',
"endash", L'',
"ensp", L'',
"epsilon", L'ε',
"equiv", L'',
"eta", L'η',
"eth", L'ð',
"euml", L'ë',
"euro", L'',
"exist", L'',
"fnof", L'ƒ',
"forall", L'',
"frac12", L'½',
"frac14", L'¼',
"frac34", L'¾',
"frasl", L'',
"gamma", L'γ',
"ge", L'',
"gt", L'>',
"hArr", L'',
"harr", L'',
"hearts", L'',
"hellip", L'',
"iacute", L'í',
"icirc", L'î',
"iexcl", L'¡',
"igrave", L'ì',
"image", L'',
"infin", L'',
"int", L'',
"iota", L'ι',
"iquest", L'¿',
"isin", L'',
"iuml", L'ï',
"kappa", L'κ',
"lArr", L'',
"lambda", L'λ',
"lang", L'',
"laquo", L'«',
"larr", L'',
"lceil", L'',
"ldots", L'',
"ldquo", L'',
"le", L'',
"lfloor", L'',
"lowast", L'',
"loz", L'',
"lrm", L'',
"lsaquo", L'',
"lsquo", L'',
"lt", L'<',
"macr", L'¯',
"mdash", L'',
"micro", L'µ',
"middot", L'·',
"minus", L'',
"mu", L'μ',
"nabla", L'',
"nbsp", L' ',
"ndash", L'',
"ne", L'',
"ni", L'',
"not", L'¬',
"notin", L'',
"nsub", L'',
"ntilde", L'ñ',
"nu", L'ν',
"oacute", L'ó',
"ocirc", L'ô',
"oelig", L'œ',
"ograve", L'ò',
"oline", L'',
"omega", L'ω',
"omicron", L'ο',
"oplus", L'',
"or", L'',
"ordf", L'ª',
"ordm", L'º',
"oslash", L'ø',
"otilde", L'õ',
"otimes", L'',
"ouml", L'ö',
"para", L'',
"part", L'',
"permil", L'',
"perp", L'',
"phi", L'φ',
"pi", L'π',
"piv", L'ϖ',
"plusmn", L'±',
"pound", L'£',
"prime", L'',
"prod", L'',
"prop", L'',
"psi", L'ψ',
"quad", L'',
"quot", L'"',
"rArr", L'',
"radic", L'',
"rang", L'',
"raquo", L'»',
"rarr", L'',
"rceil", L'',
"rdquo", L'',
"real", L'',
"reg", L'®',
"rfloor", L'',
"rho", L'ρ',
"rlm", L'',
"rsaquo", L'',
"rsquo", L'',
"sbquo", L'',
"scaron", L'š',
"sdot", L'',
"sect", L'§',
"shy", L'­',
"sigma", L'σ',
"sigmaf", L'ς',
"sim", L'',
"sp", L'',
"spades", L'',
"sub", L'',
"sube", L'',
"sum", L'',
"sup", L'',
"sup1", L'¹',
"sup2", L'²',
"sup3", L'³',
"supe", L'',
"szlig", L'ß',
"tau", L'τ',
"there4", L'',
"theta", L'θ',
"thetasym", L'ϑ',
"thinsp", L'',
"thorn", L'þ',
"tilde", L'˜',
"times", L'×',
"trade", L'',
"uArr", L'',
"uacute", L'ú',
"uarr", L'',
"ucirc", L'û',
"ugrave", L'ù',
"uml", L'¨',
"upsih", L'ϒ',
"upsilon", L'υ',
"uuml", L'ü',
"varepsilon", L'',
"varphi", L'ϕ',
"varpi", L'ϖ',
"varrho", L'ϱ',
"vdots", L'',
"vsigma", L'ς',
"vtheta", L'ϑ',
"weierp", L'',
"xi", L'ξ',
"yacute", L'ý',
"yen", L'¥',
"yuml", L'ÿ',
"zeta", L'ζ',
"zwj", L'',
"zwnj", L'',
};
int pl_entities = nelem(pl_entity);

486
sys/src/cmd/mothra/http.c Normal file
View File

@ -0,0 +1,486 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include <libsec.h> /* tlsClient */
#include "mothra.h"
typedef struct Cache Cache;
struct Cache{
int fd; /* file descriptor on which to write cached data */
ulong hash; /* hash of url, used to compute cache file name */
int modtime; /* time at which cache entry was created */
int type; /* url->type of cached entry */
};
void httpheader(Url *, char *);
int httpresponse(char *);
static char *proxyserver; /* name of proxy server */
void exitnow(void*, char*){
noted(NDFLT);
}
void hashname(char *name, int n, char *stem, Cache *c){
snprint(name, n, "/sys/lib/mothra/cache/%s.%.8lux", stem, c->hash);
}
// #define CacheEnabled
/*
* Returns fd of cached file, if found (else -1)
* Fills in Cache data structure for caller
* If stale is set, caller has determined that the existing
* cache entry for this url is stale, so we shouldn't bother re-examining it.
*/
int cacheopen(Url *url, Cache *c, int stale){
#ifdef CacheEnabled
int fd, n;
char name[NNAME+1], *s, *l;
/*
* If we're using a proxy server or the url contains a ? or =,
* don't even bother.
*/
if(proxyserver || strchr(url->reltext, '?')!=0 || strchr(url->reltext, '=')!=0){
c->fd=-1;
return -1;
}
c->hash=0;
for(s=url->fullname,n=0;*s;s++,n++) c->hash=c->hash*n+(*s&255);
if(stale)
fd=-1;
else{
hashname(name, sizeof(name), "cache", c);
fd=open(name, OREAD);
}
if(fd==-1){
hashname(name, sizeof(name), "write", c);
c->fd=create(name, OWRITE, 0444);
if(c->fd!=-1)
fprint(c->fd, "%s %10ld\n", url->fullname, time(0));
return -1;
}
c->fd=-1;
for(l=name;l!=&name[NNAME];l+=n){
n=&name[NNAME]-l;
n=read(fd, l, n);
if(n<=0) break;
}
*l='\0';
s=strchr(name, ' ');
if(s==0){
close(fd);
return -1;
}
*s='\0';
if(strcmp(url->fullname, name)!=0){
close(fd);
return -1;
}
c->modtime=atol(++s);
s=strchr(s, '\n');
if(s==0){
close(fd);
return -1;
}
s++;
if(strncmp(s, "type ", 5)!=0){
close(fd);
return -1;
}
c->type=atoi(s+5);
s=strchr(s+5, '\n');
if(s==0){
close(fd);
return -1;
}
seek(fd, s-name+1, 0);
return fd;
#else
c->fd=-1;
return -1;
#endif
}
/*
* Close url->fd and either rename the cache file or
* remove it, depending on success
*/
void cacheclose(Cache *c, int success){
char wname[NNAME+1], cname[NNAME+1], *celem;
Dir *wdir;
if(c->fd==-1) return;
close(c->fd);
hashname(wname, sizeof(wname), "write", c);
if(!success){
remove(wname);
return;
}
if((wdir = dirstat(wname)) == 0)
return;
hashname(cname, sizeof(cname), "cache", c);
if(access(cname, 0) == 0){
if(remove(cname)==-1){
remove(wname);
free(wdir);
return;
}
/*
* This looks implausible, but it's what the mv command does
*/
do; while(remove(cname)!=-1);
}
celem=strrchr(cname, '/');
if(celem==0) celem=cname;
else celem++;
strcpy(wdir->name, celem);
if(dirwstat(wname, wdir)==-1)
remove(wname);
free(wdir);
}
static char *wkday[]={
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static char *month[]={
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/*
* Sun, 06 Nov 1994 08:49:38 GMT
* 123456789 123456789 123456789
*/
char *rfc1123date(long time){
static char buf[50];
Tm *t;
t=gmtime(time);
snprint(buf, sizeof(buf), "%s, %2.2d %s %4.4d %2.2d:%2.2d:%2.2d GMT",
wkday[t->wday], t->mday, month[t->mon], t->year+1900,
t->hour, t->min, t->sec);
return buf;
}
/*
* Given a url, return a file descriptor on which caller can
* read an http document. As a side effect, we parse the
* http header and fill in some fields in the url.
* The caller is responsible for processing redirection loops.
* Method can be either GET or POST. If method==post, body
* is the text to be posted.
*/
int http(Url *url, int method, char *body){
char *addr, *com;
int fd, n, nnl, len;
int ncom, m;
int pfd[2];
char buf[1024], *bp, *ebp;
char line[1024+1], *lp, *elp;
char authstr[NAUTH], *urlname;
int gotresponse;
int response;
Cache cache;
int cfd, cookiefd;
static int firsttime=1;
static int gotcookies;
if(firsttime){
proxyserver=getenv("httpproxy");
gotcookies=(access("/mnt/webcookies/http", AREAD|AWRITE)==0);
firsttime=0;
}
*authstr = 0;
Authorize:
cfd=-1;
cookiefd=-1;
if(proxyserver && proxyserver[0]!='\0'){
addr=strdup(proxyserver);
urlname=url->fullname;
}
else{
addr=emalloc(strlen(url->ipaddr)+100);
sprint(addr, "tcp!%s!%d", url->ipaddr, url->port);
urlname=url->reltext;
}
fd=dial(addr, 0, 0, 0);
free(addr);
if(fd==-1) goto ErrReturn;
if(url->ssl){
int tfd;
TLSconn conn;
memset(&conn, 0, sizeof conn);
tfd = tlsClient(fd, &conn);
if(tfd < 0){
close(fd);
goto ErrReturn;
}
/* BUG: check cert here? */
if(conn.cert)
free(conn.cert);
close(fd);
fd = tfd;
}
ncom=strlen(urlname)+sizeof(buf);
com=emalloc(ncom+2);
cache.fd=-1;
switch(method){
case GET:
cfd=cacheopen(url, &cache, 0);
if(cfd==-1)
n=sprint(com,
"GET %s HTTP/1.0\r\n%s"
"Accept: */*\r\n"
"User-agent: mothra/%s\r\n"
"Host: %s\r\n",
urlname, authstr, version, url->ipaddr);
else
n=sprint(com,
"GET %s HTTP/1.0\r\n%s"
"If-Modified-since: %s\r\n"
"Accept: */*\r\n"
"User-agent: mothra/%s\r\n"
"Host: %s\r\n",
urlname, authstr, rfc1123date(cache.modtime), version, url->ipaddr);
break;
case POST:
len=strlen(body);
n=sprint(com,
"POST %s HTTP/1.0\r\n%s"
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-length: %d\r\n"
"User-agent: mothra/%s\r\n",
urlname, authstr, len, version);
break;
}
if(gotcookies && (cookiefd=open("/mnt/webcookies/http", ORDWR)) >= 0){
if(fprint(cookiefd, "%s", url->fullname) > 0){
while((m=read(cookiefd, buf, sizeof buf)) > 0){
if(m+n>ncom){
if(write(fd, com, n)!= n){
free(com);
goto fdErrReturn;
}
n=0;
com[0] = '\0';
}
strncat(com, buf, m);
n += m;
}
}else{
close(cookiefd);
cookiefd=-1;
}
}
strcat(com, "\r\n");
n += 2;
switch(method){
case GET:
if(write(fd, com, n)!=n){
free(com);
goto fdErrReturn;
}
break;
case POST:
if(write(fd, com, n)!=n
|| write(fd, body, len)!=len){
free(com);
goto fdErrReturn;
}
break;
}
free(com);
if(pipe(pfd)==-1) goto fdErrReturn;
n=read(fd, buf, 1024);
if(n<=0){
EarlyEof:
if(n==0){
fprint(2, "%s: EOF in header\n", url->fullname);
werrstr("EOF in header");
}
pfdErrReturn:
close(pfd[0]);
close(pfd[1]);
fdErrReturn:
close(fd);
ErrReturn:
if(cookiefd>=0)
close(cookiefd);
cacheclose(&cache, 0);
return -1;
}
bp=buf;
ebp=buf+n;
url->type=0;
if(strncmp(buf, "HTTP/", 5)==0){ /* hack test for presence of header */
SET(response);
gotresponse=0;
url->redirname[0]='\0';
nnl=0;
lp=line;
elp=line+1024;
while(nnl!=2){
if(bp==ebp){
n=read(fd, buf, 1024);
if(n<=0) goto EarlyEof;
ebp=buf+n;
bp=buf;
}
if(*bp!='\r'){
if(nnl==1 && (!gotresponse || (*bp!=' ' && *bp!='\t'))){
*lp='\0';
if(gotresponse){
if(cookiefd>=0 && cistrncmp(line, "Set-Cookie:", 11) == 0)
fprint(cookiefd, "%s\n", line);
httpheader(url, line);
}else{
response=httpresponse(line);
gotresponse=1;
}
lp=line;
}
if(*bp=='\n') nnl++;
else{
nnl=0;
if(lp!=elp) *lp++=*bp;
}
}
bp++;
}
if(gotresponse) switch(response){
case 200: /* OK */
case 201: /* Created */
case 202: /* Accepted */
break;
case 204: /* No Content */
werrstr("URL has no content");
goto pfdErrReturn;
case 301: /* Moved Permanently */
case 302: /* Moved Temporarily */
if(url->redirname[0]){
url->type=FORWARD;
werrstr("URL forwarded");
goto pfdErrReturn;
}
break;
case 304: /* Not Modified */
if(cfd!=-1){
url->type=cache.type;
close(pfd[0]);
close(pfd[1]);
close(fd);
if(cookiefd>=0)
close(cookiefd);
return cfd;
}
werrstr("Not modified!");
goto pfdErrReturn;
case 400: /* Bad Request */
werrstr("Bad Request to server");
goto pfdErrReturn;
case 401: /* Unauthorized */
case 402: /* ??? */
if(*authstr == 0){
close(pfd[0]);
close(pfd[1]);
close(fd);
if(auth(url, authstr, sizeof(authstr)) == 0){
if(cfd!=-1)
close(cfd);
goto Authorize;
}
goto ErrReturn;
}
break;
case 403: /* Forbidden */
werrstr("Forbidden by server");
goto pfdErrReturn;
case 404: /* Not Found */
werrstr("Not found on server");
goto pfdErrReturn;
case 500: /* Internal server error */
werrstr("Server choked");
goto pfdErrReturn;
case 501: /* Not implemented */
werrstr("Server can't do it!");
goto pfdErrReturn;
case 502: /* Bad gateway */
werrstr("Bad gateway");
goto pfdErrReturn;
case 503: /* Service unavailable */
werrstr("Service unavailable");
goto pfdErrReturn;
}
}
if(cfd!=-1){
close(cfd);
cfd=cacheopen(url, &cache, 1);
}
if(cookiefd>=0){
close(cookiefd);
cookiefd=-1;
}
if(url->type==0)
url->type=suffix2type(url->fullname);
if(cache.fd!=-1) fprint(cache.fd, "type %d\n", url->type);
switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
case -1:
werrstr("Can't fork");
goto pfdErrReturn;
case 0:
notify(exitnow); /* otherwise write on closed pipe below may cause havoc */
close(pfd[0]);
if(bp!=ebp){
write(pfd[1], bp, ebp-bp);
if(cache.fd!=-1) write(cache.fd, bp, ebp-bp);
}
while((n=read(fd, buf, 1024))>0){
write(pfd[1], buf, n);
if(cache.fd!=-1) write(cache.fd, buf, n);
}
cacheclose(&cache, 1);
_exits(0);
default:
if(cache.fd!=-1) close(cache.fd);
close(pfd[1]);
close(fd);
return pfd[0];
}
}
/*
* Process a header line for this url
*/
void httpheader(Url *url, char *line){
char *name, *arg, *s, *arg2;
name=line;
while(*name==' ' || *name=='\t') name++;
for(s=name;*s!=':';s++) if(*s=='\0') return;
*s++='\0';
while(*s==' ' || *s=='\t') s++;
arg=s;
while(*s!=' ' && *s!='\t' && *s!=';' && *s!='\0') s++;
while(*s == ' ' || *s == '\t' || *s == ';')
*s++ = '\0';
arg2 = s;
if(cistrcmp(name, "Content-Type")==0){
url->type|=content2type(arg, url->reltext);
if(cistrncmp(arg2, "charset=", 8) == 0){
strncpy(url->charset, arg2+8, sizeof(url->charset));
} else {
url->charset[0] = '\0';
}
}
else if(cistrcmp(name, "Content-Encoding")==0)
url->type|=encoding2type(arg);
else if(cistrcmp(name, "WWW-authenticate")==0){
strncpy(url->authtype, arg, sizeof(url->authtype));
strncpy(url->autharg, arg2, sizeof(url->autharg));
}
else if(cistrcmp(name, "URI")==0){
if(*arg!='<') return;
++arg;
for(s=arg;*s!='>';s++) if(*s=='\0') return;
*s='\0';
strncpy(url->redirname, arg, sizeof(url->redirname));
}
else if(cistrcmp(name, "Location")==0)
strncpy(url->redirname, arg, sizeof(url->redirname));
}
int httpresponse(char *line){
while(*line!=' ' && *line!='\t' && *line!='\0') line++;
return atoi(line);
}

View File

@ -0,0 +1,189 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Button Button;
struct Button{
int btype; /* button type */
Icon *icon; /* what to write on the button */
int check; /* for check/radio buttons */
void (*hit)(Panel *, int, int); /* call back user code on check/radio hit */
void (*menuhit)(int, int); /* call back user code on menu item hit */
void (*pl_buttonhit)(Panel *, int); /* call back user code on button hit */
int index; /* arg to menuhit */
int buttons;
};
/*
* Button types
*/
#define BUTTON 1
#define CHECK 2
#define RADIO 3
void pl_drawbutton(Panel *p){
Rectangle r;
Button *bp;
bp=p->data;
r=pl_box(p->b, p->r, p->state);
switch(bp->btype){
case CHECK:
r=pl_check(p->b, r, bp->check);
break;
case RADIO:
r=pl_radio(p->b, r, bp->check);
break;
}
pl_drawicon(p->b, r, PLACECEN, p->flags, bp->icon);
}
int pl_hitbutton(Panel *p, Mouse *m){
int oldstate, hitme;
Panel *sib;
Button *bp;
bp=p->data;
oldstate=p->state;
if(m->buttons&OUT){
hitme=0;
p->state=UP;
}
else if(m->buttons&7){
hitme=0;
p->state=DOWN;
bp->buttons=m->buttons;
}
else{ /* mouse inside, but no buttons down */
hitme=p->state==DOWN;
p->state=UP;
}
if(hitme) switch(bp->btype){
case CHECK:
if(hitme) bp->check=!bp->check;
break;
case RADIO:
if(bp->check) bp->check=0;
else{
if(p->parent){
for(sib=p->parent->child;sib;sib=sib->next){
if(sib->hit==pl_hitbutton
&& ((Button *)sib->data)->btype==RADIO
&& ((Button *)sib->data)->check){
((Button *)sib->data)->check=0;
pldraw(sib, p->b);
}
}
}
bp->check=1;
}
break;
}
if(hitme || oldstate!=p->state) pldraw(p, p->b);
if(hitme && bp->hit){
bp->hit(p, bp->buttons, bp->check);
p->state=UP;
}
return 0;
}
void pl_typebutton(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizebutton(Panel *p, Point children){
Point s;
int ckw;
Button *bp;
USED(children); /* shouldn't have any children */
bp=p->data;
s=pl_iconsize(p->flags, bp->icon);
if(bp->btype!=BUTTON){
ckw=pl_ckwid();
if(s.y<ckw){
s.x+=ckw;
s.y=ckw;
}
else s.x+=s.y;
}
return pl_boxsize(s, p->state);
}
void pl_childspacebutton(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void pl_initbtype(Panel *v, int flags, Icon *icon, void (*hit)(Panel *, int, int), int btype){
Button *bp;
bp=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawbutton;
v->hit=pl_hitbutton;
v->type=pl_typebutton;
v->getsize=pl_getsizebutton;
v->childspace=pl_childspacebutton;
bp->btype=btype;
bp->check=0;
bp->hit=hit;
bp->icon=icon;
switch(btype){
case BUTTON: v->kind="button"; break;
case CHECK: v->kind="checkbutton"; break;
case RADIO: v->kind="radiobutton"; break;
}
}
void pl_buttonhit(Panel *p, int buttons, int check){
USED(check);
if(((Button *)p->data)->pl_buttonhit) ((Button *)p->data)->pl_buttonhit(p, buttons);
}
void plinitbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int)){
((Button *)p->data)->pl_buttonhit=hit;
pl_initbtype(p, flags, icon, pl_buttonhit, BUTTON);
}
void plinitcheckbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){
pl_initbtype(p, flags, icon, hit, CHECK);
}
void plinitradiobutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){
pl_initbtype(p, flags, icon, hit, RADIO);
}
Panel *plbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int)){
Panel *p;
p=pl_newpanel(parent, sizeof(Button));
plinitbutton(p, flags, icon, hit);
return p;
}
Panel *plcheckbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){
Panel *p;
p=pl_newpanel(parent, sizeof(Button));
plinitcheckbutton(p, flags, icon, hit);
return p;
}
Panel *plradiobutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){
Panel *p;
p=pl_newpanel(parent, sizeof(Button));
plinitradiobutton(p, flags, icon, hit);
return p;
}
void pl_hitmenu(Panel *p, int buttons){
void (*hit)(int, int);
hit=((Button *)p->data)->menuhit;
if(hit) hit(buttons, ((Button *)p->data)->index);
}
void plinitmenu(Panel *v, int flags, Icon **item, int cflags, void (*hit)(int, int)){
Panel *b;
int i;
v->flags=flags;
v->kind="menu";
if(v->child){
plfree(v->child);
v->child=0;
}
for(i=0;item[i];i++){
b=plbutton(v, cflags, item[i], pl_hitmenu);
((Button *)b->data)->menuhit=hit;
((Button *)b->data)->index=i;
}
}
Panel *plmenu(Panel *parent, int flags, Icon **item, int cflags, void (*hit)(int, int)){
Panel *v;
v=plgroup(parent, flags);
plinitmenu(v, flags, item, cflags, hit);
return v;
}
void plsetbutton(Panel *p, int val){
((Button *)p->data)->check=val;
}

View File

@ -0,0 +1,51 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Canvas Canvas;
struct Canvas{
void (*draw)(Panel *);
void (*hit)(Panel *, Mouse *);
};
void pl_drawcanvas(Panel *p){
Canvas *c;
c=p->data;
if(c->draw) c->draw(p);
}
int pl_hitcanvas(Panel *p, Mouse *m){
Canvas *c;
c=p->data;
if(c->hit) c->hit(p, m);
return 0;
}
void pl_typecanvas(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizecanvas(Panel *p, Point children){
USED(p, children);
return Pt(0,0);
}
void pl_childspacecanvas(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
void plinitcanvas(Panel *v, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){
Canvas *c;
v->flags=flags|LEAF;
v->draw=pl_drawcanvas;
v->hit=pl_hitcanvas;
v->type=pl_typecanvas;
v->getsize=pl_getsizecanvas;
v->childspace=pl_childspacecanvas;
v->kind="canvas";
c=v->data;
c->draw=draw;
c->hit=hit;
}
Panel *plcanvas(Panel *parent, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){
Panel *p;
p=pl_newpanel(parent, sizeof(Canvas));
plinitcanvas(p, flags, draw, hit);
return p;
}

View File

@ -0,0 +1,286 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
#define PWID 1 /* width of label border */
#define BWID 1 /* width of button relief */
#define FWID 2 /* width of frame relief */
#define SPACE 1 /* space inside relief of button or frame */
#define CKSIZE 3 /* size of check mark */
#define CKSPACE 2 /* space around check mark */
#define CKWID 1 /* width of frame around check mark */
#define CKINSET 1 /* space around check mark frame */
#define CKBORDER 2 /* space around X inside frame */
static int plldepth;
static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit;
int pl_drawinit(int ldepth){
plldepth=ldepth;
/*
pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
pl_light=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF);
pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x555555FF);
pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF);
pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
*/
pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
pl_light=allocimagemix(display, DPalebluegreen, DWhite);
pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF);
pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0) return 0;
return 1;
}
void pl_relief(Image *b, Image *ul, Image *lr, Rectangle r, int wid){
int x, y;
draw(b, Rect(r.min.x, r.max.y-wid, r.max.x, r.max.y), lr, 0, ZP); /* bottom */
draw(b, Rect(r.max.x-wid, r.min.y, r.max.x, r.max.y), lr, 0, ZP); /* right */
draw(b, Rect(r.min.x, r.min.y, r.min.x+wid, r.max.y), ul, 0, ZP); /* left */
draw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+wid), ul, 0, ZP); /* top */
for(x=0;x!=wid;x++) for(y=wid-1-x;y!=wid;y++){
draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.max.x-wid, y+r.min.y)), lr, 0, ZP);
draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.min.x, y+r.max.y-wid)), lr, 0, ZP);
}
}
Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){
if(plldepth==0) switch(style){
case UP:
pl_relief(b, pl_black, pl_black, r, BWID);
r=insetrect(r, BWID);
if(fill) draw(b, r, pl_white, 0, ZP);
else border(b, r, SPACE, pl_white, ZP);
break;
case DOWN:
case DOWN1:
case DOWN2:
case DOWN3:
pl_relief(b, pl_black, pl_black, r, BWID);
r=insetrect(r, BWID);
if(fill) draw(b, r, pl_black, 0, ZP);
border(b, r, SPACE, pl_black, ZP);
break;
case PASSIVE:
if(fill) draw(b, r, pl_white, 0, ZP);
r=insetrect(r, PWID);
if(!fill) border(b, r, SPACE, pl_white, ZP);
break;
case FRAME:
pl_relief(b, pl_white, pl_black, r, FWID);
r=insetrect(r, FWID);
pl_relief(b, pl_black, pl_white, r, FWID);
r=insetrect(r, FWID);
if(fill) draw(b, r, pl_white, 0, ZP);
else border(b, r, SPACE, pl_white, ZP);
break;
}
else switch(style){
case UP:
pl_relief(b, pl_white, pl_black, r, BWID);
r=insetrect(r, BWID);
if(fill) draw(b, r, pl_light, 0, ZP);
else border(b, r, SPACE, pl_white, ZP);
break;
case DOWN:
case DOWN1:
case DOWN2:
case DOWN3:
pl_relief(b, pl_black, pl_white, r, BWID);
r=insetrect(r, BWID);
if(fill) draw(b, r, pl_dark, 0, ZP);
else border(b, r, SPACE, pl_black, ZP);
break;
case PASSIVE:
if(fill) draw(b, r, pl_light, 0, ZP);
r=insetrect(r, PWID);
if(!fill) border(b, r, SPACE, pl_white, ZP);
break;
case FRAME:
pl_relief(b, pl_white, pl_black, r, FWID);
r=insetrect(r, FWID);
pl_relief(b, pl_black, pl_white, r, FWID);
r=insetrect(r, FWID);
if(fill) draw(b, r, pl_light, 0, ZP);
else border(b, r, SPACE, pl_white, ZP);
break;
}
return insetrect(r, SPACE);
}
Rectangle pl_outline(Image *b, Rectangle r, int style){
return pl_boxoutline(b, r, style, 0);
}
Rectangle pl_box(Image *b, Rectangle r, int style){
return pl_boxoutline(b, r, style, 1);
}
Point pl_boxsize(Point interior, int state){
switch(state){
case UP:
case DOWN:
case DOWN1:
case DOWN2:
case DOWN3:
return addpt(interior, Pt(2*(BWID+SPACE), 2*(BWID+SPACE)));
case PASSIVE:
return addpt(interior, Pt(2*(PWID+SPACE), 2*(PWID+SPACE)));
case FRAME:
return addpt(interior, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE));
}
}
void pl_interior(int state, Point *ul, Point *size){
switch(state){
case UP:
case DOWN:
case DOWN1:
case DOWN2:
case DOWN3:
*ul=addpt(*ul, Pt(BWID+SPACE, BWID+SPACE));
*size=subpt(*size, Pt(2*(BWID+SPACE), 2*(BWID+SPACE)));
break;
case PASSIVE:
*ul=addpt(*ul, Pt(PWID+SPACE, PWID+SPACE));
*size=subpt(*size, Pt(2*(PWID+SPACE), 2*(PWID+SPACE)));
break;
case FRAME:
*ul=addpt(*ul, Pt(2*FWID+SPACE, 2*FWID+SPACE));
*size=subpt(*size, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE));
}
}
void pl_drawicon(Image *b, Rectangle r, int stick, int flags, Icon *s){
Rectangle save;
Point ul, offs;
save=b->clipr;
replclipr(b, b->repl, r);
ul=r.min;
offs=subpt(subpt(r.max, r.min), pl_iconsize(flags, s));
switch(stick){
case PLACENW: break;
case PLACEN: ul.x+=offs.x/2; break;
case PLACENE: ul.x+=offs.x; break;
case PLACEW: ul.y+=offs.y/2; break;
case PLACECEN: ul.x+=offs.x/2; ul.y+=offs.y/2; break;
case PLACEE: ul.x+=offs.x; break;
case PLACESW: ul.y+=offs.y; break;
case PLACES: ul.x+=offs.x/2; ul.y+=offs.y; break;
case PLACESE: ul.x+=offs.x; ul.y+=offs.y; break;
}
if(flags&BITMAP) draw(b, Rpt(ul, addpt(ul, pl_iconsize(flags, s))), s, 0, ZP);
else string(b, ul, pl_black, ZP, font, s);
replclipr(b, b->repl, save);
}
/*
* Place a check mark at the left end of r. Return the unused space.
* Caller must guarantee that r.max.x-r.min.x>=r.max.y-r.min.y!
*/
Rectangle pl_radio(Image *b, Rectangle r, int val){
Rectangle remainder;
remainder=r;
r.max.x=r.min.x+r.max.y-r.min.y;
remainder.min.x=r.max.x;
r=insetrect(r, CKINSET);
if(plldepth==0)
pl_relief(b, pl_black, pl_black, r, CKWID);
else
pl_relief(b, pl_black, pl_white, r, CKWID);
r=insetrect(r, CKWID);
if(plldepth==0)
draw(b, r, pl_white, 0, ZP);
else
draw(b, r, pl_light, 0, ZP);
if(val) draw(b, insetrect(r, CKSPACE), pl_black, 0, ZP);
return remainder;
}
Rectangle pl_check(Image *b, Rectangle r, int val){
Rectangle remainder;
remainder=r;
r.max.x=r.min.x+r.max.y-r.min.y;
remainder.min.x=r.max.x;
r=insetrect(r, CKINSET);
if(plldepth==0)
pl_relief(b, pl_black, pl_black, r, CKWID);
else
pl_relief(b, pl_black, pl_white, r, CKWID);
r=insetrect(r, CKWID);
if(plldepth==0)
draw(b, r, pl_white, 0, ZP);
else
draw(b, r, pl_light, 0, ZP);
r=insetrect(r, CKBORDER);
if(val){
line(b, Pt(r.min.x, r.min.y+1), Pt(r.max.x-1, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP);
line(b, Pt(r.min.x, r.min.y ), Pt(r.max.x, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP);
line(b, Pt(r.min.x+1, r.min.y ), Pt(r.max.x, r.max.y-1), Endsquare, Endsquare, 0, pl_black, ZP);
line(b, Pt(r.min.x , r.max.y-2), Pt(r.max.x-1, r.min.y-1), Endsquare, Endsquare, 0, pl_black, ZP);
line(b, Pt(r.min.x, r.max.y-1), Pt(r.max.x, r.min.y-1), Endsquare, Endsquare, 0, pl_black, ZP);
line(b, Pt(r.min.x+1, r.max.y-1), Pt(r.max.x, r.min.y ), Endsquare, Endsquare, 0, pl_black, ZP);
}
return remainder;
}
int pl_ckwid(void){
return 2*(CKINSET+CKSPACE+CKWID)+CKSIZE;
}
void pl_sliderupd(Image *b, Rectangle r1, int dir, int lo, int hi){
Rectangle r2, r3;
r2=r1;
r3=r1;
if(lo<0) lo=0;
if(hi<=lo) hi=lo+1;
switch(dir){
case HORIZ:
r1.max.x=r1.min.x+lo;
r2.min.x=r1.max.x;
r2.max.x=r1.min.x+hi;
if(r2.max.x>r3.max.x) r2.max.x=r3.max.x;
r3.min.x=r2.max.x;
break;
case VERT:
r1.max.y=r1.min.y+lo;
r2.min.y=r1.max.y;
r2.max.y=r1.min.y+hi;
if(r2.max.y>r3.max.y) r2.max.y=r3.max.y;
r3.min.y=r2.max.y;
break;
}
draw(b, r1, pl_light, 0, ZP);
draw(b, r2, pl_dark, 0, ZP);
draw(b, r3, pl_light, 0, ZP);
}
void pl_draw1(Panel *p, Image *b);
void pl_drawall(Panel *p, Image *b){
if(p->flags&INVIS) return;
p->b=b;
p->draw(p);
for(p=p->child;p;p=p->next) pl_draw1(p, b);
}
void pl_draw1(Panel *p, Image *b){
if(b!=0)
pl_drawall(p, b);
}
void pldraw(Panel *p, Image *b){
pl_draw1(p, b);
flushimage(display, 1);
}
void pl_invis(Panel *p, int v){
for(;p;p=p->next){
if(v) p->flags|=INVIS; else p->flags&=~INVIS;
pl_invis(p->child, v);
}
}
Point pl_iconsize(int flags, Icon *p){
if(flags&BITMAP) return subpt(((Image *)p)->r.max, ((Image *)p)->r.min);
return stringsize(font, (char *)p);
}
void pl_highlight(Image *b, Rectangle r){
draw(b, r, pl_dark, pl_hilit, ZP);
}
void pl_clr(Image *b, Rectangle r){
draw(b, r, display->white, 0, ZP);
}
void pl_fill(Image *b, Rectangle r){
draw(b, r, plldepth==0? pl_white : pl_light, 0, ZP);
}
void pl_cpy(Image *b, Point dst, Rectangle src){
draw(b, Rpt(dst, addpt(dst, subpt(src.max, src.min))), b, 0, src.min);
}

View File

@ -0,0 +1,239 @@
/*
* Interface includes:
* void plescroll(Panel *p, int top);
* move the given character position onto the top line
* void plegetsel(Panel *p, int *sel0, int *sel1);
* read the selection back
* int plelen(Panel *p);
* read the length of the text back
* Rune *pleget(Panel *p);
* get a pointer to the text
* void plesel(Panel *p, int sel0, int sel1);
* set the selection -- adjusts hiliting
* void plepaste(Panel *p, Rune *text, int ntext);
* replace the selection with the given text
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Edit Edit;
struct Edit{
Point minsize;
int sel0, sel1;
Textwin *t;
void (*hit)(Panel *);
Rune *text;
int ntext;
};
void pl_drawedit(Panel *p){
Edit *ep;
Panel *sb;
ep=p->data;
if(ep->t==0){
ep->t=twnew(p->b, font, ep->text, ep->ntext);
if(ep->t==0){
fprint(2, "pl_drawedit: can't allocate\n");
exits("no mem");
}
}
ep->t->b=p->b;
twreshape(ep->t, p->r);
twhilite(ep->t, ep->sel0, ep->sel1, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, ep->t->top, ep->t->bot, ep->t->etext-ep->t->text);
}
/*
* Should do double-clicks:
* If ep->sel0==ep->sel1 on entry and the
* call to twselect returns the same selection, then
* expand selections (| marks possible selection points, ... is expanded selection)
* <|...|> <> must nest
* (|...|) () must nest
* [|...|] [] must nest
* {|...|} {} must nest
* '|...|' no ' in ...
* "|...|" no " in ...
* \n|...|\n either newline may be the corresponding end of text
* include the trailing newline in the selection
* ...|I... I and ... are characters satisfying pl_idchar(I)
* ...I|
*/
int pl_hitedit(Panel *p, Mouse *m){
Edit *ep;
if(m->buttons&7){
ep=p->data;
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twselect(ep->t, m);
ep->sel0=ep->t->sel0;
ep->sel1=ep->t->sel1;
if(ep->hit) ep->hit(p);
}
return 0;
}
void pl_scrolledit(Panel *p, int dir, int buttons, int num, int den){
Edit *ep;
Textwin *t;
Panel *sb;
int index, nline;
if(dir!=VERT) return;
ep=p->data;
t=ep->t;
t->b=p->b;
switch(buttons){
default:
return;
case 1: /* top line moves to mouse position */
nline=(t->r.max.y-t->r.min.y)/t->hgt*num/den;
index=t->top;
while(index!=0 && nline!=0)
if(t->text[--index]=='\n') --nline;
break;
case 2: /* absolute */
index=(t->etext-t->text)*num/den;
break;
case 4: /* mouse points at new top line */
index=twpt2rune(t,
Pt(t->r.min.x, t->r.min.y+(t->r.max.y-t->r.min.y)*num/den));
break;
}
while(index!=0 && t->text[index-1]!='\n') --index;
if(index!=t->top){
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twscroll(t, index);
p->scr.pos.y=t->top;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
}
}
void pl_typeedit(Panel *p, Rune c){
Edit *ep;
Textwin *t;
int bot, scrolled;
Panel *sb;
ep=p->data;
t=ep->t;
t->b=p->b;
twhilite(t, ep->sel0, ep->sel1, 0);
switch(c){
case '\b':
if(ep->sel0!=0) --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
case '\025': /* ctrl-u */
while(ep->sel0!=0 && t->text[ep->sel0-1]!='\n') --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
case '\027': /* ctrl-w */
while(ep->sel0!=0 && !pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
while(ep->sel0!=0 && pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
default:
twreplace(t, ep->sel0, ep->sel1, &c, 1);
++ep->sel0;
break;
}
ep->sel1=ep->sel0;
/*
* Scroll up until ep->sel0 is above t->bot.
*/
scrolled=0;
do{
bot=t->bot;
if(ep->sel0<=bot) break;
twscroll(t, twpt2rune(t, Pt(t->r.min.x, t->r.min.y+font->height)));
scrolled++;
}while(bot!=t->bot);
if(scrolled){
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
}
twhilite(t, ep->sel0, ep->sel1, 1);
}
Point pl_getsizeedit(Panel *p, Point children){
USED(children);
return pl_boxsize(((Edit *)p->data)->minsize, p->state);
}
void pl_childspaceedit(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void plinitedit(Panel *v, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
Edit *ep;
ep=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawedit;
v->hit=pl_hitedit;
v->type=pl_typeedit;
v->getsize=pl_getsizeedit;
v->childspace=pl_childspaceedit;
v->kind="edit";
ep->hit=hit;
ep->minsize=minsize;
ep->text=text;
ep->ntext=ntext;
if(ep->t!=0) twfree(ep->t);
ep->t=0;
ep->sel0=-1;
ep->sel1=-1;
v->scroll=pl_scrolledit;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(ntext,0);
}
Panel *pledit(Panel *parent, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
Panel *v;
v=pl_newpanel(parent, sizeof(Edit));
((Edit *)v->data)->t=0;
plinitedit(v, flags, minsize, text, ntext, hit);
return v;
}
void plescroll(Panel *p, int top){
twscroll(((Edit *)p->data)->t, top);
}
void plegetsel(Panel *p, int *sel0, int *sel1){
Edit *ep;
ep=p->data;
*sel0=ep->sel0;
*sel1=ep->sel1;
}
int plelen(Panel *p){
Textwin *t;
t=((Edit *)p->data)->t;
return t->etext-t->text;
}
Rune *pleget(Panel *p){
return ((Edit *)p->data)->t->text;
}
void plesel(Panel *p, int sel0, int sel1){
Edit *ep;
ep=p->data;
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
ep->sel0=sel0;
ep->sel1=sel1;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
}
void plepaste(Panel *p, Rune *text, int ntext){
Edit *ep;
ep=p->data;
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twreplace(ep->t, ep->sel0, ep->sel1, text, ntext);
ep->sel1=ep->sel0+ntext;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
p->scr.size.y=ep->t->etext-ep->t->text;
p->scr.pos.y=ep->t->top;
}
void plemove(Panel *p, Point d){
Edit *ep;
ep=p->data;
if(ep->t && !eqpt(d, Pt(0,0))) twmove(ep->t, d);
}

View File

@ -0,0 +1,130 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Entry Entry;
struct Entry{
char *entry;
char *entp;
char *eent;
void (*hit)(Panel *, char *);
Point minsize;
};
#define SLACK 7 /* enough for one extra rune and ◀ and a nul */
void pl_drawentry(Panel *p){
Rectangle r;
Entry *ep;
ep=p->data;
r=pl_box(p->b, p->r, p->state);
if(stringwidth(font, ep->entry)<=r.max.x-r.min.x)
pl_drawicon(p->b, r, PLACEW, 0, ep->entry);
else
pl_drawicon(p->b, r, PLACEE, 0, ep->entry);
}
int pl_hitentry(Panel *p, Mouse *m){
int oldstate;
oldstate=p->state;
if(m->buttons&OUT)
p->state=UP;
else if(m->buttons&7)
p->state=DOWN;
else{ /* mouse inside, but no buttons down */
if(p->state==DOWN) plgrabkb(p);
p->state=UP;
}
if(p->state!=oldstate) pldraw(p, p->b);
return 0;
}
void pl_typeentry(Panel *p, Rune c){
int n;
Entry *ep;
ep=p->data;
switch(c){
case '\n':
case '\r':
*ep->entp='\0';
if(ep->hit) ep->hit(p, ep->entry);
return;
case 025: /* ctrl-u */
ep->entp=ep->entry;
*ep->entp='\0';
break;
case '\b':
while(ep->entp!=ep->entry && !pl_rune1st(ep->entp[-1])) *--ep->entp='\0';
if(ep->entp!=ep->entry) *--ep->entp='\0';
break;
case 027: /* ctrl-w */
while(ep->entp!=ep->entry && !pl_idchar(ep->entp[-1]))
--ep->entp;
while(ep->entp!=ep->entry && pl_idchar(ep->entp[-1]))
--ep->entp;
*ep->entp='\0';
break;
default:
ep->entp+=runetochar(ep->entp, &c);
if(ep->entp>ep->eent){
n=ep->entp-ep->entry;
ep->entry=realloc(ep->entry, n+100+SLACK);
if(ep->entry==0){
fprint(2, "can't realloc in pl_typeentry\n");
exits("no mem");
}
ep->entp=ep->entry+n;
ep->eent=ep->entp+100;
}
break;
}
memset(ep->entp, 0, SLACK);
/* strcpy(ep->entp, "◀"); */
pldraw(p, p->b);
}
Point pl_getsizeentry(Panel *p, Point children){
USED(children);
return pl_boxsize(((Entry *)p->data)->minsize, p->state);
}
void pl_childspaceentry(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
void pl_freeentry(Panel *p){
free(((Entry *)p->data)->entry);
}
void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, char *)){
int elen;
Entry *ep;
ep=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawentry;
v->hit=pl_hitentry;
v->type=pl_typeentry;
v->getsize=pl_getsizeentry;
v->childspace=pl_childspaceentry;
ep->minsize=Pt(wid, font->height);
v->free=pl_freeentry;
elen=100;
if(str) elen+=strlen(str);
ep->entry=pl_emalloc(elen+SLACK);
ep->eent=ep->entry+elen;
if(str)
strcpy(ep->entry, str);
else ep->entry[0]='\0';
ep->entp=ep->entry+strlen(ep->entry);
/* strcat(ep->entry, "◀"); */
ep->hit=hit;
v->kind="entry";
}
Panel *plentry(Panel *parent, int flags, int wid, char *str, void (*hit)(Panel *, char *)){
Panel *v;
v=pl_newpanel(parent, sizeof(Entry));
plinitentry(v, flags, wid, str, hit);
return v;
}
char *plentryval(Panel *p){
Entry *ep;
ep=p->data;
*ep->entp='\0';
return ep->entry;
}

View File

@ -0,0 +1,50 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
Panel *pl_kbfocus;
void plgrabkb(Panel *g){
pl_kbfocus=g;
}
void plkeyboard(Rune c){
if(pl_kbfocus){
pl_kbfocus->type(pl_kbfocus, c);
flushimage(display, 1);
}
}
/*
* Return the most leafward, highest priority panel containing p
*/
Panel *pl_ptinpanel(Point p, Panel *g){
Panel *v;
for(;g;g=g->next) if(ptinrect(p, g->r)){
v=pl_ptinpanel(p, g->child);
if(v && v->pri(v, p)>=g->pri(g, p)) return v;
return g;
}
return 0;
}
void plmouse(Panel *g, Mouse mouse){
Panel *hit, *last;
if(g->flags&REMOUSE)
hit=g->lastmouse;
else{
hit=pl_ptinpanel(mouse.xy, g);
last=g->lastmouse;
if(last && last!=hit){
mouse.buttons|=OUT;
last->hit(last, &mouse);
mouse.buttons&=~OUT;
}
}
if(hit){
if(hit->hit(hit, &mouse))
g->flags|=REMOUSE;
else
g->flags&=~REMOUSE;
g->lastmouse=hit;
}
flushimage(display, 1);
}

View File

@ -0,0 +1,39 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
void pl_drawframe(Panel *p){
pl_box(p->b, p->r, FRAME);
}
int pl_hitframe(Panel *p, Mouse *m){
USED(p, m);
return 0;
}
void pl_typeframe(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizeframe(Panel *p, Point children){
USED(p);
return pl_boxsize(children, FRAME);
}
void pl_childspaceframe(Panel *p, Point *ul, Point *size){
USED(p);
pl_interior(FRAME, ul, size);
}
void plinitframe(Panel *v, int flags){
v->flags=flags;
v->draw=pl_drawframe;
v->hit=pl_hitframe;
v->type=pl_typeframe;
v->getsize=pl_getsizeframe;
v->childspace=pl_childspaceframe;
v->kind="frame";
}
Panel *plframe(Panel *parent, int flags){
Panel *p;
p=pl_newpanel(parent, 0);
plinitframe(p, flags);
return p;
}

View File

@ -0,0 +1,38 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
void pl_drawgroup(Panel *p){
USED(p);
}
int pl_hitgroup(Panel *p, Mouse *m){
USED(p, m);
return 0;
}
void pl_typegroup(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizegroup(Panel *p, Point children){
USED(p);
return children;
}
void pl_childspacegroup(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
void plinitgroup(Panel *v, int flags){
v->flags=flags;
v->draw=pl_drawgroup;
v->hit=pl_hitgroup;
v->type=pl_typegroup;
v->getsize=pl_getsizegroup;
v->childspace=pl_childspacegroup;
v->kind="group";
}
Panel *plgroup(Panel *parent, int flags){
Panel *p;
p=pl_newpanel(parent, 0);
plinitgroup(p, flags);
return p;
}

View File

@ -0,0 +1,13 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
/*
* Just a wrapper for all the initialization routines
*/
int plinit(int ldepth){
if(!pl_drawinit(ldepth)) return 0;
return 1;
}

View File

@ -0,0 +1,50 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Label Label;
struct Label{
int placement;
Icon *icon;
};
void pl_drawlabel(Panel *p){
Label *l;
l=p->data;
pl_drawicon(p->b, pl_box(p->b, p->r, PASSIVE), l->placement, p->flags, l->icon);
}
int pl_hitlabel(Panel *p, Mouse *m){
USED(p, m);
return 0;
}
void pl_typelabel(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizelabel(Panel *p, Point children){
USED(children); /* shouldn't have any children */
return pl_boxsize(pl_iconsize(p->flags, ((Label *)p->data)->icon), PASSIVE);
}
void pl_childspacelabel(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void plinitlabel(Panel *v, int flags, Icon *icon){
v->flags=flags|LEAF;
((Label *)(v->data))->icon=icon;
v->draw=pl_drawlabel;
v->hit=pl_hitlabel;
v->type=pl_typelabel;
v->getsize=pl_getsizelabel;
v->childspace=pl_childspacelabel;
v->kind="label";
}
Panel *pllabel(Panel *parent, int flags, Icon *icon){
Panel *p;
p=pl_newpanel(parent, sizeof(Label));
plinitlabel(p, flags, icon);
plplacelabel(p, PLACECEN);
return p;
}
void plplacelabel(Panel *p, int placement){
((Label *)(p->data))->placement=placement;
}

View File

@ -0,0 +1,190 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct List List;
struct List{
void (*hit)(Panel *, int, int); /* call user back on hit */
char *(*gen)(Panel *, int); /* return text given index or 0 if out of range */
int lo; /* indices of first, last items displayed */
int sel; /* index of hilited item */
int len; /* # of items in list */
Rectangle listr;
Point minsize;
int buttons;
};
#define MAXHGT 12
void pl_listsel(Panel *p, int sel, int on){
List *lp;
int hi;
Rectangle r;
lp=p->data;
hi=lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height;
if(lp->lo<=sel && sel<hi && sel<lp->len){
r=lp->listr;
r.min.y+=(sel-lp->lo)*font->height;
r.max.y=r.min.y+font->height;
if(on)
pl_highlight(p->b, r);
else{
pl_fill(p->b, r);
pl_drawicon(p->b, r, PLACEW, 0, lp->gen(p, sel));
}
}
}
void pl_liststrings(Panel *p, int lo, int hi, Rectangle r){
Panel *sb;
List *lp;
char *s;
int i;
lp=p->data;
for(i=lo;i!=hi && (s=lp->gen(p, i));i++){
r.max.y=r.min.y+font->height;
pl_drawicon(p->b, r, PLACEW, 0, s);
r.min.y+=font->height;
}
if(lo<=lp->sel && lp->sel<hi) pl_listsel(p, lp->sel, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, lp->lo,
lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, lp->len);
}
void pl_drawlist(Panel *p){
List *lp;
lp=p->data;
lp->listr=pl_box(p->b, p->r, UP);
pl_liststrings(p, lp->lo, lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height,
lp->listr);
}
int pl_hitlist(Panel *p, Mouse *m){
int oldsel, hitme;
Point ul, size;
List *lp;
lp=p->data;
hitme=0;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
oldsel=lp->sel;
if(m->buttons&OUT){
p->state=UP;
if(m->buttons&~OUT) lp->sel=-1;
}
else if(p->state==DOWN || m->buttons&7){
lp->sel=(m->xy.y-ul.y)/font->height+lp->lo;
if(m->buttons&7){
lp->buttons=m->buttons;
p->state=DOWN;
}
else{
hitme=1;
p->state=UP;
}
}
if(oldsel!=lp->sel){
pl_listsel(p, oldsel, 0);
pl_listsel(p, lp->sel, 1);
}
if(hitme && 0<=lp->sel && lp->sel<lp->len && lp->hit)
lp->hit(p, lp->buttons, lp->sel);
return 0;
}
void pl_scrolllist(Panel *p, int dir, int buttons, int val, int len){
Point ul, size;
int nlist, oldlo, hi, nline, y;
List *lp;
Rectangle r;
lp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
nlist=size.y/font->height;
oldlo=lp->lo;
if(dir==VERT) switch(buttons){
case 1: lp->lo-=nlist*val/len; break;
case 2: lp->lo=lp->len*val/len; break;
case 4: lp->lo+=nlist*val/len; break;
}
if(lp->lo<0) lp->lo=0;
if(lp->lo>=lp->len) lp->lo=lp->len-1;
if(lp->lo==oldlo) return;
p->scr.pos.y=lp->lo;
r=lp->listr;
nline=(r.max.y-r.min.y)/font->height;
hi=lp->lo+nline;
if(hi<=oldlo || lp->lo>=oldlo+nline){
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, lp->lo, hi, r);
}
else if(lp->lo<oldlo){
y=r.min.y+(oldlo-lp->lo)*font->height;
pl_cpy(p->b, Pt(r.min.x, y),
Rect(r.min.x, r.min.y, r.max.x, r.min.y+(hi-oldlo)*font->height));
r.max.y=y;
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, lp->lo, oldlo, r);
}
else{
pl_cpy(p->b, r.min, Rect(r.min.x, r.min.y+(lp->lo-oldlo)*font->height,
r.max.x, r.max.y));
r.min.y=r.min.y+(oldlo+nline-lp->lo)*font->height;
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, oldlo+nline, hi, r);
}
}
void pl_typelist(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizelist(Panel *p, Point children){
USED(children);
return pl_boxsize(((List *)p->data)->minsize, p->state);
}
void pl_childspacelist(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void plinitlist(Panel *v, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
List *lp;
int wid, max;
char *str;
lp=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawlist;
v->hit=pl_hitlist;
v->type=pl_typelist;
v->getsize=pl_getsizelist;
v->childspace=pl_childspacelist;
lp->gen=gen;
lp->hit=hit;
max=0;
for(lp->len=0;str=gen(v, lp->len);lp->len++){
wid=stringwidth(font, str);
if(wid>max) max=wid;
}
if(flags&(FILLX|EXPAND)){
for(lp->len=0;gen(v, lp->len);lp->len++);
lp->minsize=Pt(0, nlist*font->height);
}
else{
max=0;
for(lp->len=0;str=gen(v, lp->len);lp->len++){
wid=stringwidth(font, str);
if(wid>max) max=wid;
}
lp->minsize=Pt(max, nlist*font->height);
}
lp->sel=-1;
lp->lo=0;
v->scroll=pl_scrolllist;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(0,lp->len);
v->kind="list";
}
Panel *pllist(Panel *parent, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
Panel *v;
v=pl_newpanel(parent, sizeof(List));
plinitlist(v, flags, gen, nlist, hit);
return v;
}

View File

@ -0,0 +1,108 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
void *pl_emalloc(int n){
void *v;
v=malloc(n);
if(v==0){
fprint(2, "Can't malloc!\n");
exits("no mem");
}
return v;
}
void pl_unexpected(Panel *g, char *rou){
fprint(2, "%s called unexpectedly (%s %lux)\n", rou, g->kind, (ulong)g);
abort();
}
void pl_drawerror(Panel *g){
pl_unexpected(g, "draw");
}
int pl_hiterror(Panel *g, Mouse *m){
USED(m);
pl_unexpected(g, "hit");
return 0;
}
void pl_typeerror(Panel *g, Rune c){
USED(c);
pl_unexpected(g, "type");
}
Point pl_getsizeerror(Panel *g, Point childsize){
pl_unexpected(g, "getsize");
return childsize;
}
void pl_childspaceerror(Panel *g, Point *ul, Point *size){
USED(ul, size);
pl_unexpected(g, "childspace");
}
void pl_scrollerror(Panel *g, int dir, int button, int num, int den){
USED(dir, button, num, den);
pl_unexpected(g, "scroll");
}
void pl_setscrollbarerror(Panel *g, int top, int bot, int den){
USED(top, bot, den);
pl_unexpected(g, "setscrollbar");
}
int pl_prinormal(Panel *, Point){
return PRI_NORMAL;
}
Panel *pl_newpanel(Panel *parent, int ndata){
Panel *v;
if(parent && parent->flags&LEAF){
fprint(2, "newpanel: can't create child of %s %lux\n", parent->kind, (ulong)parent);
exits("bad newpanel");
}
v=pl_emalloc(sizeof(Panel));
v->r=Rect(0,0,0,0);
v->flags=0;
v->ipad=Pt(0,0);
v->pad=Pt(0,0);
v->size=Pt(0,0);
v->sizereq=Pt(0,0);
v->lastmouse=0;
v->next=0;
v->child=0;
v->echild=0;
v->b=0;
v->pri=pl_prinormal;
v->scrollee=0;
v->xscroller=0;
v->yscroller=0;
v->parent=parent;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(0,0);
if(parent){
if(parent->child==0)
parent->child=v;
else
parent->echild->next=v;
parent->echild=v;
}
v->draw=pl_drawerror;
v->hit=pl_hiterror;
v->type=pl_typeerror;
v->getsize=pl_getsizeerror;
v->childspace=pl_childspaceerror;
v->scroll=pl_scrollerror;
v->setscrollbar=pl_setscrollbarerror;
v->free=0;
if(ndata)
v->data=pl_emalloc(ndata);
else
v->data=0;
return v;
}
void plfree(Panel *p){
Panel *cp, *ncp;
if(p==0)
return;
for(cp=p->child;cp;cp=ncp){
ncp=cp->next;
plfree(cp);
}
if(p->free) p->free(p);
if(p->data) free(p->data);
free(p);
}

View File

@ -0,0 +1,104 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Message Message;
struct Message{
char *text;
Point minsize;
};
void pl_textmsg(Image *b, Rectangle r, Font *f, char *s){
char *start, *end; /* of line */
Point where;
int lwid, c, wid;
where=r.min;
wid=r.max.x-r.min.x;
do{
start=s;
lwid=0;
end=s;
do{
for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s);
if(lwid>wid) break;
end=s;
for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s);
}while(*s!='\0');
if(end==start) /* can't even fit one word on line! */
end=s;
c=*end;
*end='\0';
string(b, where, display->black, ZP, f, start);
*end=c;
where.y+=font->height;
s=end;
while(*s==' ') s=pl_nextrune(s);
}while(*s!='\0');
}
Point pl_foldsize(Font *f, char *s, int wid){
char *start, *end; /* of line */
Point size;
int lwid, ewid;
size=Pt(0,0);
do{
start=s;
lwid=0;
end=s;
ewid=lwid;
do{
for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s);
if(lwid>wid) break;
end=s;
ewid=lwid;
for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s);
}while(*s!='\0');
if(end==start){ /* can't even fit one word on line! */
ewid=lwid;
end=s;
}
if(ewid>size.x) size.x=ewid;
size.y+=font->height;
s=end;
while(*s==' ') s=pl_nextrune(s);
}while(*s!='\0');
return size;
}
void pl_drawmessage(Panel *p){
pl_textmsg(p->b, pl_box(p->b, p->r, PASSIVE), font, ((Message *)p->data)->text);
}
int pl_hitmessage(Panel *g, Mouse *m){
USED(g, m);
return 0;
}
void pl_typemessage(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizemessage(Panel *p, Point children){
Message *mp;
USED(children);
mp=p->data;
return pl_boxsize(pl_foldsize(font, mp->text, mp->minsize.x), PASSIVE);
}
void pl_childspacemessage(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
void plinitmessage(Panel *v, int flags, int wid, char *msg){
Message *mp;
mp=v->data;
v->flags=flags|LEAF;
v->draw=pl_drawmessage;
v->hit=pl_hitmessage;
v->type=pl_typemessage;
v->getsize=pl_getsizemessage;
v->childspace=pl_childspacemessage;
mp->text=msg;
mp->minsize=Pt(wid, font->height);
v->kind="message";
}
Panel *plmessage(Panel *parent, int flags, int wid, char *msg){
Panel *v;
v=pl_newpanel(parent, sizeof(Message));
plinitmessage(v, flags, wid, msg);
return v;
}

View File

@ -0,0 +1,33 @@
</$objtype/mkfile
LIB=libpanel.$O.a
OFILES=\
button.$O\
canvas.$O\
draw.$O\
edit.$O\
entry.$O\
event.$O\
frame.$O\
group.$O\
# idollist.$O\
init.$O\
label.$O\
list.$O\
mem.$O\
message.$O\
pack.$O\
popup.$O\
print.$O\
pulldown.$O\
rtext.$O\
scroll.$O\
scrollbar.$O\
slider.$O\
textview.$O\
textwin.$O\
utf.$O
HFILES=panel.h pldefs.h rtext.h
</sys/src/cmd/mklib

View File

@ -0,0 +1,167 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
int pl_max(int a, int b){
return a>b?a:b;
}
Point pl_sizesibs(Panel *p){
Point s;
if(p==0) return Pt(0,0);
s=pl_sizesibs(p->next);
switch(p->flags&PACK){
case PACKN:
case PACKS:
s.x=pl_max(s.x, p->sizereq.x);
s.y+=p->sizereq.y;
break;
case PACKE:
case PACKW:
s.x+=p->sizereq.x;
s.y=pl_max(s.y, p->sizereq.y);
break;
}
return s;
}
/*
* Compute the requested size of p and its descendants.
*/
void pl_sizereq(Panel *p){
Panel *cp;
Point maxsize;
maxsize=Pt(0,0);
for(cp=p->child;cp;cp=cp->next){
pl_sizereq(cp);
if(cp->sizereq.x>maxsize.x) maxsize.x=cp->sizereq.x;
if(cp->sizereq.y>maxsize.y) maxsize.y=cp->sizereq.y;
}
for(cp=p->child;cp;cp=cp->next){
if(cp->flags&MAXX) cp->sizereq.x=maxsize.x;
if(cp->flags&MAXY) cp->sizereq.y=maxsize.y;
}
p->childreq=pl_sizesibs(p->child);
p->sizereq=addpt(addpt(p->getsize(p, p->childreq), p->ipad), p->pad);
if(p->flags&FIXEDX) p->sizereq.x=p->fixedsize.x;
if(p->flags&FIXEDY) p->sizereq.y=p->fixedsize.y;
}
Point pl_getshare(Panel *p){
Point share;
if(p==0) return Pt(0,0);
share=pl_getshare(p->next);
if(p->flags&EXPAND) switch(p->flags&PACK){
case PACKN:
case PACKS:
if(share.x==0) share.x=1;
share.y++;
break;
case PACKE:
case PACKW:
share.x++;
if(share.y==0) share.y=1;
break;
}
return share;
}
/*
* Set the sizes and rectangles of p and its descendants, given their requested sizes.
* Returns 1 if everything fit, 0 otherwise.
* For now we punt in the case that the children don't all fit.
* Possibly we should shrink all the children's sizereqs to fit,
* by the same means used to do EXPAND, except clamping at some minimum size,
* but that smacks of AI.
*/
Panel *pl_toosmall;
int pl_setrect(Panel *p, Point ul, Point avail){
Point space, newul, newspace, slack, share;
int l;
Panel *c;
p->size=subpt(p->sizereq, p->pad);
ul=addpt(ul, divpt(p->pad, 2));
avail=subpt(avail, p->pad);
if(p->size.x>avail.x || p->size.y>avail.y){
pl_toosmall=p;
return 0; /* not enough space! */
}
if(p->flags&(FILLX|EXPAND)) p->size.x=avail.x;
if(p->flags&(FILLY|EXPAND)) p->size.y=avail.y;
switch(p->flags&PLACE){
case PLACECEN: ul.x+=(avail.x-p->size.x)/2; ul.y+=(avail.y-p->size.y)/2; break;
case PLACES: ul.x+=(avail.x-p->size.x)/2; ul.y+= avail.y-p->size.y ; break;
case PLACEE: ul.x+= avail.x-p->size.x ; ul.y+=(avail.y-p->size.y)/2; break;
case PLACEW: ul.y+=(avail.y-p->size.y)/2; break;
case PLACEN: ul.x+=(avail.x-p->size.x)/2; break;
case PLACENE: ul.x+= avail.x-p->size.x ; break;
case PLACENW: break;
case PLACESE: ul.x+= avail.x-p->size.x ; ul.y+= avail.y-p->size.y ; break;
case PLACESW: ul.y+= avail.y-p->size.y ; break;
}
p->r=Rpt(ul, addpt(ul, p->size));
space=p->size;
p->childspace(p, &ul, &space);
slack=subpt(space, p->childreq);
share=pl_getshare(p->child);
for(c=p->child;c;c=c->next){
if(c->flags&EXPAND){
switch(c->flags&PACK){
case PACKN:
case PACKS:
c->sizereq.x+=slack.x;
l=slack.y/share.y;
c->sizereq.y+=l;
slack.y-=l;
--share.y;
break;
case PACKE:
case PACKW:
l=slack.x/share.x;
c->sizereq.x+=l;
slack.x-=l;
--share.x;
c->sizereq.y+=slack.y;
break;
}
}
switch(c->flags&PACK){
case PACKN:
newul=Pt(ul.x, ul.y+c->sizereq.y);
newspace=Pt(space.x, space.y-c->sizereq.y);
if(!pl_setrect(c, ul, Pt(space.x, c->sizereq.y))) return 0;
break;
case PACKW:
newul=Pt(ul.x+c->sizereq.x, ul.y);
newspace=Pt(space.x-c->sizereq.x, space.y);
if(!pl_setrect(c, ul, Pt(c->sizereq.x, space.y))) return 0;
break;
case PACKS:
newul=ul;
newspace=Pt(space.x, space.y-c->sizereq.y);
if(!pl_setrect(c, Pt(ul.x, ul.y+space.y-c->sizereq.y),
Pt(space.x, c->sizereq.y))) return 0;
break;
case PACKE:
newul=ul;
newspace=Pt(space.x-c->sizereq.x, space.y);
if(!pl_setrect(c, Pt(ul.x+space.x-c->sizereq.x, ul.y),
Pt(c->sizereq.x, space.y))) return 0;
break;
}
ul=newul;
space=newspace;
}
return 1;
}
int plpack(Panel *p, Rectangle where){
pl_sizereq(p);
return pl_setrect(p, where.min, subpt(where.max, where.min));
}
/*
* move an already-packed panel so that p->r=raddp(p->r, d)
*/
void plmove(Panel *p, Point d){
if(strcmp(p->kind, "edit") == 0) /* sorry */
plemove(p, d);
p->r=rectaddpt(p->r, d);
for(p=p->child;p;p=p->next) plmove(p, d);
}

View File

@ -0,0 +1,174 @@
//#pragma src "/sys/src/libpanel"
//#pragma lib "libpanel.a"
typedef struct Scroll Scroll;
typedef struct Panel Panel; /* a Graphical User Interface element */
typedef struct Rtext Rtext; /* formattable text */
typedef void Icon; /* Always used as Icon * -- Image or char */
typedef struct Idol Idol; /* A picture/text combo */
struct Scroll{
Point pos, size;
};
struct Rtext{
int hot; /* responds to hits? */
void *user; /* user data */
int space; /* how much space before, if no break */
int indent; /* how much space before, after a break */
Image *b; /* what to display, if nonzero */
Panel *p; /* what to display, if nonzero and b==0 */
Font *font; /* font in which to draw text */
char *text; /* what to display, if b==0 and p==0 */
Rtext *next; /* next piece */
/* private below */
Rtext *nextline; /* links line to line */
Rtext *last; /* last, for append */
Rectangle r; /* where to draw, if origin were Pt(0,0) */
int topy; /* y coord of top of line */
int wid; /* not including space */
};
struct Panel{
Point ipad, pad; /* extra space inside and outside */
Point fixedsize; /* size of Panel, if FIXED */
int user; /* available for user */
void *userp; /* available for user */
Rectangle r; /* where the Panel goes */
/* private below */
Panel *next; /* It's a list! */
Panel *child, *echild, *parent; /* No, it's a tree! */
Image *b; /* where we're drawn */
int flags; /* position flags, see below */
char *kind; /* what kind of panel? */
int state; /* for hitting & drawing purposes */
Point size; /* space for this Panel */
Point sizereq; /* size requested by this Panel */
Point childreq; /* total size needed by children */
Panel *lastmouse; /* who got the last mouse event? */
Panel *scrollee; /* pointer to scrolled window */
Panel *xscroller, *yscroller; /* pointers to scroll bars */
Scroll scr; /* scroll data */
void *data; /* kind-specific data */
void (*draw)(Panel *); /* draw panel and children */
int (*pri)(Panel *, Point); /* priority for hitting */
int (*hit)(Panel *, Mouse *); /* process mouse event */
void (*type)(Panel *, Rune); /* process keyboard event */
Point (*getsize)(Panel *, Point); /* return size, given child size */
void (*childspace)(Panel *, Point *, Point *); /* child ul & size given our size */
void (*scroll)(Panel *, int, int, int, int); /* scroll bar to scrollee */
void (*setscrollbar)(Panel *, int, int, int); /* scrollee to scroll bar */
void (*free)(Panel *); /* free fields of data when done */
};
/*
* Panel flags -- there are more private flags in panelprivate.h
* that need to be kept synchronized with these!
*/
#define PACK 0x0007 /* which side of the parent is the Panel attached to? */
#define PACKN 0x0000
#define PACKE 0x0001
#define PACKS 0x0002
#define PACKW 0x0003
#define PACKCEN 0x0004 /* only used by pulldown */
#define FILLX 0x0008 /* grow horizontally to fill the available space */
#define FILLY 0x0010 /* grow vertically to fill the available space */
#define PLACE 0x01e0 /* which side of its space should the Panel adhere to? */
#define PLACECEN 0x0000
#define PLACES 0x0020
#define PLACEE 0x0040
#define PLACEW 0x0060
#define PLACEN 0x0080
#define PLACENE 0x00a0
#define PLACENW 0x00c0
#define PLACESE 0x00e0
#define PLACESW 0x0100
#define EXPAND 0x0200 /* use up all extra space in the parent */
#define FIXED 0x0c00 /* don't pass children's size requests through to parent */
#define FIXEDX 0x0400
#define FIXEDY 0x0800
#define MAXX 0x1000 /* make x size as big as biggest sibling's */
#define MAXY 0x2000 /* make y size as big as biggest sibling's */
#define BITMAP 0x4000 /* text argument is a bitmap, not a string */
/*
* An extra bit in Mouse.buttons
*/
#define OUT 8 /* Mouse.buttons bit, set when mouse leaves Panel */
/*
* Priorities
*/
#define PRI_NORMAL 0 /* ordinary panels */
#define PRI_POPUP 1 /* popup menus */
#define PRI_SCROLLBAR 2 /* scroll bars */
int plinit(int); /* initialization */
int plpack(Panel *, Rectangle); /* figure out where to put the Panel & children */
void plmove(Panel *, Point); /* move an already-packed panel to a new location */
void pldraw(Panel *, Image *); /* display the panel on the bitmap */
void plfree(Panel *); /* give back space */
void plgrabkb(Panel *); /* this Panel should receive keyboard events */
void plkeyboard(Rune); /* send a keyboard event to the appropriate Panel */
void plmouse(Panel *, Mouse); /* send a Mouse event to a Panel tree */
void plscroll(Panel *, Panel *, Panel *); /* link up scroll bars */
char *plentryval(Panel *); /* entry delivers its value */
void plsetbutton(Panel *, int); /* set or clear the mark on a button */
void plsetslider(Panel *, int, int); /* set the value of a slider */
Rune *pleget(Panel *); /* get the text from an edit window */
int plelen(Panel *); /* get the length of the text from an edit window */
void plegetsel(Panel *, int *, int *); /* get the selection from an edit window */
void plepaste(Panel *, Rune *, int); /* paste in an edit window */
void plesel(Panel *, int, int); /* set the selection in an edit window */
void plescroll(Panel *, int); /* scroll an edit window */
Scroll plgetscroll(Panel *); /* get scrolling information from panel */
void plsetscroll(Panel *, Scroll); /* set scrolling information */
void plplacelabel(Panel *, int); /* label placement */
/*
* Panel creation & reinitialization functions
*/
Panel *plbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int));
Panel *plcanvas(Panel *pl, int, void (*)(Panel *), void (*)(Panel *pl, Mouse *));
Panel *plcheckbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int));
Panel *pledit(Panel *, int, Point, Rune *, int, void (*)(Panel *));
Panel *plentry(Panel *pl, int, int, char *, void (*)(Panel *pl, char *));
Panel *plframe(Panel *pl, int);
Panel *plgroup(Panel *pl, int);
Panel *plidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*));
Panel *pllabel(Panel *pl, int, Icon *);
Panel *pllist(Panel *pl, int, char *(*)(Panel *, int), int, void(*)(Panel *pl, int, int));
Panel *plmenu(Panel *pl, int, Icon **, int, void (*)(int, int));
Panel *plmenubar(Panel *pl, int, int, Icon *, Panel *pl, Icon *, ...);
Panel *plmessage(Panel *pl, int, int, char *);
Panel *plpopup(Panel *pl, int, Panel *pl, Panel *pl, Panel *pl);
Panel *plpulldown(Panel *pl, int, Icon *, Panel *pl, int);
Panel *plradiobutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int));
Panel *plscrollbar(Panel *plparent, int flags);
Panel *plslider(Panel *pl, int, Point, void(*)(Panel *pl, int, int, int));
Panel *pltextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *));
void plinitbutton(Panel *, int, Icon *, void (*)(Panel *, int));
void plinitcanvas(Panel *, int, void (*)(Panel *), void (*)(Panel *, Mouse *));
void plinitcheckbutton(Panel *, int, Icon *, void (*)(Panel *, int, int));
void plinitedit(Panel *, int, Point, Rune *, int, void (*)(Panel *));
void plinitentry(Panel *, int, int, char *, void (*)(Panel *, char *));
void plinitframe(Panel *, int);
void plinitgroup(Panel *, int);
void plinitidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*));
void plinitlabel(Panel *, int, Icon *);
void plinitlist(Panel *, int, char *(*)(Panel *, int), int, void(*)(Panel *, int, int));
void plinitmenu(Panel *, int, Icon **, int, void (*)(int, int));
void plinitmessage(Panel *, int, int, char *);
void plinitpopup(Panel *, int, Panel *, Panel *, Panel *);
void plinitpulldown(Panel *, int, Icon *, Panel *, int);
void plinitradiobutton(Panel *, int, Icon *, void (*)(Panel *, int, int));
void plinitscrollbar(Panel *parent, int flags);
void plinitslider(Panel *, int, Point, void(*)(Panel *, int, int, int));
void plinittextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *));
/*
* Rtext constructors & destructor
*/
Rtext *plrtstr(Rtext **, int, int, Font *, char *, int, void *);
Rtext *plrtbitmap(Rtext **, int, int, Image *, int, void *);
Rtext *plrtpanel(Rtext **, int, int, Panel *, void *);
void plrtfree(Rtext *);
int plgetpostextview(Panel *);
void plsetpostextview(Panel *, int);
/*
* Idols
*/
Idol *plmkidol(Idol**, Image*, Image*, char*, void*);
void plfreeidol(Idol*);
Point plidolsize(Idol*, Font*, int);
void *plidollistgetsel(Panel*);

Binary file not shown.

View File

@ -0,0 +1,103 @@
/*
* Definitions for internal use only
*/
/*
* Variable-font text routines
* These could make a separate library.
*/
int pl_rtfmt(Rtext *, int);
void pl_rtdraw(Image *, Rectangle, Rtext *, int);
void pl_rtredraw(Image *, Rectangle, Rtext *, int, int);
Rtext *pl_rthit(Rtext *, int, Point, Point);
#define HITME 0x08000 /* tells ptinpanel not to look at children */
#define LEAF 0x10000 /* newpanel will refuse to attach children */
#define INVIS 0x20000 /* don't draw this */
#define REMOUSE 0x40000 /* send next mouse event here, even if not inside */
/*
* States, also styles
*/
enum{
UP,
DOWN1,
DOWN2,
DOWN3,
DOWN,
PASSIVE,
FRAME
};
/*
* Scroll flags
*/
enum{
SCROLLUP,
SCROLLDOWN,
SCROLLABSY,
SCROLLLEFT,
SCROLLRIGHT,
SCROLLABSX,
};
/*
* Scrollbar, slider orientations
*/
enum{
HORIZ,
VERT
};
Panel *pl_newpanel(Panel *, int); /* make a new Panel, given parent & data size */
void *pl_emalloc(int); /* allocate some space, exit on error */
void pl_print(Panel *); /* print a Panel tree */
Panel *pl_ptinpanel(Point, Panel *); /* highest-priority subpanel containing point */
/*
* Drawing primitives
*/
int pl_drawinit(int);
Rectangle pl_box(Image *, Rectangle, int);
Rectangle pl_outline(Image *, Rectangle, int);
Point pl_boxsize(Point, int);
void pl_interior(int, Point *, Point *);
void pl_drawicon(Image *, Rectangle, int, int, Icon *);
Rectangle pl_check(Image *, Rectangle, int);
Rectangle pl_radio(Image *, Rectangle, int);
int pl_ckwid(void);
void pl_sliderupd(Image *, Rectangle, int, int, int);
void pl_invis(Panel *, int);
Point pl_iconsize(int, Icon *);
void pl_highlight(Image *, Rectangle);
void pl_clr(Image *, Rectangle);
void pl_fill(Image *, Rectangle);
void pl_cpy(Image *, Point, Rectangle);
/*
* Rune mangling functions
*/
int pl_idchar(int);
int pl_rune1st(int);
char *pl_nextrune(char *);
int pl_runewidth(Font *, char *);
/*
* Fixed-font Text-window routines
* These could be separated out into a separate library.
*/
typedef struct Textwin Textwin;
struct Textwin{
Rune *text, *etext, *eslack; /* text, with some slack off the end */
int top, bot; /* range of runes visible on screen */
int sel0, sel1; /* selection */
Point *loc, *eloc; /* ul corners of visible runes (+1 more at end!) */
Image *b; /* bitmap the text is drawn in */
Rectangle r; /* rectangle the text is drawn in */
Font *font; /* font text is drawn in */
int hgt; /* same as font->height */
int tabstop; /* tab settings are every tabstop pixels */
int mintab; /* the minimum size of a tab */
};
Textwin *twnew(Image *, Font *, Rune *, int);
void twfree(Textwin *);
void twhilite(Textwin *, int, int, int);
void twselect(Textwin *, Mouse *);
void twreplace(Textwin *, int, int, Rune *, int);
void twscroll(Textwin *, int);
int twpt2rune(Textwin *, Point);
void twreshape(Textwin *, Rectangle);
void twmove(Textwin *, Point);
void plemove(Panel *, Point);

View File

@ -0,0 +1,113 @@
/*
* popup
* looks like a group, except diverts hits on certain buttons to
* panels that it temporarily pops up.
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Popup Popup;
struct Popup{
Image *save; /* where to save what the popup covers */
Panel *pop[3]; /* what to pop up */
};
void pl_drawpopup(Panel *p){
USED(p);
}
int pl_hitpopup(Panel *g, Mouse *m){
Panel *p;
Point d;
Popup *pp;
pp=g->data;
if(g->state==UP){
switch(m->buttons&7){
case 0: p=g->child; break;
case 1: p=pp->pop[0]; g->state=DOWN1; break;
case 2: p=pp->pop[1]; g->state=DOWN2; break;
case 4: p=pp->pop[2]; g->state=DOWN3; break;
default: p=0; break;
}
if(p==0){
p=g->child;
g->state=DOWN;
}
else if(g->state!=UP){
plpack(p, screen->clipr);
if(p->lastmouse)
d=subpt(m->xy, divpt(addpt(p->lastmouse->r.min,
p->lastmouse->r.max), 2));
else
d=subpt(m->xy, divpt(addpt(p->r.min, p->r.max), 2));
if(p->r.min.x+d.x<g->r.min.x) d.x=g->r.min.x-p->r.min.x;
if(p->r.max.x+d.x>g->r.max.x) d.x=g->r.max.x-p->r.max.x;
if(p->r.min.y+d.y<g->r.min.y) d.y=g->r.min.y-p->r.min.y;
if(p->r.max.y+d.y>g->r.max.y) d.y=g->r.max.y-p->r.max.y;
plmove(p, d);
pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill);
if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min);
pl_invis(p, 0);
pldraw(p, g->b);
}
}
else{
switch(g->state){
default: SET(p); break; /* can't happen! */
case DOWN1: p=pp->pop[0]; break;
case DOWN2: p=pp->pop[1]; break;
case DOWN3: p=pp->pop[2]; break;
case DOWN: p=g->child; break;
}
if((m->buttons&7)==0){
if(g->state!=DOWN){
if(pp->save!=0){
draw(g->b, p->r, pp->save, 0, p->r.min);
flushimage(display, 1);
freeimage(pp->save);
}
pl_invis(p, 1);
}
g->state=UP;
}
}
plmouse(p, *m);
return (m->buttons&7)!=0;
}
void pl_typepopup(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizepopup(Panel *g, Point children){
USED(g);
return children;
}
void pl_childspacepopup(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
int pl_pripopup(Panel *, Point){
return PRI_POPUP;
}
void plinitpopup(Panel *v, int flags, Panel *pop0, Panel *pop1, Panel *pop2){
Popup *pp;
pp=v->data;
v->flags=flags;
v->pri=pl_pripopup;
v->state=UP;
v->draw=pl_drawpopup;
v->hit=pl_hitpopup;
v->type=pl_typepopup;
v->getsize=pl_getsizepopup;
v->childspace=pl_childspacepopup;
pp->pop[0]=pop0;
pp->pop[1]=pop1;
pp->pop[2]=pop2;
pp->save=0;
v->kind="popup";
}
Panel *plpopup(Panel *parent, int flags, Panel *pop0, Panel *pop1, Panel *pop2){
Panel *v;
v=pl_newpanel(parent, sizeof(Popup));
plinitpopup(v, flags, pop0, pop1, pop2);
return v;
}

View File

@ -0,0 +1,56 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
void pl_iprint(int indent, char *fmt, ...){
char buf[8192];
va_list arg;
memset(buf, '\t', indent);
va_start(arg, fmt);
write(1, buf, vsnprint(buf+indent, sizeof(buf)-indent, fmt, arg));
va_end(arg);
}
void pl_ipprint(Panel *p, int n){
Panel *c;
char *place, *stick;
pl_iprint(n, "%s (0x%.8x)\n", p->kind, p);
pl_iprint(n, " r=(%d %d, %d %d)\n",
p->r.min.x, p->r.min.y, p->r.max.x, p->r.max.y);
switch(p->flags&PACK){
default: SET(place); break;
case PACKN: place="n"; break;
case PACKE: place="e"; break;
case PACKS: place="s"; break;
case PACKW: place="w"; break;
}
switch(p->flags&PLACE){
default: SET(stick); break;
case PLACECEN: stick=""; break;
case PLACES: stick=" stick s"; break;
case PLACEE: stick=" stick e"; break;
case PLACEW: stick=" stick w"; break;
case PLACEN: stick=" stick n"; break;
case PLACENE: stick=" stick ne"; break;
case PLACENW: stick=" stick nw"; break;
case PLACESE: stick=" stick se"; break;
case PLACESW: stick=" stick sw"; break;
}
pl_iprint(n, " place %s%s%s%s%s%s\n",
place,
p->flags&FILLX?" fill x":"",
p->flags&FILLY?" fill y":"",
stick,
p->flags&EXPAND?" expand":"",
p->flags&FIXED?" fixed":"");
if(!eqpt(p->pad, Pt(0, 0))) pl_iprint(n, " pad=%d,%d)\n", p->pad.x, p->pad.y);
if(!eqpt(p->ipad, Pt(0, 0))) pl_iprint(n, " ipad=%d,%d)\n", p->ipad.x, p->ipad.y);
pl_iprint(n, " size=(%d,%d), sizereq=(%d,%d)\n",
p->size.x, p->size.y, p->sizereq.x, p->sizereq.y);
for(c=p->child;c;c=c->next)
pl_ipprint(c, n+1);
}
void pl_print(Panel *p){
pl_ipprint(p, 0);
}

View File

@ -0,0 +1,160 @@
/*
* pulldown
* makes a button that pops up a panel when hit
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Pulldown Pulldown;
struct Pulldown{
Icon *icon; /* button label */
Panel *pull; /* Panel to pull down */
int side; /* which side of the button to put the panel on */
Image *save; /* where to save what we draw the panel on */
};
void pl_drawpulldown(Panel *p){
pl_drawicon(p->b, pl_box(p->b, p->r, p->state), PLACECEN,
p->flags, ((Pulldown *)p->data)->icon);
}
int pl_hitpulldown(Panel *g, Mouse *m){
int oldstate, passon;
Rectangle r;
Panel *p, *hitme;
Pulldown *pp;
pp=g->data;
oldstate=g->state;
p=pp->pull;
hitme=0;
switch(g->state){
case UP:
if(!ptinrect(m->xy, g->r))
g->state=UP;
else if(m->buttons&7){
r=g->b->r;
p->flags&=~PLACE;
switch(pp->side){
case PACKN:
r.min.x=g->r.min.x;
r.max.y=g->r.min.y;
p->flags|=PLACESW;
break;
case PACKS:
r.min.x=g->r.min.x;
r.min.y=g->r.max.y;
p->flags|=PLACENW;
break;
case PACKE:
r.min.x=g->r.max.x;
r.min.y=g->r.min.y;
p->flags|=PLACENW;
break;
case PACKW:
r.max.x=g->r.min.x;
r.min.y=g->r.min.y;
p->flags|=PLACENE;
break;
case PACKCEN:
r.min=g->r.min;
p->flags|=PLACENW;
break;
}
plpack(p, r);
pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill);
if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min);
pl_invis(p, 0);
pldraw(p, g->b);
g->state=DOWN;
}
break;
case DOWN:
if(!ptinrect(m->xy, g->r)){
switch(pp->side){
default: SET(passon); break; /* doesn't happen */
case PACKN: passon=m->xy.y<g->r.min.y; break;
case PACKS: passon=m->xy.y>=g->r.max.y; break;
case PACKE: passon=m->xy.x>=g->r.max.x; break;
case PACKW: passon=m->xy.x<g->r.min.x; break;
case PACKCEN: passon=1; break;
}
if(passon){
hitme=p;
if((m->buttons&7)==0) g->state=UP;
}
else g->state=UP;
}
else if((m->buttons&7)==0) g->state=UP;
else hitme=p;
if(g->state!=DOWN && pp->save){
draw(g->b, p->r, pp->save, 0, p->r.min);
freeimage(pp->save);
pp->save=0;
pl_invis(p, 1);
hitme=p;
}
}
if(g->state!=oldstate) pldraw(g, g->b);
if(hitme) plmouse(hitme, *m);
return g->state==DOWN;
}
void pl_typepulldown(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizepulldown(Panel *p, Point children){
USED(p, children);
return pl_boxsize(pl_iconsize(p->flags, ((Pulldown *)p->data)->icon), p->state);
}
void pl_childspacepulldown(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
void plinitpulldown(Panel *v, int flags, Icon *icon, Panel *pullthis, int side){
Pulldown *pp;
pp=v->data;
v->flags=flags|LEAF;
v->draw=pl_drawpulldown;
v->hit=pl_hitpulldown;
v->type=pl_typepulldown;
v->getsize=pl_getsizepulldown;
v->childspace=pl_childspacepulldown;
pp->pull=pullthis;
pp->side=side;
pp->icon=icon;
v->kind="pulldown";
}
Panel *plpulldown(Panel *parent, int flags, Icon *icon, Panel *pullthis, int side){
Panel *v;
v=pl_newpanel(parent, sizeof(Pulldown));
v->state=UP;
((Pulldown *)v->data)->save=0;
plinitpulldown(v, flags, icon, pullthis, side);
return v;
}
Panel *plmenubar(Panel *parent, int flags, int cflags, Icon *l1, Panel *m1, Icon *l2, ...){
Panel *v;
va_list arg;
Icon *s;
int pulldir;
switch(cflags&PACK){
default:
SET(pulldir);
break;
case PACKE:
case PACKW:
pulldir=PACKS;
break;
case PACKN:
case PACKS:
pulldir=PACKE;
break;
}
v=plgroup(parent, flags);
va_start(arg, cflags);
while((s=va_arg(arg, Icon *))!=0)
plpulldown(v, cflags, s, va_arg(arg, Panel *), pulldir);
va_end(arg);
USED(l1, m1, l2);
v->kind="menubar";
return v;
}

View File

@ -0,0 +1,230 @@
/*
* Rich text with images.
* Should there be an offset field, to do subscripts & kerning?
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
#include "rtext.h"
#define LEAD 4 /* extra space between lines */
Rtext *pl_rtnew(Rtext **t, int space, int indent, Image *b, Panel *p, Font *f, char *s, int hot, void *user){
Rtext *new;
new=malloc(sizeof(Rtext));
if(new==0) return 0;
new->hot=hot;
new->user=user;
new->space=space;
new->indent=indent;
new->b=b;
new->p=p;
new->font=f;
new->text=s;
new->next=0;
new->r=Rect(0,0,0,0);
if(*t)
(*t)->last->next=new;
else
*t=new;
(*t)->last=new;
return new;
}
Rtext *plrtpanel(Rtext **t, int space, int indent, Panel *p, void *user){
return pl_rtnew(t, space, indent, 0, p, 0, 0, 1, user);
}
Rtext *plrtstr(Rtext **t, int space, int indent, Font *f, char *s, int hot, void *user){
return pl_rtnew(t, space, indent, 0, 0, f, s, hot, user);
}
Rtext *plrtbitmap(Rtext **t, int space, int indent, Image *b, int hot, void *user){
return pl_rtnew(t, space, indent, b, 0, 0, 0, hot, user);
}
void plrtfree(Rtext *t){
Rtext *next;
while(t){
next=t->next;
free(t);
t=next;
}
}
int pl_tabmin, pl_tabsize;
void pltabsize(int min, int size){
pl_tabmin=min;
pl_tabsize=size;
}
int pl_space(int space, int pos, int indent){
if(space>=0) return space;
switch(PL_OP(space)){
default:
return 0;
case PL_TAB:
return ((pos-indent+pl_tabmin)/pl_tabsize+PL_ARG(space))*pl_tabsize+indent-pos;
}
}
/*
* initialize rectangles & nextlines of text starting at t,
* galley width is wid. Returns the total length of the text
*/
int pl_rtfmt(Rtext *t, int wid){
Rtext *tp, *eline;
int ascent, descent, x, space, a, d, w, topy, indent;
Point p;
p=Pt(0,0);
eline=t;
while(t){
ascent=0;
descent=0;
indent=space=pl_space(t->indent, 0, 0);
x=0;
tp=t;
for(;;){
if(tp->b){
a=tp->b->r.max.y-tp->b->r.min.y+2;
d=0;
w=tp->b->r.max.x-tp->b->r.min.x+4;
}
else if(tp->p){
/* what if plpack fails? */
plpack(tp->p, Rect(0,0,wid,wid));
plmove(tp->p, subpt(Pt(0,0), tp->p->r.min));
a=tp->p->r.max.y-tp->p->r.min.y;
d=0;
w=tp->p->r.max.x-tp->p->r.min.x;
}
else{
a=tp->font->ascent;
d=tp->font->height-a;
w=tp->wid=stringwidth(tp->font, tp->text);
}
if(x+w+space>wid) break;
if(a>ascent) ascent=a;
if(d>descent) descent=d;
x+=w+space;
tp=tp->next;
if(tp==0){
eline=0;
break;
}
space=pl_space(tp->space, x, indent);
if(space) eline=tp;
}
if(eline==t){ /* No progress! Force fit the first block! */
if(a>ascent) ascent=a;
if(d>descent) descent=d;
if(tp==t)
eline=tp->next;
else
eline=tp;
}
topy=p.y;
p.y+=ascent;
p.x=indent=pl_space(t->indent, 0, 0);
for(;;){
t->topy=topy;
t->r.min.x=p.x;
if(t->b){
t->r.max.y=p.y;
t->r.min.y=p.y-(t->b->r.max.y-t->b->r.min.y);
p.x+=t->b->r.max.x-t->b->r.min.x+2;
t->r=rectaddpt(t->r, Pt(2, 2));
}
else if(t->p){
t->r.max.y=p.y;
t->r.min.y=p.y-t->p->r.max.y;
p.x+=t->p->r.max.x;
}
else{
t->r.min.y=p.y-t->font->ascent;
t->r.max.y=t->r.min.y+t->font->height;
p.x+=t->wid;
}
t->r.max.x=p.x;
t->nextline=eline;
t=t->next;
if(t==eline) break;
p.x+=pl_space(t->space, p.x, indent);
}
p.y+=descent+LEAD;
}
return p.y;
}
void pl_rtdraw(Image *b, Rectangle r, Rtext *t, int yoffs){
Point offs;
Rectangle dr;
Rectangle cr;
cr=b->clipr;
replclipr(b, b->repl, r);
pl_clr(b, r);
offs=subpt(r.min, Pt(0, yoffs));
for(;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
dr=rectaddpt(t->r, offs);
if(dr.max.y>r.min.y
&& dr.min.y<r.max.y){
if(t->b){
// bitblt(b, dr.min, t->b, t->b->r, S|D);
draw(b, Rpt(dr.min, addpt(dr.min, subpt(t->b->r.max, t->b->r.min))), t->b, 0, t->b->r.min);
if(t->hot) border(b, insetrect(dr, -2), 1, display->black, ZP);
}
else if(t->p){
plmove(t->p, subpt(dr.min, t->p->r.min));
pldraw(t->p, b);
}
else{
string(b, dr.min, display->black, ZP, t->font, t->text);
if(t->hot)
line(b, Pt(dr.min.x, dr.max.y-1),
Pt(dr.max.x, dr.max.y-1),
Endsquare, Endsquare, 0,
display->black, ZP);
}
}
}
replclipr(b, b->repl, cr);
}
/*
* Reposition text already drawn in the window.
* We just move the pixels and update the positions of any
* enclosed panels
*/
void pl_reposition(Rtext *t, Image *b, Point p, Rectangle r){
Point offs;
pl_cpy(b, p, r);
offs=subpt(p, r.min);
for(;t;t=t->next)
if(!eqrect(t->r, Rect(0,0,0,0)) && !t->b && t->p)
plmove(t->p, offs);
}
/*
* Rectangle r of Image b contains an image of Rtext t, offset by oldoffs.
* Redraw the text to have offset yoffs.
*/
void pl_rtredraw(Image *b, Rectangle r, Rtext *t, int yoffs, int oldoffs){
int dy, size;
dy=oldoffs-yoffs;
size=r.max.y-r.min.y;
if(dy>=size || -dy>=size)
pl_rtdraw(b, r, t, yoffs);
else if(dy<0){
pl_reposition(t, b, r.min,
Rect(r.min.x, r.min.y-dy, r.max.x, r.max.y));
pl_rtdraw(b, Rect(r.min.x, r.max.y+dy, r.max.x, r.max.y),
t, yoffs+size+dy);
}
else if(dy>0){
pl_reposition(t, b, Pt(r.min.x, r.min.y+dy),
Rect(r.min.x, r.min.y, r.max.x, r.max.y-dy));
pl_rtdraw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+dy), t, yoffs);
}
}
Rtext *pl_rthit(Rtext *t, int yoffs, Point p, Point ul){
if(t==0) return 0;
p.x-=ul.x;
p.y+=yoffs-ul.y;
while(t->nextline && t->nextline->topy<=p.y) t=t->nextline;
for(;t!=0;t=t->next){
if(t->topy>p.y) return 0;
if(ptinrect(p, t->r)) return t;
}
return 0;
}

View File

@ -0,0 +1,11 @@
/*
* Rtext definitions
*/
#define PL_NOPBIT 4
#define PL_NARGBIT 12
#define PL_ARGMASK ((1<<PL_NARGBIT)-1)
#define PL_SPECIAL(op) (((-1<<PL_NOPBIT)|op)<<PL_NARGBIT)
#define PL_OP(t) ((t)&~PL_ARGMASK)
#define PL_ARG(t) ((t)&PL_ARGMASK)
#define PL_TAB PL_SPECIAL(0) /* # of tab stops before text */
void pltabsize(int, int); /* set min tab and tab size */

View File

@ -0,0 +1,65 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
Panel *root, *list;
char *genlist(Panel *, int which){
static char buf[7];
if(which<0 || 26<=which) return 0;
sprint(buf, "item %c", which+'a');
return buf;
}
void hitgen(Panel *p, int buttons, int sel){
USED(p, buttons, sel);
}
void ereshaped(Rectangle r){
screen.r=r;
r=inset(r, 4);
plpack(root, r);
bitblt(&screen, screen.r.min, &screen, screen.r, Zero);
pldraw(root, &screen);
}
void done(Panel *p, int buttons){
USED(p, buttons);
bitblt(&screen, screen.r.min, &screen, screen.r, Zero);
exits(0);
}
Panel *msg;
void message(char *s, ...){
char buf[1024], *out;
va_list arg;
va_start(arg, s);
out = doprint(buf, buf+sizeof(buf), s, arg);
va_end(arg);
*out='\0';
plinitlabel(msg, PACKN|FILLX, buf);
pldraw(msg, &screen);
}
Scroll s;
void save(Panel *p, int buttons){
USED(p, buttons);
s=plgetscroll(list);
message("save %d %d %d %d", s);
}
void revert(Panel *p, int buttons){
USED(p, buttons);
plsetscroll(list, s, &screen);
message("revert %d %d %d %d", s);
}
void main(void){
Panel *g;
binit(0,0,0);
einit(Emouse);
plinit(screen.ldepth);
root=plgroup(0, 0);
g=plgroup(root, PACKN|EXPAND);
list=pllist(g, PACKE|EXPAND, genlist, 8, hitgen);
plscroll(list, 0, plscrollbar(g, PACKW));
msg=pllabel(root, PACKN|FILLX, "");
plbutton(root, PACKW, "save", save);
plbutton(root, PACKW, "revert", revert);
plbutton(root, PACKE, "done", done);
ereshaped(screen.r);
for(;;) plmouse(root, emouse(), &screen);
}

View File

@ -0,0 +1,21 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
void plscroll(Panel *scrollee, Panel *xscroller, Panel *yscroller){
scrollee->xscroller=xscroller;
scrollee->yscroller=yscroller;
if(xscroller) xscroller->scrollee=scrollee;
if(yscroller) yscroller->scrollee=scrollee;
}
Scroll plgetscroll(Panel *p){
return p->scr;
}
void plsetscroll(Panel *p, Scroll s){
if(p->scroll){
if(s.size.x) p->scroll(p, HORIZ, 2, s.pos.x, s.size.x);
if(s.size.y) p->scroll(p, VERT, 2, s.pos.y, s.size.y);
}
}

View File

@ -0,0 +1,148 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Scrollbar Scrollbar;
struct Scrollbar{
int dir; /* HORIZ or VERT */
int lo, hi; /* setting, in screen coordinates */
int buttons; /* saved mouse buttons for transmittal to scrollee */
Rectangle interior;
Point minsize;
};
#define SBWID 15 /* should come from draw.c? */
void pl_drawscrollbar(Panel *p){
Scrollbar *sp;
sp=p->data;
sp->interior=pl_outline(p->b, p->r, p->state);
pl_sliderupd(p->b, sp->interior, sp->dir, sp->lo, sp->hi);
}
int pl_hitscrollbar(Panel *g, Mouse *m){
int oldstate, pos, len, dy;
Point ul, size;
Scrollbar *sp;
sp=g->data;
ul=g->r.min;
size=subpt(g->r.max, g->r.min);
pl_interior(g->state, &ul, &size);
oldstate=g->state;
if(m->buttons&OUT && m->buttons&7){
if(m->xy.y<g->r.min.y) m->xy.y=g->r.min.y;
if(m->xy.y>=g->r.max.y) m->xy.y=g->r.max.y-1;
if(ptinrect(m->xy, g->r))
m->buttons&=~OUT;
}
if(sp->dir==HORIZ){
pos=m->xy.x-ul.x;
len=size.x;
}
else{
pos=m->xy.y-ul.y;
len=size.y;
}
if(pos<0) pos=0;
else if(pos>len) pos=len;
if(m->buttons&7){
g->state=DOWN;
if(g->r.min.x<=m->xy.x && m->xy.x<g->r.max.x){
sp->buttons=m->buttons;
switch(m->buttons){
case 1:
dy=pos*(sp->hi-sp->lo)/len;
pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo-dy,
sp->hi-dy);
break;
case 2:
if(g->scrollee && g->scrollee->scroll)
g->scrollee->scroll(g->scrollee, sp->dir,
m->buttons, pos, len);
break;
case 4:
dy=pos*(sp->hi-sp->lo)/len;
pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo+dy,
sp->hi+dy);
break;
}
}
}
else{
if(!(sp->buttons&2) && g->state==DOWN && g->scrollee && g->scrollee->scroll)
g->scrollee->scroll(g->scrollee, sp->dir, sp->buttons,
pos, len);
g->state=UP;
}
if(oldstate!=g->state) pldraw(g, g->b);
return g->state==DOWN;
}
void pl_typescrollbar(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizescrollbar(Panel *p, Point children){
USED(children);
return pl_boxsize(((Scrollbar *)p->data)->minsize, p->state);
}
void pl_childspacescrollbar(Panel *p, Point *ul, Point *size){
USED(p, ul, size);
}
/*
* Arguments lo, hi and len are in the scrollee's natural coordinates
*/
void pl_setscrollbarscrollbar(Panel *p, int lo, int hi, int len){
Point ul, size;
int mylen;
Scrollbar *sp;
sp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
mylen=sp->dir==HORIZ?size.x:size.y;
if(len==0) len=1;
sp->lo=lo*mylen/len;
sp->hi=hi*mylen/len;
if(sp->lo<0) sp->lo=0;
if(sp->lo>=mylen) sp->hi=mylen-1;
if(sp->hi<=sp->lo) sp->hi=sp->lo+1;
if(sp->hi>mylen) sp->hi=mylen;
pldraw(p, p->b);
}
int pl_priscrollbar(Panel *, Point){
return PRI_SCROLLBAR;
}
void plinitscrollbar(Panel *v, int flags){
Scrollbar *sp;
sp=v->data;
v->flags=flags|LEAF;
v->pri=pl_priscrollbar;
v->state=UP;
v->draw=pl_drawscrollbar;
v->hit=pl_hitscrollbar;
v->type=pl_typescrollbar;
v->getsize=pl_getsizescrollbar;
v->childspace=pl_childspacescrollbar;
v->setscrollbar=pl_setscrollbarscrollbar;
switch(flags&PACK){
case PACKN:
case PACKS:
sp->dir=HORIZ;
sp->minsize=Pt(0, SBWID);
v->flags|=FILLX;
break;
case PACKE:
case PACKW:
sp->dir=VERT;
sp->minsize=Pt(SBWID, 0);
v->flags|=FILLY;
break;
}
sp->lo=0;
sp->hi=0;
v->kind="scrollbar";
}
Panel *plscrollbar(Panel *parent, int flags){
Panel *v;
v=pl_newpanel(parent, sizeof(Scrollbar));
plinitscrollbar(v, flags);
return v;
}

View File

@ -0,0 +1,97 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Slider Slider;
struct Slider{
int dir; /* HORIZ or VERT */
int val; /* setting, in screen coordinates */
Point minsize;
void (*hit)(Panel *, int, int, int); /* call back to user when slider changes */
int buttons;
};
void pl_drawslider(Panel *p){
Rectangle r;
Slider *sp;
sp=p->data;
r=pl_box(p->b, p->r, UP);
switch(sp->dir){
case HORIZ: pl_sliderupd(p->b, r, sp->dir, 0, sp->val); break;
case VERT: pl_sliderupd(p->b, r, sp->dir, r.max.y-sp->val, r.max.y); break;
}
}
int pl_hitslider(Panel *p, Mouse *m){
int oldstate, oldval, len;
Point ul, size;
Slider *sp;
sp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
oldstate=p->state;
oldval=sp->val;
SET(len);
if(m->buttons&OUT)
p->state=UP;
else if(m->buttons&7){
p->state=DOWN;
sp->buttons=m->buttons;
if(sp->dir==HORIZ){
sp->val=m->xy.x-ul.x;
len=size.x;
}
else{
sp->val=ul.y+size.y-m->xy.y;
len=size.y;
}
if(sp->val<0) sp->val=0;
else if(sp->val>len) sp->val=len;
}
else /* mouse inside, but no buttons down */
p->state=UP;
if(oldval!=sp->val || oldstate!=p->state) pldraw(p, p->b);
if(oldval!=sp->val && sp->hit) sp->hit(p, sp->buttons, sp->val, len);
return 0;
}
void pl_typeslider(Panel *p, Rune c){
USED(p, c);
}
Point pl_getsizeslider(Panel *p, Point children){
USED(children);
return pl_boxsize(((Slider *)p->data)->minsize, p->state);
}
void pl_childspaceslider(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void plinitslider(Panel *v, int flags, Point size, void (*hit)(Panel *, int, int, int)){
Slider *sp;
sp=v->data;
v->r=Rect(0,0,size.x,size.y);
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawslider;
v->hit=pl_hitslider;
v->type=pl_typeslider;
v->getsize=pl_getsizeslider;
v->childspace=pl_childspaceslider;
sp->minsize=size;
sp->dir=size.x>size.y?HORIZ:VERT;
sp->hit=hit;
v->kind="slider";
}
Panel *plslider(Panel *parent, int flags, Point size, void (*hit)(Panel *, int, int, int)){
Panel *p;
p=pl_newpanel(parent, sizeof(Slider));
plinitslider(p, flags, size, hit);
return p;
}
void plsetslider(Panel *p, int value, int range){
Slider *sp;
sp=p->data;
if(value<0) value=0;
else if(value>range) value=range;
if(sp->dir==HORIZ) sp->val=value*(p->r.max.x-p->r.min.x)/range;
else sp->val=value*(p->r.max.y-p->r.min.y)/range;
}

View File

@ -0,0 +1,246 @@
/*
* Fonted text viewer, calls out to code in rtext.c
*
* Should redo this to copy the already-visible parts on scrolling & only
* update the newly appearing stuff -- then the offscreen assembly bitmap can go away.
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct Textview Textview;
struct Textview{
void (*hit)(Panel *, int, Rtext *); /* call back to user on hit */
Rtext *text; /* text */
int yoffs; /* offset of top of screen */
Rtext *hitword; /* text to hilite */
Image *hitsave; /* for restoring hilit text */
int twid; /* text width */
int thgt; /* text height */
Point minsize; /* smallest acceptible window size */
int buttons;
};
void pl_hiliteword(Panel *p, Rtext *w, int on){
Point ul, size;
Rectangle r;
Textview *tp;
if(w==0 || (w->b==0 && w->p!=0)) return;
tp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(UP, &ul, &size);
ul.y-=tp->yoffs;
r=rectaddpt(w->r, ul);
if(rectclip(&r, p->r)){
if(on){
if(tp->hitsave) freeimage(tp->hitsave);
tp->hitsave = allocimage(display, r, screen->chan, 0, DNofill);
if(tp->hitsave) draw(tp->hitsave, r, p->b, 0, r.min);
pl_highlight(p->b, r);
}else{
if(tp->hitsave){
draw(p->b, r, tp->hitsave, 0, r.min);
freeimage(tp->hitsave);
tp->hitsave = 0;
}
}
}
}
void pl_stuffbitmap(Panel *p, Image *b){
p->b=b;
for(p=p->child;p;p=p->next)
pl_stuffbitmap(p, b);
}
/*
* If we draw the text in a backup bitmap and copy it onto the screen,
* the bitmap pointers in all the subpanels point to the wrong bitmap.
* This code fixes them.
*/
void pl_drawnon(Rtext *rp, Image *b){
for(;rp!=0;rp=rp->next)
if(rp->b==0 && rp->p!=0)
pl_stuffbitmap(rp->p, b);
}
/*
* Mark the hilite and update the scroll bar
*/
void pl_fixtextview(Panel *p, Textview *tp, Rectangle r){
Panel *sb;
int lo, hi;
pl_hiliteword(p, tp->hitword, 1);
lo=tp->yoffs;
hi=lo+r.max.y-r.min.y; /* wrong? */
sb=p->yscroller;
if(sb && sb->setscrollbar) sb->setscrollbar(sb, lo, hi, tp->thgt);
}
void pl_drawtextview(Panel *p){
int twid;
Rectangle r;
Textview *tp;
Image *b;
tp=p->data;
b=allocimage(display, p->r, screen->chan, 0, DNofill);
if(b==0) b=p->b;
r=pl_outline(b, p->r, p->state);
twid=r.max.x-r.min.x;
if(twid!=tp->twid){
tp->twid=twid;
tp->thgt=pl_rtfmt(tp->text, tp->twid);
p->scr.size.y=tp->thgt;
}
p->scr.pos.y=tp->yoffs;
pl_rtdraw(b, r, tp->text, tp->yoffs);
if(b!=p->b){
draw(p->b, p->r, b, 0, b->r.min);
freeimage(b);
pl_drawnon(tp->text, p->b);
}
pl_fixtextview(p, tp, r);
}
/*
* If t is a panel word, pass the mouse event on to it
*/
void pl_passon(Rtext *t, Mouse *m){
if(t && t->b==0 && t->p!=0) plmouse(t->p, *m);
}
int pl_hittextview(Panel *p, Mouse *m){
Rtext *oldhitword;
int hitme;
Point ul, size;
Textview *tp;
tp=p->data;
oldhitword=tp->hitword;
hitme=0;
pl_passon(oldhitword, m);
if(m->buttons&OUT)
p->state=UP;
else if(m->buttons&7){
tp->buttons=m->buttons;
p->state=DOWN;
if(oldhitword==0
|| oldhitword->b!=0
|| oldhitword->p==0
|| (oldhitword->p->flags&REMOUSE)==0){
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
tp->hitword=pl_rthit(tp->text, tp->yoffs, m->xy, ul);
if(tp->hitword!=0 && tp->hitword->hot==0) tp->hitword=0;
}
}
else{
if(p->state==DOWN) hitme=1;
p->state=UP;
}
if(tp->hitword!=oldhitword){
pl_hiliteword(p, oldhitword, 0);
pl_hiliteword(p, tp->hitword, 1);
pl_passon(tp->hitword, m);
}
if(hitme && tp->hit && tp->hitword){
pl_hiliteword(p, tp->hitword, 0);
if(tp->hitword->b!=0 || tp->hitword->p==0)
tp->hit(p, tp->buttons, tp->hitword);
tp->hitword=0;
}
return 0;
}
void pl_scrolltextview(Panel *p, int dir, int buttons, int num, int den){
int yoffs;
Point ul, size;
Textview *tp;
Rectangle r;
if(dir!=VERT) return;
tp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
switch(buttons){
default:
SET(yoffs);
break;
case 1: /* left -- top moves to pointer */
yoffs=tp->yoffs-num*size.y/den;
if(yoffs<0) yoffs=0;
break;
case 2: /* middle -- absolute index of file */
yoffs=tp->thgt*num/den;
break;
case 4: /* right -- line pointed at moves to top */
yoffs=tp->yoffs+num*size.y/den;
if(yoffs>tp->thgt) yoffs=tp->thgt;
break;
}
if(yoffs!=tp->yoffs){
pl_hiliteword(p, tp->hitword, 0);
r=pl_outline(p->b, p->r, p->state);
pl_rtredraw(p->b, r, tp->text, yoffs, tp->yoffs);
tp->yoffs=yoffs;
pl_fixtextview(p, tp, r);
}
}
void pl_typetextview(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizetextview(Panel *p, Point children){
USED(children);
return pl_boxsize(((Textview *)p->data)->minsize, p->state);
}
void pl_childspacetextview(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
/*
* Priority depends on what thing inside the panel we're pointing at.
*/
int pl_pritextview(Panel *p, Point xy){
Point ul, size;
Textview *tp;
Rtext *h;
tp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
h=pl_rthit(tp->text, tp->yoffs, xy, ul);
if(h && h->b==0 && h->p!=0){
p=pl_ptinpanel(xy, h->p);
if(p) return p->pri(p, xy);
}
return PRI_NORMAL;
}
void plinittextview(Panel *v, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){
Textview *tp;
tp=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawtextview;
v->hit=pl_hittextview;
v->type=pl_typetextview;
v->getsize=pl_getsizetextview;
v->childspace=pl_childspacetextview;
v->kind="textview";
v->pri=pl_pritextview;
tp->hit=hit;
tp->minsize=minsize;
tp->text=t;
tp->yoffs=0;
tp->hitword=0;
v->scroll=pl_scrolltextview;
tp->twid=-1;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(0,1);
}
Panel *pltextview(Panel *parent, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){
Panel *v;
v=pl_newpanel(parent, sizeof(Textview));
plinittextview(v, flags, minsize, t, hit);
return v;
}
int plgetpostextview(Panel *p){
return ((Textview *)p->data)->yoffs;
}
void plsetpostextview(Panel *p, int yoffs){
((Textview *)p->data)->yoffs=yoffs;
pldraw(p, p->b);
}

View File

@ -0,0 +1,488 @@
/*
* Text windows
* void twhilite(Textwin *t, int sel0, int sel1, int on)
* hilite (on=1) or unhilite (on=0) a range of characters
* void twselect(Textwin *t, Mouse *m)
* set t->sel0, t->sel1 from mouse input.
* Also hilites selection.
* Caller should first unhilite previous selection.
* void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins)
* Replace the given range of characters with the given insertion.
* Caller should unhilite selection while this is called.
* void twscroll(Textwin *t, int top)
* Character with index top moves to the top line of the screen.
* int twpt2rune(Textwin *t, Point p)
* which character is displayed at point p?
* void twreshape(Textwin *t, Rectangle r)
* save r and redraw the text
* Textwin *twnew(Bitmap *b, Font *f, Rune *text, int ntext)
* create a new text window
* void twfree(Textwin *t)
* get rid of a surplus Textwin
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
/*
* Is text at point a before or after that at point b?
*/
int tw_before(Textwin *t, Point a, Point b){
return a.y<b.y || a.y<b.y+t->hgt && a.x<b.x;
}
/*
* Return the character index indicated by point p, or -1
* if its off-screen. The screen must be up-to-date.
*
* Linear search should be binary search.
*/
int twpt2rune(Textwin *t, Point p){
Point *el, *lp;
el=t->loc+(t->bot-t->top);
for(lp=t->loc;lp!=el;lp++)
if(tw_before(t, p, *lp)){
if(lp==t->loc) return t->top;
return lp-t->loc+t->top-1;
}
return t->bot;
}
/*
* Return ul corner of the character with the given index
*/
Point tw_rune2pt(Textwin *t, int i){
if(i<t->top) return t->r.min;
if(i>t->bot) return t->r.max;
return t->loc[i-t->top];
}
/*
* Store p at t->loc[l], extending t->loc if necessary
*/
void tw_storeloc(Textwin *t, int l, Point p){
int nloc;
if(l>t->eloc-t->loc){
nloc=l+100;
t->loc=realloc(t->loc, nloc*sizeof(Point));
if(t->loc==0){
fprint(2, "No mem in tw_storeloc\n");
exits("no mem");
}
t->eloc=t->loc+nloc;
}
t->loc[l]=p;
}
/*
* Set the locations at which the given runes should appear.
* Returns the index of the first rune not set, which might not
* be last because we reached the bottom of the window.
*
* N.B. this zaps the loc of r[last], so that value should be saved first,
* if it's important.
*/
int tw_setloc(Textwin *t, int first, int last, Point ul){
Rune *r, *er;
int x, dt, lp;
char buf[UTFmax+1];
er=t->text+last;
for(r=t->text+first,lp=first-t->top;r!=er && ul.y+t->hgt<=t->r.max.y;r++,lp++){
tw_storeloc(t, lp, ul);
switch(*r){
case '\n':
ul.x=t->r.min.x;
ul.y+=t->hgt;
break;
case '\t':
x=ul.x-t->r.min.x+t->mintab+t->tabstop;
x-=x%t->tabstop;
ul.x=x+t->r.min.x;
if(ul.x>t->r.max.x){
ul.x=t->r.min.x;
ul.y+=t->hgt;
tw_storeloc(t, lp, ul);
if(ul.y+t->hgt>t->r.max.y) return r-t->text;
ul.x+=+t->tabstop;
}
break;
default:
buf[runetochar(buf, r)]='\0';
dt=stringwidth(t->font, buf);
ul.x+=dt;
if(ul.x>t->r.max.x){
ul.x=t->r.min.x;
ul.y+=t->hgt;
tw_storeloc(t, lp, ul);
if(ul.y+t->hgt>t->r.max.y) return r-t->text;
ul.x+=dt;
}
break;
}
}
tw_storeloc(t, lp, ul);
return r-t->text;
}
/*
* Draw the given runes at their locations.
* Bug -- saving up multiple characters would
* reduce the number of calls to string,
* and probably make this a lot faster.
*/
void tw_draw(Textwin *t, int first, int last){
Rune *r, *er;
Point *lp, ul, ur;
char buf[UTFmax+1];
if(first<t->top) first=t->top;
if(last>t->bot) last=t->bot;
if(last<=first) return;
er=t->text+last;
for(r=t->text+first,lp=t->loc+(first-t->top);r!=er;r++,lp++){
if(lp->y+t->hgt>t->r.max.y){
fprint(2, "chr %C, index %ld of %d, loc %d %d, off bottom\n",
*r, lp-t->loc, t->bot-t->top, lp->x, lp->y);
return;
}
switch(*r){
case '\n':
ur=*lp;
break;
case '\t':
ur=*lp;
if(lp[1].y!=lp[0].y)
ul=Pt(t->r.min.x, lp[1].y);
else
ul=*lp;
pl_clr(t->b, Rpt(ul, Pt(lp[1].x, ul.y+t->hgt)));
break;
default:
buf[runetochar(buf, r)]='\0';
/***/ pl_clr(t->b, Rpt(*lp, addpt(*lp, stringsize(t->font, buf))));
ur=string(t->b, *lp, display->black, ZP, t->font, buf);
break;
}
if(lp[1].y!=lp[0].y)
/***/ pl_clr(t->b, Rpt(ur, Pt(t->r.max.x, ur.y+t->hgt)));
}
}
/*
* Hilight the characters with tops between ul and ur
*/
void tw_hilitep(Textwin *t, Point ul, Point ur){
Point swap;
int y;
if(tw_before(t, ur, ul)){ swap=ul; ul=ur; ur=swap;}
y=ul.y+t->hgt;
if(y>t->r.max.y) y=t->r.max.y;
if(ul.y==ur.y)
pl_highlight(t->b, Rpt(ul, Pt(ur.x, y)));
else{
pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, y)));
ul=Pt(t->r.min.x, y);
pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, ur.y)));
ul=Pt(t->r.min.x, ur.y);
y=ur.y+t->hgt;
if(y>t->r.max.y) y=t->r.max.y;
pl_highlight(t->b, Rpt(ul, Pt(ur.x, y)));
}
}
/*
* Hilite/unhilite the given range of characters
*/
void twhilite(Textwin *t, int sel0, int sel1, int on){
Point ul, ur;
int swap, y;
if(sel1<sel0){ swap=sel0; sel0=sel1; sel1=swap; }
if(sel1<t->top || t->bot<sel0) return;
if(sel0<t->top) sel0=t->top;
if(sel1>t->bot) sel1=t->bot;
if(!on){
if(sel1==sel0){
ul=t->loc[sel0-t->top];
y=ul.y+t->hgt;
if(y>t->r.max.y) y=t->r.max.y;
pl_clr(t->b, Rpt(ul, Pt(ul.x+1, y)));
}else
tw_draw(t, sel0, sel1);
return;
}
ul=t->loc[sel0-t->top];
if(sel1==sel0)
ur=addpt(ul, Pt(1, 0));
else
ur=t->loc[sel1-t->top];
tw_hilitep(t, ul, ur);
}
/*
* Set t->sel[01] from mouse input.
* Also hilites the selection.
* Caller should unhilite the previous
* selection before calling this.
*/
void twselect(Textwin *t, Mouse *m){
int sel0, sel1, newsel;
Point p0, p1, newp;
sel0=sel1=twpt2rune(t, m->xy);
p0=tw_rune2pt(t, sel0);
p1=addpt(p0, Pt(1, 0));
twhilite(t, sel0, sel1, 1);
for(;;){
*m=emouse();
if(m->buttons==0) break;
newsel=twpt2rune(t, m->xy);
newp=tw_rune2pt(t, newsel);
if(eqpt(newp, p0)) newp=addpt(newp, Pt(1, 0));
if(!eqpt(newp, p1)){
if((sel0<=sel1 && sel1<newsel) || (newsel<sel1 && sel1<sel0))
tw_hilitep(t, p1, newp);
else if((sel0<=newsel && newsel<sel1) || (sel1<newsel && newsel<=sel0)){
twhilite(t, sel1, newsel, 0);
if(newsel==sel0)
tw_hilitep(t, p0, newp);
}else if((newsel<sel0 && sel0<=sel1) || (sel1<sel0 && sel0<=newsel)){
twhilite(t, sel0, sel1, 0);
tw_hilitep(t, p0, newp);
}
sel1=newsel;
p1=newp;
}
}
if(sel0<=sel1){
t->sel0=sel0;
t->sel1=sel1;
}
else{
t->sel0=sel1;
t->sel1=sel0;
}
}
/*
* Clear the area following the last displayed character
*/
void tw_clrend(Textwin *t){
Point ul;
int y;
ul=t->loc[t->bot-t->top];
y=ul.y+t->hgt;
if(y>t->r.max.y) y=t->r.max.y;
pl_clr(t->b, Rpt(ul, Pt(t->r.max.x, y)));
ul=Pt(t->r.min.x, y);
pl_clr(t->b, Rpt(ul, t->r.max));
}
/*
* Move part of a line of text, truncating the source or padding
* the destination on the right if necessary.
*/
void tw_moverect(Textwin *t, Point uld, Point urd, Point uls, Point urs){
int sw, dw, d;
if(urs.y!=uls.y) urs=Pt(t->r.max.x, uls.y);
if(urd.y!=uld.y) urd=Pt(t->r.max.x, uld.y);
sw=uls.x-urs.x;
dw=uld.x-urd.x;
if(dw>sw){
d=dw-sw;
pl_clr(t->b, Rect(urd.x-d, urd.y, urd.x, urd.y+t->hgt));
dw=sw;
}
pl_cpy(t->b, uld, Rpt(uls, Pt(uls.x+dw, uls.y+t->hgt)));
}
/*
* Move a block of characters up or to the left:
* Identify contiguous runs of characters whose width doesn't change, and
* move them in one bitblt per run.
* If we get to a point where source and destination are x-aligned,
* they will remain x-aligned for the rest of the block.
* Then, if they are y-aligned, they're already in the right place.
* Otherwise, we can move them in three bitblts; one if all the
* remaining characters are on one line.
*/
void tw_moveup(Textwin *t, Point *dp, Point *sp, Point *esp){
Point uld, uls; /* upper left of destination/source */
int y;
while(sp!=esp && sp->x!=dp->x){
uld=*dp;
uls=*sp;
while(sp!=esp && sp->y==uls.y && dp->y==uld.y && sp->x-uls.x==dp->x-uld.x){
sp++;
dp++;
}
tw_moverect(t, uld, *dp, uls, *sp);
}
if(sp==esp || esp->y==dp->y) return;
if(esp->y==sp->y){ /* one line only */
pl_cpy(t->b, *dp, Rpt(*sp, Pt(esp->x, sp->y+t->hgt)));
return;
}
y=sp->y+t->hgt;
pl_cpy(t->b, *dp, Rpt(*sp, Pt(t->r.max.x, y)));
pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt),
Rect(t->r.min.x, y, t->r.max.x, esp->y));
y=dp->y+esp->y-sp->y;
pl_cpy(t->b, Pt(t->r.min.x, y),
Rect(t->r.min.x, esp->y, esp->x, esp->y+t->hgt));
}
/*
* Same as above, but moving down and in reverse order, so as not to overwrite stuff
* not moved yet.
*/
void tw_movedn(Textwin *t, Point *dp, Point *bsp, Point *esp){
Point *sp, urs, urd;
int dy;
dp+=esp-bsp;
sp=esp;
dy=dp->y-sp->y;
while(sp!=bsp && dp[-1].x==sp[-1].x){
--dp;
--sp;
}
if(dy!=0){
if(sp->y==esp->y)
pl_cpy(t->b, *dp, Rect(sp->x, sp->y, esp->x, esp->y+t->hgt));
else{
pl_cpy(t->b, Pt(t->r.min.x, sp->x+dy),
Rect(t->r.min.x, sp->y, esp->x, esp->y+t->hgt));
pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt),
Rect(t->r.min.x, sp->y+t->hgt, t->r.max.x, esp->y));
pl_cpy(t->b, *dp,
Rect(sp->x, sp->y, t->r.max.x, sp->y+t->hgt));
}
}
while(sp!=bsp){
urd=*dp;
urs=*sp;
while(sp!=bsp && sp[-1].y==sp[0].y && dp[-1].y==dp[0].y
&& sp[-1].x-sp[0].x==dp[-1].x-dp[0].x){
--sp;
--dp;
}
tw_moverect(t, *dp, urd, *sp, urs);
}
}
/*
* Move the given range of characters, already drawn on
* the given textwin, to the given location.
* Start and end must both index characters that are initially on-screen.
*/
void tw_relocate(Textwin *t, int first, int last, Point dst){
Point *srcloc;
int nbyte;
if(first<t->top || last<first || t->bot<last) return;
nbyte=(last-first+1)*sizeof(Point);
srcloc=malloc(nbyte);
if(srcloc==0) return;
memmove(srcloc, &t->loc[first-t->top], nbyte);
tw_setloc(t, first, last, dst);
if(tw_before(t, dst, srcloc[0]))
tw_moveup(t, t->loc+first-t->top, srcloc, srcloc+(last-first));
else
tw_movedn(t, t->loc+first-t->top, srcloc, srcloc+(last-first));
}
/*
* Replace the runes with indices from r0 to r1-1 with the text
* pointed to by text, and with length ntext.
* Open up a hole in t->text, t->loc.
* Insert new text, calculate their locs (save the extra loc that's overwritten first)
* (swap saved & overwritten locs)
* move tail.
* calc locs and draw new text after tail, if necessary.
* draw new text, if necessary
*/
void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins){
int olen, nlen, tlen, dtop;
Rune *ntext;
olen=t->etext-t->text;
nlen=olen+r0-r1+nins;
tlen=t->eslack-t->text;
if(nlen>tlen){
tlen=nlen+100;
ntext=malloc(tlen*sizeof(Rune));
memmove(ntext, t->text, r0*sizeof(Rune));
memmove(ntext+r0+nins, t->text+r1, (olen-r1)*sizeof(Rune));
t->text=ntext;
t->eslack=ntext+tlen;
}
else if(olen!=nlen)
memmove(t->text+r0+nins, t->text+r1, (olen-r1)*sizeof(Rune));
if(nins!=0) /* ins can be 0 if nins==0 */
memmove(t->text+r0, ins, nins*sizeof(Rune));
t->etext=t->text+nlen;
if(r0>t->bot) /* insertion is completely below visible text */
return;
if(r1<t->top){ /* insertion is completely above visible text */
dtop=nlen-olen;
t->top+=dtop;
t->bot+=dtop;
return;
}
if(1 || t->bot<=r0+nins){ /* no useful text on screen below r0 */
if(r0<=t->top) /* no useful text above, either */
t->top=r0;
t->bot=tw_setloc(t, r0, nlen, t->loc[r0-t->top]);
tw_draw(t, r0, t->bot);
tw_clrend(t);
return;
}
/*
* code for case where there is useful text below is missing (see `1 ||' above)
*/
}
/*
* This works but is stupid.
*/
void twscroll(Textwin *t, int top){
while(top!=0 && t->text[top-1]!='\n') --top;
t->top=top;
t->bot=tw_setloc(t, top, t->etext-t->text, t->r.min);
tw_draw(t, t->top, t->bot);
tw_clrend(t);
}
void twreshape(Textwin *t, Rectangle r){
t->r=r;
t->bot=tw_setloc(t, t->top, t->etext-t->text, t->r.min);
tw_draw(t, t->top, t->bot);
tw_clrend(t);
}
Textwin *twnew(Image *b, Font *f, Rune *text, int ntext){
Textwin *t;
t=malloc(sizeof(Textwin));
if(t==0) return 0;
t->text=malloc((ntext+100)*sizeof(Rune));
if(t->text==0){
free(t);
return 0;
}
t->loc=malloc(100*sizeof(Point));
if(t->loc==0){
free(t->text);
free(t);
return 0;
}
t->eloc=t->loc+100;
t->etext=t->text+ntext;
t->eslack=t->etext+100;
if(ntext) memmove(t->text, text, ntext*sizeof(Rune));
t->top=0;
t->bot=0;
t->sel0=0;
t->sel1=0;
t->b=b;
t->font=f;
t->hgt=f->height;
t->mintab=stringwidth(f, "0");
t->tabstop=8*t->mintab;
return t;
}
void twfree(Textwin *t){
free(t->loc);
free(t->text);
free(t);
}
/*
* Correct the character locations in a textwin after the panel is moved.
* This horrid hack would not be necessary if loc values were relative
* to the panel, rather than absolute.
*/
void twmove(Textwin *t, Point d){
Point *lp;
t->r = rectaddpt(t->r, d);
for(lp=t->loc; lp<t->eloc; lp++)
*lp = addpt(*lp, d);
}

View File

@ -0,0 +1,30 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
/*
* This is the same definition that 8½ uses
*/
int pl_idchar(int c){
if(c<=' '
|| 0x7F<=c && c<=0xA0
|| utfrune("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c))
return 0;
return 1;
}
int pl_rune1st(int c){
return (c&0xc0)!=0x80;
}
char *pl_nextrune(char *s){
do s++; while(!pl_rune1st(*s));
return s;
}
int pl_runewidth(Font *f, char *s){
char r[4], *t;
t=r;
do *t++=*s++; while(!pl_rune1st(*s));
*t='\0';
return stringwidth(f, r);
}

35
sys/src/cmd/mothra/mkfile Normal file
View File

@ -0,0 +1,35 @@
</$objtype/mkfile
TARG=mothra
LIB=libpanel/libpanel.$O.a
CFILES= \
cistr.c \
crackurl.c \
file.c \
filetype.c \
forms.c \
ftp.c \
getpix.c \
gopher.c \
html.syntax.c \
http.c \
mothra.c \
rdhtml.c \
auth.c \
OFILES=${CFILES:%.c=%.$O} version.$O
HFILES=mothra.h html.h tcs.h libpanel/panel.h libpanel/rtext.h
BIN=/$objtype/bin
</sys/src/cmd/mkone
CFLAGS=-Dplan9 -Ilibpanel
version.c: $CFILES
date|sed 's/^... //;s/ +/-/g;s/.*/char version[]="&";/' >version.c
$LIB:V:
cd libpanel
mk
clean nuke:V:
@{ cd libpanel; mk $target }
rm -f *.[$OS] [$OS].out $TARG

1072
sys/src/cmd/mothra/mothra.c Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

143
sys/src/cmd/mothra/mothra.h Normal file
View File

@ -0,0 +1,143 @@
enum{
NWWW=64, /* # of pages we hold in the log */
NNAME=512,
NLINE=256,
NAUTH=128,
NTITLE=81, /* length of title (including nul at end) */
NLABEL=50, /* length of option name in forms */
NREDIR=10, /* # of redirections we'll tolerate before declaring a loop */
};
typedef struct Action Action;
typedef struct Url Url;
typedef struct Www Www;
typedef struct Scheme Scheme;
typedef struct Field Field;
struct Scheme{
char *name;
int type;
int flags;
int port;
};
struct Action{
char *image;
Field *field;
char *link;
char *name;
int ismap;
int width;
int height;
};
struct Url{
char fullname[NNAME];
Scheme *scheme;
char ipaddr[NNAME];
char reltext[NNAME];
char tag[NNAME];
char redirname[NNAME];
char autharg[NAUTH];
char authtype[NTITLE];
char charset[NNAME];
int port;
int access;
int type;
int map; /* is this an image map? */
int ssl;
};
struct Www{
Url *url;
Url *base;
void *pix;
void *form;
char title[NTITLE];
Rtext *text;
int yoffs;
int changed; /* reader sets this every time it updates page */
int finished; /* reader sets this when done */
int alldone; /* page will not change further -- used to adjust cursor */
};
/*
* url reference types -- COMPRESS and GUNZIP are flags that can modify any other type
* Changing these in a non-downward compatible way spoils cache entries
*/
enum{
GIF=1,
HTML,
JPEG,
PIC,
TIFF,
AUDIO,
PLAIN,
XBM,
POSTSCRIPT,
FORWARD,
PDF,
SUFFIX,
ZIP,
PNG,
COMPRESS=16,
GUNZIP=32,
COMPRESSION=16+32,
};
/*
* url access types
*/
enum{
HTTP=1,
FTP,
FILE,
TELNET,
MAILTO,
GOPHER,
};
/*
* authentication types
*/
enum{
ANONE,
ABASIC,
};
Image *hrule, *bullet, *linespace;
char home[512]; /* where to put files */
int chrwidth; /* nominal width of characters in font */
Panel *text; /* Panel displaying the current www page */
int debug; /* command line flag */
/*
* HTTP methods
*/
enum{
GET=1,
POST,
};
void plrdhtml(char *, int, Www *);
void plrdplain(char *, int, Www *);
void htmlerror(char *, int, char *, ...); /* user-supplied routine */
void crackurl(Url *, char *, Url *);
void getpix(Rtext *, Www *);
int pipeline(char *, int);
int urlopen(Url *, int, char *);
void getfonts(void);
void *emalloc(int);
void *emallocz(int, int);
void setbitmap(Rtext *);
void message(char *, ...);
int ftp(Url *);
int http(Url *, int, char *);
int gopher(Url *);
int cistrcmp(char *, char *);
int cistrncmp(char *, char *, int);
int suffix2type(char *);
int content2type(char *, char *);
int encoding2type(char *);
void mkfieldpanel(Rtext *);
void geturl(char *, int, char *, int, int);
int dir2html(char *, int);
int auth(Url*, char*, int);
char version[];

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

1091
sys/src/cmd/mothra/rdhtml.c Normal file

File diff suppressed because it is too large Load Diff

172
sys/src/cmd/mothra/tcs.h Normal file
View File

@ -0,0 +1,172 @@
/* mapping from web charset to tcs */
char *tcs[] ={
"iso_8859-1:1987", "8859-1",
"iso-ir-100", "8859-1",
"iso_8859-1", "8859-1",
"iso-8859-1", "8859-1",
"latin1", "8859-1",
"l1", "8859-1",
"ibm819", "8859-1",
"cp819", "8859-1",
"csisolatin1", "8859-1",
"iso_8859-2:1987", "8859-2",
"iso-ir-101", "8859-2",
"iso_8859-2", "8859-2",
"iso-8859-2", "8859-2",
"latin2", "8859-2",
"l2", "8859-2",
"csisolatin2", "8859-2",
"iso_8859-3:1988", "8859-3",
"iso-ir-109", "8859-3",
"iso_8859-3", "8859-3",
"iso-8859-3", "8859-3",
"latin3", "8859-3",
"l3", "8859-3",
"csisolatin3", "8859-3",
"iso_8859-4:1988", "8859-4",
"iso-ir-110", "8859-4",
"iso_8859-4", "8859-4",
"iso-8859-4", "8859-4",
"latin4", "8859-4",
"l4", "8859-4",
"csisolatin4", "8859-4",
"iso_8859-5:1988", "8859-5",
"iso-ir-144", "8859-5",
"iso_8859-5", "8859-5",
"iso-8859-5", "8859-5",
"cyrillic", "8859-5",
"csisolatincyrillic", "8859-5",
"iso_8859-6:1987", "8859-6",
"iso-ir-127", "8859-6",
"iso_8859-6", "8859-6",
"iso-8859-6", "8859-6",
"ecma-114", "8859-6",
"asmo-708", "8859-6",
"arabic", "8859-6",
"csisolatinarabic", "8859-6",
"iso_8859-7:1987", "8859-7",
"iso-ir-126", "8859-7",
"iso_8859-7", "8859-7",
"iso-8859-7", "8859-7",
"elot_928", "8859-7",
"ecma-118", "8859-7",
"greek", "8859-7",
"greek8", "8859-7",
"csisolatingreek", "8859-7",
"iso_8859-8:1988", "8859-8",
"iso-ir-138", "8859-8",
"iso_8859-8", "8859-8",
"iso-8859-8", "8859-8",
"hebrew", "8859-8",
"csisolatinhebrew", "8859-8",
"iso_8859-9:1989", "8859-9",
"iso-ir-148", "8859-9",
"iso_8859-9", "8859-9",
"iso-8859-9", "8859-9",
"latin5", "8859-9",
"l5", "8859-9",
"csisolatin5", "8859-9",
"iso-8859-15", "8859-15",
"iso_8859-15", "8859-15",
"latin-9", "8859-15",
"ansi_x3.4-1968", "ascii",
"iso-ir-6", "ascii",
"ansi_x3.4-1986", "ascii",
"iso_646.irv:1991", "ascii",
"ascii", "ascii",
"iso646-us", "ascii",
"us-ascii", "ascii",
"us", "ascii",
"ibm367", "ascii",
"cp367", "ascii",
"csascii", "ascii",
"big5", "big5",
"csbig5", "big5",
"ibm437", "ibm437",
"cp437", "ibm437",
"437", "ibm437",
"cspc8codepage437", "ibm437",
"ibm850", "ibm850",
"cp850", "ibm850",
"850", "ibm850",
"cspc850multilingual", "ibm850",
"ibm852", "ibm852",
"cp852", "ibm852",
"852", "ibm852",
"cspcp852", "ibm852",
"ibm855", "ibm855",
"cp855", "ibm855",
"855", "ibm855",
"csibm855", "ibm855",
"ibm857", "ibm857",
"cp857", "ibm857",
"857", "ibm857",
"csibm857", "ibm857",
"ibm862", "ibm862",
"cp862", "ibm862",
"862", "ibm862",
"cspc862latinhebrew", "ibm862",
"ibm866", "ibm866",
"cp866", "ibm866",
"866", "ibm866",
"csibm866", "ibm866",
"windows-1250", "windows-1250",
"windows-1251", "windows-1251",
"windows-1252", "windows-1252",
"windows-1253", "windows-1253",
"windows-1254", "windows-1254",
"windows-1255", "windows-1255",
"windows-1256", "windows-1256",
"windows-1257", "windows-1257",
"windows-1258", "windows-1258",
"ks_c_5601-1987", "euc-k",
"iso-ir-149", "euc-k",
"ks_c_5601-1989", "euc-k",
"ksc_5601", "euc-k",
"korean", "euc-k",
"csksc56011987", "euc-k",
"euc-kr", "euc-k",
"cseuckr", "euc-k",
"gb2312", "gb",
"gbk", "gbk",
"csgb2312", "gb",
"gb_2312-80", "gb",
"iso-ir-58", "gb",
"chinese", "gb",
"csiso58gb231280", "gb",
"iso-2022-jp", "jis-kanji",
"csiso2022jp", "jis-kanji",
"koi8-r", "koi8",
"cskoi8r", "koi8",
"macintosh", "macrom",
"mac", "macrom",
"csmacintosh", "macrom",
"ibm865", "msdos2",
"cp865", "msdos2",
"865", "msdos2",
"csibm865", "msdos2",
"shift_jis", "ms-kanji",
"ms_kanji", "ms-kanji",
"csshiftjis", "ms-kanji",
"sen_850200_b", "sf1",
"iso-ir-10", "sf1",
"fi", "sf1",
"iso646-fi", "sf1",
"iso646-se", "sf1",
"se", "sf1",
"csiso10swedish", "sf1",
"sen_850200_c", "sf2",
"iso-ir-11", "sf2",
"iso646-se2", "sf2",
"se2", "sf2",
"csiso11swedishfornames", "sf2",
"tis-620", "tis",
"extended_unix_code_packed_format_for_japanese", "ujis",
"cseucpkdfmtjapanese", "ujis",
"euc-jp", "ujis",
"iso-10646-utf-1", "utf1",
"csiso10646utf1", "utf1",
"viscii", "viscii",
"csviscii", "viscii",
nil, nil,
};

View File

@ -0,0 +1,70 @@
#include <u.h>
#include <libc.h>
void *emalloc(int n){
void *p;
p=malloc(n);
if(p==0){
fprint(2, "can't malloc\n");
exits("no mem");
}
return p;
}
void urlcanon(char *name){
char *s, *t;
char **comp, **p, **q;
int rooted;
rooted=name[0]=='/';
/*
* Break the name into a list of components
*/
comp=emalloc(strlen(name)*sizeof(char *));
p=comp;
*p++=name;
for(s=name;;s++){
if(*s=='/'){
*p++=s+1;
*s='\0';
}
else if(*s=='\0')
break;
}
*p=0;
/*
* go through the component list, deleting components that are empty (except
* the last component) or ., and any .. and its non-.. predecessor.
*/
p=q=comp;
while(*p){
if(strcmp(*p, "")==0 && p[1]!=0
|| strcmp(*p, ".")==0)
p++;
else if(strcmp(*p, "..")==0 && q!=comp && strcmp(q[-1], "..")!=0){
--q;
p++;
}
else
*q++=*p++;
}
*q=0;
/*
* rebuild the path name
*/
s=name;
if(rooted) *s++='/';
for(p=comp;*p;p++){
t=*p;
while(*t) *s++=*t++;
if(p[1]!=0) *s++='/';
}
*s='\0';
free(comp);
}
void main(int argc, char *argv[]){
int i;
for(i=1;i!=argc;i++){
print("%s: ", argv[i]);
urlcanon(argv[i]);
print("%s\n", argv[i]);
}
exits(0);
}

View File

@ -0,0 +1 @@
char version[]="Sep-4-19:07:30-CET-2011";