More files related to user-level file servers.

Also add acme!
This commit is contained in:
rsc 2003-12-11 17:50:28 +00:00
parent 32f69c36e0
commit b3994ec5c7
35 changed files with 14133 additions and 0 deletions

949
src/cmd/acme/acme.c Normal file
View File

@ -0,0 +1,949 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
/* for generating syms in mkfile only: */
#include <bio.h>
#include "edit.h"
void mousethread(void*);
void keyboardthread(void*);
void waitthread(void*);
void xfidallocthread(void*);
void newwindowthread(void*);
void plumbproc(void*);
Reffont **fontcache;
int nfontcache;
char wdir[512] = ".";
Reffont *reffonts[2];
int snarffd = -1;
int mainpid;
int plumbsendfd;
int plumbeditfd;
enum{
NSnarf = 1000 /* less than 1024, I/O buffer size */
};
Rune snarfrune[NSnarf+1];
char *fontnames[2] =
{
"/lib/font/bit/lucidasans/euro.8.font",
"/lib/font/bit/lucm/unicode.9.font",
};
Command *command;
void acmeerrorinit(void);
void readfile(Column*, char*);
int shutdown(void*, char*);
void
derror(Display *d, char *errorstr)
{
USED(d);
error(errorstr);
}
void
threadmain(int argc, char *argv[])
{
int i;
char *p, *loadfile;
char buf[256];
Column *c;
int ncol;
Display *d;
rfork(RFENVG|RFNAMEG);
ncol = -1;
loadfile = nil;
ARGBEGIN{
case 'b':
bartflag = TRUE;
break;
case 'c':
p = ARGF();
if(p == nil)
goto Usage;
ncol = atoi(p);
if(ncol <= 0)
goto Usage;
break;
case 'f':
fontnames[0] = ARGF();
if(fontnames[0] == nil)
goto Usage;
break;
case 'F':
fontnames[1] = ARGF();
if(fontnames[1] == nil)
goto Usage;
break;
case 'l':
loadfile = ARGF();
if(loadfile == nil)
goto Usage;
break;
default:
Usage:
fprint(2, "usage: acme -c ncol -f fontname -F fixedwidthfontname -l loadfile\n");
exits("usage");
}ARGEND
cputype = getenv("cputype");
objtype = getenv("objtype");
home = getenv("home");
p = getenv("tabstop");
if(p != nil){
maxtab = strtoul(p, nil, 0);
free(p);
}
if(maxtab == 0)
maxtab = 4;
if(loadfile)
rowloadfonts(loadfile);
putenv("font", fontnames[0]);
snarffd = open("/dev/snarf", OREAD|OCEXEC);
/*
if(cputype){
sprint(buf, "/acme/bin/%s", cputype);
bind(buf, "/bin", MBEFORE);
}
bind("/acme/bin", "/bin", MBEFORE);
*/
getwd(wdir, sizeof wdir);
/*
if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
fprint(2, "acme: can't open display: %r\n");
exits("geninitdraw");
}
*/
if(initdraw(derror, fontnames[0], "acme") < 0){
fprint(2, "acme: can't open display: %r\n");
exits("initdraw");
}
d = display;
font = d->defaultfont;
reffont.f = font;
reffonts[0] = &reffont;
incref(&reffont.ref); /* one to hold up 'font' variable */
incref(&reffont.ref); /* one to hold up reffonts[0] */
fontcache = emalloc(sizeof(Reffont*));
nfontcache = 1;
fontcache[0] = &reffont;
iconinit();
timerinit();
rxinit();
cwait = threadwaitchan();
ccommand = chancreate(sizeof(Command**), 0);
ckill = chancreate(sizeof(Rune*), 0);
cxfidalloc = chancreate(sizeof(Xfid*), 0);
cxfidfree = chancreate(sizeof(Xfid*), 0);
cnewwindow = chancreate(sizeof(Channel*), 0);
cerr = chancreate(sizeof(char*), 0);
cedit = chancreate(sizeof(int), 0);
cexit = chancreate(sizeof(int), 0);
if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil){
fprint(2, "acme: can't create initial channels: %r\n");
exits("channels");
}
mousectl = initmouse(nil, screen);
if(mousectl == nil){
fprint(2, "acme: can't initialize mouse: %r\n");
exits("mouse");
}
mouse = &mousectl->m;
keyboardctl = initkeyboard(nil);
if(keyboardctl == nil){
fprint(2, "acme: can't initialize keyboard: %r\n");
exits("keyboard");
}
mainpid = getpid();
plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
if(plumbeditfd < 0)
fprint(2, "acme: can't initialize plumber: %r\n");
else{
cplumb = chancreate(sizeof(Plumbmsg*), 0);
proccreate(plumbproc, nil, STACK);
}
plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
fsysinit();
#define WPERCOL 8
disk = diskinit();
if(loadfile)
rowload(&row, loadfile, TRUE);
else{
rowinit(&row, screen->clipr);
if(ncol < 0){
if(argc == 0)
ncol = 2;
else{
ncol = (argc+(WPERCOL-1))/WPERCOL;
if(ncol < 2)
ncol = 2;
}
}
if(ncol == 0)
ncol = 2;
for(i=0; i<ncol; i++){
c = rowadd(&row, nil, -1);
if(c==nil && i==0)
error("initializing columns");
}
c = row.col[row.ncol-1];
if(argc == 0)
readfile(c, wdir);
else
for(i=0; i<argc; i++){
p = utfrrune(argv[i], '/');
if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
readfile(c, argv[i]);
else
readfile(row.col[i/WPERCOL], argv[i]);
}
}
flushimage(display, 1);
acmeerrorinit();
threadcreate(keyboardthread, nil, STACK);
threadcreate(mousethread, nil, STACK);
threadcreate(waitthread, nil, STACK);
threadcreate(xfidallocthread, nil, STACK);
threadcreate(newwindowthread, nil, STACK);
threadnotify(shutdown, 1);
recvul(cexit);
killprocs();
threadexitsall(nil);
}
void
readfile(Column *c, char *s)
{
Window *w;
Rune rb[256];
int nb, nr;
Runestr rs;
w = coladd(c, nil, nil, -1);
cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
rs = cleanrname((Runestr){rb, nr});
winsetname(w, rs.r, rs.nr);
textload(&w->body, 0, s, 1);
w->body.file->mod = FALSE;
w->dirty = FALSE;
winsettag(w);
textscrdraw(&w->body);
textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
}
char *oknotes[] ={
"delete",
"hangup",
"kill",
"exit",
nil
};
int dumping;
int
shutdown(void *v, char *msg)
{
int i;
if(strcmp(msg, "sys: write on closed pipe") == 0)
return 1;
USED(v);
killprocs();
if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
dumping = TRUE;
rowdump(&row, nil);
}
for(i=0; oknotes[i]; i++)
if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
threadexitsall(msg);
print("acme: %s\n", msg);
abort();
return 0;
}
void
killprocs(void)
{
Command *c;
fsysclose();
// if(display)
// flushimage(display, 1);
for(c=command; c; c=c->next)
postnote(PNGROUP, c->pid, "hangup");
}
static int errorfd;
int erroutfd;
void
acmeerrorproc(void *v)
{
char *buf;
int n;
USED(v);
threadsetname("acmeerrorproc");
buf = emalloc(8192+1);
while((n=read(errorfd, buf, 8192)) >= 0){
buf[n] = '\0';
sendp(cerr, estrdup(buf));
}
}
void
acmeerrorinit(void)
{
int fd, pfd[2];
char buf[64];
if(pipe(pfd) < 0)
error("can't create pipe");
#if 0
sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
fd = create(acmeerrorfile, OWRITE, 0666);
if(fd < 0){
remove(acmeerrorfile);
fd = create(acmeerrorfile, OWRITE, 0666);
if(fd < 0)
error("can't create acmeerror file");
}
sprint(buf, "%d", pfd[0]);
write(fd, buf, strlen(buf));
close(fd);
/* reopen pfd[1] close on exec */
sprint(buf, "/fd/%d", pfd[1]);
errorfd = open(buf, OREAD|OCEXEC);
#endif
fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
erroutfd = pfd[0];
errorfd = pfd[1];
if(errorfd < 0)
error("can't re-open acmeerror file");
proccreate(acmeerrorproc, nil, STACK);
}
void
plumbproc(void *v)
{
Plumbmsg *m;
USED(v);
threadsetname("plumbproc");
for(;;){
m = plumbrecv(plumbeditfd);
if(m == nil)
threadexits(nil);
sendp(cplumb, m);
}
}
void
keyboardthread(void *v)
{
Rune r;
Timer *timer;
Text *t;
enum { KTimer, KKey, NKALT };
static Alt alts[NKALT+1];
USED(v);
alts[KTimer].c = nil;
alts[KTimer].v = nil;
alts[KTimer].op = CHANNOP;
alts[KKey].c = keyboardctl->c;
alts[KKey].v = &r;
alts[KKey].op = CHANRCV;
alts[NKALT].op = CHANEND;
timer = nil;
typetext = nil;
threadsetname("keyboardthread");
for(;;){
switch(alt(alts)){
case KTimer:
timerstop(timer);
t = typetext;
if(t!=nil && t->what==Tag){
winlock(t->w, 'K');
wincommit(t->w, t);
winunlock(t->w);
flushimage(display, 1);
}
alts[KTimer].c = nil;
alts[KTimer].op = CHANNOP;
break;
case KKey:
casekeyboard:
typetext = rowtype(&row, r, mouse->xy);
t = typetext;
if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
activecol = t->col;
if(t!=nil && t->w!=nil)
t->w->body.file->curtext = &t->w->body;
if(timer != nil)
timercancel(timer);
if(t!=nil && t->what==Tag) {
timer = timerstart(500);
alts[KTimer].c = timer->c;
alts[KTimer].op = CHANRCV;
}else{
timer = nil;
alts[KTimer].c = nil;
alts[KTimer].op = CHANNOP;
}
if(nbrecv(keyboardctl->c, &r) > 0)
goto casekeyboard;
flushimage(display, 1);
break;
}
}
}
void
mousethread(void *v)
{
Text *t, *argt;
int but;
uint q0, q1;
Window *w;
Plumbmsg *pm;
Mouse m;
char *act;
enum { MResize, MMouse, MPlumb, NMALT };
static Alt alts[NMALT+1];
USED(v);
threadsetname("mousethread");
alts[MResize].c = mousectl->resizec;
alts[MResize].v = nil;
alts[MResize].op = CHANRCV;
alts[MMouse].c = mousectl->c;
alts[MMouse].v = &mousectl->m;
alts[MMouse].op = CHANRCV;
alts[MPlumb].c = cplumb;
alts[MPlumb].v = &pm;
alts[MPlumb].op = CHANRCV;
if(cplumb == nil)
alts[MPlumb].op = CHANNOP;
alts[NMALT].op = CHANEND;
for(;;){
switch(alt(alts)){
case MResize:
if(getwindow(display, Refnone) < 0)
error("attach to window");
draw(screen, screen->r, display->white, nil, ZP);
scrlresize();
rowresize(&row, screen->clipr);
flushimage(display, 1);
break;
case MPlumb:
if(strcmp(pm->type, "text") == 0){
act = plumblookup(pm->attr, "action");
if(act==nil || strcmp(act, "showfile")==0)
plumblook(pm);
else if(strcmp(act, "showdata")==0)
plumbshow(pm);
}
flushimage(display, 1);
plumbfree(pm);
break;
case MMouse:
/*
* Make a copy so decisions are consistent; mousectl changes
* underfoot. Can't just receive into m because this introduces
* another race; see /sys/src/libdraw/mouse.c.
*/
m = mousectl->m;
qlock(&row.lk);
t = rowwhich(&row, m.xy);
if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
winlock(mousetext->w, 'M');
mousetext->eq0 = ~0;
wincommit(mousetext->w, mousetext);
winunlock(mousetext->w);
}
mousetext = t;
if(t == nil)
goto Continue;
w = t->w;
if(t==nil || m.buttons==0)
goto Continue;
but = 0;
if(m.buttons == 1)
but = 1;
else if(m.buttons == 2)
but = 2;
else if(m.buttons == 4)
but = 3;
barttext = t;
if(t->what==Body && ptinrect(m.xy, t->scrollr)){
if(but){
winlock(w, 'M');
t->eq0 = ~0;
textscroll(t, but);
winunlock(w);
}
goto Continue;
}
if(ptinrect(m.xy, t->scrollr)){
if(but){
if(t->what == Columntag)
rowdragcol(&row, t->col, but);
else if(t->what == Tag){
coldragwin(t->col, t->w, but);
if(t->w)
barttext = &t->w->body;
}
if(t->col)
activecol = t->col;
}
goto Continue;
}
if(m.buttons){
if(w)
winlock(w, 'M');
t->eq0 = ~0;
if(w)
wincommit(w, t);
else
textcommit(t, TRUE);
if(m.buttons & 1){
textselect(t);
if(w)
winsettag(w);
argtext = t;
seltext = t;
if(t->col)
activecol = t->col; /* button 1 only */
if(t->w!=nil && t==&t->w->body)
activewin = t->w;
}else if(m.buttons & 2){
if(textselect2(t, &q0, &q1, &argt))
execute(t, q0, q1, FALSE, argt);
}else if(m.buttons & 4){
if(textselect3(t, &q0, &q1))
look3(t, q0, q1, FALSE);
}
if(w)
winunlock(w);
goto Continue;
}
Continue:
flushimage(display, 1);
qunlock(&row.lk);
break;
}
}
}
/*
* There is a race between process exiting and our finding out it was ever created.
* This structure keeps a list of processes that have exited we haven't heard of.
*/
typedef struct Pid Pid;
struct Pid
{
int pid;
char msg[ERRMAX];
Pid *next;
};
void
waitthread(void *v)
{
Waitmsg *w;
Command *c, *lc;
uint pid;
int found, ncmd;
Rune *cmd;
char *err;
Text *t;
Pid *pids, *p, *lastp;
enum { WErr, WKill, WWait, WCmd, NWALT };
Alt alts[NWALT+1];
USED(v);
threadsetname("waitthread");
pids = nil;
alts[WErr].c = cerr;
alts[WErr].v = &err;
alts[WErr].op = CHANRCV;
alts[WKill].c = ckill;
alts[WKill].v = &cmd;
alts[WKill].op = CHANRCV;
alts[WWait].c = cwait;
alts[WWait].v = &w;
alts[WWait].op = CHANRCV;
alts[WCmd].c = ccommand;
alts[WCmd].v = &c;
alts[WCmd].op = CHANRCV;
alts[NWALT].op = CHANEND;
command = nil;
for(;;){
switch(alt(alts)){
case WErr:
qlock(&row.lk);
warning(nil, "%s", err);
free(err);
flushimage(display, 1);
qunlock(&row.lk);
break;
case WKill:
found = FALSE;
ncmd = runestrlen(cmd);
for(c=command; c; c=c->next){
/* -1 for blank */
if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
if(postnote(PNGROUP, c->pid, "kill") < 0)
warning(nil, "kill %S: %r\n", cmd);
found = TRUE;
}
}
if(!found)
warning(nil, "Kill: no process %S\n", cmd);
free(cmd);
break;
case WWait:
pid = w->pid;
lc = nil;
for(c=command; c; c=c->next){
if(c->pid == pid){
if(lc)
lc->next = c->next;
else
command = c->next;
break;
}
lc = c;
}
qlock(&row.lk);
t = &row.tag;
textcommit(t, TRUE);
if(c == nil){
/* helper processes use this exit status */
if(strncmp(w->msg, "libthread", 9) != 0){
p = emalloc(sizeof(Pid));
p->pid = pid;
strncpy(p->msg, w->msg, sizeof(p->msg));
p->next = pids;
pids = p;
}
}else{
if(search(t, c->name, c->nname)){
textdelete(t, t->q0, t->q1, TRUE);
textsetselect(t, 0, 0);
}
if(w->msg[0])
warning(c->md, "%s: %s\n", c->name, w->msg);
flushimage(display, 1);
}
qunlock(&row.lk);
free(w);
Freecmd:
if(c){
if(c->iseditcmd)
sendul(cedit, 0);
free(c->text);
free(c->name);
fsysdelid(c->md);
free(c);
}
break;
case WCmd:
/* has this command already exited? */
lastp = nil;
for(p=pids; p!=nil; p=p->next){
if(p->pid == c->pid){
if(p->msg[0])
warning(c->md, "%s\n", p->msg);
if(lastp == nil)
pids = p->next;
else
lastp->next = p->next;
free(p);
goto Freecmd;
}
lastp = p;
}
c->next = command;
command = c;
qlock(&row.lk);
t = &row.tag;
textcommit(t, TRUE);
textinsert(t, 0, c->name, c->nname, TRUE);
textsetselect(t, 0, 0);
flushimage(display, 1);
qunlock(&row.lk);
break;
}
}
}
void
xfidallocthread(void *v)
{
Xfid *xfree, *x;
enum { Alloc, Free, N };
static Alt alts[N+1];
USED(v);
threadsetname("xfidallocthread");
alts[Alloc].c = cxfidalloc;
alts[Alloc].v = nil;
alts[Alloc].op = CHANRCV;
alts[Free].c = cxfidfree;
alts[Free].v = &x;
alts[Free].op = CHANRCV;
alts[N].op = CHANEND;
xfree = nil;
for(;;){
switch(alt(alts)){
case Alloc:
x = xfree;
if(x)
xfree = x->next;
else{
x = emalloc(sizeof(Xfid));
x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
x->arg = x;
threadcreate(xfidctl, x->arg, STACK);
}
sendp(cxfidalloc, x);
break;
case Free:
x->next = xfree;
xfree = x;
break;
}
}
}
/* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
void
newwindowthread(void *v)
{
Window *w;
USED(v);
threadsetname("newwindowthread");
for(;;){
/* only fsysproc is talking to us, so synchronization is trivial */
recvp(cnewwindow);
w = makenewwindow(nil);
winsettag(w);
sendp(cnewwindow, w);
}
}
Reffont*
rfget(int fix, int save, int setfont, char *name)
{
Reffont *r;
Font *f;
int i;
r = nil;
if(name == nil){
name = fontnames[fix];
r = reffonts[fix];
}
if(r == nil){
for(i=0; i<nfontcache; i++)
if(strcmp(name, fontcache[i]->f->name) == 0){
r = fontcache[i];
goto Found;
}
f = openfont(display, name);
if(f == nil){
warning(nil, "can't open font file %s: %r\n", name);
return nil;
}
r = emalloc(sizeof(Reffont));
r->f = f;
fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
fontcache[nfontcache++] = r;
}
Found:
if(save){
incref(&r->ref);
if(reffonts[fix])
rfclose(reffonts[fix]);
reffonts[fix] = r;
free(fontnames[fix]);
fontnames[fix] = name;
}
if(setfont){
reffont.f = r->f;
incref(&r->ref);
rfclose(reffonts[0]);
font = r->f;
reffonts[0] = r;
incref(&r->ref);
iconinit();
}
incref(&r->ref);
return r;
}
void
rfclose(Reffont *r)
{
int i;
if(decref(&r->ref) == 0){
for(i=0; i<nfontcache; i++)
if(r == fontcache[i])
break;
if(i >= nfontcache)
warning(nil, "internal error: can't find font in cache\n");
else{
nfontcache--;
memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
}
freefont(r->f);
free(r);
}
}
Cursor boxcursor = {
{-7, -7},
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
};
void
iconinit(void)
{
Rectangle r;
Image *tmp;
/* Blue */
tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
tagcols[TEXT] = display->black;
tagcols[HTEXT] = display->black;
/* Yellow */
textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
textcols[TEXT] = display->black;
textcols[HTEXT] = display->black;
if(button){
freeimage(button);
freeimage(modbutton);
freeimage(colbutton);
}
r = Rect(0, 0, Scrollwid+2, font->height+1);
button = allocimage(display, r, screen->chan, 0, DNofill);
draw(button, r, tagcols[BACK], nil, r.min);
r.max.x -= 2;
border(button, r, 2, tagcols[BORD], ZP);
r = button->r;
modbutton = allocimage(display, r, screen->chan, 0, DNofill);
draw(modbutton, r, tagcols[BACK], nil, r.min);
r.max.x -= 2;
border(modbutton, r, 2, tagcols[BORD], ZP);
r = insetrect(r, 2);
tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
draw(modbutton, r, tmp, nil, ZP);
freeimage(tmp);
r = button->r;
colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
}
/*
* /dev/snarf updates when the file is closed, so we must open our own
* fd here rather than use snarffd
*/
/* rio truncates larges snarf buffers, so this avoids using the
* service if the string is huge */
#define MAXSNARF 100*1024
void
acmeputsnarf(void)
{
int fd, i, n;
if(snarffd<0 || snarfbuf.nc==0)
return;
if(snarfbuf.nc > MAXSNARF)
return;
fd = open("/dev/snarf", OWRITE);
if(fd < 0)
return;
for(i=0; i<snarfbuf.nc; i+=n){
n = snarfbuf.nc-i;
if(n >= NSnarf)
n = NSnarf;
bufread(&snarfbuf, i, snarfrune, n);
if(fprint(fd, "%.*S", n, snarfrune) < 0)
break;
}
close(fd);
}
void
acmegetsnarf()
{
int nulls;
if(snarfbuf.nc > MAXSNARF)
return;
if(snarffd < 0)
return;
seek(snarffd, 0, 0);
bufreset(&snarfbuf);
bufload(&snarfbuf, 0, snarffd, &nulls);
}

269
src/cmd/acme/addr.c Normal file
View File

@ -0,0 +1,269 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
enum
{
None = 0,
Fore = '+',
Back = '-',
};
enum
{
Char,
Line,
};
int
isaddrc(int r)
{
if(r && utfrune("0123456789+-/$.#,;", r)!=nil)
return TRUE;
return FALSE;
}
/*
* quite hard: could be almost anything but white space, but we are a little conservative,
* aiming for regular expressions of alphanumerics and no white space
*/
int
isregexc(int r)
{
if(r == 0)
return FALSE;
if(isalnum(r))
return TRUE;
if(utfrune("^+-.*?#,;[]()$", r)!=nil)
return TRUE;
return FALSE;
}
Range
number(Mntdir *md, Text *t, Range r, int line, int dir, int size, int *evalp)
{
uint q0, q1;
if(size == Char){
if(dir == Fore)
line = r.q1+line;
else if(dir == Back){
if(r.q0==0 && line>0)
r.q0 = t->file->b.nc;
line = r.q0 - line;
}
if(line<0 || line>t->file->b.nc)
goto Rescue;
*evalp = TRUE;
return (Range){line, line};
}
q0 = r.q0;
q1 = r.q1;
switch(dir){
case None:
q0 = 0;
q1 = 0;
Forward:
while(line>0 && q1<t->file->b.nc)
if(textreadc(t, q1++) == '\n' || q1==t->file->b.nc)
if(--line > 0)
q0 = q1;
if(line > 0)
goto Rescue;
break;
case Fore:
if(q1 > 0)
while(textreadc(t, q1-1) != '\n')
q1++;
q0 = q1;
goto Forward;
case Back:
if(q0 < t->file->b.nc)
while(q0>0 && textreadc(t, q0-1)!='\n')
q0--;
q1 = q0;
while(line>0 && q0>0){
if(textreadc(t, q0-1) == '\n'){
if(--line >= 0)
q1 = q0;
}
--q0;
}
if(line > 0)
goto Rescue;
while(q0>0 && textreadc(t, q0-1)!='\n')
--q0;
}
*evalp = TRUE;
return (Range){q0, q1};
Rescue:
if(md != nil)
warning(nil, "address out of range\n");
*evalp = FALSE;
return r;
}
Range
regexp(Mntdir *md, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp)
{
int found;
Rangeset sel;
int q;
if(pat[0] == '\0' && rxnull()){
warning(md, "no previous regular expression\n");
*foundp = FALSE;
return r;
}
if(pat[0] && rxcompile(pat) == FALSE){
*foundp = FALSE;
return r;
}
if(dir == Back)
found = rxbexecute(t, r.q0, &sel);
else{
if(lim.q0 < 0)
q = Infinity;
else
q = lim.q1;
found = rxexecute(t, nil, r.q1, q, &sel);
}
if(!found && md==nil)
warning(nil, "no match for regexp\n");
*foundp = found;
return sel.r[0];
}
Range
address(Mntdir *md, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint), int *evalp, uint *qp)
{
int dir, size, npat;
int prevc, c, nc, n;
uint q;
Rune *pat;
Range r, nr;
r = ar;
q = q0;
dir = None;
size = Line;
c = 0;
while(q < q1){
prevc = c;
c = (*getc)(a, q++);
switch(c){
default:
*qp = q-1;
return r;
case ';':
ar = r;
/* fall through */
case ',':
if(prevc == 0) /* lhs defaults to 0 */
r.q0 = 0;
if(q>=q1 && t!=nil && t->file!=nil) /* rhs defaults to $ */
r.q1 = t->file->b.nc;
else{
nr = address(md, t, lim, ar, a, q, q1, getc, evalp, &q);
r.q1 = nr.q1;
}
*qp = q;
return r;
case '+':
case '-':
if(*evalp && (prevc=='+' || prevc=='-'))
if((nc=(*getc)(a, q))!='#' && nc!='/' && nc!='?')
r = number(md, t, r, 1, prevc, Line, evalp); /* do previous one */
dir = c;
break;
case '.':
case '$':
if(q != q0+1){
*qp = q-1;
return r;
}
if(*evalp)
if(c == '.')
r = ar;
else
r = (Range){t->file->b.nc, t->file->b.nc};
if(q < q1)
dir = Fore;
else
dir = None;
break;
case '#':
if(q==q1 || (c=(*getc)(a, q++))<'0' || '9'<c){
*qp = q-1;
return r;
}
size = Char;
/* fall through */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = c -'0';
while(q<q1){
c = (*getc)(a, q++);
if(c<'0' || '9'<c){
q--;
break;
}
n = n*10+(c-'0');
}
if(*evalp)
r = number(md, t, r, n, dir, size, evalp);
dir = None;
size = Line;
break;
case '?':
dir = Back;
/* fall through */
case '/':
npat = 0;
pat = nil;
while(q<q1){
c = (*getc)(a, q++);
switch(c){
case '\n':
--q;
goto out;
case '\\':
pat = runerealloc(pat, npat+1);
pat[npat++] = c;
if(q == q1)
goto out;
c = (*getc)(a, q++);
break;
case '/':
goto out;
}
pat = runerealloc(pat, npat+1);
pat[npat++] = c;
}
out:
pat = runerealloc(pat, npat+1);
pat[npat] = 0;
if(*evalp)
r = regexp(md, t, lim, r, pat, dir, evalp);
free(pat);
dir = None;
size = Line;
break;
}
}
if(*evalp && dir != None)
r = number(md, t, r, 1, dir, Line, evalp); /* do previous one */
*qp = q;
return r;
}

