add mothra
parent
f6e73a6a22
commit
6842f87125
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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("<"); break;
|
||||
case '>': print(">"); break;
|
||||
case '&': print("&"); break;
|
||||
case '"': print("""); 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);
|
||||
}
|
|
@ -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½ 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>
|
|
@ -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 *);
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
|
@ -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 |
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
char version[]="Sep-4-19:07:30-CET-2011";
|
Loading…
Reference in New Issue