#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <control.h>
#include <bio.h>
typedef struct
{
char **s;
int n;
}Lines;
typedef struct
{
char *name;
char *user;
Lines detail[4];
}Pkg;
typedef struct
{
char *cmd;
int p[2]; /* p[1] is write to program; p[0] set to prog fd 0*/
int q[2]; /* q[0] is read from program; q[1] set to prog fd 1 */
Channel *sync; /* chan(ulong) */
}Exec;
char *action[] = {
"Install",
"Exit",
};
enum {
Info,
Files,
Depends,
Changes,
};
char *detail[] = {
"Info",
"Files",
"Depends",
"Changes",
};
#define STACK 1024*16
int mainstacksize = STACK;
int ctldeletequits = 1;
Controlset *cs;
Channel *cevent, *cstatus;
Pkg *pkg;
int npkg;
int nlog;
Pkg *curpkg;
void
status(char *fmt, ...)
{
va_list arg;
char *p;
va_start(arg, fmt);
p = vsmprint(fmt, arg);
va_end(arg);
chanprint(cs->ctl, "log_text add %q", p);
chanprint(cs->ctl, "log_text scroll 1");
chanprint(cs->ctl, "log_scroll max %d", ++nlog);
free(p);
}
void
addline(Lines *l, char *s)
{
if(l->n%16 == 0)
l->s = ctlrealloc(l->s, (l->n+16)*sizeof(char*));
l->s[l->n++] = ctlstrdup(s);
}
void
execproc(void *v)
{
Channel *sync;
Exec *e;
int p[2], q[2];
char *cmd;
threadsetname("execproc");
e = v;
p[0] = e->p[0];
p[1] = e->p[1];
q[0] = e->q[0];
q[1] = e->q[1];
cmd = e->cmd;
sync = e->sync;
rfork(RFFDG);
free(e);
dup(p[0], 0);
close(p[0]);
close(p[1]);
if(q[0]){
dup(q[1], 1);
close(q[0]);
close(q[1]);
}
if(1)
close(2);
procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
sysfatal("can't exec");
}
void
procruncmd(void *v)
{
Exec *e;
Channel *sync, *chan;
Biobuf bi;
char *s, *cmd;
int p[2], q[2];
void **a;
a = v;
cmd = a[0];
chan = a[1];
free(a);
if(pipe(p)<0 || pipe(q)<0)
sysfatal("can't create pipe: %r");
sync = chancreate(sizeof(ulong), 0);
if(sync == nil)
sysfatal("can't create channel: %r");
e = ctlmalloc(sizeof(Exec));
e->p[0] = p[0];
e->p[1] = p[1];
e->q[0] = q[0];
e->q[1] = q[1];
e->cmd = cmd;
e->sync = sync;
proccreate(execproc, e, STACK);
recvul(sync);
chanfree(sync);
close(p[0]);
close(p[1]);
close(q[1]);
Binit(&bi, q[0], OREAD);
while((s=Brdstr(&bi, '\n', 1)) != nil)
sendp(chan, s);
Bterm(&bi);
sendp(chan, nil);
close(q[0]);
free(s);
}
void
runcmd(char *cmd, Channel* chan)
{
void **a;
a = ctlmalloc(sizeof(void*)*2);
a[0] = cmd;
a[1] = chan;
proccreate(procruncmd, a, STACK);
}
void
getfile(int f)
{
Lines *l;
char *cmd, *s;
Channel *c;
assert(curpkg != nil);
l = &curpkg->detail[f];
if(l->n != 0)
return;
status("fetching %s for packages %s/%s", detail[f], curpkg->user, curpkg->name);
cmd = nil;
switch(f){
case Info:
cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/inf | fmt", curpkg->user, curpkg->name);
break;
case Files:
cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/db", curpkg->user, curpkg->name);
break;
case Depends:
cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/dep", curpkg->user, curpkg->name);
break;
case Changes:
cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/chg", curpkg->user, curpkg->name);
break;
}
c = chancreate(sizeof(char *), 0);
runcmd(cmd, c);
while((s=recvp(c)) != nil)
addline(l, s);
chanfree(c);
}
void
setdetail(int d)
{
Lines *l;
int i;
chanprint(cs->ctl, "detail_text clear");
for(i=0; i<nelem(detail); i++)
chanprint(cs->ctl, "%s value %d", detail[i], i==d? 1: 0);
if(curpkg == nil)
return;
getfile(d);
l = &curpkg->detail[d];
if(l->n == -1)
return;
for(i=0; i<l->n; i++)
chanprint(cs->ctl, "detail_text add %q", l->s[i]);
chanprint(cs->ctl, "detail_scroll max %d", l->n);
}
int
pkgcmp(void *a, void *b)
{
return strcmp(((Pkg*)a)->name, ((Pkg*)b)->name);
}
void
mkpkgs(void)
{
Channel *c;
char *cmd, *s, *p;
int i, w, h, mw;
status("fetching package list...");
c = chancreate(sizeof(char *), 0);
cmd = smprint("contrib/listall");
runcmd(cmd, c);
npkg = 0;
pkg = nil;
while((s=recvp(c)) != nil) {
pkg = ctlrealloc(pkg, sizeof(Pkg)*(npkg+1));
p = strchr(s, '/');
if(p == nil)
sysfatal("bad list file: %r");
*p = '\0';
pkg[npkg].user = ctlstrdup(s);
pkg[npkg].name = ctlstrdup(p+1);
memset(pkg[npkg].detail, 0, sizeof(Lines)*4);
npkg++;
free(s);
}
chanfree(c);
qsort(pkg, npkg, sizeof(Pkg), pkgcmp);
mw = 0;
for(i=0; i<npkg; i++){
chanprint(cs->ctl, "list_text add %q", pkg[i].name);
w = stringwidth(font, pkg[i].name);
if(w > mw)
mw = w;
}
w = mw+4;
h = font->height+4;
chanprint(cs->ctl, "list_text size %d %d %d %d\n", w, h, w, 2048);
chanprint(cs->ctl, "list_scroll max %d", npkg);
status("done.");
resizecontrolset(cs);
}
void
install(void)
{
char *s;
if(curpkg != nil){
status("trying to install %s/%s", curpkg->user, curpkg->name);
s = smprint("contrib/install %s/%s", curpkg->user, curpkg->name);
runcmd(s, cstatus);
}
chanprint(cs->ctl, "Install value 0");
}
void
initcolors(void)
{
Image *i;
i = allocimagemix(display, DPaleyellow, DWhite);
namectlimage(i, "textbg");
i = allocimagemix(display, DPalebluegreen, DWhite);
namectlimage(i, "butbg");
i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
namectlimage(i, "scrollbg");
i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
namectlimage(i, "selbg");
i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DBlue);
namectlimage(i, "pressbg");
}
void
mkbutton(char *s)
{
int w, h;
createtextbutton(cs, s);
chanprint(cs->ctl, "%s text %s", s, s);
chanprint(cs->ctl, "%s pressedtextcolor pressbg", s);
chanprint(cs->ctl, "%s image butbg", s);
chanprint(cs->ctl, "%s mask transparent", s);
chanprint(cs->ctl, "%s align center", s);
chanprint(cs->ctl, "%s border 1", s);
w = stringwidth(font, s)+4;
h = font->height + 4;
chanprint(cs->ctl, "%s size %d %d %d %d", s, w, h, 1024, h);
controlwire(controlcalled(s), "event", cevent);
}
void
mktext(char *s, char *sbg)
{
createtext(cs, s);
chanprint(cs->ctl, "%s border 1", s);
chanprint(cs->ctl, "%s image textbg", s);
chanprint(cs->ctl, "%s selectcolor %s", s, sbg);
}
void
mkscroll(char *s, char *text)
{
createslider(cs, s);
chanprint(cs->ctl, "%s border 1", s);
chanprint(cs->ctl, "%s image scrollbg", s);
chanprint(cs->ctl, "%s indicatorcolor textbg", s);
chanprint(cs->ctl, "%s size 12 10 12 2048", s);
chanprint(cs->ctl, "%s absolute 0", s);
chanprint(cs->ctl, "%s format '%%q: %s topline %%d'", s, text);
controlwire(controlcalled(s), "event", cs->ctl);
}
void
mklabel(char *s)
{
int w, h;
createlabel(cs, s);
chanprint(cs->ctl, "%s align center", s);
chanprint(cs->ctl, "%s border 1", s);
chanprint(cs->ctl, "%s image paleyellow", s);
chanprint(cs->ctl, "%s value %q", s, s);
w = stringwidth(font, s)+4;
h = font->height+4;
chanprint(cs->ctl, "%s size %d %d %d %d", s, w, h, 1024, h);
}
void
mkcontrols(void)
{
int i;
createcolumn(cs, "window");
createrow(cs, "browser");
chanprint(cs->ctl, "browser separation %d", 1);
createrow(cs, "log");
createrow(cs, "buttons");
chanprint(cs->ctl, "window add browser log buttons");
createcolumn(cs, "list");
mklabel("Packages");
mktext("list_text", "selbg");
controlwire(controlcalled("list_text"), "event", cevent);
mkscroll("list_scroll", "list_text");
chanprint(cs->ctl, "list add Packages list_text");
createrow(cs, "detail");
createcolumn(cs, "detail_content");
createrow(cs, "detail_buttons");
for(i=0; i<nelem(detail); i++){
mkbutton(detail[i]);
chanprint(cs->ctl, "detail_buttons add %s", detail[i]);
}
mktext("detail_text", "textbg");
mkscroll("detail_scroll", "detail_text");
chanprint(cs->ctl, "detail_content add detail_buttons detail_text");
chanprint(cs->ctl, "detail add detail_scroll detail_content");
mktext("log_text", "white");
chanprint(cs->ctl, "log_text size %d %d %d %d\n", 10, (font->height*4)+4, 2048, (font->height*4)+4);
mkscroll("log_scroll", "log_text");
chanprint(cs->ctl, "log add log_scroll log_text");
for(i=0; i<nelem(action); i++){
mkbutton(action[i]);
chanprint(cs->ctl, "buttons add %s", action[i]);
}
chanprint(cs->ctl, "browser add list_scroll list detail");
activate(controlcalled("window"));
}
void
resizecontrolset(Controlset *)
{
if(getwindow(display, Refnone) < 0)
sysfatal("resize failed: %r");
chanprint(cs->ctl, "window size");
chanprint(cs->ctl, "window rect %R", screen->r);
chanprint(cs->ctl, "log_scroll vis '%d'", Dy(controlcalled("log_scroll")->rect)/font->height);
chanprint(cs->ctl, "list_scroll vis '%d'", Dy(controlcalled("list_scroll")->rect)/font->height);
chanprint(cs->ctl, "detail_scroll vis '%d'", Dy(controlcalled("detail_scroll")->rect)/font->height);
chanprint(cs->ctl, "window show");
}
void
work(void)
{
char *s, *args[8];
int n, i;
enum { AEvent, AStatus, NALT};
static Alt alts[NALT];
alts[AEvent].c = cevent;
alts[AEvent].v = &s;
alts[AEvent].op = CHANRCV;
alts[AStatus].c = cstatus;
alts[AStatus].v = &s;
alts[AStatus].op = CHANRCV;
alts[NALT].op = CHANEND;
for(;;){
switch(alt(alts)){
case AStatus:
if(s){
status(s);
free(s);
}
break;
case AEvent:
n = tokenize(s, args, nelem(args));
if(n == 3){
if(strcmp(args[0], "Exit:")==0)
threadexitsall(nil);
if(strcmp(args[0], "Install:")==0){
install();
}else{
for(i=0; i<nelem(detail); i++)
if(strncmp(args[0], detail[i], strlen(detail[i])) == 0){
setdetail(i);
break;
}
}
}else if(n==4 && strcmp(args[0], "list_text:")==0){
i = atoi(args[2]);
if(i>=0 && i<npkg)
curpkg = &pkg[i];
setdetail(Info);
}
free(s);
break;
}
}
}
void
threadmain(int argc, char *argv[])
{
ARGBEGIN{
}ARGEND
if(initdraw(0, 0, "contrib") < 0)
sysfatal("can't open display");
initcontrols();
cs = newcontrolset(screen, nil, nil, nil);
cs->clicktotype = 1;
cevent = chancreate(sizeof(char *), 0);
cstatus = chancreate(sizeof(char *), 0);
if(cevent==nil || cstatus==nil)
sysfatal("can't create channels. %r");
initcolors();
mkcontrols();
resizecontrolset(cs);
mkpkgs();
work();
}
|