322
src/cmd/acme/buff.c Normal file
View File

@ -0,0 +1,322 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
enum
{
Slop = 100, /* room to grow with reallocation */
};
static
void
sizecache(Buffer *b, uint n)
{
if(n <= b->cmax)
return;
b->cmax = n+Slop;
b->c = runerealloc(b->c, b->cmax);
}
static
void
addblock(Buffer *b, uint i, uint n)
{
if(i > b->nbl)
error("internal error: addblock");
b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]);
if(i < b->nbl)
memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*));
b->bl[i] = disknewblock(disk, n);
b->nbl++;
}
static
void
delblock(Buffer *b, uint i)
{
if(i >= b->nbl)
error("internal error: delblock");
diskrelease(disk, b->bl[i]);
b->nbl--;
if(i < b->nbl)
memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*));
b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]);
}
/*
* Move cache so b->cq <= q0 < b->cq+b->cnc.
* If at very end, q0 will fall on end of cache block.
*/
static
void
flush(Buffer *b)
{
if(b->cdirty || b->cnc==0){
if(b->cnc == 0)
delblock(b, b->cbi);
else
diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc);
b->cdirty = FALSE;
}
}
static
void
setcache(Buffer *b, uint q0)
{
Block **blp, *bl;
uint i, q;
if(q0 > b->nc)
error("internal error: setcache");
/*
* flush and reload if q0 is not in cache.
*/
if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc))
return;
/*
* if q0 is at end of file and end of cache, continue to grow this block
*/
if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<Maxblock)
return;
flush(b);
/* find block */
if(q0 < b->cq){
q = 0;
i = 0;
}else{
q = b->cq;
i = b->cbi;
}
blp = &b->bl[i];
while(q+(*blp)->u.n <= q0 && q+(*blp)->u.n < b->nc){
q += (*blp)->u.n;
i++;
blp++;
if(i >= b->nbl)
error("block not found");
}
bl = *blp;
/* remember position */
b->cbi = i;
b->cq = q;
sizecache(b, bl->u.n);
b->cnc = bl->u.n;
/*read block*/
diskread(disk, bl, b->c, b->cnc);
}
void
bufinsert(Buffer *b, uint q0, Rune *s, uint n)
{
uint i, m, t, off;
if(q0 > b->nc)
error("internal error: bufinsert");
while(n > 0){
setcache(b, q0);
off = q0-b->cq;
if(b->cnc+n <= Maxblock){
/* Everything fits in one block. */
t = b->cnc+n;
m = n;
if(b->bl == nil){ /* allocate */
if(b->cnc != 0)
error("internal error: bufinsert1 cnc!=0");
addblock(b, 0, t);
b->cbi = 0;
}
sizecache(b, t);
runemove(b->c+off+m, b->c+off, b->cnc-off);
runemove(b->c+off, s, m);
b->cnc = t;
goto Tail;
}
/*
* We must make a new block. If q0 is at
* the very beginning or end of this block,
* just make a new block and fill it.
*/
if(q0==b->cq || q0==b->cq+b->cnc){
if(b->cdirty)
flush(b);
m = min(n, Maxblock);
if(b->bl == nil){ /* allocate */
if(b->cnc != 0)
error("internal error: bufinsert2 cnc!=0");
i = 0;
}else{
i = b->cbi;
if(q0 > b->cq)
i++;
}
addblock(b, i, m);
sizecache(b, m);
runemove(b->c, s, m);
b->cq = q0;
b->cbi = i;
b->cnc = m;
goto Tail;
}
/*
* Split the block; cut off the right side and
* let go of it.
*/
m = b->cnc-off;
if(m > 0){
i = b->cbi+1;
addblock(b, i, m);
diskwrite(disk, &b->bl[i], b->c+off, m);
b->cnc -= m;
}
/*
* Now at end of block. Take as much input
* as possible and tack it on end of block.
*/
m = min(n, Maxblock-b->cnc);
sizecache(b, b->cnc+m);
runemove(b->c+b->cnc, s, m);
b->cnc += m;
Tail:
b->nc += m;
q0 += m;
s += m;
n -= m;
b->cdirty = TRUE;
}
}
void
bufdelete(Buffer *b, uint q0, uint q1)
{
uint m, n, off;
if(!(q0<=q1 && q0<=b->nc && q1<=b->nc))
error("internal error: bufdelete");
while(q1 > q0){
setcache(b, q0);
off = q0-b->cq;
if(q1 > b->cq+b->cnc)
n = b->cnc - off;
else
n = q1-q0;
m = b->cnc - (off+n);
if(m > 0)
runemove(b->c+off, b->c+off+n, m);
b->cnc -= n;
b->cdirty = TRUE;
q1 -= n;
b->nc -= n;
}
}
static int
bufloader(void *v, uint q0, Rune *r, int nr)
{
bufinsert(v, q0, r, nr);
return nr;
}
uint
loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg)
{
char *p;
Rune *r;
int l, m, n, nb, nr;
uint q1;
p = emalloc((Maxblock+UTFmax+1)*sizeof p[0]);
r = runemalloc(Maxblock);
m = 0;
n = 1;
q1 = q0;
/*
* At top of loop, may have m bytes left over from
* last pass, possibly representing a partial rune.
*/
while(n > 0){
n = read(fd, p+m, Maxblock);
if(n < 0){
warning(nil, "read error in Buffer.load");
break;
}
m += n;
p[m] = 0;
l = m;
if(n > 0)
l -= UTFmax;
cvttorunes(p, l, r, &nb, &nr, nulls);
memmove(p, p+nb, m-nb);
m -= nb;
q1 += (*f)(arg, q1, r, nr);
}
free(p);
free(r);
return q1-q0;
}
uint
bufload(Buffer *b, uint q0, int fd, int *nulls)
{
if(q0 > b->nc)
error("internal error: bufload");
return loadfile(fd, q0, nulls, bufloader, b);
}
void
bufread(Buffer *b, uint q0, Rune *s, uint n)
{
uint m;
if(!(q0<=b->nc && q0+n<=b->nc))
error("bufread: internal error");
while(n > 0){
setcache(b, q0);
m = min(n, b->cnc-(q0-b->cq));
runemove(s, b->c+(q0-b->cq), m);
q0 += m;
s += m;
n -= m;
}
}
void
bufreset(Buffer *b)
{
int i;
b->nc = 0;
b->cnc = 0;
b->cq = 0;
b->cdirty = 0;
b->cbi = 0;
/* delete backwards to avoid n² behavior */
for(i=b->nbl-1; --i>=0; )
delblock(b, i);
}
void
bufclose(Buffer *b)
{
bufreset(b);
free(b->c);
b->c = nil;
b->cnc = 0;
free(b->bl);
b->bl = nil;
b->nbl = 0;
}

556
src/cmd/acme/cols.c Normal file
View File

@ -0,0 +1,556 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Rune Lheader[] = {
'N', 'e', 'w', ' ',
'C', 'u', 't', ' ',
'P', 'a', 's', 't', 'e', ' ',
'S', 'n', 'a', 'r', 'f', ' ',
'S', 'o', 'r', 't', ' ',
'Z', 'e', 'r', 'o', 'x', ' ',
'D', 'e', 'l', 'c', 'o', 'l', ' ',
0
};
void
colinit(Column *c, Rectangle r)
{
Rectangle r1;
Text *t;
draw(screen, r, display->white, nil, ZP);
c->r = r;
c->w = nil;
c->nw = 0;
t = &c->tag;
t->w = nil;
t->col = c;
r1 = r;
r1.max.y = r1.min.y + font->height;
textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols);
t->what = Columntag;
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
textinsert(t, 0, Lheader, 38, TRUE);
textsetselect(t, t->file->b.nc, t->file->b.nc);
draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
c->safe = TRUE;
}
Window*
coladd(Column *c, Window *w, Window *clone, int y)
{
Rectangle r, r1;
Window *v;
int i, t;
v = nil;
r = c->r;
r.min.y = c->tag.fr.r.max.y+Border;
if(y<r.min.y && c->nw>0){ /* steal half of last window by default */
v = c->w[c->nw-1];
y = v->body.fr.r.min.y+Dy(v->body.fr.r)/2;
}
/* look for window we'll land on */
for(i=0; i<c->nw; i++){
v = c->w[i];
if(y < v->r.max.y)
break;
}
if(c->nw > 0){
if(i < c->nw)
i++; /* new window will go after v */
/*
* if v's too small, grow it first.
*/
if(!c->safe || v->body.fr.maxlines<=3){
colgrow(c, v, 1);
y = v->body.fr.r.min.y+Dy(v->body.fr.r)/2;
}
r = v->r;
if(i == c->nw)
t = c->r.max.y;
else
t = c->w[i]->r.min.y-Border;
r.max.y = t;
draw(screen, r, textcols[BACK], nil, ZP);
r1 = r;
y = min(y, t-(v->tag.fr.font->height+v->body.fr.font->height+Border+1));
r1.max.y = min(y, v->body.fr.r.min.y+v->body.fr.nlines*v->body.fr.font->height);
r1.min.y = winresize(v, r1, FALSE);
r1.max.y = r1.min.y+Border;
draw(screen, r1, display->black, nil, ZP);
r.min.y = r1.max.y;
}
if(w == nil){
w = emalloc(sizeof(Window));
w->col = c;
draw(screen, r, textcols[BACK], nil, ZP);
wininit(w, clone, r);
}else{
w->col = c;
winresize(w, r, FALSE);
}
w->tag.col = c;
w->tag.row = c->row;
w->body.col = c;
w->body.row = c->row;
c->w = realloc(c->w, (c->nw+1)*sizeof(Window*));
memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*));
c->nw++;
c->w[i] = w;
savemouse(w);
/* near but not on the button */
moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
barttext = &w->body;
c->safe = TRUE;
return w;
}
void
colclose(Column *c, Window *w, int dofree)
{
Rectangle r;
int i;
/* w is locked */
if(!c->safe)
colgrow(c, w, 1);
for(i=0; i<c->nw; i++)
if(c->w[i] == w)
goto Found;
error("can't find window");
Found:
r = w->r;
w->tag.col = nil;
w->body.col = nil;
w->col = nil;
restoremouse(w);
if(dofree){
windelete(w);
winclose(w);
}
memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
c->nw--;
c->w = realloc(c->w, c->nw*sizeof(Window*));
if(c->nw == 0){
draw(screen, r, display->white, nil, ZP);
return;
}
if(i == c->nw){ /* extend last window down */
w = c->w[i-1];
r.min.y = w->r.min.y;
r.max.y = c->r.max.y;
}else{ /* extend next window up */
w = c->w[i];
r.max.y = w->r.max.y;
}
draw(screen, r, textcols[BACK], nil, ZP);
if(c->safe)
winresize(w, r, FALSE);
}
void
colcloseall(Column *c)
{
int i;
Window *w;
if(c == activecol)
activecol = nil;
textclose(&c->tag);
for(i=0; i<c->nw; i++){
w = c->w[i];
winclose(w);
}
c->nw = 0;
free(c->w);
free(c);
clearmouse();
}
void
colmousebut(Column *c)
{
moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
}
void
colresize(Column *c, Rectangle r)
{
int i;
Rectangle r1, r2;
Window *w;
clearmouse();
r1 = r;
r1.max.y = r1.min.y + c->tag.fr.font->height;
textresize(&c->tag, r1);
draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
r1.max.y = r.max.y;
for(i=0; i<c->nw; i++){
w = c->w[i];
w->maxlines = 0;
if(i == c->nw-1)
r1.max.y = r.max.y;
else
r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r);
r2 = r1;
r2.max.y = r2.min.y+Border;
draw(screen, r2, display->black, nil, ZP);
r1.min.y = r2.max.y;
r1.min.y = winresize(w, r1, FALSE);
}
c->r = r;
}
static
int
colcmp(const void *a, const void *b)
{
Rune *r1, *r2;
int i, nr1, nr2;
r1 = (*(Window**)a)->body.file->name;
nr1 = (*(Window**)a)->body.file->nname;
r2 = (*(Window**)b)->body.file->name;
nr2 = (*(Window**)b)->body.file->nname;
for(i=0; i<nr1 && i<nr2; i++){
if(*r1 != *r2)
return *r1-*r2;
r1++;
r2++;
}
return nr1-nr2;
}
void
colsort(Column *c)
{
int i, y;
Rectangle r, r1, *rp;
Window **wp, *w;
if(c->nw == 0)
return;
clearmouse();
rp = emalloc(c->nw*sizeof(Rectangle));
wp = emalloc(c->nw*sizeof(Window*));
memmove(wp, c->w, c->nw*sizeof(Window*));
qsort(wp, c->nw, sizeof(Window*), colcmp);
for(i=0; i<c->nw; i++)
rp[i] = wp[i]->r;
r = c->r;
r.min.y = c->tag.fr.r.max.y;
draw(screen, r, textcols[BACK], nil, ZP);
y = r.min.y;
for(i=0; i<c->nw; i++){
w = wp[i];
r.min.y = y;
if(i == c->nw-1)
r.max.y = c->r.max.y;
else
r.max.y = r.min.y+Dy(w->r)+Border;
r1 = r;
r1.max.y = r1.min.y+Border;
draw(screen, r1, display->black, nil, ZP);
r.min.y = r1.max.y;
y = winresize(w, r, FALSE);
}
free(rp);
free(c->w);
c->w = wp;
}
void
colgrow(Column *c, Window *w, int but)
{
Rectangle r, cr;
int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h;
Window *v;
for(i=0; i<c->nw; i++)
if(c->w[i] == w)
goto Found;
error("can't find window");
Found:
cr = c->r;
if(but < 0){ /* make sure window fills its own space properly */
r = w->r;
if(i==c->nw-1 || c->safe==FALSE)
r.max.y = cr.max.y;
else
r.max.y = c->w[i+1]->r.min.y;
winresize(w, r, FALSE);
return;
}
cr.min.y = c->w[0]->r.min.y;
if(but == 3){ /* full size */
if(i != 0){
v = c->w[0];
c->w[0] = w;
c->w[i] = v;
}
draw(screen, cr, textcols[BACK], nil, ZP);
winresize(w, cr, FALSE);
for(i=1; i<c->nw; i++)
c->w[i]->body.fr.maxlines = 0;
c->safe = FALSE;
return;
}
/* store old #lines for each window */
onl = w->body.fr.maxlines;
nl = emalloc(c->nw * sizeof(int));
ny = emalloc(c->nw * sizeof(int));
tot = 0;
for(j=0; j<c->nw; j++){
l = c->w[j]->body.fr.maxlines;
nl[j] = l;
tot += l;
}
/* approximate new #lines for this window */
if(but == 2){ /* as big as can be */
memset(nl, 0, c->nw * sizeof(int));
goto Pack;
}
nnl = min(onl + max(min(5, w->maxlines), onl/2), tot);
if(nnl < w->maxlines)
nnl = (w->maxlines+nnl)/2;
if(nnl == 0)
nnl = 2;
dnl = nnl - onl;
/* compute new #lines for each window */
for(k=1; k<c->nw; k++){
/* prune from later window */
j = i+k;
if(j<c->nw && nl[j]){
l = min(dnl, max(1, nl[j]/2));
nl[j] -= l;
nl[i] += l;
dnl -= l;
}
/* prune from earlier window */
j = i-k;
if(j>=0 && nl[j]){
l = min(dnl, max(1, nl[j]/2));
nl[j] -= l;
nl[i] += l;
dnl -= l;
}
}
Pack:
/* pack everyone above */
y1 = cr.min.y;
for(j=0; j<i; j++){
v = c->w[j];
r = v->r;
r.min.y = y1;
r.max.y = y1+Dy(v->tag.all);
if(nl[j])
r.max.y += 1 + nl[j]*v->body.fr.font->height;
if(!c->safe || !eqrect(v->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
winresize(v, r, c->safe);
}
r.min.y = v->r.max.y;
r.max.y += Border;
draw(screen, r, display->black, nil, ZP);
y1 = r.max.y;
}
/* scan to see new size of everyone below */
y2 = c->r.max.y;
for(j=c->nw-1; j>i; j--){
v = c->w[j];
r = v->r;
r.min.y = y2-Dy(v->tag.all);
if(nl[j])
r.min.y -= 1 + nl[j]*v->body.fr.font->height;
r.min.y -= Border;
ny[j] = r.min.y;
y2 = r.min.y;
}
/* compute new size of window */
r = w->r;
r.min.y = y1;
r.max.y = r.min.y+Dy(w->tag.all);
h = w->body.fr.font->height;
if(y2-r.max.y >= 1+h+Border){
r.max.y += 1;
r.max.y += h*((y2-r.max.y)/h);
}
/* draw window */
if(!c->safe || !eqrect(w->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
winresize(w, r, c->safe);
}
if(i < c->nw-1){
r.min.y = r.max.y;
r.max.y += Border;
draw(screen, r, display->black, nil, ZP);
for(j=i+1; j<c->nw; j++)
ny[j] -= (y2-r.max.y);
}
/* pack everyone below */
y1 = r.max.y;
for(j=i+1; j<c->nw; j++){
v = c->w[j];
r = v->r;
r.min.y = y1;
r.max.y = y1+Dy(v->tag.all);
if(nl[j])
r.max.y += 1 + nl[j]*v->body.fr.font->height;
if(!c->safe || !eqrect(v->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
winresize(v, r, c->safe);
}
if(j < c->nw-1){ /* no border on last window */
r.min.y = v->r.max.y;
r.max.y += Border;
draw(screen, r, display->black, nil, ZP);
}
y1 = r.max.y;
}
r = w->r;
r.min.y = y1;
r.max.y = c->r.max.y;
draw(screen, r, textcols[BACK], nil, ZP);
free(nl);
free(ny);
c->safe = TRUE;
winmousebut(w);
}
void
coldragwin(Column *c, Window *w, int but)
{
Rectangle r;
int i, b;
Point p, op;
Window *v;
Column *nc;
clearmouse();
setcursor(mousectl, &boxcursor);
b = mouse->buttons;
op = mouse->xy;
while(mouse->buttons == b)
readmouse(mousectl);
setcursor(mousectl, nil);
if(mouse->buttons){
while(mouse->buttons)
readmouse(mousectl);
return;
}
for(i=0; i<c->nw; i++)
if(c->w[i] == w)
goto Found;
error("can't find window");
Found:
p = mouse->xy;
if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
colgrow(c, w, but);
winmousebut(w);
return;
}
/* is it a flick to the right? */
if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
p.x += Dx(w->r); /* yes: toss to next column */
nc = rowwhichcol(c->row, p);
if(nc!=nil && nc!=c){
colclose(c, w, FALSE);
coladd(nc, w, nil, p.y);
winmousebut(w);
return;
}
if(i==0 && c->nw==1)
return; /* can't do it */
if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
|| (i==0 && p.y>w->r.max.y)){
/* shuffle */
colclose(c, w, FALSE);
coladd(c, w, nil, p.y);
winmousebut(w);
return;
}
if(i == 0)
return;
v = c->w[i-1];
if(p.y < v->tag.all.max.y)
p.y = v->tag.all.max.y;
if(p.y > w->r.max.y-Dy(w->tag.all)-Border)
p.y = w->r.max.y-Dy(w->tag.all)-Border;
r = v->r;
r.max.y = p.y;
if(r.max.y > v->body.fr.r.min.y){
r.max.y -= (r.max.y-v->body.fr.r.min.y)%v->body.fr.font->height;
if(v->body.fr.r.min.y == v->body.fr.r.max.y)
r.max.y++;
}
if(!eqrect(v->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
winresize(v, r, c->safe);
}
r.min.y = v->r.max.y;
r.max.y = r.min.y+Border;
draw(screen, r, display->black, nil, ZP);
r.min.y = r.max.y;
if(i == c->nw-1)
r.max.y = c->r.max.y;
else
r.max.y = c->w[i+1]->r.min.y-Border;
if(!eqrect(w->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
winresize(w, r, c->safe);
}
c->safe = TRUE;
winmousebut(w);
}
Text*
colwhich(Column *c, Point p)
{
int i;
Window *w;
if(!ptinrect(p, c->r))
return nil;
if(ptinrect(p, c->tag.all))
return &c->tag;
for(i=0; i<c->nw; i++){
w = c->w[i];
if(ptinrect(p, w->r)){
if(ptinrect(p, w->tag.all))
return &w->tag;
return &w->body;
}
}
return nil;
}
int
colclean(Column *c)
{
int i, clean;
clean = TRUE;
for(i=0; i<c->nw; i++)
clean &= winclean(c->w[i], TRUE);
return clean;
}

546
src/cmd/acme/dat.h Normal file
View File

@ -0,0 +1,546 @@
enum
{
Qdir,
Qacme,
Qcons,
Qconsctl,
Qdraw,
Qeditout,
Qindex,
Qlabel,
Qnew,
QWaddr,
QWbody,
QWctl,
QWdata,
QWeditout,
QWevent,
QWrdsel,
QWwrsel,
QWtag,
QMAX,
};
enum
{
Blockincr = 256,
Maxblock = 8*1024,
NRange = 10,
Infinity = 0x7FFFFFFF, /* huge value for regexp address */
};
typedef struct Block Block;
typedef struct Buffer Buffer;
typedef struct Command Command;
typedef struct Column Column;
typedef struct Dirlist Dirlist;
typedef struct Dirtab Dirtab;
typedef struct Disk Disk;
typedef struct Expand Expand;
typedef struct Fid Fid;
typedef struct File File;
typedef struct Elog Elog;
typedef struct Mntdir Mntdir;
typedef struct Range Range;
typedef struct Rangeset Rangeset;
typedef struct Reffont Reffont;
typedef struct Row Row;
typedef struct Runestr Runestr;
typedef struct Text Text;
typedef struct Timer Timer;
typedef struct Window Window;
typedef struct Xfid Xfid;
struct Runestr
{
Rune *r;
int nr;
};
struct Range
{
int q0;
int q1;
};
struct Block
{
uint addr; /* disk address in bytes */
union
{
uint n; /* number of used runes in block */
Block *next; /* pointer to next in free list */
} u;
};
struct Disk
{
int fd;
uint addr; /* length of temp file */
Block *free[Maxblock/Blockincr+1];
};
Disk* diskinit(void);
Block* disknewblock(Disk*, uint);
void diskrelease(Disk*, Block*);
void diskread(Disk*, Block*, Rune*, uint);
void diskwrite(Disk*, Block**, Rune*, uint);
struct Buffer
{
uint nc;
Rune *c; /* cache */
uint cnc; /* bytes in cache */
uint cmax; /* size of allocated cache */
uint cq; /* position of cache */
int cdirty; /* cache needs to be written */
uint cbi; /* index of cache Block */
Block **bl; /* array of blocks */
uint nbl; /* number of blocks */
};
void bufinsert(Buffer*, uint, Rune*, uint);
void bufdelete(Buffer*, uint, uint);
uint bufload(Buffer*, uint, int, int*);
void bufread(Buffer*, uint, Rune*, uint);
void bufclose(Buffer*);
void bufreset(Buffer*);
struct Elog
{
short type; /* Delete, Insert, Filename */
uint q0; /* location of change (unused in f) */
uint nd; /* number of deleted characters */
uint nr; /* # runes in string or file name */
Rune *r;
};
void elogterm(File*);
void elogclose(File*);
void eloginsert(File*, int, Rune*, int);
void elogdelete(File*, int, int);
void elogreplace(File*, int, int, Rune*, int);
void elogapply(File*);
struct File
{
Buffer b; /* the data */
Buffer delta; /* transcript of changes */
Buffer epsilon; /* inversion of delta for redo */
Buffer *elogbuf; /* log of pending editor changes */
Elog elog; /* current pending change */
Rune *name; /* name of associated file */
int nname; /* size of name */
uvlong qidpath; /* of file when read */
uint mtime; /* of file when read */
int dev; /* of file when read */
int unread; /* file has not been read from disk */
int editclean; /* mark clean after edit command */
int seq; /* if seq==0, File acts like Buffer */
int mod;
Text *curtext; /* most recently used associated text */
Text **text; /* list of associated texts */
int ntext;
int dumpid; /* used in dumping zeroxed windows */
};
File* fileaddtext(File*, Text*);
void fileclose(File*);
void filedelete(File*, uint, uint);
void filedeltext(File*, Text*);
void fileinsert(File*, uint, Rune*, uint);
uint fileload(File*, uint, int, int*);
void filemark(File*);
void filereset(File*);
void filesetname(File*, Rune*, int);
void fileundelete(File*, Buffer*, uint, uint);
void fileuninsert(File*, Buffer*, uint, uint);
void fileunsetname(File*, Buffer*);
void fileundo(File*, int, uint*, uint*);
uint fileredoseq(File*);
enum /* Text.what */
{
Columntag,
Rowtag,
Tag,
Body,
};
struct Text
{
File *file;
Frame fr;
Reffont *reffont;
uint org;
uint q0;
uint q1;
int what;
int tabstop;
Window *w;
Rectangle scrollr;
Rectangle lastsr;
Rectangle all;
Row *row;
Column *col;
uint eq0; /* start of typing for ESC */
uint cq0; /* cache position */
int ncache; /* storage for insert */
int ncachealloc;
Rune *cache;
int nofill;
};
uint textbacknl(Text*, uint, uint);
uint textbsinsert(Text*, uint, Rune*, uint, int, int*);
int textbswidth(Text*, Rune);
int textclickmatch(Text*, int, int, int, uint*);
void textclose(Text*);
void textcolumnate(Text*, Dirlist**, int);
void textcommit(Text*, int);
void textconstrain(Text*, uint, uint, uint*, uint*);
void textdelete(Text*, uint, uint, int);
void textdoubleclick(Text*, uint*, uint*);
void textfill(Text*);
void textframescroll(Text*, int);
void textinit(Text*, File*, Rectangle, Reffont*, Image**);
void textinsert(Text*, uint, Rune*, uint, int);
uint textload(Text*, uint, char*, int);
Rune textreadc(Text*, uint);
void textredraw(Text*, Rectangle, Font*, Image*, int);
void textreset(Text*);
int textresize(Text*, Rectangle);
void textscrdraw(Text*);
void textscroll(Text*, int);
void textselect(Text*);
int textselect2(Text*, uint*, uint*, Text**);
int textselect23(Text*, uint*, uint*, Image*, int);
int textselect3(Text*, uint*, uint*);
void textsetorigin(Text*, uint, int);
void textsetselect(Text*, uint, uint);
void textshow(Text*, uint, uint, int);
void texttype(Text*, Rune);
struct Window
{
QLock lk;
Ref ref;
Text tag;
Text body;
Rectangle r;
uchar isdir;
uchar isscratch;
uchar filemenu;
uchar dirty;
int id;
Range addr;
Range limit;
uchar nopen[QMAX];
uchar nomark;
uchar noscroll;
Range wrselrange;
int rdselfd;
int neditwrsel;
Column *col;
Xfid *eventx;
char *events;
int nevents;
int owner;
int maxlines;
Dirlist **dlp;
int ndl;
int putseq;
int nincl;
Rune **incl;
Reffont *reffont;
QLock ctllock;
uint ctlfid;
char *dumpstr;
char *dumpdir;
int dumpid;
int utflastqid;
int utflastboff;
int utflastq;
};
void wininit(Window*, Window*, Rectangle);
void winlock(Window*, int);
void winlock1(Window*, int);
void winunlock(Window*);
void wintype(Window*, Text*, Rune);
void winundo(Window*, int);
void winsetname(Window*, Rune*, int);
void winsettag(Window*);
void winsettag1(Window*);
void wincommit(Window*, Text*);
int winresize(Window*, Rectangle, int);
void winclose(Window*);
void windelete(Window*);
int winclean(Window*, int);
void windirfree(Window*);
void winevent(Window*, char*, ...);
void winmousebut(Window*);
void winaddincl(Window*, Rune*, int);
void wincleartag(Window*);
void winctlprint(Window*, char*, int);
struct Column
{
Rectangle r;
Text tag;
Row *row;
Window **w;
int nw;
int safe;
};
void colinit(Column*, Rectangle);
Window* coladd(Column*, Window*, Window*, int);
void colclose(Column*, Window*, int);
void colcloseall(Column*);
void colresize(Column*, Rectangle);
Text* colwhich(Column*, Point);
void coldragwin(Column*, Window*, int);
void colgrow(Column*, Window*, int);
int colclean(Column*);
void colsort(Column*);
void colmousebut(Column*);
struct Row
{
QLock lk;
Rectangle r;
Text tag;
Column **col;
int ncol;
};
void rowinit(Row*, Rectangle);
Column* rowadd(Row*, Column *c, int);
void rowclose(Row*, Column*, int);
Text* rowwhich(Row*, Point);
Column* rowwhichcol(Row*, Point);
void rowresize(Row*, Rectangle);
Text* rowtype(Row*, Rune, Point);
void rowdragcol(Row*, Column*, int but);
int rowclean(Row*);
void rowdump(Row*, char*);
void rowload(Row*, char*, int);
void rowloadfonts(char*);
struct Timer
{
int dt;
int cancel;
Channel *c; /* chan(int) */
Timer *next;
};
struct Command
{
int pid;
Rune *name;
int nname;
char *text;
char **av;
int iseditcmd;
Mntdir *md;
Command *next;
};
struct Dirtab
{
char *name;
uchar type;
uint qid;
uint perm;
};
struct Mntdir
{
int id;
int ref;
Rune *dir;
int ndir;
Mntdir *next;
int nincl;
Rune **incl;
};
struct Fid
{
int fid;
int busy;
int open;
Qid qid;
Window *w;
Dirtab *dir;
Fid *next;
Mntdir *mntdir;
int nrpart;
uchar rpart[UTFmax];
};
struct Xfid
{
void *arg; /* args to xfidinit */
Fcall fcall;
Xfid *next;
Channel *c; /* chan(void(*)(Xfid*)) */
Fid *f;
uchar *buf;
int flushed;
};
void xfidctl(void *);
void xfidflush(Xfid*);
void xfidopen(Xfid*);
void xfidclose(Xfid*);
void xfidread(Xfid*);
void xfidwrite(Xfid*);
void xfidctlwrite(Xfid*, Window*);
void xfideventread(Xfid*, Window*);
void xfideventwrite(Xfid*, Window*);
void xfidindexread(Xfid*);
void xfidutfread(Xfid*, Text*, uint, int);
int xfidruneread(Xfid*, Text*, uint, uint);
struct Reffont
{
Ref ref;
Font *f;
};
Reffont *rfget(int, int, int, char*);
void rfclose(Reffont*);
struct Rangeset
{
Range r[NRange];
};
struct Dirlist
{
Rune *r;
int nr;
int wid;
};
struct Expand
{
uint q0;
uint q1;
Rune *name;
int nname;
char *bname;
int jump;
union{
Text *at;
Rune *ar;
} u;
int (*agetc)(void*, uint);
int a0;
int a1;
};
enum
{
/* fbufalloc() guarantees room off end of BUFSIZE */
BUFSIZE = Maxblock+IOHDRSZ, /* size from fbufalloc() */
RBUFSIZE = BUFSIZE/sizeof(Rune),
EVENTSIZE = 256,
Scrollwid = 12, /* width of scroll bar */
Scrollgap = 4, /* gap right of scroll bar */
Margin = 4, /* margin around text */
Border = 2, /* line between rows, cols, windows */
};
#define QID(w,q) ((w<<8)|(q))
#define WIN(q) ((((ulong)(q).path)>>8) & 0xFFFFFF)
#define FILE(q) ((q).path & 0xFF)
enum
{
FALSE,
TRUE,
XXX,
};
enum
{
Empty = 0,
Null = '-',
Delete = 'd',
Insert = 'i',
Replace = 'r',
Filename = 'f',
};
enum /* editing */
{
Inactive = 0,
Inserting,
Collecting,
};
uint seq;
uint maxtab; /* size of a tab, in units of the '0' character */
Display *display;
Image *screen;
Font *font;
Mouse *mouse;
Mousectl *mousectl;
Keyboardctl *keyboardctl;
Reffont reffont;
Image *modbutton;
Image *colbutton;
Image *button;
Image *but2col;
Image *but3col;
Cursor boxcursor;
Row row;
int timerpid;
Disk *disk;
Text *seltext;
Text *argtext;
Text *mousetext; /* global because Text.close needs to clear it */
Text *typetext; /* global because Text.close needs to clear it */
Text *barttext; /* shared between mousetask and keyboardthread */
int bartflag;
Window *activewin;
Column *activecol;
Buffer snarfbuf;
Rectangle nullrect;
int fsyspid;
char *cputype;
char *objtype;
char *home;
char *fontnames[2];
Image *tagcols[NCOL];
Image *textcols[NCOL];
int plumbsendfd;
int plumbeditfd;
extern char wdir[];
int editing;
int erroutfd;
int messagesize; /* negotiated in 9P version setup */
Channel *ckeyboard; /* chan(Rune)[10] */
Channel *cplumb; /* chan(Plumbmsg*) */
Channel *cwait; /* chan(Waitmsg) */
Channel *ccommand; /* chan(Command*) */
Channel *ckill; /* chan(Rune*) */
Channel *cxfidalloc; /* chan(Xfid*) */
Channel *cxfidfree; /* chan(Xfid*) */
Channel *cnewwindow; /* chan(Channel*) */
Channel *mouseexit0; /* chan(int) */
Channel *mouseexit1; /* chan(int) */
Channel *cexit; /* chan(int) */
Channel *cerr; /* chan(char*) */
Channel *cedit; /* chan(int) */
#define STACK 32768

129
src/cmd/acme/disk.c Normal file
View File

@ -0,0 +1,129 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Block *blist;
int
tempfile(void)
{
char buf[128];
int i, fd;
snprint(buf, sizeof buf, "/tmp/X%d.%.4sacme", getpid(), getuser());
for(i='A'; i<='Z'; i++){
buf[5] = i;
if(access(buf, AEXIST) == 0)
continue;
fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600);
if(fd >= 0)
return fd;
}
return -1;
}
Disk*
diskinit()
{
Disk *d;
d = emalloc(sizeof(Disk));
d->fd = tempfile();
if(d->fd < 0){
fprint(2, "acme: can't create temp file: %r\n");
threadexitsall("diskinit");
}
return d;
}
static
uint
ntosize(uint n, uint *ip)
{
uint size;
if(n > Maxblock)
error("internal error: ntosize");
size = n;
if(size & (Blockincr-1))
size += Blockincr - (size & (Blockincr-1));
/* last bucket holds blocks of exactly Maxblock */
if(ip)
*ip = size/Blockincr;
return size * sizeof(Rune);
}
Block*
disknewblock(Disk *d, uint n)
{
uint i, j, size;
Block *b;
size = ntosize(n, &i);
b = d->free[i];
if(b)
d->free[i] = b->u.next;
else{
/* allocate in chunks to reduce malloc overhead */
if(blist == nil){
blist = emalloc(100*sizeof(Block));
for(j=0; j<100-1; j++)
blist[j].u.next = &blist[j+1];
}
b = blist;
blist = b->u.next;
b->addr = d->addr;
d->addr += size;
}
b->u.n = n;
return b;
}
void
diskrelease(Disk *d, Block *b)
{
uint i;
ntosize(b->u.n, &i);
b->u.next = d->free[i];
d->free[i] = b;
}
void
diskwrite(Disk *d, Block **bp, Rune *r, uint n)
{
int size, nsize;
Block *b;
b = *bp;
size = ntosize(b->u.n, nil);
nsize = ntosize(n, nil);
if(size != nsize){
diskrelease(d, b);
b = disknewblock(d, n);
*bp = b;
}
if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
error("write error to temp file");
b->u.n = n;
}
void
diskread(Disk *d, Block *b, Rune *r, uint n)
{
if(n > b->u.n)
error("internal error: diskread");
ntosize(b->u.n, nil);
if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
error("read error from temp file");
}

1325
src/cmd/acme/ecmd.c Normal file

File diff suppressed because it is too large Load Diff

682
src/cmd/acme/edit.c Normal file
View File

@ -0,0 +1,682 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "edit.h"
#include "fns.h"
static char linex[]="\n";
static char wordx[]=" \t\n";
struct cmdtab cmdtab[]={
/* cmdc text regexp addr defcmd defaddr count token fn */
'\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd,
'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd,
'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd,
'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd,
'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd,
'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd,
'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd,
'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd,
'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd,
's', 0, 1, 0, 0, aDot, 1, 0, s_cmd,
't', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd,
'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd,
'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
'=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd,
'B', 0, 0, 0, 0, aNo, 0, linex, B_cmd,
'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd,
'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
'<', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
'|', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
'>', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
/* deliberately unimplemented:
'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd,
'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd,
'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd,
'!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd,
*/
0, 0, 0, 0, 0, 0, 0, 0,
};
Cmd *parsecmd(int);
Addr *compoundaddr(void);
Addr *simpleaddr(void);
void freecmd(void);
void okdelim(int);
Rune *cmdstartp;
Rune *cmdendp;
Rune *cmdp;
Channel *editerrc;
String *lastpat;
int patset;
List cmdlist;
List addrlist;
List stringlist;
Text *curtext;
int editing = Inactive;
String* newstring(int);
void
editthread(void *v)
{
Cmd *cmdp;
USED(v);
threadsetname("editthread");
while((cmdp=parsecmd(0)) != 0){
// ocurfile = curfile;
// loaded = curfile && !curfile->unread;
if(cmdexec(curtext, cmdp) == 0)
break;
freecmd();
}
sendp(editerrc, nil);
}
void
allelogterm(Window *w, void *x)
{
USED(x);
elogterm(w->body.file);
}
void
alleditinit(Window *w, void *x)
{
USED(x);
textcommit(&w->tag, TRUE);
textcommit(&w->body, TRUE);
w->body.file->editclean = FALSE;
}
void
allupdate(Window *w, void *x)
{
Text *t;
int i;
File *f;
USED(x);
t = &w->body;
f = t->file;
if(f->curtext != t) /* do curtext only */
return;
if(f->elog.type == Null)
elogterm(f);
else if(f->elog.type != Empty){
elogapply(f);
if(f->editclean){
f->mod = FALSE;
for(i=0; i<f->ntext; i++)
f->text[i]->w->dirty = FALSE;
}
}
textsetselect(t, t->q0, t->q1);
textscrdraw(t);
winsettag(w);
}
void
editerror(char *fmt, ...)
{
va_list arg;
char *s;
va_start(arg, fmt);
s = vsmprint(fmt, arg);
va_end(arg);
freecmd();
allwindows(allelogterm, nil); /* truncate the edit logs */
sendp(editerrc, s);
threadexits(nil);
}
void
editcmd(Text *ct, Rune *r, uint n)
{
char *err;
if(n == 0)
return;
if(2*n > RBUFSIZE){
warning(nil, "string too long\n");
return;
}
allwindows(alleditinit, nil);
if(cmdstartp)
free(cmdstartp);
cmdstartp = runemalloc(n+2);
runemove(cmdstartp, r, n);
if(r[n] != '\n')
cmdstartp[n++] = '\n';
cmdstartp[n] = '\0';
cmdendp = cmdstartp+n;
cmdp = cmdstartp;
if(ct->w == nil)
curtext = nil;
else
curtext = &ct->w->body;
resetxec();
if(editerrc == nil){
editerrc = chancreate(sizeof(char*), 0);
lastpat = allocstring(0);
}
threadcreate(editthread, nil, STACK);
err = recvp(editerrc);
editing = Inactive;
if(err != nil){
if(err[0] != '\0')
warning(nil, "Edit: %s\n", err);
free(err);
}
/* update everyone whose edit log has data */
allwindows(allupdate, nil);
}
int
getch(void)
{
if(*cmdp == *cmdendp)
return -1;
return *cmdp++;
}
int
nextc(void)
{
if(*cmdp == *cmdendp)
return -1;
return *cmdp;
}
void
ungetch(void)
{
if(--cmdp < cmdstartp)
error("ungetch");
}
long
getnum(int signok)
{
long n;
int c, sign;
n = 0;
sign = 1;
if(signok>1 && nextc()=='-'){
sign = -1;
getch();
}
if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */
return sign;
while('0'<=(c=getch()) && c<='9')
n = n*10 + (c-'0');
ungetch();
return sign*n;
}
int
cmdskipbl(void)
{
int c;
do
c = getch();
while(c==' ' || c=='\t');
if(c >= 0)
ungetch();
return c;
}
/*
* Check that list has room for one more element.
*/
void
growlist(List *l)
{
if(l->u.listptr==0 || l->nalloc==0){
l->nalloc = INCR;
l->u.listptr = emalloc(INCR*sizeof(long));
l->nused = 0;
}else if(l->nused == l->nalloc){
l->u.listptr = erealloc(l->u.listptr, (l->nalloc+INCR)*sizeof(long));
memset((void*)(l->u.longptr+l->nalloc), 0, INCR*sizeof(long));
l->nalloc += INCR;
}
}
/*
* Remove the ith element from the list
*/
void
dellist(List *l, int i)
{
memmove(&l->u.longptr[i], &l->u.longptr[i+1], (l->nused-(i+1))*sizeof(long));
l->nused--;
}
/*
* Add a new element, whose position is i, to the list
*/
void
inslist(List *l, int i, long val)
{
growlist(l);
memmove(&l->u.longptr[i+1], &l->u.longptr[i], (l->nused-i)*sizeof(long));
l->u.longptr[i] = val;
l->nused++;
}
void
listfree(List *l)
{
free(l->u.listptr);
free(l);
}
String*
allocstring(int n)
{
String *s;
s = emalloc(sizeof(String));
s->n = n;
s->nalloc = n+10;
s->r = emalloc(s->nalloc*sizeof(Rune));
s->r[n] = '\0';
return s;
}
void
freestring(String *s)
{
free(s->r);
free(s);
}
Cmd*
newcmd(void){
Cmd *p;
p = emalloc(sizeof(Cmd));
inslist(&cmdlist, cmdlist.nused, (long)p);
return p;
}
String*
newstring(int n)
{
String *p;
p = allocstring(n);
inslist(&stringlist, stringlist.nused, (long)p);
return p;
}
Addr*
newaddr(void)
{
Addr *p;
p = emalloc(sizeof(Addr));
inslist(&addrlist, addrlist.nused, (long)p);
return p;
}
void
freecmd(void)
{
int i;
while(cmdlist.nused > 0)
free(cmdlist.u.ucharptr[--cmdlist.nused]);
while(addrlist.nused > 0)
free(addrlist.u.ucharptr[--addrlist.nused]);
while(stringlist.nused>0){
i = --stringlist.nused;
freestring(stringlist.u.stringptr[i]);
}
}
void
okdelim(int c)
{
if(c=='\\' || ('a'<=c && c<='z')
|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
editerror("bad delimiter %c\n", c);
}
void
atnl(void)
{
int c;
cmdskipbl();
c = getch();
if(c != '\n')
editerror("newline expected (saw %C)", c);
}
void
Straddc(String *s, int c)
{
if(s->n+1 >= s->nalloc){
s->nalloc += 10;
s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
}
s->r[s->n++] = c;
s->r[s->n] = '\0';
}
void
getrhs(String *s, int delim, int cmd)
{
int c;
while((c = getch())>0 && c!=delim && c!='\n'){
if(c == '\\'){
if((c=getch()) <= 0)
error("bad right hand side");
if(c == '\n'){
ungetch();
c='\\';
}else if(c == 'n')
c='\n';
else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */
Straddc(s, '\\');
}
Straddc(s, c);
}
ungetch(); /* let client read whether delimiter, '\n' or whatever */
}
String *
collecttoken(char *end)
{
String *s = newstring(0);
int c;
while((c=nextc())==' ' || c=='\t')
Straddc(s, getch()); /* blanks significant for getname() */
while((c=getch())>0 && utfrune(end, c)==0)
Straddc(s, c);
if(c != '\n')
atnl();
return s;
}
String *
collecttext(void)
{
String *s;
int begline, i, c, delim;
s = newstring(0);
if(cmdskipbl()=='\n'){
getch();
i = 0;
do{
begline = i;
while((c = getch())>0 && c!='\n')
i++, Straddc(s, c);
i++, Straddc(s, '\n');
if(c < 0)
goto Return;
}while(s->r[begline]!='.' || s->r[begline+1]!='\n');
s->r[s->n-2] = '\0';
}else{
okdelim(delim = getch());
getrhs(s, delim, 'a');
if(nextc()==delim)
getch();
atnl();
}
Return:
return s;
}
int
cmdlookup(int c)
{
int i;
for(i=0; cmdtab[i].cmdc; i++)
if(cmdtab[i].cmdc == c)
return i;
return -1;
}
Cmd*
parsecmd(int nest)
{
int i, c;
struct cmdtab *ct;
Cmd *cp, *ncp;
Cmd cmd;
cmd.next = cmd.u.cmd = 0;
cmd.re = 0;
cmd.flag = cmd.num = 0;
cmd.addr = compoundaddr();
if(cmdskipbl() == -1)
return 0;
if((c=getch())==-1)
return 0;
cmd.cmdc = c;
if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */
getch(); /* the 'd' */
cmd.cmdc='c'|0x100;
}
i = cmdlookup(cmd.cmdc);
if(i >= 0){
if(cmd.cmdc == '\n')
goto Return; /* let nl_cmd work it all out */
ct = &cmdtab[i];
if(ct->defaddr==aNo && cmd.addr)
editerror("command takes no address");
if(ct->count)
cmd.num = getnum(ct->count);
if(ct->regexp){
/* x without pattern -> .*\n, indicated by cmd.re==0 */
/* X without pattern is all files */
if((ct->cmdc!='x' && ct->cmdc!='X') ||
((c = nextc())!=' ' && c!='\t' && c!='\n')){
cmdskipbl();
if((c = getch())=='\n' || c<0)
editerror("no address");
okdelim(c);
cmd.re = getregexp(c);
if(ct->cmdc == 's'){
cmd.u.text = newstring(0);
getrhs(cmd.u.text, c, 's');
if(nextc() == c){
getch();
if(nextc() == 'g')
cmd.flag = getch();
}
}
}
}
if(ct->addr && (cmd.u.mtaddr=simpleaddr())==0)
editerror("bad address");
if(ct->defcmd){
if(cmdskipbl() == '\n'){
getch();
cmd.u.cmd = newcmd();
cmd.u.cmd->cmdc = ct->defcmd;
}else if((cmd.u.cmd = parsecmd(nest))==0)
error("defcmd");
}else if(ct->text)
cmd.u.text = collecttext();
else if(ct->token)
cmd.u.text = collecttoken(ct->token);
else
atnl();
}else
switch(cmd.cmdc){
case '{':
cp = 0;
do{
if(cmdskipbl()=='\n')
getch();
ncp = parsecmd(nest+1);
if(cp)
cp->next = ncp;
else
cmd.u.cmd = ncp;
}while(cp = ncp);
break;
case '}':
atnl();
if(nest==0)
editerror("right brace with no left brace");
return 0;
default:
editerror("unknown command %c", cmd.cmdc);
}
Return:
cp = newcmd();
*cp = cmd;
return cp;
}
String*
getregexp(int delim)
{
String *buf, *r;
int i, c;
buf = allocstring(0);
for(i=0; ; i++){
if((c = getch())=='\\'){
if(nextc()==delim)
c = getch();
else if(nextc()=='\\'){
Straddc(buf, c);
c = getch();
}
}else if(c==delim || c=='\n')
break;
if(i >= RBUFSIZE)
editerror("regular expression too long");
Straddc(buf, c);
}
if(c!=delim && c)
ungetch();
if(buf->n > 0){
patset = TRUE;
freestring(lastpat);
lastpat = buf;
}else
freestring(buf);
if(lastpat->n == 0)
editerror("no regular expression defined");
r = newstring(lastpat->n);
runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */
return r;
}
Addr *
simpleaddr(void)
{
Addr addr;
Addr *ap, *nap;
addr.next = 0;
addr.u.left = 0;
switch(cmdskipbl()){
case '#':
addr.type = getch();
addr.num = getnum(1);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
addr.num = getnum(1);
addr.type='l';
break;
case '/': case '?': case '"':
addr.u.re = getregexp(addr.type = getch());
break;
case '.':
case '$':
case '+':
case '-':
case '\'':
addr.type = getch();
break;
default:
return 0;
}
if(addr.next = simpleaddr())
switch(addr.next->type){
case '.':
case '$':
case '\'':
if(addr.type!='"')
case '"':
editerror("bad address syntax");
break;
case 'l':
case '#':
if(addr.type=='"')
break;
/* fall through */
case '/':
case '?':
if(addr.type!='+' && addr.type!='-'){
/* insert the missing '+' */
nap = newaddr();
nap->type='+';
nap->next = addr.next;
addr.next = nap;
}
break;
case '+':
case '-':
break;
default:
error("simpleaddr");
}
ap = newaddr();
*ap = addr;
return ap;
}
Addr *
compoundaddr(void)
{
Addr addr;
Addr *ap, *next;
addr.u.left = simpleaddr();
if((addr.type = cmdskipbl())!=',' && addr.type!=';')
return addr.u.left;
getch();
next = addr.next = compoundaddr();
if(next && (next->type==',' || next->type==';') && next->u.left==0)
editerror("bad address syntax");
ap = newaddr();
*ap = addr;
return ap;
}

101
src/cmd/acme/edit.h Normal file
View File

@ -0,0 +1,101 @@
/*#pragma varargck argpos editerror 1*/
typedef struct Addr Addr;
typedef struct Address Address;
typedef struct Cmd Cmd;
typedef struct List List;
typedef struct String String;
struct String
{
int n; /* excludes NUL */
Rune *r; /* includes NUL */
int nalloc;
};
struct Addr
{
char type; /* # (char addr), l (line addr), / ? . $ + - , ; */
union{
String *re;
Addr *left; /* left side of , and ; */
} u;
ulong num;
Addr *next; /* or right side of , and ; */
};
struct Address
{
Range r;
File *f;
};
struct Cmd
{
Addr *addr; /* address (range of text) */
String *re; /* regular expression for e.g. 'x' */
union{
Cmd *cmd; /* target of x, g, {, etc. */
String *text; /* text of a, c, i; rhs of s */
Addr *mtaddr; /* address for m, t */
} u;
Cmd *next; /* pointer to next element in {} */
short num;
ushort flag; /* whatever */
ushort cmdc; /* command character; 'x' etc. */
};
extern struct cmdtab{
ushort cmdc; /* command character */
uchar text; /* takes a textual argument? */
uchar regexp; /* takes a regular expression? */
uchar addr; /* takes an address (m or t)? */
uchar defcmd; /* default command; 0==>none */
uchar defaddr; /* default address */
uchar count; /* takes a count e.g. s2/// */
char *token; /* takes text terminated by one of these */
int (*fn)(Text*, Cmd*); /* function to call with parse tree */
}cmdtab[];
#define INCR 25 /* delta when growing list */
struct List /* code depends on a long being able to hold a pointer */
{
int nalloc;
int nused;
union{
void *listptr;
Block *blkptr;
long *longptr;
uchar* *ucharptr;
String* *stringptr;
File* *fileptr;
} u;
};
enum Defaddr{ /* default addresses */
aNo,
aDot,
aAll,
};
int nl_cmd(Text*, Cmd*), a_cmd(Text*, Cmd*), b_cmd(Text*, Cmd*);
int c_cmd(Text*, Cmd*), d_cmd(Text*, Cmd*);
int B_cmd(Text*, Cmd*), D_cmd(Text*, Cmd*), e_cmd(Text*, Cmd*);
int f_cmd(Text*, Cmd*), g_cmd(Text*, Cmd*), i_cmd(Text*, Cmd*);
int k_cmd(Text*, Cmd*), m_cmd(Text*, Cmd*), n_cmd(Text*, Cmd*);
int p_cmd(Text*, Cmd*);
int s_cmd(Text*, Cmd*), u_cmd(Text*, Cmd*), w_cmd(Text*, Cmd*);
int x_cmd(Text*, Cmd*), X_cmd(Text*, Cmd*), pipe_cmd(Text*, Cmd*);
int eq_cmd(Text*, Cmd*);
String *allocstring(int);
void freestring(String*);
String *getregexp(int);
Addr *newaddr(void);
Address cmdaddress(Addr*, Address, int);
int cmdexec(Text*, Cmd*);
void editerror(char*, ...);
int cmdlookup(int);
void resetxec(void);
void Straddc(String*, int);

350
src/cmd/acme/elog.c Normal file
View File

@ -0,0 +1,350 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
#include "edit.h"
static char Wsequence[] = "warning: changes out of sequence\n";
static int warned = FALSE;
/*
* Log of changes made by editing commands. Three reasons for this:
* 1) We want addresses in commands to apply to old file, not file-in-change.
* 2) It's difficult to track changes correctly as things move, e.g. ,x m$
* 3) This gives an opportunity to optimize by merging adjacent changes.
* It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
* separate implementation. To do this well, we use Replace as well as
* Insert and Delete
*/
typedef struct Buflog Buflog;
struct Buflog
{
short type; /* Replace, Filename */
uint q0; /* location of change (unused in f) */
uint nd; /* # runes to delete */
uint nr; /* # runes in string or file name */
};
enum
{
Buflogsize = sizeof(Buflog)/sizeof(Rune),
};
/*
* Minstring shouldn't be very big or we will do lots of I/O for small changes.
* Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
*/
enum
{
Minstring = 16, /* distance beneath which we merge changes */
Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */
};
void
eloginit(File *f)
{
if(f->elog.type != Empty)
return;
f->elog.type = Null;
if(f->elogbuf == nil)
f->elogbuf = emalloc(sizeof(Buffer));
if(f->elog.r == nil)
f->elog.r = fbufalloc();
bufreset(f->elogbuf);
}
void
elogclose(File *f)
{
if(f->elogbuf){
bufclose(f->elogbuf);
free(f->elogbuf);
f->elogbuf = nil;
}
}
void
elogreset(File *f)
{
f->elog.type = Null;
f->elog.nd = 0;
f->elog.nr = 0;
}
void
elogterm(File *f)
{
elogreset(f);
if(f->elogbuf)
bufreset(f->elogbuf);
f->elog.type = Empty;
fbuffree(f->elog.r);
f->elog.r = nil;
warned = FALSE;
}
void
elogflush(File *f)
{
Buflog b;
b.type = f->elog.type;
b.q0 = f->elog.q0;
b.nd = f->elog.nd;
b.nr = f->elog.nr;
switch(f->elog.type){
default:
warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
break;
case Null:
break;
case Insert:
case Replace:
if(f->elog.nr > 0)
bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
/* fall through */
case Delete:
bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
break;
}
elogreset(f);
}
void
elogreplace(File *f, int q0, int q1, Rune *r, int nr)
{
uint gap;
if(q0==q1 && nr==0)
return;
eloginit(f);
if(f->elog.type!=Null && q0<f->elog.q0){
if(warned++ == 0)
warning(nil, Wsequence);
elogflush(f);
}
/* try to merge with previous */
gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
if(gap < Minstring){
if(gap > 0){
bufread(&f->b, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
f->elog.nr += gap;
}
f->elog.nd += gap + q1-q0;
runemove(f->elog.r+f->elog.nr, r, nr);
f->elog.nr += nr;
return;
}
}
elogflush(f);
f->elog.type = Replace;
f->elog.q0 = q0;
f->elog.nd = q1-q0;
f->elog.nr = nr;
if(nr > RBUFSIZE)
editerror("internal error: replacement string too large(%d)", nr);
runemove(f->elog.r, r, nr);
}
void
eloginsert(File *f, int q0, Rune *r, int nr)
{
int n;
if(nr == 0)
return;
eloginit(f);
if(f->elog.type!=Null && q0<f->elog.q0){
if(warned++ == 0)
warning(nil, Wsequence);
elogflush(f);
}
/* try to merge with previous */
if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0<Maxstring){
runemove(f->elog.r+f->elog.nr, r, nr);
f->elog.nr += nr;
return;
}
while(nr > 0){
elogflush(f);
f->elog.type = Insert;
f->elog.q0 = q0;
n = nr;
if(n > RBUFSIZE)
n = RBUFSIZE;
f->elog.nr = n;
runemove(f->elog.r, r, n);
r += n;
nr -= n;
}
}
void
elogdelete(File *f, int q0, int q1)
{
if(q0 == q1)
return;
eloginit(f);
if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
if(warned++ == 0)
warning(nil, Wsequence);
elogflush(f);
}
/* try to merge with previous */
if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
f->elog.nd += q1-q0;
return;
}
elogflush(f);
f->elog.type = Delete;
f->elog.q0 = q0;
f->elog.nd = q1-q0;
}
#define tracelog 0
void
elogapply(File *f)
{
Buflog b;
Rune *buf;
uint i, n, up, mod;
uint q0, q1, tq0, tq1;
Buffer *log;
Text *t;
elogflush(f);
log = f->elogbuf;
t = f->curtext;
buf = fbufalloc();
mod = FALSE;
/*
* The edit commands have already updated the selection in t->q0, t->q1.
* (At least, they are supposed to have updated them.
* We still keep finding commands that don't do it right.)
* The textinsert and textdelete calls below will update it again, so save the
* current setting and restore it at the end.
*/
q0 = t->q0;
q1 = t->q1;
/*
* We constrain the addresses in here (with textconstrain()) because
* overlapping changes will generate bogus addresses. We will warn
* about changes out of sequence but proceed anyway; here we must
* keep things in range.
*/
while(log->nc > 0){
up = log->nc-Buflogsize;
bufread(log, up, (Rune*)&b, Buflogsize);
switch(b.type){
default:
fprint(2, "elogapply: 0x%ux\n", b.type);
abort();
break;
case Replace:
if(tracelog)
warning(nil, "elog replace %d %d\n",
b.q0, b.q0+b.nd);
if(!mod){
mod = TRUE;
filemark(f);
}
textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
textdelete(t, tq0, tq1, TRUE);
up -= b.nr;
for(i=0; i<b.nr; i+=n){
n = b.nr - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(log, up+i, buf, n);
textinsert(t, tq0+i, buf, n, TRUE);
}
break;
case Delete:
if(tracelog)
warning(nil, "elog delete %d %d\n",
b.q0, b.q0+b.nd);
if(!mod){
mod = TRUE;
filemark(f);
}
textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
textdelete(t, tq0, tq1, TRUE);
break;
case Insert:
if(tracelog)
warning(nil, "elog insert %d %d\n",
b.q0, b.q0+b.nr);
if(!mod){
mod = TRUE;
filemark(f);
}
textconstrain(t, b.q0, b.q0, &tq0, &tq1);
up -= b.nr;
for(i=0; i<b.nr; i+=n){
n = b.nr - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(log, up+i, buf, n);
textinsert(t, tq0+i, buf, n, TRUE);
}
break;
/* case Filename:
f->seq = u.seq;
fileunsetname(f, epsilon);
f->mod = u.mod;
up -= u.n;
free(f->name);
if(u.n == 0)
f->name = nil;
else
f->name = runemalloc(u.n);
bufread(delta, up, f->name, u.n);
f->nname = u.n;
break;
*/
}
bufdelete(log, up, log->nc);
}
fbuffree(buf);
if(warned){
/*
* Changes were out of order, so the q0 and q1
* computed while generating those changes are not
* to be trusted.
*/
q1 = min(q1, f->b.nc);
q0 = min(q0, q1);
}
elogterm(f);
/*
* The q0 and q1 are supposed to be fine (see comment
* above, where we saved them), but bad addresses
* will cause bufload to crash, so double check.
*/
if(q0 > f->b.nc || q1 > f->b.nc || q0 > q1){
warning(nil, "elogapply: can't happen %d %d %d\n", q0, q1, f->b.nc);
q1 = min(q1, f->b.nc);
q0 = min(q0, q1);
}
t->q0 = q0;
t->q1 = q1;
}

1491
src/cmd/acme/exec.c Normal file

File diff suppressed because it is too large Load Diff

310
src/cmd/acme/file.c Normal file
View File

@ -0,0 +1,310 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
/*
* Structure of Undo list:
* The Undo structure follows any associated data, so the list
* can be read backwards: read the structure, then read whatever
* data is associated (insert string, file name) and precedes it.
* The structure includes the previous value of the modify bit
* and a sequence number; successive Undo structures with the
* same sequence number represent simultaneous changes.
*/
typedef struct Undo Undo;
struct Undo
{
short type; /* Delete, Insert, Filename */
short mod; /* modify bit */
uint seq; /* sequence number */
uint p0; /* location of change (unused in f) */
uint n; /* # runes in string or file name */
};
enum
{
Undosize = sizeof(Undo)/sizeof(Rune),
};
File*
fileaddtext(File *f, Text *t)
{
if(f == nil){
f = emalloc(sizeof(File));
f->unread = TRUE;
}
f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
f->text[f->ntext++] = t;
f->curtext = t;
return f;
}
void
filedeltext(File *f, Text *t)
{
int i;
for(i=0; i<f->ntext; i++)
if(f->text[i] == t)
goto Found;
error("can't find text in filedeltext");
Found:
f->ntext--;
if(f->ntext == 0){
fileclose(f);
return;
}
memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
if(f->curtext == t)
f->curtext = f->text[0];
}
void
fileinsert(File *f, uint p0, Rune *s, uint ns)
{
if(p0 > f->b.nc)
error("internal error: fileinsert");
if(f->seq > 0)
fileuninsert(f, &f->delta, p0, ns);
bufinsert(&f->b, p0, s, ns);
if(ns)
f->mod = TRUE;
}
void
fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
{
Undo u;
/* undo an insertion by deleting */
u.type = Delete;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = p0;
u.n = ns;
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
void
filedelete(File *f, uint p0, uint p1)
{
if(!(p0<=p1 && p0<=f->b.nc && p1<=f->b.nc))
error("internal error: filedelete");
if(f->seq > 0)
fileundelete(f, &f->delta, p0, p1);
bufdelete(&f->b, p0, p1);
if(p1 > p0)
f->mod = TRUE;
}
void
fileundelete(File *f, Buffer *delta, uint p0, uint p1)
{
Undo u;
Rune *buf;
uint i, n;
/* undo a deletion by inserting */
u.type = Insert;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = p0;
u.n = p1-p0;
buf = fbufalloc();
for(i=p0; i<p1; i+=n){
n = p1 - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(&f->b, i, buf, n);
bufinsert(delta, delta->nc, buf, n);
}
fbuffree(buf);
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
void
filesetname(File *f, Rune *name, int n)
{
if(f->seq > 0)
fileunsetname(f, &f->delta);
free(f->name);
f->name = runemalloc(n);
runemove(f->name, name, n);
f->nname = n;
f->unread = TRUE;
}
void
fileunsetname(File *f, Buffer *delta)
{
Undo u;
/* undo a file name change by restoring old name */
u.type = Filename;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = 0; /* unused */
u.n = f->nname;
if(f->nname)
bufinsert(delta, delta->nc, f->name, f->nname);
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
uint
fileload(File *f, uint p0, int fd, int *nulls)
{
if(f->seq > 0)
error("undo in file.load unimplemented");
return bufload(&f->b, p0, fd, nulls);
}
/* return sequence number of pending redo */
uint
fileredoseq(File *f)
{
Undo u;
Buffer *delta;
delta = &f->epsilon;
if(delta->nc == 0)
return 0;
bufread(delta, delta->nc-Undosize, (Rune*)&u, Undosize);
return u.seq;
}
void
fileundo(File *f, int isundo, uint *q0p, uint *q1p)
{
Undo u;
Rune *buf;
uint i, j, n, up;
uint stop;
Buffer *delta, *epsilon;
if(isundo){
/* undo; reverse delta onto epsilon, seq decreases */
delta = &f->delta;
epsilon = &f->epsilon;
stop = f->seq;
}else{
/* redo; reverse epsilon onto delta, seq increases */
delta = &f->epsilon;
epsilon = &f->delta;
stop = 0; /* don't know yet */
}
buf = fbufalloc();
while(delta->nc > 0){
up = delta->nc-Undosize;
bufread(delta, up, (Rune*)&u, Undosize);
if(isundo){
if(u.seq < stop){
f->seq = u.seq;
goto Return;
}
}else{
if(stop == 0)
stop = u.seq;
if(u.seq > stop)
goto Return;
}
switch(u.type){
default:
fprint(2, "undo: 0x%ux\n", u.type);
abort();
break;
case Delete:
f->seq = u.seq;
fileundelete(f, epsilon, u.p0, u.p0+u.n);
f->mod = u.mod;
bufdelete(&f->b, u.p0, u.p0+u.n);
for(j=0; j<f->ntext; j++)
textdelete(f->text[j], u.p0, u.p0+u.n, FALSE);
*q0p = u.p0;
*q1p = u.p0;
break;
case Insert:
f->seq = u.seq;
fileuninsert(f, epsilon, u.p0, u.n);
f->mod = u.mod;
up -= u.n;
for(i=0; i<u.n; i+=n){
n = u.n - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(delta, up+i, buf, n);
bufinsert(&f->b, u.p0+i, buf, n);
for(j=0; j<f->ntext; j++)
textinsert(f->text[j], u.p0+i, buf, n, FALSE);
}
*q0p = u.p0;
*q1p = u.p0+u.n;
break;
case Filename:
f->seq = u.seq;
fileunsetname(f, epsilon);
f->mod = u.mod;
up -= u.n;
free(f->name);
if(u.n == 0)
f->name = nil;
else
f->name = runemalloc(u.n);
bufread(delta, up, f->name, u.n);
f->nname = u.n;
break;
}
bufdelete(delta, up, delta->nc);
}
if(isundo)
f->seq = 0;
Return:
fbuffree(buf);
}
void
filereset(File *f)
{
bufreset(&f->delta);
bufreset(&f->epsilon);
f->seq = 0;
}
void
fileclose(File *f)
{
free(f->name);
f->nname = 0;
f->name = nil;
free(f->text);
f->ntext = 0;
f->text = nil;
bufclose(&f->b);
bufclose(&f->delta);
bufclose(&f->epsilon);
elogclose(f);
free(f);
}
void
filemark(File *f)
{
if(f->epsilon.nc)
bufdelete(&f->epsilon, 0, f->epsilon.nc);
f->seq = seq;
}

92
src/cmd/acme/fns.h Normal file
View File

@ -0,0 +1,92 @@
/*
#pragma varargck argpos warning 2
#pragma varargck argpos warningew 2
*/
void warning(Mntdir*, char*, ...);
void warningew(Window*, Mntdir*, char*, ...);
#define fbufalloc() emalloc(BUFSIZE)
#define fbuffree(x) free(x)
void plumblook(Plumbmsg*m);
void plumbshow(Plumbmsg*m);
void acmeputsnarf(void);
void acmegetsnarf(void);
int tempfile(void);
void scrlresize(void);
Font* getfont(int, int, char*);
char* getarg(Text*, int, int, Rune**, int*);
char* getbytearg(Text*, int, int, char**);
void new(Text*, Text*, Text*, int, int, Rune*, int);
void undo(Text*, Text*, Text*, int, int, Rune*, int);
void scrsleep(uint);
void savemouse(Window*);
void restoremouse(Window*);
void clearmouse(void);
void allwindows(void(*)(Window*, void*), void*);
uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*);
Window* errorwin(Mntdir*, int, Window*);
Runestr cleanrname(Runestr);
void run(Window*, char*, Rune*, int, int, char*, char*, int);
void fsysclose(void);
void setcurtext(Text*, int);
int isfilec(Rune);
void rxinit(void);
int rxnull(void);
Runestr dirname(Text*, Rune*, int);
void error(char*);
void cvttorunes(char*, int, Rune*, int*, int*, int*);
void* tmalloc(uint);
void tfree(void);
void killprocs(void);
void killtasks(void);
int runeeq(Rune*, uint, Rune*, uint);
int ALEF_tid(void);
void iconinit(void);
Timer* timerstart(int);
void timerstop(Timer*);
void timercancel(Timer*);
void timerinit(void);
void cut(Text*, Text*, Text*, int, int, Rune*, int);
void paste(Text*, Text*, Text*, int, int, Rune*, int);
void get(Text*, Text*, Text*, int, int, Rune*, int);
void put(Text*, Text*, Text*, int, int, Rune*, int);
void putfile(File*, int, int, Rune*, int);
void fontx(Text*, Text*, Text*, int, int, Rune*, int);
int isalnum(Rune);
void execute(Text*, uint, uint, int, Text*);
int search(Text*, Rune*, uint);
void look3(Text*, uint, uint, int);
void editcmd(Text*, Rune*, uint);
uint min(uint, uint);
uint max(uint, uint);
Window* lookfile(Rune*, int);
Window* lookid(int, int);
char* runetobyte(Rune*, int);
Rune* bytetorune(char*, int*);
void fsysinit(void);
Mntdir* fsysmount(Rune*, int, Rune**, int);
void fsysdelid(Mntdir*);
Xfid* respond(Xfid*, Fcall*, char*);
int rxcompile(Rune*);
int rgetc(void*, uint);
int tgetc(void*, uint);
int isaddrc(int);
int isregexc(int);
void *emalloc(uint);
void *erealloc(void*, uint);
char *estrdup(char*);
Range address(Mntdir*, Text*, Range, Range, void*, uint, uint, int (*)(void*, uint), int*, uint*);
int rxexecute(Text*, Rune*, uint, uint, Rangeset*);
int rxbexecute(Text*, uint, Rangeset*);
Window* makenewwindow(Text *t);
int expand(Text*, uint, uint, Expand*);
Rune* skipbl(Rune*, int, int*);
Rune* findbl(Rune*, int, int*);
char* edittext(Window*, int, Rune*, int);
#define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune))
#define runerealloc(a, b) (Rune*)erealloc((a), (b)*sizeof(Rune))
#define runemove(a, b, c) memmove((a), (b), (c)*sizeof(Rune))

717
src/cmd/acme/fsys.c Normal file
View File

@ -0,0 +1,717 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static int sfd;
enum
{
Nhash = 16,
DEBUG = 0
};
static Fid *fids[Nhash];
Fid *newfid(int);
static Xfid* fsysflush(Xfid*, Fid*);
static Xfid* fsysauth(Xfid*, Fid*);
static Xfid* fsysversion(Xfid*, Fid*);
static Xfid* fsysattach(Xfid*, Fid*);
static Xfid* fsyswalk(Xfid*, Fid*);
static Xfid* fsysopen(Xfid*, Fid*);
static Xfid* fsyscreate(Xfid*, Fid*);
static Xfid* fsysread(Xfid*, Fid*);
static Xfid* fsyswrite(Xfid*, Fid*);
static Xfid* fsysclunk(Xfid*, Fid*);
static Xfid* fsysremove(Xfid*, Fid*);
static Xfid* fsysstat(Xfid*, Fid*);
static Xfid* fsyswstat(Xfid*, Fid*);
Xfid* (*fcall[Tmax])(Xfid*, Fid*) =
{
[Tflush] = fsysflush,
[Tversion] = fsysversion,
[Tauth] = fsysauth,
[Tattach] = fsysattach,
[Twalk] = fsyswalk,
[Topen] = fsysopen,
[Tcreate] = fsyscreate,
[Tread] = fsysread,
[Twrite] = fsyswrite,
[Tclunk] = fsysclunk,
[Tremove]= fsysremove,
[Tstat] = fsysstat,
[Twstat] = fsyswstat,
};
char Eperm[] = "permission denied";
char Eexist[] = "file does not exist";
char Enotdir[] = "not a directory";
Dirtab dirtab[]=
{
{ ".", QTDIR, Qdir, 0500|DMDIR },
{ "acme", QTDIR, Qacme, 0500|DMDIR },
{ "cons", QTFILE, Qcons, 0600 },
{ "consctl", QTFILE, Qconsctl, 0000 },
{ "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */
{ "editout", QTFILE, Qeditout, 0200 },
{ "index", QTFILE, Qindex, 0400 },
{ "label", QTFILE, Qlabel, 0600 },
{ "new", QTDIR, Qnew, 0500|DMDIR },
{ nil, }
};
Dirtab dirtabw[]=
{
{ ".", QTDIR, Qdir, 0500|DMDIR },
{ "addr", QTFILE, QWaddr, 0600 },
{ "body", QTAPPEND, QWbody, 0600|DMAPPEND },
{ "ctl", QTFILE, QWctl, 0600 },
{ "data", QTFILE, QWdata, 0600 },
{ "editout", QTFILE, QWeditout, 0200 },
{ "event", QTFILE, QWevent, 0600 },
{ "rdsel", QTFILE, QWrdsel, 0400 },
{ "wrsel", QTFILE, QWwrsel, 0200 },
{ "tag", QTAPPEND, QWtag, 0600|DMAPPEND },
{ nil, }
};
typedef struct Mnt Mnt;
struct Mnt
{
QLock lk;
int id;
Mntdir *md;
};
Mnt mnt;
Xfid* respond(Xfid*, Fcall*, char*);
int dostat(int, Dirtab*, uchar*, int, uint);
uint getclock(void);
char *user = "Wile E. Coyote";
static int closing = 0;
int messagesize = Maxblock+IOHDRSZ; /* good start */
void fsysproc(void *);
void
fsysinit(void)
{
int p[2];
int n, fd;
char buf[256], *u;
if(pipe(p) < 0)
error("can't create pipe");
if(post9pservice(p[0], "acme") < 0)
error("can't post service");
sfd = p[1];
fmtinstall('F', fcallfmt);
if((u = getuser()) != nil)
user = estrdup(u);
proccreate(fsysproc, nil, STACK);
}
void
fsysproc(void *v)
{
int n;
Xfid *x;
Fid *f;
Fcall t;
uchar *buf;
USED(v);
x = nil;
for(;;){
buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
n = read9pmsg(sfd, buf, messagesize);
if(n <= 0){
if(closing)
break;
error("i/o error on server channel");
}
if(x == nil){
sendp(cxfidalloc, nil);
x = recvp(cxfidalloc);
}
x->buf = buf;
if(convM2S(buf, n, &x->fcall) != n)
error("convert error in convM2S");
if(DEBUG)
fprint(2, "%F\n", &x->fcall);
if(fcall[x->fcall.type] == nil)
x = respond(x, &t, "bad fcall type");
else{
if(x->fcall.type==Tversion || x->fcall.type==Tauth)
f = nil;
else
f = newfid(x->fcall.fid);
x->f = f;
x = (*fcall[x->fcall.type])(x, f);
}
}
}
Mntdir*
fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
{
Mntdir *m;
int id;
qlock(&mnt.lk);
id = ++mnt.id;
m = emalloc(sizeof *m);
m->id = id;
m->dir = dir;
m->ref = 1; /* one for Command, one will be incremented in attach */
m->ndir = ndir;
m->next = mnt.md;
m->incl = incl;
m->nincl = nincl;
mnt.md = m;
qunlock(&mnt.lk);
return m;
}
void
fsysdelid(Mntdir *idm)
{
Mntdir *m, *prev;
int i;
char buf[64];
if(idm == nil)
return;
qlock(&mnt.lk);
if(--idm->ref > 0){
qunlock(&mnt.lk);
return;
}
prev = nil;
for(m=mnt.md; m; m=m->next){
if(m == idm){
if(prev)
prev->next = m->next;
else
mnt.md = m->next;
for(i=0; i<m->nincl; i++)
free(m->incl[i]);
free(m->incl);
free(m->dir);
free(m);
qunlock(&mnt.lk);
return;
}
prev = m;
}
qunlock(&mnt.lk);
sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
sendp(cerr, estrdup(buf));
}
/*
* Called only in exec.c:/^run(), from a different FD group
*/
Mntdir*
fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
{
return fsysaddid(dir, ndir, incl, nincl);
}
void
fsysclose(void)
{
closing = 1;
close(sfd);
}
Xfid*
respond(Xfid *x, Fcall *t, char *err)
{
int n;
if(err){
t->type = Rerror;
t->ename = err;
}else
t->type = x->fcall.type+1;
t->fid = x->fcall.fid;
t->tag = x->fcall.tag;
if(x->buf == nil)
x->buf = emalloc(messagesize);
n = convS2M(t, x->buf, messagesize);
if(n <= 0)
error("convert error in convS2M");
if(write(sfd, x->buf, n) != n)
error("write error in respond");
free(x->buf);
x->buf = nil;
if(DEBUG)
fprint(2, "r: %F\n", t);
return x;
}
static
Xfid*
fsysversion(Xfid *x, Fid *f)
{
Fcall t;
USED(f);
if(x->fcall.msize < 256)
return respond(x, &t, "version: message size too small");
messagesize = x->fcall.msize;
t.msize = messagesize;
if(strncmp(x->fcall.version, "9P2000", 6) != 0)
return respond(x, &t, "unrecognized 9P version");
t.version = "9P2000";
return respond(x, &t, nil);
}
static
Xfid*
fsysauth(Xfid *x, Fid *f)
{
USED(f);
return respond(x, nil, "acme: authentication not required");
}
static
Xfid*
fsysflush(Xfid *x, Fid *f)
{
USED(f);
sendp(x->c, xfidflush);
return nil;
}
static
Xfid*
fsysattach(Xfid *x, Fid *f)
{
Fcall t;
int id;
Mntdir *m;
if(strcmp(x->fcall.uname, user) != 0)
return respond(x, &t, Eperm);
f->busy = TRUE;
f->open = FALSE;
f->qid.path = Qdir;
f->qid.type = QTDIR;
f->qid.vers = 0;
f->dir = dirtab;
f->nrpart = 0;
f->w = nil;
t.qid = f->qid;
f->mntdir = nil;
id = atoi(x->fcall.aname);
qlock(&mnt.lk);
for(m=mnt.md; m; m=m->next)
if(m->id == id){
f->mntdir = m;
m->ref++;
break;
}
if(m == nil)
sendp(cerr, estrdup("unknown id in attach"));
qunlock(&mnt.lk);
return respond(x, &t, nil);
}
static
Xfid*
fsyswalk(Xfid *x, Fid *f)
{
Fcall t;
int c, i, j, id;
Qid q;
uchar type;
ulong path;
Fid *nf;
Dirtab *d, *dir;
Window *w;
char *err;
nf = nil;
w = nil;
if(f->open)
return respond(x, &t, "walk of open file");
if(x->fcall.fid != x->fcall.newfid){
nf = newfid(x->fcall.newfid);
if(nf->busy)
return respond(x, &t, "newfid already in use");
nf->busy = TRUE;
nf->open = FALSE;
nf->mntdir = f->mntdir;
if(f->mntdir)
f->mntdir->ref++;
nf->dir = f->dir;
nf->qid = f->qid;
nf->w = f->w;
nf->nrpart = 0; /* not open, so must be zero */
if(nf->w)
incref(&nf->w->ref);
f = nf; /* walk f */
}
t.nwqid = 0;
err = nil;
dir = nil;
id = WIN(f->qid);
q = f->qid;
if(x->fcall.nwname > 0){
for(i=0; i<x->fcall.nwname; i++){
if((q.type & QTDIR) == 0){
err = Enotdir;
break;
}
if(strcmp(x->fcall.wname[i], "..") == 0){
type = QTDIR;
path = Qdir;
id = 0;
if(w){
winclose(w);
w = nil;
}
Accept:
if(i == MAXWELEM){
err = "name too long";
break;
}
q.type = type;
q.vers = 0;
q.path = QID(id, path);
t.wqid[t.nwqid++] = q;
continue;
}
/* is it a numeric name? */
for(j=0; (c=x->fcall.wname[i][j]); j++)
if(c<'0' || '9'<c)
goto Regular;
/* yes: it's a directory */
if(w) /* name has form 27/23; get out before losing w */
break;
id = atoi(x->fcall.wname[i]);
qlock(&row.lk);
w = lookid(id, FALSE);
if(w == nil){
qunlock(&row.lk);
break;
}
incref(&w->ref); /* we'll drop reference at end if there's an error */
path = Qdir;
type = QTDIR;
qunlock(&row.lk);
dir = dirtabw;
goto Accept;
Regular:
// if(FILE(f->qid) == Qacme) /* empty directory */
// break;
if(strcmp(x->fcall.wname[i], "new") == 0){
if(w)
error("w set in walk to new");
sendp(cnewwindow, nil); /* signal newwindowthread */
w = recvp(cnewwindow); /* receive new window */
incref(&w->ref);
type = QTDIR;
path = QID(w->id, Qdir);
id = w->id;
dir = dirtabw;
goto Accept;
}
if(id == 0)
d = dirtab;
else
d = dirtabw;
d++; /* skip '.' */
for(; d->name; d++)
if(strcmp(x->fcall.wname[i], d->name) == 0){
path = d->qid;
type = d->type;
dir = d;
goto Accept;
}
break; /* file not found */
}
if(i==0 && err == nil)
err = Eexist;
}
if(err!=nil || t.nwqid<x->fcall.nwname){
if(nf){
nf->busy = FALSE;
fsysdelid(nf->mntdir);
}
}else if(t.nwqid == x->fcall.nwname){
if(w){
f->w = w;
w = nil; /* don't drop the reference */
}
if(dir)
f->dir = dir;
f->qid = q;
}
if(w != nil)
winclose(w);
return respond(x, &t, err);
}
static
Xfid*
fsysopen(Xfid *x, Fid *f)
{
Fcall t;
int m;
/* can't truncate anything, so just disregard */
x->fcall.mode &= ~(OTRUNC|OCEXEC);
/* can't execute or remove anything */
if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
goto Deny;
switch(x->fcall.mode){
default:
goto Deny;
case OREAD:
m = 0400;
break;
case OWRITE:
m = 0200;
break;
case ORDWR:
m = 0600;
break;
}
if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
goto Deny;
sendp(x->c, xfidopen);
return nil;
Deny:
return respond(x, &t, Eperm);
}
static
Xfid*
fsyscreate(Xfid *x, Fid *f)
{
Fcall t;
USED(f);
return respond(x, &t, Eperm);
}
static
int
idcmp(const void *a, const void *b)
{
return *(int*)a - *(int*)b;
}
static
Xfid*
fsysread(Xfid *x, Fid *f)
{
Fcall t;
uchar *b;
int i, id, n, o, e, j, k, *ids, nids;
Dirtab *d, dt;
Column *c;
uint clock, len;
char buf[16];
if(f->qid.type & QTDIR){
if(FILE(f->qid) == Qacme){ /* empty dir */
t.data = nil;
t.count = 0;
respond(x, &t, nil);
return x;
}
o = x->fcall.offset;
e = x->fcall.offset+x->fcall.count;
clock = getclock();
b = emalloc(messagesize);
id = WIN(f->qid);
n = 0;
if(id > 0)
d = dirtabw;
else
d = dirtab;
d++; /* first entry is '.' */
for(i=0; d->name!=nil && i<e; i+=len){
len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
if(len <= BIT16SZ)
break;
if(i >= o)
n += len;
d++;
}
if(id == 0){
qlock(&row.lk);
nids = 0;
ids = nil;
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(k=0; k<c->nw; k++){
ids = realloc(ids, (nids+1)*sizeof(int));
ids[nids++] = c->w[k]->id;
}
}
qunlock(&row.lk);
qsort(ids, nids, sizeof ids[0], idcmp);
j = 0;
dt.name = buf;
for(; j<nids && i<e; i+=len){
k = ids[j];
sprint(dt.name, "%d", k);
dt.qid = QID(k, Qdir);
dt.type = QTDIR;
dt.perm = DMDIR|0700;
len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
if(len == 0)
break;
if(i >= o)
n += len;
j++;
}
free(ids);
}
t.data = (char*)b;
t.count = n;
respond(x, &t, nil);
free(b);
return x;
}
sendp(x->c, xfidread);
return nil;
}
static
Xfid*
fsyswrite(Xfid *x, Fid *f)
{
USED(f);
sendp(x->c, xfidwrite);
return nil;
}
static
Xfid*
fsysclunk(Xfid *x, Fid *f)
{
fsysdelid(f->mntdir);
sendp(x->c, xfidclose);
return nil;
}
static
Xfid*
fsysremove(Xfid *x, Fid *f)
{
Fcall t;
USED(f);
return respond(x, &t, Eperm);
}
static
Xfid*
fsysstat(Xfid *x, Fid *f)
{
Fcall t;
t.stat = emalloc(messagesize-IOHDRSZ);
t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
x = respond(x, &t, nil);
free(t.stat);
return x;
}
static
Xfid*
fsyswstat(Xfid *x, Fid *f)
{
Fcall t;
USED(f);
return respond(x, &t, Eperm);
}
Fid*
newfid(int fid)
{
Fid *f, *ff, **fh;
ff = nil;
fh = &fids[fid&(Nhash-1)];
for(f=*fh; f; f=f->next)
if(f->fid == fid)
return f;
else if(ff==nil && f->busy==FALSE)
ff = f;
if(ff){
ff->fid = fid;
return ff;
}
f = emalloc(sizeof *f);
f->fid = fid;
f->next = *fh;
*fh = f;
return f;
}
uint
getclock(void)
{
/*
char buf[32];
buf[0] = '\0';
pread(clockfd, buf, sizeof buf, 0);
return atoi(buf);
*/
return time(0);
}
int
dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
{
Dir d;
d.qid.path = QID(id, dir->qid);
d.qid.vers = 0;
d.qid.type = dir->type;
d.mode = dir->perm;
d.length = 0; /* would be nice to do better */
d.name = dir->name;
d.uid = user;
d.gid = user;
d.muid = user;
d.atime = clock;
d.mtime = clock;
return convD2M(&d, buf, nbuf);
}

772
src/cmd/acme/look.c Normal file
View File

@ -0,0 +1,772 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <regexp.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
Window* openfile(Text*, Expand*);
int nuntitled;
void
look3(Text *t, uint q0, uint q1, int external)
{
int n, c, f, expanded;
Text *ct;
Expand e;
Rune *r;
uint p;
Plumbmsg *m;
Runestr dir;
char buf[32];
ct = seltext;
if(ct == nil)
seltext = t;
expanded = expand(t, q0, q1, &e);
if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
/* send alphanumeric expansion to external client */
if(expanded == FALSE)
return;
f = 0;
if((e.u.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil))
f = 1; /* acme can do it without loading a file */
if(q0!=e.q0 || q1!=e.q1)
f |= 2; /* second (post-expand) message follows */
if(e.nname)
f |= 4; /* it's a file name */
c = 'l';
if(t->what == Body)
c = 'L';
n = q1-q0;
if(n <= EVENTSIZE){
r = runemalloc(n);
bufread(&t->file->b, q0, r, n);
winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r);
free(r);
}else
winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n);
if(q0==e.q0 && q1==e.q1)
return;
if(e.nname){
n = e.nname;
if(e.a1 > e.a0)
n += 1+(e.a1-e.a0);
r = runemalloc(n);
runemove(r, e.name, e.nname);
if(e.a1 > e.a0){
r[e.nname] = ':';
bufread(&e.u.at->file->b, e.a0, r+e.nname+1, e.a1-e.a0);
}
}else{
n = e.q1 - e.q0;
r = runemalloc(n);
bufread(&t->file->b, e.q0, r, n);
}
f &= ~2;
if(n <= EVENTSIZE)
winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r);
else
winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n);
free(r);
goto Return;
}
if(plumbsendfd >= 0){
/* send whitespace-delimited word to plumber */
m = emalloc(sizeof(Plumbmsg));
m->src = estrdup("acme");
m->dst = nil;
dir = dirname(t, nil, 0);
if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
free(dir.r);
dir.r = nil;
dir.nr = 0;
}
if(dir.nr == 0)
m->wdir = estrdup(wdir);
else
m->wdir = runetobyte(dir.r, dir.nr);
free(dir.r);
m->type = estrdup("text");
m->attr = nil;
buf[0] = '\0';
if(q1 == q0){
if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
q0 = t->q0;
q1 = t->q1;
}else{
p = q0;
while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n')
q0--;
while(q1<t->file->b.nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n')
q1++;
if(q1 == q0){
plumbfree(m);
goto Return;
}
sprint(buf, "click=%d", p-q0);
m->attr = plumbunpackattr(buf);
}
}
r = runemalloc(q1-q0);
bufread(&t->file->b, q0, r, q1-q0);
m->data = runetobyte(r, q1-q0);
m->ndata = strlen(m->data);
free(r);
if(m->ndata<messagesize-1024 && plumbsend(plumbsendfd, m) >= 0){
plumbfree(m);
goto Return;
}
plumbfree(m);
/* plumber failed to match; fall through */
}
/* interpret alphanumeric string ourselves */
if(expanded == FALSE)
return;
if(e.name || e.u.at)
openfile(t, &e);
else{
if(t->w == nil)
return;
ct = &t->w->body;
if(t->w != ct->w)
winlock(ct->w, 'M');
if(t == ct)
textsetselect(ct, e.q1, e.q1);
n = e.q1 - e.q0;
r = runemalloc(n);
bufread(&t->file->b, e.q0, r, n);
if(search(ct, r, n) && e.jump)
moveto(mousectl, addpt(frptofchar(&ct->fr, ct->fr.p0), Pt(4, ct->fr.font->height-4)));
if(t->w != ct->w)
winunlock(ct->w);
free(r);
}
Return:
free(e.name);
free(e.bname);
}
int
plumbgetc(void *a, uint n)
{
Rune *r;
r = a;
if(n<0 || n>runestrlen(r))
return 0;
return r[n];
}
void
plumblook(Plumbmsg *m)
{
Expand e;
char *addr;
if(m->ndata >= BUFSIZE){
warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data);
return;
}
e.q0 = 0;
e.q1 = 0;
if(m->data[0] == '\0')
return;
e.u.ar = nil;
e.bname = m->data;
e.name = bytetorune(e.bname, &e.nname);
e.jump = TRUE;
e.a0 = 0;
e.a1 = 0;
addr = plumblookup(m->attr, "addr");
if(addr != nil){
e.u.ar = bytetorune(addr, &e.a1);
e.agetc = plumbgetc;
}
openfile(nil, &e);
free(e.name);
free(e.u.at);
}
void
plumbshow(Plumbmsg *m)
{
Window *w;
Rune rb[256], *r;
int nb, nr;
Runestr rs;
char *name, *p, namebuf[16];
w = makenewwindow(nil);
name = plumblookup(m->attr, "filename");
if(name == nil){
name = namebuf;
nuntitled++;
snprint(namebuf, sizeof namebuf, "Untitled-%d", nuntitled);
}
p = nil;
if(name[0]!='/' && m->wdir!=nil && m->wdir[0]!='\0'){
nb = strlen(m->wdir) + 1 + strlen(name) + 1;
p = emalloc(nb);
snprint(p, nb, "%s/%s", m->wdir, name);
name = p;
}
cvttorunes(name, strlen(name), rb, &nb, &nr, nil);
free(p);
rs = cleanrname((Runestr){rb, nr});
winsetname(w, rs.r, rs.nr);
r = runemalloc(m->ndata);
cvttorunes(m->data, m->ndata, r, &nb, &nr, nil);
textinsert(&w->body, 0, r, nr, TRUE);
free(r);
w->body.file->mod = FALSE;
w->dirty = FALSE;
winsettag(w);
textscrdraw(&w->body);
textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
}
int
search(Text *ct, Rune *r, uint n)
{
uint q, nb, maxn;
int around;
Rune *s, *b, *c;
if(n==0 || n>ct->file->b.nc)
return FALSE;
if(2*n > RBUFSIZE){
warning(nil, "string too long\n");
return FALSE;
}
maxn = max(2*n, RBUFSIZE);
s = fbufalloc();
b = s;
nb = 0;
b[nb] = 0;
around = 0;
q = ct->q1;
for(;;){
if(q >= ct->file->b.nc){
q = 0;
around = 1;
nb = 0;
b[nb] = 0;
}
if(nb > 0){
c = runestrchr(b, r[0]);
if(c == nil){
q += nb;
nb = 0;
b[nb] = 0;
if(around && q>=ct->q1)
break;
continue;
}
q += (c-b);
nb -= (c-b);
b = c;
}
/* reload if buffer covers neither string nor rest of file */
if(nb<n && nb!=ct->file->b.nc-q){
nb = ct->file->b.nc-q;
if(nb >= maxn)
nb = maxn-1;
bufread(&ct->file->b, q, s, nb);
b = s;
b[nb] = '\0';
}
/* this runeeq is fishy but the null at b[nb] makes it safe */
if(runeeq(b, n, r, n)==TRUE){
if(ct->w){
textshow(ct, q, q+n, 1);
winsettag(ct->w);
}else{
ct->q0 = q;
ct->q1 = q+n;
}
seltext = ct;
fbuffree(s);
return TRUE;
}
if(around && q>=ct->q1)
break;
--nb;
b++;
q++;
}
fbuffree(s);
return FALSE;
}
int
isfilec(Rune r)
{
static Rune Lx[] = { '.', '-', '+', '/', ':', 0 };
if(isalnum(r))
return TRUE;
if(runestrchr(Lx, r))
return TRUE;
return FALSE;
}
Runestr
cleanrname(Runestr rs)
{
int i, j, found;
Rune *b;
int n;
static Rune Lslashdotdot[] = { '/', '.', '.', 0 };
b = rs.r;
n = rs.nr;
/* compress multiple slashes */
for(i=0; i<n-1; i++)
if(b[i]=='/' && b[i+1]=='/'){
runemove(b+i, b+i+1, n-i-1);
--n;
--i;
}
/* eliminate ./ */
for(i=0; i<n-1; i++)
if(b[i]=='.' && b[i+1]=='/' && (i==0 || b[i-1]=='/')){
runemove(b+i, b+i+2, n-i-2);
n -= 2;
--i;
}
/* eliminate trailing . */
if(n>=2 && b[n-2]=='/' && b[n-1]=='.')
--n;
do{
/* compress xx/.. */
found = FALSE;
for(i=1; i<=n-3; i++)
if(runeeq(b+i, 3, Lslashdotdot, 3)){
if(i==n-3 || b[i+3]=='/'){
found = TRUE;
break;
}
}
if(found)
for(j=i-1; j>=0; --j)
if(j==0 || b[j-1]=='/'){
i += 3; /* character beyond .. */
if(i<n && b[i]=='/')
++i;
runemove(b+j, b+i, n-i);
n -= (i-j);
break;
}
}while(found);
if(n == 0){
*b = '.';
n = 1;
}
return (Runestr){b, n};
}
Runestr
includefile(Rune *dir, Rune *file, int nfile)
{
int m, n;
char *a;
Rune *r;
static Rune Lslash[] = { '/', 0 };
m = runestrlen(dir);
a = emalloc((m+1+nfile)*UTFmax+1);
sprint(a, "%S/%.*S", dir, nfile, file);
n = access(a, 0);
free(a);
if(n < 0)
return (Runestr){nil, 0};
r = runemalloc(m+1+nfile);
runemove(r, dir, m);
runemove(r+m, Lslash, 1);
runemove(r+m+1, file, nfile);
free(file);
return cleanrname((Runestr){r, m+1+nfile});
}
static Rune *objdir;
Runestr
includename(Text *t, Rune *r, int n)
{
Window *w;
char buf[128];
Rune Lsysinclude[] = { '/', 's', 'y', 's', '/', 'i', 'n', 'c', 'l', 'u', 'd', 'e', 0 };
Runestr file;
int i;
if(objdir==nil && objtype!=nil){
sprint(buf, "/%s/include", objtype);
objdir = bytetorune(buf, &i);
objdir = runerealloc(objdir, i+1);
objdir[i] = '\0';
}
w = t->w;
if(n==0 || r[0]=='/' || w==nil)
goto Rescue;
if(n>2 && r[0]=='.' && r[1]=='/')
goto Rescue;
file.r = nil;
file.nr = 0;
for(i=0; i<w->nincl && file.r==nil; i++)
file = includefile(w->incl[i], r, n);
if(file.r == nil)
file = includefile(Lsysinclude, r, n);
if(file.r==nil && objdir!=nil)
file = includefile(objdir, r, n);
if(file.r == nil)
goto Rescue;
return file;
Rescue:
return (Runestr){r, n};
}
Runestr
dirname(Text *t, Rune *r, int n)
{
Rune *b, c;
uint m, nt;
int slash;
Runestr tmp;
b = nil;
if(t==nil || t->w==nil)
goto Rescue;
nt = t->w->tag.file->b.nc;
if(nt == 0)
goto Rescue;
if(n>=1 && r[0]=='/')
goto Rescue;
b = runemalloc(nt+n+1);
bufread(&t->w->tag.file->b, 0, b, nt);
slash = -1;
for(m=0; m<nt; m++){
c = b[m];
if(c == '/')
slash = m;
if(c==' ' || c=='\t')
break;
}
if(slash < 0)
goto Rescue;
runemove(b+slash+1, r, n);
free(r);
return cleanrname((Runestr){b, slash+1+n});
Rescue:
free(b);
tmp = (Runestr){r, n};
if(r)
return cleanrname(tmp);
return tmp;
}
int
expandfile(Text *t, uint q0, uint q1, Expand *e)
{
int i, n, nname, colon, eval;
uint amin, amax;
Rune *r, c;
Window *w;
Runestr rs;
amax = q1;
if(q1 == q0){
colon = -1;
while(q1<t->file->b.nc && isfilec(c=textreadc(t, q1))){
if(c == ':'){
colon = q1;
break;
}
q1++;
}
while(q0>0 && (isfilec(c=textreadc(t, q0-1)) || isaddrc(c) || isregexc(c))){
q0--;
if(colon<0 && c==':')
colon = q0;
}
/*
* if it looks like it might begin file: , consume address chars after :
* otherwise terminate expansion at :
*/
if(colon >= 0){
q1 = colon;
if(colon<t->file->b.nc-1 && isaddrc(textreadc(t, colon+1))){
q1 = colon+1;
while(q1<t->file->b.nc-1 && isaddrc(textreadc(t, q1)))
q1++;
}
}
if(q1 > q0)
if(colon >= 0){ /* stop at white space */
for(amax=colon+1; amax<t->file->b.nc; amax++)
if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n')
break;
}else
amax = t->file->b.nc;
}
amin = amax;
e->q0 = q0;
e->q1 = q1;
n = q1-q0;
if(n == 0)
return FALSE;
/* see if it's a file name */
r = runemalloc(n);
bufread(&t->file->b, q0, r, n);
/* first, does it have bad chars? */
nname = -1;
for(i=0; i<n; i++){
c = r[i];
if(c==':' && nname<0){
if(q0+i+1<t->file->b.nc && (i==n-1 || isaddrc(textreadc(t, q0+i+1))))
amin = q0+i;
else
goto Isntfile;
nname = i;
}
}
if(nname == -1)
nname = n;
for(i=0; i<nname; i++)
if(!isfilec(r[i]))
goto Isntfile;
/*
* See if it's a file name in <>, and turn that into an include
* file name if so. Should probably do it for "" too, but that's not
* restrictive enough syntax and checking for a #include earlier on the
* line would be silly.
*/
if(q0>0 && textreadc(t, q0-1)=='<' && q1<t->file->b.nc && textreadc(t, q1)=='>'){
rs = includename(t, r, nname);
r = rs.r;
nname = rs.nr;
}
else if(amin == q0)
goto Isfile;
else{
rs = dirname(t, r, nname);
r = rs.r;
nname = rs.nr;
}
e->bname = runetobyte(r, nname);
/* if it's already a window name, it's a file */
w = lookfile(r, nname);
if(w != nil)
goto Isfile;
/* if it's the name of a file, it's a file */
if(access(e->bname, 0) < 0){
free(e->bname);
e->bname = nil;
goto Isntfile;
}
Isfile:
e->name = r;
e->nname = nname;
e->u.at = t;
e->a0 = amin+1;
eval = FALSE;
address(nil, nil, (Range){-1,-1}, (Range){0, 0}, t, e->a0, amax, tgetc, &eval, (uint*)&e->a1);
return TRUE;
Isntfile:
free(r);
return FALSE;
}
int
expand(Text *t, uint q0, uint q1, Expand *e)
{
memset(e, 0, sizeof *e);
e->agetc = tgetc;
/* if in selection, choose selection */
e->jump = TRUE;
if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
q0 = t->q0;
q1 = t->q1;
if(t->what == Tag)
e->jump = FALSE;
}
if(expandfile(t, q0, q1, e))
return TRUE;
if(q0 == q1){
while(q1<t->file->b.nc && isalnum(textreadc(t, q1)))
q1++;
while(q0>0 && isalnum(textreadc(t, q0-1)))
q0--;
}
e->q0 = q0;
e->q1 = q1;
return q1 > q0;
}
Window*
lookfile(Rune *s, int n)
{
int i, j, k;
Window *w;
Column *c;
Text *t;
/* avoid terminal slash on directories */
if(n>1 && s[n-1] == '/')
--n;
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(i=0; i<c->nw; i++){
w = c->w[i];
t = &w->body;
k = t->file->nname;
if(k>1 && t->file->name[k-1] == '/')
k--;
if(runeeq(t->file->name, k, s, n)){
w = w->body.file->curtext->w;
if(w->col != nil) /* protect against race deleting w */
return w;
}
}
}
return nil;
}
Window*
lookid(int id, int dump)
{
int i, j;
Window *w;
Column *c;
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(i=0; i<c->nw; i++){
w = c->w[i];
if(dump && w->dumpid == id)
return w;
if(!dump && w->id == id)
return w;
}
}
return nil;
}
Window*
openfile(Text *t, Expand *e)
{
Range r;
Window *w, *ow;
int eval, i, n;
Rune *rp;
uint dummy;
if(e->nname == 0){
w = t->w;
if(w == nil)
return nil;
}else
w = lookfile(e->name, e->nname);
if(w){
t = &w->body;
if(!t->col->safe && t->fr.maxlines==0) /* window is obscured by full-column window */
colgrow(t->col, t->col->w[0], 1);
}else{
ow = nil;
if(t)
ow = t->w;
w = makenewwindow(t);
t = &w->body;
winsetname(w, e->name, e->nname);
textload(t, 0, e->bname, 1);
t->file->mod = FALSE;
t->w->dirty = FALSE;
winsettag(t->w);
textsetselect(&t->w->tag, t->w->tag.file->b.nc, t->w->tag.file->b.nc);
if(ow != nil)
for(i=ow->nincl; --i>=0; ){
n = runestrlen(ow->incl[i]);
rp = runemalloc(n);
runemove(rp, ow->incl[i], n);
winaddincl(w, rp, n);
}
}
if(e->a1 == e->a0)
eval = FALSE;
else{
eval = TRUE;
r = address(nil, t, (Range){-1, -1}, (Range){t->q0, t->q1}, e->u.at, e->a0, e->a1, e->agetc, &eval, &dummy);
if(eval == FALSE)
e->jump = FALSE; /* don't jump if invalid address */
}
if(eval == FALSE){
r.q0 = t->q0;
r.q1 = t->q1;
}
textshow(t, r.q0, r.q1, 1);
winsettag(t->w);
seltext = t;
if(e->jump)
moveto(mousectl, addpt(frptofchar(&t->fr, t->fr.p0), Pt(4, font->height-4)));
return w;
}
void
new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg)
{
int ndone;
Rune *a, *f;
int na, nf;
Expand e;
Runestr rs;
getarg(argt, FALSE, TRUE, &a, &na);
if(a){
new(et, t, nil, flag1, flag2, a, na);
if(narg == 0)
return;
}
/* loop condition: *arg is not a blank */
for(ndone=0; ; ndone++){
a = findbl(arg, narg, &na);
if(a == arg){
if(ndone==0 && et->col!=nil)
winsettag(coladd(et->col, nil, nil, -1));
break;
}
nf = narg-na;
f = runemalloc(nf);
runemove(f, arg, nf);
rs = dirname(et, f, nf);
f = rs.r;
nf = rs.nr;
memset(&e, 0, sizeof e);
e.name = f;
e.nname = nf;
e.bname = runetobyte(f, nf);
e.jump = TRUE;
openfile(et, &e);
free(f);
free(e.bname);
arg = skipbl(a, na, &narg);
}
}

41
src/cmd/acme/mkfile Normal file
View File

@ -0,0 +1,41 @@
PLAN9=../../..
<$PLAN9/src/mkhdr
TARG=acme
OFILES=\
acme.$O\
addr.$O\
buff.$O\
cols.$O\
disk.$O\
ecmd.$O\
edit.$O\
elog.$O\
exec.$O\
file.$O\
fsys.$O\
look.$O\
regx.$O\
rows.$O\
scrl.$O\
text.$O\
time.$O\
util.$O\
wind.$O\
xfid.$O\
HFILES=dat.h\
edit.h\
fns.h\
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
<$PLAN9/src/mkone
LDFLAGS=$LDFLAGS -lfs -lmux -lplumb -lthread -lframe -ldraw -lbio -l9 -lfmt -lutf -L$X11/lib -lX11
edit.$O ecmd.$O elog.$O: edit.h

835
src/cmd/acme/regx.c Normal file
View File

@ -0,0 +1,835 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
Rangeset sel;
Rune *lastregexp;
/*
* Machine Information
*/
typedef struct Inst Inst;
struct Inst
{
uint type; /* < 0x10000 ==> literal, otherwise action */
union {
int sid;
int subid;
int class;
Inst *other;
Inst *right;
} u;
union{
Inst *left;
Inst *next;
} u1;
};
#define NPROG 1024
Inst program[NPROG];
Inst *progp;
Inst *startinst; /* First inst. of program; might not be program[0] */
Inst *bstartinst; /* same for backwards machine */
Channel *rechan; /* chan(Inst*) */
typedef struct Ilist Ilist;
struct Ilist
{
Inst *inst; /* Instruction of the thread */
Rangeset se;
uint startp; /* first char of match */
};
#define NLIST 128
Ilist *tl, *nl; /* This list, next list */
Ilist list[2][NLIST];
static Rangeset sempty;
/*
* Actions and Tokens
*
* 0x100xx are operators, value == precedence
* 0x200xx are tokens, i.e. operands for operators
*/
#define OPERATOR 0x10000 /* Bitmask of all operators */
#define START 0x10000 /* Start, used for marker on stack */
#define RBRA 0x10001 /* Right bracket, ) */
#define LBRA 0x10002 /* Left bracket, ( */
#define OR 0x10003 /* Alternation, | */
#define CAT 0x10004 /* Concatentation, implicit operator */
#define STAR 0x10005 /* Closure, * */
#define PLUS 0x10006 /* a+ == aa* */
#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 or 1 a's */
#define ANY 0x20000 /* Any character but newline, . */
#define NOP 0x20001 /* No operation, internal use only */
#define BOL 0x20002 /* Beginning of line, ^ */
#define EOL 0x20003 /* End of line, $ */
#define CCLASS 0x20004 /* Character class, [] */
#define NCCLASS 0x20005 /* Negated character class, [^] */
#define END 0x20077 /* Terminate: match found */
#define ISATOR 0x10000
#define ISAND 0x20000
/*
* Parser Information
*/
typedef struct Node Node;
struct Node
{
Inst *first;
Inst *last;
};
#define NSTACK 20
Node andstack[NSTACK];
Node *andp;
int atorstack[NSTACK];
int *atorp;
int lastwasand; /* Last token was operand */
int cursubid;
int subidstack[NSTACK];
int *subidp;
int backwards;
int nbra;
Rune *exprp; /* pointer to next character in source expression */
#define DCLASS 10 /* allocation increment */
int nclass; /* number active */
int Nclass; /* high water mark */
Rune **class;
int negateclass;
void addinst(Ilist *l, Inst *inst, Rangeset *sep);
void newmatch(Rangeset*);
void bnewmatch(Rangeset*);
void pushand(Inst*, Inst*);
void pushator(int);
Node *popand(int);
int popator(void);
void startlex(Rune*);
int lex(void);
void operator(int);
void operand(int);
void evaluntil(int);
void optimize(Inst*);
void bldcclass(void);
void
rxinit(void)
{
rechan = chancreate(sizeof(Inst*), 0);
lastregexp = runemalloc(1);
}
void
regerror(char *e)
{
lastregexp[0] = 0;
warning(nil, "regexp: %s\n", e);
sendp(rechan, nil);
threadexits(nil);
}
Inst *
newinst(int t)
{
if(progp >= &program[NPROG])
regerror("expression too long");
progp->type = t;
progp->u1.left = nil;
progp->u.right = nil;
return progp++;
}
void
realcompile(void *arg)
{
int token;
Rune *s;
threadsetname("regcomp");
s = arg;
startlex(s);
atorp = atorstack;
andp = andstack;
subidp = subidstack;
cursubid = 0;
lastwasand = FALSE;
/* Start with a low priority operator to prime parser */
pushator(START-1);
while((token=lex()) != END){
if((token&ISATOR) == OPERATOR)
operator(token);
else
operand(token);
}
/* Close with a low priority operator */
evaluntil(START);
/* Force END */
operand(END);
evaluntil(START);
if(nbra)
regerror("unmatched `('");
--andp; /* points to first and only operand */
sendp(rechan, andp->first);
threadexits(nil);
}
/* r is null terminated */
int
rxcompile(Rune *r)
{
int i, nr;
Inst *oprogp;
nr = runestrlen(r)+1;
if(runeeq(lastregexp, runestrlen(lastregexp)+1, r, nr)==TRUE)
return TRUE;
lastregexp[0] = 0;
for(i=0; i<nclass; i++)
free(class[i]);
nclass = 0;
progp = program;
backwards = FALSE;
bstartinst = nil;
threadcreate(realcompile, r, STACK);
startinst = recvp(rechan);
if(startinst == nil)
return FALSE;
optimize(program);
oprogp = progp;
backwards = TRUE;
threadcreate(realcompile, r, STACK);
bstartinst = recvp(rechan);
if(bstartinst == nil)
return FALSE;
optimize(oprogp);
lastregexp = runerealloc(lastregexp, nr);
runemove(lastregexp, r, nr);
return TRUE;
}
void
operand(int t)
{
Inst *i;
if(lastwasand)
operator(CAT); /* catenate is implicit */
i = newinst(t);
if(t == CCLASS){
if(negateclass)
i->type = NCCLASS; /* UGH */
i->u.class = nclass-1; /* UGH */
}
pushand(i, i);
lastwasand = TRUE;
}
void
operator(int t)
{
if(t==RBRA && --nbra<0)
regerror("unmatched `)'");
if(t==LBRA){
cursubid++; /* silently ignored */
nbra++;
if(lastwasand)
operator(CAT);
}else
evaluntil(t);
if(t!=RBRA)
pushator(t);
lastwasand = FALSE;
if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
lastwasand = TRUE; /* these look like operands */
}
void
pushand(Inst *f, Inst *l)
{
if(andp >= &andstack[NSTACK])
error("operand stack overflow");
andp->first = f;
andp->last = l;
andp++;
}
void
pushator(int t)
{
if(atorp >= &atorstack[NSTACK])
error("operator stack overflow");
*atorp++=t;
if(cursubid >= NRange)
*subidp++= -1;
else
*subidp++=cursubid;
}
Node *
popand(int op)
{
char buf[64];
if(andp <= &andstack[0])
if(op){
sprint(buf, "missing operand for %c", op);
regerror(buf);
}else
regerror("malformed regexp");
return --andp;
}
int
popator()
{
if(atorp <= &atorstack[0])
error("operator stack underflow");
--subidp;
return *--atorp;
}
void
evaluntil(int pri)
{
Node *op1, *op2, *t;
Inst *inst1, *inst2;
while(pri==RBRA || atorp[-1]>=pri){
switch(popator()){
case LBRA:
op1 = popand('(');
inst2 = newinst(RBRA);
inst2->u.subid = *subidp;
op1->last->u1.next = inst2;
inst1 = newinst(LBRA);
inst1->u.subid = *subidp;
inst1->u1.next = op1->first;
pushand(inst1, inst2);
return; /* must have been RBRA */
default:
error("unknown regexp operator");
break;
case OR:
op2 = popand('|');
op1 = popand('|');
inst2 = newinst(NOP);
op2->last->u1.next = inst2;
op1->last->u1.next = inst2;
inst1 = newinst(OR);
inst1->u.right = op1->first;
inst1->u1.left = op2->first;
pushand(inst1, inst2);
break;
case CAT:
op2 = popand(0);
op1 = popand(0);
if(backwards && op2->first->type!=END){
t = op1;
op1 = op2;
op2 = t;
}
op1->last->u1.next = op2->first;
pushand(op1->first, op2->last);
break;
case STAR:
op2 = popand('*');
inst1 = newinst(OR);
op2->last->u1.next = inst1;
inst1->u.right = op2->first;
pushand(inst1, inst1);
break;
case PLUS:
op2 = popand('+');
inst1 = newinst(OR);
op2->last->u1.next = inst1;
inst1->u.right = op2->first;
pushand(op2->first, inst1);
break;
case QUEST:
op2 = popand('?');
inst1 = newinst(OR);
inst2 = newinst(NOP);
inst1->u1.left = inst2;
inst1->u.right = op2->first;
op2->last->u1.next = inst2;
pushand(inst1, inst2);
break;
}
}
}
void
optimize(Inst *start)
{
Inst *inst, *target;
for(inst=start; inst->type!=END; inst++){
target = inst->u1.next;
while(target->type == NOP)
target = target->u1.next;
inst->u1.next = target;
}
}
void
startlex(Rune *s)
{
exprp = s;
nbra = 0;
}
int
lex(void){
int c;
c = *exprp++;
switch(c){
case '\\':
if(*exprp)
if((c= *exprp++)=='n')
c='\n';
break;
case 0:
c = END;
--exprp; /* In case we come here again */
break;
case '*':
c = STAR;
break;
case '?':
c = QUEST;
break;
case '+':
c = PLUS;
break;
case '|':
c = OR;
break;
case '.':
c = ANY;
break;
case '(':
c = LBRA;
break;
case ')':
c = RBRA;
break;
case '^':
c = BOL;
break;
case '$':
c = EOL;
break;
case '[':
c = CCLASS;
bldcclass();
break;
}
return c;
}
int
nextrec(void)
{
if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0))
regerror("malformed `[]'");
if(exprp[0] == '\\'){
exprp++;
if(*exprp=='n'){
exprp++;
return '\n';
}
return *exprp++|0x10000;
}
return *exprp++;
}
void
bldcclass(void)
{
int c1, c2, n, na;
Rune *classp;
classp = runemalloc(DCLASS);
n = 0;
na = DCLASS;
/* we have already seen the '[' */
if(*exprp == '^'){
classp[n++] = '\n'; /* don't match newline in negate case */
negateclass = TRUE;
exprp++;
}else
negateclass = FALSE;
while((c1 = nextrec()) != ']'){
if(c1 == '-'){
Error:
free(classp);
regerror("malformed `[]'");
}
if(n+4 >= na){ /* 3 runes plus NUL */
na += DCLASS;
classp = runerealloc(classp, na);
}
if(*exprp == '-'){
exprp++; /* eat '-' */
if((c2 = nextrec()) == ']')
goto Error;
classp[n+0] = 0xFFFF;
classp[n+1] = c1;
classp[n+2] = c2;
n += 3;
}else
classp[n++] = c1;
}
classp[n] = 0;
if(nclass == Nclass){
Nclass += DCLASS;
class = realloc(class, Nclass*sizeof(Rune*));
}
class[nclass++] = classp;
}
int
classmatch(int classno, int c, int negate)
{
Rune *p;
p = class[classno];
while(*p){
if(*p == 0xFFFF){
if(p[1]<=c && c<=p[2])
return !negate;
p += 3;
}else if(*p++ == c)
return !negate;
}
return negate;
}
/*
* Note optimization in addinst:
* *l must be pending when addinst called; if *l has been looked
* at already, the optimization is a bug.
*/
void
addinst(Ilist *l, Inst *inst, Rangeset *sep)
{
Ilist *p;
for(p = l; p->inst; p++){
if(p->inst==inst){
if((sep)->r[0].q0 < p->se.r[0].q0)
p->se= *sep; /* this would be bug */
return; /* It's already there */
}
}
p->inst = inst;
p->se= *sep;
(p+1)->inst = nil;
}
int
rxnull(void)
{
return startinst==nil || bstartinst==nil;
}
/* either t!=nil or r!=nil, and we match the string in the appropriate place */
int
rxexecute(Text *t, Rune *r, uint startp, uint eof, Rangeset *rp)
{
int flag;
Inst *inst;
Ilist *tlp;
uint p;
int nnl, ntl;
int nc, c;
int wrapped;
int startchar;
flag = 0;
p = startp;
startchar = 0;
wrapped = 0;
nnl = 0;
if(startinst->type<OPERATOR)
startchar = startinst->type;
list[0][0].inst = list[1][0].inst = nil;
sel.r[0].q0 = -1;
if(t != nil)
nc = t->file->b.nc;
else
nc = runestrlen(r);
/* Execute machine once for each character */
for(;;p++){
doloop:
if(p>=eof || p>=nc){
switch(wrapped++){
case 0: /* let loop run one more click */
case 2:
break;
case 1: /* expired; wrap to beginning */
if(sel.r[0].q0>=0 || eof!=Infinity)
goto Return;
list[0][0].inst = list[1][0].inst = nil;
p = 0;
goto doloop;
default:
goto Return;
}
c = 0;
}else{
if(((wrapped && p>=startp) || sel.r[0].q0>0) && nnl==0)
break;
if(t != nil)
c = textreadc(t, p);
else
c = r[p];
}
/* fast check for first char */
if(startchar && nnl==0 && c!=startchar)
continue;
tl = list[flag];
nl = list[flag^=1];
nl->inst = nil;
ntl = nnl;
nnl = 0;
if(sel.r[0].q0<0 && (!wrapped || p<startp || startp==eof)){
/* Add first instruction to this list */
if(++ntl >= NLIST){
Overflow:
warning(nil, "regexp list overflow\n");
sel.r[0].q0 = -1;
goto Return;
}
sempty.r[0].q0 = p;
addinst(tl, startinst, &sempty);
}
/* Execute machine until this list is empty */
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
Switchstmt:
switch(inst->type){
default: /* regular character */
if(inst->type==c){
Addinst:
if(++nnl >= NLIST)
goto Overflow;
addinst(nl, inst->u1.next, &tlp->se);
}
break;
case LBRA:
if(inst->u.subid>=0)
tlp->se.r[inst->u.subid].q0 = p;
inst = inst->u1.next;
goto Switchstmt;
case RBRA:
if(inst->u.subid>=0)
tlp->se.r[inst->u.subid].q1 = p;
inst = inst->u1.next;
goto Switchstmt;
case ANY:
if(c!='\n')
goto Addinst;
break;
case BOL:
if(p==0 || (t!=nil && textreadc(t, p-1)=='\n') || (r!=nil && r[p-1]=='\n')){
Step:
inst = inst->u1.next;
goto Switchstmt;
}
break;
case EOL:
if(c == '\n')
goto Step;
break;
case CCLASS:
if(c>=0 && classmatch(inst->u.class, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>=0 && classmatch(inst->u.class, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(++ntl >= NLIST)
goto Overflow;
addinst(tlp, inst->u.right, &tlp->se);
/* efficiency: advance and re-evaluate */
inst = inst->u1.left;
goto Switchstmt;
case END: /* Match! */
tlp->se.r[0].q1 = p;
newmatch(&tlp->se);
break;
}
}
}
Return:
*rp = sel;
return sel.r[0].q0 >= 0;
}
void
newmatch(Rangeset *sp)
{
if(sel.r[0].q0<0 || sp->r[0].q0<sel.r[0].q0 ||
(sp->r[0].q0==sel.r[0].q0 && sp->r[0].q1>sel.r[0].q1))
sel = *sp;
}
int
rxbexecute(Text *t, uint startp, Rangeset *rp)
{
int flag;
Inst *inst;
Ilist *tlp;
int p;
int nnl, ntl;
int c;
int wrapped;
int startchar;
flag = 0;
nnl = 0;
wrapped = 0;
p = startp;
startchar = 0;
if(bstartinst->type<OPERATOR)
startchar = bstartinst->type;
list[0][0].inst = list[1][0].inst = nil;
sel.r[0].q0= -1;
/* Execute machine once for each character, including terminal NUL */
for(;;--p){
doloop:
if(p <= 0){
switch(wrapped++){
case 0: /* let loop run one more click */
case 2:
break;
case 1: /* expired; wrap to end */
if(sel.r[0].q0>=0)
goto Return;
list[0][0].inst = list[1][0].inst = nil;
p = t->file->b.nc;
goto doloop;
case 3:
default:
goto Return;
}
c = 0;
}else{
if(((wrapped && p<=startp) || sel.r[0].q0>0) && nnl==0)
break;
c = textreadc(t, p-1);
}
/* fast check for first char */
if(startchar && nnl==0 && c!=startchar)
continue;
tl = list[flag];
nl = list[flag^=1];
nl->inst = nil;
ntl = nnl;
nnl = 0;
if(sel.r[0].q0<0 && (!wrapped || p>startp)){
/* Add first instruction to this list */
if(++ntl >= NLIST){
Overflow:
warning(nil, "regexp list overflow\n");
sel.r[0].q0 = -1;
goto Return;
}
/* the minus is so the optimizations in addinst work */
sempty.r[0].q0 = -p;
addinst(tl, bstartinst, &sempty);
}
/* Execute machine until this list is empty */
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
Switchstmt:
switch(inst->type){
default: /* regular character */
if(inst->type == c){
Addinst:
if(++nnl >= NLIST)
goto Overflow;
addinst(nl, inst->u1.next, &tlp->se);
}
break;
case LBRA:
if(inst->u.subid>=0)
tlp->se.r[inst->u.subid].q0 = p;
inst = inst->u1.next;
goto Switchstmt;
case RBRA:
if(inst->u.subid >= 0)
tlp->se.r[inst->u.subid].q1 = p;
inst = inst->u1.next;
goto Switchstmt;
case ANY:
if(c != '\n')
goto Addinst;
break;
case BOL:
if(c=='\n' || p==0){
Step:
inst = inst->u1.next;
goto Switchstmt;
}
break;
case EOL:
if(p<t->file->b.nc && textreadc(t, p)=='\n')
goto Step;
break;
case CCLASS:
if(c>0 && classmatch(inst->u.class, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>0 && classmatch(inst->u.class, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(++ntl >= NLIST)
goto Overflow;
addinst(tlp, inst->u.right, &tlp->se);
/* efficiency: advance and re-evaluate */
inst = inst->u1.left;
goto Switchstmt;
case END: /* Match! */
tlp->se.r[0].q0 = -tlp->se.r[0].q0; /* minus sign */
tlp->se.r[0].q1 = p;
bnewmatch(&tlp->se);
break;
}
}
}
Return:
*rp = sel;
return sel.r[0].q0 >= 0;
}
void
bnewmatch(Rangeset *sp)
{
int i;
if(sel.r[0].q0<0 || sp->r[0].q0>sel.r[0].q1 || (sp->r[0].q0==sel.r[0].q1 && sp->r[0].q1<sel.r[0].q0))
for(i = 0; i<NRange; i++){ /* note the reversal; q0<=q1 */
sel.r[i].q0 = sp->r[i].q1;
sel.r[i].q1 = sp->r[i].q0;
}
}

731
src/cmd/acme/rows.c Normal file
View File

@ -0,0 +1,731 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <bio.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Rune Lcolhdr[] = {
'N', 'e', 'w', 'c', 'o', 'l', ' ',
'K', 'i', 'l', 'l', ' ',
'P', 'u', 't', 'a', 'l', 'l', ' ',
'D', 'u', 'm', 'p', ' ',
'E', 'x', 'i', 't', ' ',
0
};
void
rowinit(Row *row, Rectangle r)
{
Rectangle r1;
Text *t;
draw(screen, r, display->white, nil, ZP);
row->r = r;
row->col = nil;
row->ncol = 0;
r1 = r;
r1.max.y = r1.min.y + font->height;
t = &row->tag;
textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols);
t->what = Rowtag;
t->row = row;
t->w = nil;
t->col = nil;
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
textinsert(t, 0, Lcolhdr, 29, TRUE);
textsetselect(t, t->file->b.nc, t->file->b.nc);
}
Column*
rowadd(Row *row, Column *c, int x)
{
Rectangle r, r1;
Column *d;
int i;
d = nil;
r = row->r;
r.min.y = row->tag.fr.r.max.y+Border;
if(x<r.min.x && row->ncol>0){ /*steal 40% of last column by default */
d = row->col[row->ncol-1];
x = d->r.min.x + 3*Dx(d->r)/5;
}
/* look for column we'll land on */
for(i=0; i<row->ncol; i++){
d = row->col[i];
if(x < d->r.max.x)
break;
}
if(row->ncol > 0){
if(i < row->ncol)
i++; /* new column will go after d */
r = d->r;
if(Dx(r) < 100)
return nil;
draw(screen, r, display->white, nil, ZP);
r1 = r;
r1.max.x = min(x, r.max.x-50);
if(Dx(r1) < 50)
r1.max.x = r1.min.x+50;
colresize(d, r1);
r1.min.x = r1.max.x;
r1.max.x = r1.min.x+Border;
draw(screen, r1, display->black, nil, ZP);
r.min.x = r1.max.x;
}
if(c == nil){
c = emalloc(sizeof(Column));
colinit(c, r);
incref(&reffont.ref);
}else
colresize(c, r);
c->row = row;
c->tag.row = row;
row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*));
memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*));
row->col[i] = c;
row->ncol++;
clearmouse();
return c;
}
void
rowresize(Row *row, Rectangle r)
{
int i, dx, odx;
Rectangle r1, r2;
Column *c;
dx = Dx(r);
odx = Dx(row->r);
row->r = r;
r1 = r;
r1.max.y = r1.min.y + font->height;
textresize(&row->tag, r1);
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
r.min.y = r1.max.y;
r1 = r;
r1.max.x = r1.min.x;
for(i=0; i<row->ncol; i++){
c = row->col[i];
r1.min.x = r1.max.x;
if(i == row->ncol-1)
r1.max.x = r.max.x;
else
r1.max.x = r1.min.x+Dx(c->r)*dx/odx;
if(i > 0){
r2 = r1;
r2.max.x = r2.min.x+Border;
draw(screen, r2, display->black, nil, ZP);
r1.min.x = r2.max.x;
}
colresize(c, r1);
}
}
void
rowdragcol(Row *row, Column *c, int _0)
{
Rectangle r;
int i, b, x;
Point p, op;
Column *d;
USED(_0);
clearmouse();
setcursor(mousectl, &boxcursor);
b = mouse->buttons;
op = mouse->xy;
while(mouse->buttons == b)
readmouse(mousectl);
setcursor(mousectl, nil);
if(mouse->buttons){
while(mouse->buttons)
readmouse(mousectl);
return;
}
for(i=0; i<row->ncol; i++)
if(row->col[i] == c)
goto Found;
error("can't find column");
Found:
if(i == 0)
return;
p = mouse->xy;
if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
return;
if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
/* shuffle */
x = c->r.min.x;
rowclose(row, c, FALSE);
if(rowadd(row, c, p.x) == nil) /* whoops! */
if(rowadd(row, c, x) == nil) /* WHOOPS! */
if(rowadd(row, c, -1)==nil){ /* shit! */
rowclose(row, c, TRUE);
return;
}
colmousebut(c);
return;
}
d = row->col[i-1];
if(p.x < d->r.min.x+80+Scrollwid)
p.x = d->r.min.x+80+Scrollwid;
if(p.x > c->r.max.x-80-Scrollwid)
p.x = c->r.max.x-80-Scrollwid;
r = d->r;
r.max.x = c->r.max.x;
draw(screen, r, display->white, nil, ZP);
r.max.x = p.x;
colresize(d, r);
r = c->r;
r.min.x = p.x;
r.max.x = r.min.x;
r.max.x += Border;
draw(screen, r, display->black, nil, ZP);
r.min.x = r.max.x;
r.max.x = c->r.max.x;
colresize(c, r);
colmousebut(c);
}
void
rowclose(Row *row, Column *c, int dofree)
{
Rectangle r;
int i;
for(i=0; i<row->ncol; i++)
if(row->col[i] == c)
goto Found;
error("can't find column");
Found:
r = c->r;
if(dofree)
colcloseall(c);
memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
row->ncol--;
row->col = realloc(row->col, row->ncol*sizeof(Column*));
if(row->ncol == 0){
draw(screen, r, display->white, nil, ZP);
return;
}
if(i == row->ncol){ /* extend last column right */
c = row->col[i-1];
r.min.x = c->r.min.x;
r.max.x = row->r.max.x;
}else{ /* extend next window left */
c = row->col[i];
r.max.x = c->r.max.x;
}
draw(screen, r, display->white, nil, ZP);
colresize(c, r);
}
Column*
rowwhichcol(Row *row, Point p)
{
int i;
Column *c;
for(i=0; i<row->ncol; i++){
c = row->col[i];
if(ptinrect(p, c->r))
return c;
}
return nil;
}
Text*
rowwhich(Row *row, Point p)
{
Column *c;
if(ptinrect(p, row->tag.all))
return &row->tag;
c = rowwhichcol(row, p);
if(c)
return colwhich(c, p);
return nil;
}
Text*
rowtype(Row *row, Rune r, Point p)
{
Window *w;
Text *t;
clearmouse();
qlock(&row->lk);
if(bartflag)
t = barttext;
else
t = rowwhich(row, p);
if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){
w = t->w;
if(w == nil)
texttype(t, r);
else{
winlock(w, 'K');
wintype(w, t, r);
winunlock(w);
}
}
qunlock(&row->lk);
return t;
}
int
rowclean(Row *row)
{
int clean;
int i;
clean = TRUE;
for(i=0; i<row->ncol; i++)
clean &= colclean(row->col[i]);
return clean;
}
void
rowdump(Row *row, char *file)
{
int i, j, fd, m, n, dumped;
uint q0, q1;
Biobuf *b;
char *buf, *a, *fontname;
Rune *r;
Column *c;
Window *w, *w1;
Text *t;
if(row->ncol == 0)
return;
buf = fbufalloc();
if(file == nil){
if(home == nil){
warning(nil, "can't find file for dump: $home not defined\n");
goto Rescue;
}
sprint(buf, "%s/acme.dump", home);
file = buf;
}
fd = create(file, OWRITE, 0600);
if(fd < 0){
warning(nil, "can't open %s: %r\n", file);
goto Rescue;
}
b = emalloc(sizeof(Biobuf));
Binit(b, fd, OWRITE);
r = fbufalloc();
Bprint(b, "%s\n", wdir);
Bprint(b, "%s\n", fontnames[0]);
Bprint(b, "%s\n", fontnames[1]);
for(i=0; i<row->ncol; i++){
c = row->col[i];
Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r));
if(i == row->ncol-1)
Bputc(b, '\n');
else
Bputc(b, ' ');
}
for(i=0; i<row->ncol; i++){
c = row->col[i];
for(j=0; j<c->nw; j++)
c->w[j]->body.file->dumpid = 0;
}
for(i=0; i<row->ncol; i++){
c = row->col[i];
for(j=0; j<c->nw; j++){
w = c->w[j];
wincommit(w, &w->tag);
t = &w->body;
/* windows owned by others get special treatment */
if(w->nopen[QWevent] > 0)
if(w->dumpstr == nil)
continue;
/* zeroxes of external windows are tossed */
if(t->file->ntext > 1)
for(n=0; n<t->file->ntext; n++){
w1 = t->file->text[n]->w;
if(w == w1)
continue;
if(w1->nopen[QWevent])
goto Continue2;
}
fontname = "";
if(t->reffont->f != font)
fontname = t->reffont->f->name;
if(t->file->nname)
a = runetobyte(t->file->name, t->file->nname);
else
a = emalloc(1);
if(t->file->dumpid){
dumped = FALSE;
Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
w->body.q0, w->body.q1,
100*(w->r.min.y-c->r.min.y)/Dy(c->r),
fontname);
}else if(w->dumpstr){
dumped = FALSE;
Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
0, 0,
100*(w->r.min.y-c->r.min.y)/Dy(c->r),
fontname);
}else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){
dumped = FALSE;
t->file->dumpid = w->id;
Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id,
w->body.q0, w->body.q1,
100*(w->r.min.y-c->r.min.y)/Dy(c->r),
fontname);
}else{
dumped = TRUE;
t->file->dumpid = w->id;
Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j,
w->body.q0, w->body.q1,
100*(w->r.min.y-c->r.min.y)/Dy(c->r),
w->body.file->b.nc, fontname);
}
free(a);
winctlprint(w, buf, 0);
Bwrite(b, buf, strlen(buf));
m = min(RBUFSIZE, w->tag.file->b.nc);
bufread(&w->tag.file->b, 0, r, m);
n = 0;
while(n<m && r[n]!='\n')
n++;
r[n++] = '\n';
Bprint(b, "%.*S", n, r);
if(dumped){
q0 = 0;
q1 = t->file->b.nc;
while(q0 < q1){
n = q1 - q0;
if(n > BUFSIZE/UTFmax)
n = BUFSIZE/UTFmax;
bufread(&t->file->b, q0, r, n);
Bprint(b, "%.*S", n, r);
q0 += n;
}
}
if(w->dumpstr){
if(w->dumpdir)
Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
else
Bprint(b, "\n%s\n", w->dumpstr);
}
Continue2:;
}
}
Bterm(b);
close(fd);
free(b);
fbuffree(r);
Rescue:
fbuffree(buf);
}
static
char*
rdline(Biobuf *b, int *linep)
{
char *l;
l = Brdline(b, '\n');
if(l)
(*linep)++;
return l;
}
/*
* Get font names from load file so we don't load fonts we won't use
*/
void
rowloadfonts(char *file)
{
int i;
Biobuf *b;
char *l;
b = Bopen(file, OREAD);
if(b == nil)
return;
/* current directory */
l = Brdline(b, '\n');
if(l == nil)
goto Return;
/* global fonts */
for(i=0; i<2; i++){
l = Brdline(b, '\n');
if(l == nil)
goto Return;
l[Blinelen(b)-1] = 0;
if(*l && strcmp(l, fontnames[i])!=0)
fontnames[i] = estrdup(l);
}
Return:
Bterm(b);
}
void
rowload(Row *row, char *file, int initing)
{
int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd;
Biobuf *b, *bout;
char *buf, *l, *t, *fontname;
Rune *r, rune, *fontr;
Column *c, *c1, *c2;
uint q0, q1;
Rectangle r1, r2;
Window *w;
buf = fbufalloc();
if(file == nil){
if(home == nil){
warning(nil, "can't find file for load: $home not defined\n");
goto Rescue1;
}
sprint(buf, "%s/acme.dump", home);
file = buf;
}
b = Bopen(file, OREAD);
if(b == nil){
warning(nil, "can't open load file %s: %r\n", file);
goto Rescue1;
}
/* current directory */
line = 0;
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(chdir(l) < 0){
warning(nil, "can't chdir %s\n", l);
goto Rescue2;
}
/* global fonts */
for(i=0; i<2; i++){
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(*l && strcmp(l, fontnames[i])!=0)
rfget(i, TRUE, i==0 && initing, estrdup(l));
}
if(initing && row->ncol==0)
rowinit(row, screen->clipr);
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
j = Blinelen(b)/12;
if(j<=0 || j>10)
goto Rescue2;
for(i=0; i<j; i++){
percent = atoi(l+i*12);
if(percent<0 || percent>=100)
goto Rescue2;
x = row->r.min.x+percent*Dx(row->r)/100;
if(i < row->ncol){
if(i == 0)
continue;
c1 = row->col[i-1];
c2 = row->col[i];
r1 = c1->r;
r2 = c2->r;
r1.max.x = x;
r2.min.x = x+Border;
if(Dx(r1) < 50 || Dx(r2) < 50)
continue;
draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
colresize(c1, r1);
colresize(c2, r2);
r2.min.x = x;
r2.max.x = x+Border;
draw(screen, r2, display->black, nil, ZP);
}
if(i >= row->ncol)
rowadd(row, nil, x);
}
for(;;){
l = rdline(b, &line);
if(l == nil)
break;
dumpid = 0;
switch(l[0]){
case 'e':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
l = rdline(b, &line); /* ctl line; ignored */
if(l == nil)
goto Rescue2;
l = rdline(b, &line); /* directory */
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(*l == '\0'){
if(home == nil)
r = bytetorune("./", &nr);
else{
t = emalloc(strlen(home)+1+1);
sprint(t, "%s/", home);
r = bytetorune(t, &nr);
free(t);
}
}else
r = bytetorune(l, &nr);
l = rdline(b, &line); /* command */
if(l == nil)
goto Rescue2;
t = emalloc(Blinelen(b)+1);
memmove(t, l, Blinelen(b));
run(nil, t, r, nr, TRUE, nil, nil, FALSE);
/* r is freed in run() */
continue;
case 'f':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
fontname = l+1+5*12;
ndumped = -1;
break;
case 'F':
if(Blinelen(b) < 1+6*12+1)
goto Rescue2;
fontname = l+1+6*12;
ndumped = atoi(l+1+5*12+1);
break;
case 'x':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
fontname = l+1+5*12;
ndumped = -1;
dumpid = atoi(l+1+1*12);
break;
default:
goto Rescue2;
}
l[Blinelen(b)-1] = 0;
fontr = nil;
nfontr = 0;
if(*fontname)
fontr = bytetorune(fontname, &nfontr);
i = atoi(l+1+0*12);
j = atoi(l+1+1*12);
q0 = atoi(l+1+2*12);
q1 = atoi(l+1+3*12);
percent = atoi(l+1+4*12);
if(i<0 || i>10)
goto Rescue2;
if(i > row->ncol)
i = row->ncol;
c = row->col[i];
y = c->r.min.y+(percent*Dy(c->r))/100;
if(y<c->r.min.y || y>=c->r.max.y)
y = -1;
if(dumpid == 0)
w = coladd(c, nil, nil, y);
else
w = coladd(c, nil, lookid(dumpid, TRUE), y);
if(w == nil)
continue;
w->dumpid = j;
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
r = bytetorune(l+5*12, &nr);
ns = -1;
for(n=0; n<nr; n++){
if(r[n] == '/')
ns = n;
if(r[n] == ' ')
break;
}
if(dumpid == 0)
winsetname(w, r, n);
for(; n<nr; n++)
if(r[n] == '|')
break;
wincleartag(w);
textinsert(&w->tag, w->tag.file->b.nc, r+n+1, nr-(n+1), TRUE);
free(r);
if(ndumped >= 0){
/* simplest thing is to put it in a file and load that */
sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser());
fd = create(buf, OWRITE|ORCLOSE, 0600);
if(fd < 0){
warning(nil, "can't create temp file: %r\n");
goto Rescue2;
}
bout = emalloc(sizeof(Biobuf));
Binit(bout, fd, OWRITE);
for(n=0; n<ndumped; n++){
rune = Bgetrune(b);
if(rune == '\n')
line++;
if(rune == (Rune)Beof){
Bterm(bout);
free(bout);
close(fd);
goto Rescue2;
}
Bputrune(bout, rune);
}
Bterm(bout);
free(bout);
textload(&w->body, 0, buf, 1);
close(fd);
w->body.file->mod = TRUE;
for(n=0; n<w->body.file->ntext; n++)
w->body.file->text[n]->w->dirty = TRUE;
winsettag(w);
}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
get(&w->body, nil, nil, FALSE, XXX, nil, 0);
if(fontr){
fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
free(fontr);
}
if(q0>w->body.file->b.nc || q1>w->body.file->b.nc || q0>q1)
q0 = q1 = 0;
textshow(&w->body, q0, q1, 1);
w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
}
Bterm(b);
Rescue1:
fbuffree(buf);
return;
Rescue2:
warning(nil, "bad load file %s:%d\n", file, line);
Bterm(b);
goto Rescue1;
}
void
allwindows(void (*f)(Window*, void*), void *arg)
{
int i, j;
Column *c;
for(i=0; i<row.ncol; i++){
c = row.col[i];
for(j=0; j<c->nw; j++)
(*f)(c->w[j], arg);
}
}

165
src/cmd/acme/scrl.c Normal file
View File

@ -0,0 +1,165 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Image *scrtmp;
static
Rectangle
scrpos(Rectangle r, uint p0, uint p1, uint tot)
{
Rectangle q;
int h;
q = r;
h = q.max.y-q.min.y;
if(tot == 0)
return q;
if(tot > 1024*1024){
tot>>=10;
p0>>=10;
p1>>=10;
}
if(p0 > 0)
q.min.y += h*p0/tot;
if(p1 < tot)
q.max.y -= h*(tot-p1)/tot;
if(q.max.y < q.min.y+2){
if(q.min.y+2 <= r.max.y)
q.max.y = q.min.y+2;
else
q.min.y = q.max.y-2;
}
return q;
}
void
scrlresize(void)
{
freeimage(scrtmp);
scrtmp = allocimage(display, Rect(0, 0, 32, screen->r.max.y), screen->chan, 0, DNofill);
if(scrtmp == nil)
error("scroll alloc");
}
void
textscrdraw(Text *t)
{
Rectangle r, r1, r2;
Image *b;
if(t->w==nil || t!=&t->w->body)
return;
if(scrtmp == nil)
scrlresize();
r = t->scrollr;
b = scrtmp;
r1 = r;
r1.min.x = 0;
r1.max.x = Dx(r);
r2 = scrpos(r1, t->org, t->org+t->fr.nchars, t->file->b.nc);
if(!eqrect(r2, t->lastsr)){
t->lastsr = r2;
draw(b, r1, t->fr.cols[BORD], nil, ZP);
draw(b, r2, t->fr.cols[BACK], nil, ZP);
r2.min.x = r2.max.x-1;
draw(b, r2, t->fr.cols[BORD], nil, ZP);
draw(t->fr.b, r, b, nil, Pt(0, r1.min.y));
/*flushimage(display, 1);*//*BUG?*/
}
}
void
scrsleep(uint dt)
{
Timer *timer;
static Alt alts[3];
timer = timerstart(dt);
alts[0].c = timer->c;
alts[0].v = nil;
alts[0].op = CHANRCV;
alts[1].c = mousectl->c;
alts[1].v = &mousectl->m;
alts[1].op = CHANRCV;
alts[2].op = CHANEND;
for(;;)
switch(alt(alts)){
case 0:
timerstop(timer);
return;
case 1:
timercancel(timer);
return;
}
}
void
textscroll(Text *t, int but)
{
uint p0, oldp0;
Rectangle s;
int x, y, my, h, first;
s = insetrect(t->scrollr, 1);
h = s.max.y-s.min.y;
x = (s.min.x+s.max.x)/2;
oldp0 = ~0;
first = TRUE;
do{
flushimage(display, 1);
if(mouse->xy.x<s.min.x || s.max.x<=mouse->xy.x){
readmouse(mousectl);
}else{
my = mouse->xy.y;
if(my < s.min.y)
my = s.min.y;
if(my >= s.max.y)
my = s.max.y;
if(!eqpt(mouse->xy, Pt(x, my))){
moveto(mousectl, Pt(x, my));
readmouse(mousectl); /* absorb event generated by moveto() */
}
if(but == 2){
y = my;
if(y > s.max.y-2)
y = s.max.y-2;
if(t->file->b.nc > 1024*1024)
p0 = ((t->file->b.nc>>10)*(y-s.min.y)/h)<<10;
else
p0 = t->file->b.nc*(y-s.min.y)/h;
if(oldp0 != p0)
textsetorigin(t, p0, FALSE);
oldp0 = p0;
readmouse(mousectl);
continue;
}
if(but == 1)
p0 = textbacknl(t, t->org, (my-s.min.y)/t->fr.font->height);
else
p0 = t->org+frcharofpt(&t->fr, Pt(s.max.x, my));
if(oldp0 != p0)
textsetorigin(t, p0, TRUE);
oldp0 = p0;
/* debounce */
if(first){
flushimage(display, 1);
sleep(200);
nbrecv(mousectl->c, &mousectl->m);
first = FALSE;
}
scrsleep(80);
}
}while(mouse->buttons & (1<<(but-1)));
while(mouse->buttons)
readmouse(mousectl);
}

1221
src/cmd/acme/text.c Normal file

File diff suppressed because it is too large Load Diff

121
src/cmd/acme/time.c Normal file
View File

@ -0,0 +1,121 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Channel* ctimer; /* chan(Timer*)[100] */
static Timer *timer;
static
uint
msec(void)
{
return nsec()/1000000;
}
void
timerstop(Timer *t)
{
t->next = timer;
timer = t;
}
void
timercancel(Timer *t)
{
t->cancel = TRUE;
}
static
void
timerproc(void *v)
{
int i, nt, na, dt, del;
Timer **t, *x;
uint old, new;
USED(v);
threadsetname("timerproc");
rfork(RFFDG);
t = nil;
na = 0;
nt = 0;
old = msec();
for(;;){
sleep(1); /* will sleep minimum incr */
new = msec();
dt = new-old;
old = new;
if(dt < 0) /* timer wrapped; go around, losing a tick */
continue;
for(i=0; i<nt; i++){
x = t[i];
x->dt -= dt;
del = FALSE;
if(x->cancel){
timerstop(x);
del = TRUE;
}else if(x->dt <= 0){
/*
* avoid possible deadlock if client is
* now sending on ctimer
*/
if(nbsendul(x->c, 0) > 0)
del = TRUE;
}
if(del){
memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]);
--nt;
--i;
}
}
if(nt == 0){
x = recvp(ctimer);
gotit:
if(nt == na){
na += 10;
t = realloc(t, na*sizeof(Timer*));
if(t == nil)
error("timer realloc failed");
}
t[nt++] = x;
old = msec();
}
if(nbrecv(ctimer, &x) > 0)
goto gotit;
}
}
void
timerinit(void)
{
ctimer = chancreate(sizeof(Timer*), 100);
proccreate(timerproc, nil, STACK);
}
Timer*
timerstart(int dt)
{
Timer *t;
t = timer;
if(t)
timer = timer->next;
else{
t = emalloc(sizeof(Timer));
t->c = chancreate(sizeof(int), 0);
}
t->next = nil;
t->dt = dt;
t->cancel = FALSE;
sendp(ctimer, t);
return t;
}

395
src/cmd/acme/util.c Normal file
View File

@ -0,0 +1,395 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
static Point prevmouse;
static Window *mousew;
void
cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
{
uchar *q;
Rune *s;
int j, w;
/*
* Always guaranteed that n bytes may be interpreted
* without worrying about partial runes. This may mean
* reading up to UTFmax-1 more bytes than n; the caller
* knows this. If n is a firm limit, the caller should
* set p[n] = 0.
*/
q = (uchar*)p;
s = r;
for(j=0; j<n; j+=w){
if(*q < Runeself){
w = 1;
*s = *q++;
}else{
w = chartorune(s, (char*)q);
q += w;
}
if(*s)
s++;
else if(nulls)
*nulls = TRUE;
}
*nb = (char*)q-p;
*nr = s-r;
}
void
error(char *s)
{
fprint(2, "acme: %s: %r\n", s);
abort();
}
Window*
errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
{
Window *w;
Rune *r;
int i, n;
static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
r = runemalloc(ndir+7);
if(n = ndir) /* assign = */
runemove(r, dir, ndir);
runemove(r+n, Lpluserrors, 7);
n += 7;
w = lookfile(r, n);
if(w == nil){
if(row.ncol == 0)
if(rowadd(&row, nil, -1) == nil)
error("can't create column to make error window");
w = coladd(row.col[row.ncol-1], nil, nil, -1);
w->filemenu = FALSE;
winsetname(w, r, n);
}
free(r);
for(i=nincl; --i>=0; ){
n = runestrlen(incl[i]);
r = runemalloc(n);
runemove(r, incl[i], n);
winaddincl(w, r, n);
}
return w;
}
/* make new window, if necessary; return with it locked */
Window*
errorwin(Mntdir *md, int owner, Window *e)
{
Window *w;
for(;;){
if(md == nil)
w = errorwin1(nil, 0, nil, 0);
else
w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
if(w != e)
winlock(w, owner);
if(w->col != nil)
break;
/* window was deleted too fast */
if(w != e)
winunlock(w);
}
return w;
}
static void
printwarning(Window *ew, Mntdir *md, Rune *r)
{
int nr, q0, owner;
Window *w;
Text *t;
if(r == nil)
error("runevsmprint failed");
nr = runestrlen(r);
if(row.ncol == 0){ /* really early error */
rowinit(&row, screen->clipr);
rowadd(&row, nil, -1);
rowadd(&row, nil, -1);
if(row.ncol == 0)
error("initializing columns in warning()");
}
w = errorwin(md, 'E', ew);
t = &w->body;
owner = w->owner;
if(owner == 0)
w->owner = 'E';
wincommit(w, t);
q0 = textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
textshow(t, q0, q0+nr, 1);
winsettag(t->w);
textscrdraw(t);
w->owner = owner;
w->dirty = FALSE;
if(ew != w)
winunlock(w);
free(r);
}
void
warning(Mntdir *md, char *s, ...)
{
Rune *r;
va_list arg;
va_start(arg, s);
r = runevsmprint(s, arg);
va_end(arg);
printwarning(nil, md, r);
}
/*
* Warningew is like warning but avoids locking the error window
* if it's already locked by checking that ew!=error window.
*/
void
warningew(Window *ew, Mntdir *md, char *s, ...)
{
Rune *r;
va_list arg;
va_start(arg, s);
r = runevsmprint(s, arg);
va_end(arg);
printwarning(ew, md, r);
}
int
runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
{
if(n1 != n2)
return FALSE;
return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
}
uint
min(uint a, uint b)
{
if(a < b)
return a;
return b;
}
uint
max(uint a, uint b)
{
if(a > b)
return a;
return b;
}
char*
runetobyte(Rune *r, int n)
{
char *s;
if(r == nil)
return nil;
s = emalloc(n*UTFmax+1);
setmalloctag(s, getcallerpc(&r));
snprint(s, n*UTFmax+1, "%.*S", n, r);
return s;
}
Rune*
bytetorune(char *s, int *ip)
{
Rune *r;
int nb, nr;
nb = strlen(s);
r = runemalloc(nb+1);
cvttorunes(s, nb, r, &nb, &nr, nil);
r[nr] = '\0';
*ip = nr;
return r;
}
int
isalnum(Rune c)
{
/*
* Hard to get absolutely right. Use what we know about ASCII
* and assume anything above the Latin control characters is
* potentially an alphanumeric.
*/
if(c <= ' ')
return FALSE;
if(0x7F<=c && c<=0xA0)
return FALSE;
if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
return FALSE;
return TRUE;
}
int
rgetc(void *v, uint n)
{
return ((Rune*)v)[n];
}
int
tgetc(void *a, uint n)
{
Text *t;
t = a;
if(n >= t->file->b.nc)
return 0;
return textreadc(t, n);
}
Rune*
skipbl(Rune *r, int n, int *np)
{
while(n>0 && *r==' ' || *r=='\t' || *r=='\n'){
--n;
r++;
}
*np = n;
return r;
}
Rune*
findbl(Rune *r, int n, int *np)
{
while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
--n;
r++;
}
*np = n;
return r;
}
void
savemouse(Window *w)
{
prevmouse = mouse->xy;
mousew = w;
}
void
restoremouse(Window *w)
{
if(mousew!=nil && mousew==w)
moveto(mousectl, prevmouse);
mousew = nil;
}
void
clearmouse()
{
mousew = nil;
}
char*
estrdup(char *s)
{
char *t;
t = strdup(s);
if(t == nil)
error("strdup failed");
setmalloctag(t, getcallerpc(&s));
return t;
}
void*
emalloc(uint n)
{
void *p;
p = malloc(n);
if(p == nil){
fprint(2, "allocating %d from %lux: %r\n", n, getcallerpc(&n));
*(int*)0=0;
error("malloc failed");
}
setmalloctag(p, getcallerpc(&n));
memset(p, 0, n);
return p;
}
void*
erealloc(void *p, uint n)
{
p = realloc(p, n);
if(p == nil){
fprint(2, "reallocating %d: %r\n", n);
error("realloc failed");
}
setmalloctag(p, getcallerpc(&n));
return p;
}
/*
* Heuristic city.
*/
Window*
makenewwindow(Text *t)
{
Column *c;
Window *w, *bigw, *emptyw;
Text *emptyb;
int i, y, el;
if(activecol)
c = activecol;
else if(seltext && seltext->col)
c = seltext->col;
else if(t && t->col)
c = t->col;
else{
if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
error("can't make column");
c = row.col[row.ncol-1];
}
activecol = c;
if(t==nil || t->w==nil || c->nw==0)
return coladd(c, nil, nil, -1);
/* find biggest window and biggest blank spot */
emptyw = c->w[0];
bigw = emptyw;
for(i=1; i<c->nw; i++){
w = c->w[i];
/* use >= to choose one near bottom of screen */
if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
bigw = w;
if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
emptyw = w;
}
emptyb = &emptyw->body;
el = emptyb->fr.maxlines-emptyb->fr.nlines;
/* if empty space is big, use it */
if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
else{
/* if this window is in column and isn't much smaller, split it */
if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
bigw = t->w;
y = (bigw->r.min.y + bigw->r.max.y)/2;
}
w = coladd(c, nil, nil, y);
if(w->body.fr.maxlines < 2)
colgrow(w->col, w, 1);
return w;
}

576
src/cmd/acme/wind.c Normal file
View File

@ -0,0 +1,576 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
int winid;
void
wininit(Window *w, Window *clone, Rectangle r)
{
Rectangle r1, br;
File *f;
Reffont *rf;
Rune *rp;
int nc;
w->tag.w = w;
w->body.w = w;
w->id = ++winid;
incref(&w->ref);
w->ctlfid = ~0;
w->utflastqid = -1;
r1 = r;
r1.max.y = r1.min.y + font->height;
incref(&reffont.ref);
f = fileaddtext(nil, &w->tag);
textinit(&w->tag, f, r1, &reffont, tagcols);
w->tag.what = Tag;
/* tag is a copy of the contents, not a tracked image */
if(clone){
textdelete(&w->tag, 0, w->tag.file->b.nc, TRUE);
nc = clone->tag.file->b.nc;
rp = runemalloc(nc);
bufread(&clone->tag.file->b, 0, rp, nc);
textinsert(&w->tag, 0, rp, nc, TRUE);
free(rp);
filereset(w->tag.file);
textsetselect(&w->tag, nc, nc);
}
r1 = r;
r1.min.y += font->height + 1;
if(r1.max.y < r1.min.y)
r1.max.y = r1.min.y;
f = nil;
if(clone){
f = clone->body.file;
w->body.org = clone->body.org;
w->isscratch = clone->isscratch;
rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name);
}else
rf = rfget(FALSE, FALSE, FALSE, nil);
f = fileaddtext(f, &w->body);
w->body.what = Body;
textinit(&w->body, f, r1, rf, textcols);
r1.min.y -= 1;
r1.max.y = r1.min.y+1;
draw(screen, r1, tagcols[BORD], nil, ZP);
textscrdraw(&w->body);
w->r = r;
w->r.max.y = w->body.fr.r.max.y;
br.min = w->tag.scrollr.min;
br.max.x = br.min.x + Dx(button->r);
br.max.y = br.min.y + Dy(button->r);
draw(screen, br, button, nil, button->r.min);
w->filemenu = TRUE;
w->maxlines = w->body.fr.maxlines;
if(clone){
w->dirty = clone->dirty;
textsetselect(&w->body, clone->body.q0, clone->body.q1);
winsettag(w);
}
}
int
winresize(Window *w, Rectangle r, int safe)
{
Rectangle r1;
int y;
Image *b;
Rectangle br;
r1 = r;
r1.max.y = r1.min.y + font->height;
y = r1.max.y;
if(!safe || !eqrect(w->tag.fr.r, r1)){
y = textresize(&w->tag, r1);
b = button;
if(w->body.file->mod && !w->isdir && !w->isscratch)
b = modbutton;
br.min = w->tag.scrollr.min;
br.max.x = br.min.x + Dx(b->r);
br.max.y = br.min.y + Dy(b->r);
draw(screen, br, b, nil, b->r.min);
}
if(!safe || !eqrect(w->body.fr.r, r1)){
if(y+1+font->height > r.max.y){ /* no body */
r1.min.y = y;
r1.max.y = y;
textresize(&w->body, r1);
w->r = r;
w->r.max.y = y;
return y;
}
r1 = r;
r1.min.y = y;
r1.max.y = y + 1;
draw(screen, r1, tagcols[BORD], nil, ZP);
r1.min.y = y + 1;
r1.max.y = r.max.y;
y = textresize(&w->body, r1);
w->r = r;
w->r.max.y = y;
textscrdraw(&w->body);
}
w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
return w->r.max.y;
}
void
winlock1(Window *w, int owner)
{
incref(&w->ref);
qlock(&w->lk);
w->owner = owner;
}
void
winlock(Window *w, int owner)
{
int i;
File *f;
f = w->body.file;
for(i=0; i<f->ntext; i++)
winlock1(f->text[i]->w, owner);
}
void
winunlock(Window *w)
{
int i;
File *f;
f = w->body.file;
for(i=0; i<f->ntext; i++){
w = f->text[i]->w;
w->owner = 0;
qunlock(&w->lk);
winclose(w);
/* winclose() can change up f->text; beware */
if(f->ntext>0 && w != f->text[i]->w)
--i; /* winclose() deleted window */
}
}
void
winmousebut(Window *w)
{
moveto(mousectl, divpt(addpt(w->tag.scrollr.min, w->tag.scrollr.max), 2));
}
void
windirfree(Window *w)
{
int i;
Dirlist *dl;
if(w->isdir){
for(i=0; i<w->ndl; i++){
dl = w->dlp[i];
free(dl->r);
free(dl);
}
free(w->dlp);
}
w->dlp = nil;
w->ndl = 0;
}
void
winclose(Window *w)
{
int i;
if(decref(&w->ref) == 0){
windirfree(w);
textclose(&w->tag);
textclose(&w->body);
if(activewin == w)
activewin = nil;
for(i=0; i<w->nincl; i++)
free(w->incl[i]);
free(w->incl);
free(w->events);
free(w);
}
}
void
windelete(Window *w)
{
Xfid *x;
x = w->eventx;
if(x){
w->nevents = 0;
free(w->events);
w->events = nil;
w->eventx = nil;
sendp(x->c, nil); /* wake him up */
}
}
void
winundo(Window *w, int isundo)
{
Text *body;
int i;
File *f;
Window *v;
w->utflastqid = -1;
body = &w->body;
fileundo(body->file, isundo, &body->q0, &body->q1);
textshow(body, body->q0, body->q1, 1);
f = body->file;
for(i=0; i<f->ntext; i++){
v = f->text[i]->w;
v->dirty = (f->seq != v->putseq);
if(v != w){
v->body.q0 = v->body.fr.p0+v->body.org;
v->body.q1 = v->body.fr.p1+v->body.org;
}
}
winsettag(w);
}
void
winsetname(Window *w, Rune *name, int n)
{
Text *t;
Window *v;
int i;
static Rune Lslashguide[] = { '/', 'g', 'u', 'i', 'd', 'e', 0 };
static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
t = &w->body;
if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
return;
w->isscratch = FALSE;
if(n>=6 && runeeq(Lslashguide, 6, name+(n-6), 6))
w->isscratch = TRUE;
else if(n>=7 && runeeq(Lpluserrors, 7, name+(n-7), 7))
w->isscratch = TRUE;
filesetname(t->file, name, n);
for(i=0; i<t->file->ntext; i++){
v = t->file->text[i]->w;
winsettag(v);
v->isscratch = w->isscratch;
}
}
void
wintype(Window *w, Text *t, Rune r)
{
int i;
texttype(t, r);
if(t->what == Body)
for(i=0; i<t->file->ntext; i++)
textscrdraw(t->file->text[i]);
winsettag(w);
}
void
wincleartag(Window *w)
{
int i, n;
Rune *r;
/* w must be committed */
n = w->tag.file->b.nc;
r = runemalloc(n);
bufread(&w->tag.file->b, 0, r, n);
for(i=0; i<n; i++)
if(r[i]==' ' || r[i]=='\t')
break;
for(; i<n; i++)
if(r[i] == '|')
break;
if(i == n)
return;
i++;
textdelete(&w->tag, i, n, TRUE);
free(r);
w->tag.file->mod = FALSE;
if(w->tag.q0 > i)
w->tag.q0 = i;
if(w->tag.q1 > i)
w->tag.q1 = i;
textsetselect(&w->tag, w->tag.q0, w->tag.q1);
}
void
winsettag1(Window *w)
{
int i, j, k, n, bar, dirty;
Rune *new, *old, *r;
Image *b;
uint q0, q1;
Rectangle br;
static Rune Ldelsnarf[] = { ' ', 'D', 'e', 'l', ' ',
'S', 'n', 'a', 'r', 'f', 0 };
static Rune Lundo[] = { ' ', 'U', 'n', 'd', 'o', 0 };
static Rune Lredo[] = { ' ', 'R', 'e', 'd', 'o', 0 };
static Rune Lget[] = { ' ', 'G', 'e', 't', 0 };
static Rune Lput[] = { ' ', 'P', 'u', 't', 0 };
static Rune Llook[] = { ' ', 'L', 'o', 'o', 'k', ' ', 0 };
static Rune Lpipe[] = { ' ', '|', 0 };
/* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */
if(w->tag.ncache!=0 || w->tag.file->mod)
wincommit(w, &w->tag); /* check file name; also guarantees we can modify tag contents */
old = runemalloc(w->tag.file->b.nc+1);
bufread(&w->tag.file->b, 0, old, w->tag.file->b.nc);
old[w->tag.file->b.nc] = '\0';
for(i=0; i<w->tag.file->b.nc; i++)
if(old[i]==' ' || old[i]=='\t')
break;
if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){
textdelete(&w->tag, 0, i, TRUE);
textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE);
free(old);
old = runemalloc(w->tag.file->b.nc+1);
bufread(&w->tag.file->b, 0, old, w->tag.file->b.nc);
old[w->tag.file->b.nc] = '\0';
}
new = runemalloc(w->body.file->nname+100);
i = 0;
runemove(new+i, w->body.file->name, w->body.file->nname);
i += w->body.file->nname;
runemove(new+i, Ldelsnarf, 10);
i += 10;
if(w->filemenu){
if(w->body.file->delta.nc>0 || w->body.ncache){
runemove(new+i, Lundo, 5);
i += 5;
}
if(w->body.file->epsilon.nc > 0){
runemove(new+i, Lredo, 5);
i += 5;
}
dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
if(!w->isdir && dirty){
runemove(new+i, Lput, 4);
i += 4;
}
}
if(w->isdir){
runemove(new+i, Lget, 4);
i += 4;
}
runemove(new+i, Lpipe, 2);
i += 2;
r = runestrchr(old, '|');
if(r)
k = r-old+1;
else{
k = w->tag.file->b.nc;
if(w->body.file->seq == 0){
runemove(new+i, Llook, 6);
i += 6;
}
}
new[i] = 0;
if(runestrlen(new) != i)
fprint(2, "s '%S' len not %d\n", new, i);
assert(i==runestrlen(new));
if(runeeq(new, i, old, k) == FALSE){
n = k;
if(n > i)
n = i;
for(j=0; j<n; j++)
if(old[j] != new[j])
break;
q0 = w->tag.q0;
q1 = w->tag.q1;
textdelete(&w->tag, j, k, TRUE);
textinsert(&w->tag, j, new+j, i-j, TRUE);
/* try to preserve user selection */
r = runestrchr(old, '|');
if(r){
bar = r-old;
if(q0 > bar){
bar = (runestrchr(new, '|')-new)-bar;
w->tag.q0 = q0+bar;
w->tag.q1 = q1+bar;
}
}
}
free(old);
free(new);
w->tag.file->mod = FALSE;
n = w->tag.file->b.nc+w->tag.ncache;
if(w->tag.q0 > n)
w->tag.q0 = n;
if(w->tag.q1 > n)
w->tag.q1 = n;
textsetselect(&w->tag, w->tag.q0, w->tag.q1);
b = button;
if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache))
b = modbutton;
br.min = w->tag.scrollr.min;
br.max.x = br.min.x + Dx(b->r);
br.max.y = br.min.y + Dy(b->r);
draw(screen, br, b, nil, b->r.min);
}
void
winsettag(Window *w)
{
int i;
File *f;
Window *v;
f = w->body.file;
for(i=0; i<f->ntext; i++){
v = f->text[i]->w;
if(v->col->safe || v->body.fr.maxlines>0)
winsettag1(v);
}
}
void
wincommit(Window *w, Text *t)
{
Rune *r;
int i;
File *f;
textcommit(t, TRUE);
f = t->file;
if(f->ntext > 1)
for(i=0; i<f->ntext; i++)
textcommit(f->text[i], FALSE); /* no-op for t */
if(t->what == Body)
return;
r = runemalloc(w->tag.file->b.nc);
bufread(&w->tag.file->b, 0, r, w->tag.file->b.nc);
for(i=0; i<w->tag.file->b.nc; i++)
if(r[i]==' ' || r[i]=='\t')
break;
if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){
seq++;
filemark(w->body.file);
w->body.file->mod = TRUE;
w->dirty = TRUE;
winsetname(w, r, i);
winsettag(w);
}
free(r);
}
void
winaddincl(Window *w, Rune *r, int n)
{
char *a;
Dir *d;
Runestr rs;
a = runetobyte(r, n);
d = dirstat(a);
if(d == nil){
if(a[0] == '/')
goto Rescue;
rs = dirname(&w->body, r, n);
r = rs.r;
n = rs.nr;
free(a);
a = runetobyte(r, n);
d = dirstat(a);
if(d == nil)
goto Rescue;
r = runerealloc(r, n+1);
r[n] = 0;
}
free(a);
if((d->qid.type&QTDIR) == 0){
free(d);
warning(nil, "%s: not a directory\n", a);
free(r);
return;
}
free(d);
w->nincl++;
w->incl = realloc(w->incl, w->nincl*sizeof(Rune*));
memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*));
w->incl[0] = runemalloc(n+1);
runemove(w->incl[0], r, n);
free(r);
return;
Rescue:
warning(nil, "%s: %r\n", a);
free(r);
free(a);
return;
}
int
winclean(Window *w, int conservative) /* as it stands, conservative is always TRUE */
{
if(w->isscratch || w->isdir) /* don't whine if it's a guide file, error window, etc. */
return TRUE;
if(!conservative && w->nopen[QWevent]>0)
return TRUE;
if(w->dirty){
if(w->body.file->nname)
warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name);
else{
if(w->body.file->b.nc < 100) /* don't whine if it's too small */
return TRUE;
warning(nil, "unnamed file modified\n");
}
w->dirty = FALSE;
return FALSE;
}
return TRUE;
}
void
winctlprint(Window *w, char *buf, int fonts)
{
int n;
n = sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->b.nc,
w->body.file->b.nc, w->isdir, w->dirty);
if(fonts)
sprint(buf+n, "%11d %s" , Dx(w->body.fr.r), w->body.reffont->f->name);
}
void
winevent(Window *w, char *fmt, ...)
{
int n;
char *b;
Xfid *x;
va_list arg;
if(w->nopen[QWevent] == 0)
return;
if(w->owner == 0)
error("no window owner");
va_start(arg, fmt);
b = vsmprint(fmt, arg);
va_end(arg);
if(b == nil)
error("vsmprint failed");
n = strlen(b);
w->events = realloc(w->events, w->nevents+1+n);
w->events[w->nevents++] = w->owner;
memmove(w->events+w->nevents, b, n);
free(b);
w->nevents += n;
x = w->eventx;
if(x){
w->eventx = nil;
sendp(x->c, nil);
}
}

1046
src/cmd/acme/xfid.c Normal file

File diff suppressed because it is too large Load Diff

46
src/lib9/_p9translate.c Normal file
View File

@ -0,0 +1,46 @@
#include <u.h>
#include <libc.h>
/*
* I don't want too many of these,
* but the ones we have are just too useful.
*/
static struct {
char *old;
char *new;
} replace[] = {
"#9", nil, /* must be first */
"#d", "/dev/fd",
};
char*
_p9translate(char *old)
{
char *new;
int i, olen, nlen, len;
if(replace[0].new == nil){
replace[0].new = getenv("PLAN9");
if(replace[0].new == nil)
replace[0].new = "/usr/local/plan9";
}
for(i=0; i<nelem(replace); i++){
if(!replace[i].new)
continue;
olen = strlen(replace[i].old);
if(strncmp(old, replace[i].old, olen) != 0
|| (old[olen] != '\0' && old[olen] != '/'))
continue;
nlen = strlen(replace[i].new);
len = strlen(old)+nlen-olen;
new = malloc(len+1);
if(new == nil)
return nil;
strcpy(new, replace[i].new);
strcpy(new+nlen, old+olen);
assert(strlen(new) == len);
return new;
}
return old;
}

19
src/lib9/access.c Normal file
View File

@ -0,0 +1,19 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
char *_p9translate(char*);
int
p9access(char *xname, int what)
{
int ret;
char *name;
if((name = _p9translate(xname)) == nil)
return -1;
ret = access(name, what);
if(name != xname)
free(name);
return ret;
}

74
src/lib9/getns.c Normal file
View File

@ -0,0 +1,74 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
/*
* Absent other hints, it works reasonably well to use
* the X11 display name as the name space identifier.
* This is how sam's B has worked since the early days.
* Since most programs using name spaces are also using X,
* this still seems reasonable. Terminal-only sessions
* can set $NAMESPACE.
*/
static char*
nsfromdisplay(void)
{
int fd;
Dir *d;
char *disp, *p;
if((disp = getenv("DISPLAY")) == nil){
werrstr("$DISPLAY not set");
return nil;
}
/* canonicalize: xxx:0.0 => xxx:0 */
p = strrchr(disp, ':');
if(p){
p++;
while(isdigit((uchar)*p))
p++;
if(strcmp(p, ".0") == 0)
*p = 0;
}
p = smprint("/tmp/ns.%s.%s", getuser(), disp);
free(disp);
if(p == nil){
werrstr("out of memory");
return p;
}
if((fd=create(p, OREAD, DMDIR|0700)) >= 0){
close(fd);
return p;
}
if((d = dirstat(p)) == nil){
free(d);
werrstr("stat %s: %r", p);
free(p);
return nil;
}
if((d->mode&0777) != 0700 || strcmp(d->uid, getuser()) != 0){
werrstr("bad name space dir %s", p);
free(p);
free(d);
return nil;
}
free(d);
return p;
}
char*
getns(void)
{
char *ns;
ns = getenv("NAMESPACE");
if(ns == nil)
ns = nsfromdisplay();
if(ns == nil){
werrstr("$NAMESPACE not set, %r");
return nil;
}
return ns;
}

11
src/lib9/malloc.c Normal file
View File

@ -0,0 +1,11 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
void*
p9malloc(ulong n)
{
if(n == 0)
n++;
return malloc(n);
}

38
src/lib9/open.c Normal file
View File

@ -0,0 +1,38 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
extern char* _p9translate(char*);
int
p9open(char *xname, int mode)
{
char *name;
int cexec, rclose;
int fd, umode;
umode = mode&3;
cexec = mode&OCEXEC;
rclose = mode&ORCLOSE;
mode &= ~(3|OCEXEC|ORCLOSE);
if(mode&OTRUNC){
umode |= O_TRUNC;
mode ^= OTRUNC;
}
if(mode){
werrstr("mode not supported");
return -1;
}
if((name = _p9translate(xname)) == nil)
return -1;
fd = open(name, umode);
if(fd >= 0){
if(cexec)
fcntl(fd, F_SETFL, FD_CLOEXEC);
if(rclose)
remove(name);
}
if(name != xname)
free(name);
return fd;
}

10
src/lib9/pipe.c Normal file
View File

@ -0,0 +1,10 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/socket.h>
int
p9pipe(int fd[2])
{
return socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
}

40
src/lib9/post9p.c Normal file
View File

@ -0,0 +1,40 @@
#include <u.h>
#include <libc.h>
int
post9pservice(int fd, char *name)
{
int i;
char *ns, *s;
Waitmsg *w;
if((ns = getns()) == nil)
return -1;
s = smprint("unix!%s/%s", ns, name);
free(ns);
if(s == nil)
return -1;
switch(rfork(RFPROC|RFFDG)){
case -1:
return -1;
case 0:
dup(fd, 0);
dup(fd, 1);
for(i=3; i<20; i++)
close(i);
execlp("9pserve", "9pserve", "-u", s, (char*)0);
fprint(2, "exec 9pserve: %r\n");
_exits("exec");
default:
w = wait();
close(fd);
free(s);
if(w->msg && w->msg[0]){
free(w);
werrstr("9pserve failed");
return -1;
}
free(w);
return 0;
}
}

79
src/lib9/sendfd.c Normal file
View File

@ -0,0 +1,79 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>
typedef struct Sendfd Sendfd;
struct Sendfd {
struct cmsghdr cmsg;
int fd;
};
int
sendfd(int s, int fd)
{
char buf[1];
struct iovec iov;
struct msghdr msg;
int n;
Sendfd sfd;
buf[0] = 0;
iov.iov_base = buf;
iov.iov_len = 1;
memset(&msg, 0, sizeof msg);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sfd.cmsg.cmsg_len = sizeof sfd;
sfd.cmsg.cmsg_level = SOL_SOCKET;
sfd.cmsg.cmsg_type = SCM_RIGHTS;
sfd.fd = fd;
msg.msg_control = &sfd;
msg.msg_controllen = sizeof sfd;
if((n=sendmsg(s, &msg, 0)) != iov.iov_len)
return -1;
return 0;
}
int
recvfd(int s)
{
int n;
char buf[1];
struct iovec iov;
struct msghdr msg;
Sendfd sfd;
iov.iov_base = buf;
iov.iov_len = 1;
memset(&msg, 0, sizeof msg);
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
memset(&sfd, 0, sizeof sfd);
sfd.fd = -1;
sfd.cmsg.cmsg_len = sizeof sfd;
sfd.cmsg.cmsg_level = SOL_SOCKET;
sfd.cmsg.cmsg_type = SCM_RIGHTS;
msg.msg_control = &sfd;
msg.msg_controllen = sizeof sfd;
if((n=recvmsg(s, &msg, 0)) < 0)
return -1;
if(n==0 && sfd.fd==-1){
werrstr("eof in recvfd");
return -1;
}
return sfd.fd;
}

12
src/libbio/_lib9.h Normal file
View File

@ -0,0 +1,12 @@
#include <fmt.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define OREAD O_RDONLY
#define OWRITE O_WRONLY
#include <utf.h>
#define nil ((void*)0)

36
src/libfs/ns.c Normal file
View File

@ -0,0 +1,36 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <fs.h>
#include <ctype.h>
Fsys*
nsmount(char *name, char *aname)
{
char *addr, *ns;
int fd;
Fsys *fs;
ns = getns();
if(ns == nil)
return nil;
addr = smprint("unix!%s/%s", ns, name);
free(ns);
if(addr == nil)
return nil;
fd = dial(addr, 0, 0, 0);
if(fd < 0){
werrstr("dial %s: %r", addr);
return nil;
}
fs = fsmount(fd, aname);
if(fs == nil){
close(fd);
return nil;
}
return fs;
}

26
src/libfs/openfd.c Normal file
View File

@ -0,0 +1,26 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <fs.h>
#include "fsimpl.h"
int
fsopenfd(Fsys *fs, char *name, int mode)
{
Fid *fid;
Fcall tx, rx;
if((fid = fswalk(fs->root, name)) == nil)
return -1;
tx.type = Topenfd;
tx.fid = fid->fid;
tx.mode = mode&~OCEXEC;
if(fsrpc(fs, &tx, &rx, 0) < 0){
fsclose(fid);
return -1;
}
_fsputfid(fid);
if(mode&OCEXEC && rx.unixfd>=0)
fcntl(rx.unixfd, F_SETFL, FD_CLOEXEC);
return rx.unixfd;
}