This commit is contained in:
Yuvia 2025-08-12 13:11:11 +00:00
commit 4337185678
47 changed files with 14581 additions and 0 deletions

BIN
6.out Executable file

Binary file not shown.

BIN
acme.6 Normal file

Binary file not shown.

955
acme.c Normal file
View file

@ -0,0 +1,955 @@
#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];
Command *command;
void acmeerrorinit(void);
void readfile(Column*, char*);
int shutdown(void*, char*);
void
derror(Display*, char *errorstr)
{
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 'a':
globalindent[AUTOINDENT] = TRUE;
break;
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 'i':
globalindent[SPACESINDENT] = TRUE;
break;
case 'l':
loadfile = ARGF();
if(loadfile == nil)
goto Usage;
break;
default:
Usage:
fprint(2, "usage: acme [-aib] [-c ncol] [-f font] [-F fixedfont] [-l loadfile | file...]\n");
exits("usage");
}ARGEND
if(fontnames[0] != nil)
fontnames[0] = estrdup(fontnames[0]);
else
if((fontnames[0] = getenv("font")) == nil)
fontnames[0] = estrdup("/lib/font/bit/vga/unicode.font");
if(access(fontnames[0], 0) < 0){
fprint(2, "acme: can't access %s: %r\n", fontnames[0]);
exits("font open");
}
if(fontnames[1] == nil)
fontnames[1] = fontnames[0];
fontnames[1] = estrdup(fontnames[1]);
quotefmtinstall();
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");
}
d = display;
font = d->defaultfont;
reffont.f = font;
reffonts[0] = &reffont;
incref(&reffont); /* one to hold up 'font' variable */
incref(&reffont); /* 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);
cwarn = chancreate(sizeof(void*), 1);
if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cnewwindow==nil || cerr==nil || cedit==nil || cexit==nil || cwarn==nil){
fprint(2, "acme: can't create initial channels: %r\n");
threadexitsall("channels");
}
mousectl = initmouse(nil, screen);
if(mousectl == nil){
fprint(2, "acme: can't initialize mouse: %r\n");
threadexitsall("mouse");
}
mouse = mousectl;
keyboardctl = initkeyboard(nil);
if(keyboardctl == nil){
fprint(2, "acme: can't initialize keyboard: %r\n");
threadexitsall("keyboard");
}
mainpid = getpid();
plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
if(plumbeditfd >= 0){
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)){
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->nc, w->tag.file->nc);
xfidlog(w, "new");
}
char *oknotes[] ={
"delete",
"hangup",
"kill",
"exit",
nil
};
int dumping;
int
shutdown(void*, char *msg)
{
int i;
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();
}
void
killprocs(void)
{
Command *c;
fsysclose();
for(c=command; c; c=c->next)
postnote(PNGROUP, c->pid, "hangup");
remove(acmeerrorfile);
}
static int errorfd;
void
acmeerrorproc(void *)
{
char *buf, *s;
int n;
threadsetname("acmeerrorproc");
buf = emalloc(8192+1);
while((n=read(errorfd, buf, 8192)) >= 0){
buf[n] = '\0';
s = estrdup(buf);
sendp(cerr, s);
}
free(buf);
}
void
acmeerrorinit(void)
{
int fd, pfd[2];
char buf[64];
if(pipe(pfd) < 0)
error("can't create pipe");
sprint(acmeerrorfile, "/srv/acme.%s.%d", user, 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);
if(errorfd < 0)
error("can't re-open acmeerror file");
close(pfd[1]);
close(pfd[0]);
proccreate(acmeerrorproc, nil, STACK);
}
void
plumbproc(void *)
{
Plumbmsg *m;
threadsetname("plumbproc");
for(;;){
m = plumbrecv(plumbeditfd);
if(m == nil)
threadexits(nil);
sendp(cplumb, m);
}
}
void
keyboardthread(void *)
{
Rune r;
Timer *timer;
Text *t;
enum { KTimer, KKey, NKALT };
static Alt alts[NKALT+1];
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 *)
{
Text *t, *argt;
int but;
uint q0, q1;
Window *w;
Plumbmsg *pm;
Mouse m;
char *act;
enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
static Alt alts[NMALT+1];
threadsetname("mousethread");
alts[MResize].c = mousectl->resizec;
alts[MResize].v = nil;
alts[MResize].op = CHANRCV;
alts[MMouse].c = mousectl->c;
alts[MMouse].v = &mousectl->Mouse;
alts[MMouse].op = CHANRCV;
alts[MPlumb].c = cplumb;
alts[MPlumb].v = &pm;
alts[MPlumb].op = CHANRCV;
alts[MWarnings].c = cwarn;
alts[MWarnings].v = nil;
alts[MWarnings].op = CHANRCV;
if(cplumb == nil)
alts[MPlumb].op = CHANNOP;
alts[NMALT].op = CHANEND;
for(;;){
qlock(&row);
flushwarnings();
qunlock(&row);
flushimage(display, 1);
switch(alt(alts)){
case MResize:
if(getwindow(display, Refnone) < 0)
error("attach to window");
scrlresize();
rowresize(&row, screen->clipr);
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);
}
plumbfree(pm);
break;
case MWarnings:
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->Mouse;
qlock(&row);
t = rowwhich(&row, m.xy);
if((t!=mousetext && t!=nil && t->w!=nil) &&
(mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
xfidlog(t->w, "focus");
}
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;
}
/* scroll buttons, wheels, etc. */
if(t->what==Body && w != nil && (m.buttons & (8|16))){
if(m.buttons & 8)
but = Kscrolloneup;
else
but = Kscrollonedown;
winlock(w, 'M');
t->eq0 = ~0;
texttype(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:
qunlock(&row);
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 *)
{
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];
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);
warning(nil, "%s", err);
free(err);
flushimage(display, 1);
qunlock(&row);
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);
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\n", w->msg);
flushimage(display, 1);
}
qunlock(&row);
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);
t = &row.tag;
textcommit(t, TRUE);
textinsert(t, 0, c->name, c->nname, TRUE);
textsetselect(t, 0, 0);
flushimage(display, 1);
qunlock(&row);
break;
}
}
}
void
xfidallocthread(void*)
{
Xfid *xfree, *x;
enum { Alloc, Free, N };
static Alt alts[N+1];
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*)
{
Window *w;
threadsetname("newwindowthread");
for(;;){
/* only fsysproc is talking to us, so synchronization is trivial */
recvp(cnewwindow);
w = makenewwindow(nil);
winsettag(w);
xfidlog(w, "new");
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 = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
fontcache[nfontcache++] = r;
}
Found:
if(save){
incref(r);
if(reffonts[fix])
rfclose(reffonts[fix]);
reffonts[fix] = r;
if(name != fontnames[fix]){
free(fontnames[fix]);
fontnames[fix] = estrdup(name);
}
}
if(setfont){
reffont.f = r->f;
incref(r);
rfclose(reffonts[0]);
font = r->f;
reffonts[0] = r;
incref(r);
iconinit();
}
incref(r);
return r;
}
void
rfclose(Reffont *r)
{
int i;
if(decref(r) == 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;
tagcols[BACK] = display->white;
tagcols[HIGH] = allocimagemix(display, DBlack, 0xb53a38FF);
tagcols[BORD] = allocimagemix(display, DBlack, 0xb53a38FF);
tagcols[TEXT] = display->black;
tagcols[HTEXT] = display->white;
textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
textcols[HIGH] = allocimagemix(display, DBlack, 0xb53a38FF);
textcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DWhite);
draw(textcols[BORD], Rect(1,1,2,2), display->black, nil, ZP);
draw(textcols[BORD], Rect(0,0,1,1), display->black, nil, ZP);
textcols[TEXT] = display->black;
textcols[HTEXT] = display->white;
if(button){
freeimage(button);
freeimage(modbutton);
freeimage(colbutton);
}
r = Rect(0, 0, Scrollwid, font->height+1);
button = allocimage(display, r, screen->chan, 0, DNofill);
draw(button, r, tagcols[BORD], nil, r.min);
r.max.x -= 4;
fillellipse(button, (Point){r.min.x + 5, r.min.y + 7}, 3, 3, display->white, ZP);
r = button->r;
modbutton = allocimage(display, r, screen->chan, 0, DNofill);
draw(modbutton, r, tagcols[BORD], nil, r.min);
r = insetrect(r, 2);
fillellipse(modbutton, (Point){r.min.x + 3, r.min.y + 5}, 3, 3, display->black, ZP);
r = button->r;
colbutton = allocimagemix(display, DBlack, 0xb53a38FF);
but2col = allocimage(display, r, screen->chan, 1, 0x000000FF);
but3col = allocimagemix(display, DBlack, 0xb53a38FF);
}
/*
* /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
putsnarf(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
getsnarf()
{
int nulls;
if(snarfbuf.nc > MAXSNARF)
return;
if(snarffd < 0)
return;
seek(snarffd, 0, 0);
bufreset(&snarfbuf);
bufload(&snarfbuf, 0, snarffd, &nulls);
}

BIN
addr.6 Normal file

Binary file not shown.

291
addr.c Normal file
View file

@ -0,0 +1,291 @@
#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;
}
// nlcounttopos starts at q0 and advances nl lines,
// being careful not to walk past the end of the text,
// and then nr chars, being careful not to walk past
// the end of the current line.
// It returns the final position.
long
nlcounttopos(Text *t, long q0, long nl, long nr)
{
while(nl > 0 && q0 < t->file->nc) {
if(textreadc(t, q0++) == '\n')
nl--;
}
if(nl > 0)
return q0;
while(nr > 0 && q0 < t->file->nc && textreadc(t, q0) != '\n') {
q0++;
nr--;
}
return q0;
}
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->nc;
line = r.q0 - line;
}
if(line<0 || line>t->file->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->nc)
if(textreadc(t, q1++) == '\n' || q1==t->file->nc)
if(--line > 0)
q0 = q1;
if(line > 0)
goto Rescue;
break;
case Fore:
if(q1 > 0)
while(q1<t->file->nc && textreadc(t, q1-1) != '\n')
q1++;
q0 = q1;
goto Forward;
case Back:
if(q0 < t->file->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;
}
/* :1-1 is :0 = #0, but :1-2 is an error */
if(line > 1)
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->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->nc, t->file->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){
nc = (*getc)(a, q++);
if(nc<'0' || '9'<nc){
q--;
break;
}
n = n*10+(nc-'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;
}

BIN
buff.6 Normal file

Binary file not shown.

322
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)->n <= q0 && q+(*blp)->n < b->nc){
q += (*blp)->n;
i++;
blp++;
if(i >= b->nbl)
error("block not found");
}
bl = *blp;
/* remember position */
b->cbi = i;
b->cq = q;
sizecache(b, bl->n);
b->cnc = bl->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;
}

BIN
cols.6 Normal file

Binary file not shown.

551
cols.c Normal file
View file

@ -0,0 +1,551 @@
#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"
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, L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE);
textsetselect(t, t->file->nc, t->file->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.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.r.min.y+Dy(v->body.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.maxlines<=3){
colgrow(c, v, 1);
y = v->body.r.min.y+Dy(v->body.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.font->height+v->body.font->height+Border+1));
r1.max.y = min(y, v->body.r.min.y+v->body.nlines*v->body.font->height);
r1.min.y = winresize(v, r1, FALSE, 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->rdselfd = -1;
w->col = c;
draw(screen, r, textcols[BACK], nil, ZP);
wininit(w, clone, r);
}else{
w->col = c;
winresize(w, r, FALSE, TRUE);
}
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, didmouse, up;
/* 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;
didmouse = restoremouse(w);
if(dofree){
windelete(w);
winclose(w);
}
c->nw--;
memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
c->w = realloc(c->w, c->nw*sizeof(Window*));
if(c->nw == 0){
draw(screen, r, display->white, nil, ZP);
return;
}
up = 0;
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 */
up = 1;
w = c->w[i];
r.max.y = w->r.max.y;
}
draw(screen, r, textcols[BACK], nil, ZP);
if(c->safe) {
if(!didmouse && up)
w->showdel = TRUE;
winresize(w, r, FALSE, TRUE);
if(!didmouse && up)
movetodel(w);
}
}
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, old, new;
Rectangle r1, r2;
Window *w;
clearmouse();
r1 = r;
r1.max.y = r1.min.y + c->tag.font->height;
textresize(&c->tag, r1, TRUE);
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;
new = Dy(r) - c->nw*(Border + font->height);
old = Dy(c->r) - c->nw*(Border + font->height);
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;
if(new > 0 && old > 0 && Dy(w->r) > Border+font->height)
r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
}
r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
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, i==c->nw-1);
}
c->r = r;
}
static
int
colcmp(void *a, 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.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, i==c->nw-1);
}
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 - Border;
winresize(w, r, FALSE, TRUE);
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, TRUE);
for(i=1; i<c->nw; i++)
c->w[i]->body.maxlines = 0;
c->safe = FALSE;
return;
}
/* store old #lines for each window */
onl = w->body.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.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.font->height;
if(!c->safe || !eqrect(v->r, r)){
draw(screen, r, textcols[BACK], nil, ZP);
r.min.y = winresize(v, r, c->safe, FALSE);
}else
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.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 = y2;
h = w->body.font->height;
if(Dy(r) < Dy(w->tagtop)+1+h+Border)
r.max.y = r.min.y + Dy(w->tagtop)+1+h+Border;
/* draw window */
draw(screen, r, textcols[BACK], nil, ZP);
r.max.y = winresize(w, r, c->safe, TRUE);
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.font->height;
draw(screen, r, textcols[BACK], nil, ZP);
y1 = winresize(v, r, c->safe, j==c->nw-1);
if(j < c->nw-1){ /* no border on last window */
r.min.y = y1;
r.max.y += Border;
draw(screen, r, display->black, nil, ZP);
y1 = r.max.y;
}
}
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:
if(w->tagexpand) /* force recomputation of window tag size */
w->taglines = 1;
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 = op.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.r.min.y){
r.max.y -= (r.max.y-v->body.r.min.y)%v->body.font->height;
if(v->body.r.min.y == v->body.r.max.y)
r.max.y++;
}
draw(screen, r, textcols[BACK], nil, ZP);
r.min.y = winresize(v, r, c->safe, FALSE);
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, TRUE);
}
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;
}
/* scrollr drops below w->r on low windows */
if(ptinrect(p, w->body.scrollr))
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;
}

570
dat.h Normal file
View file

@ -0,0 +1,570 @@
enum
{
Qdir,
Qacme,
Qcons,
Qconsctl,
Qdraw,
Qeditout,
Qindex,
Qlabel,
Qlog,
Qnew,
QWaddr,
QWbody,
QWctl,
QWdata,
QWeditout,
QWerrors,
QWevent,
QWrdsel,
QWwrsel,
QWtag,
QWxdata,
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
{
vlong addr; /* disk address in bytes */
union
{
uint n; /* number of used runes in block */
Block *next; /* pointer to next in free list */
};
};
struct Disk
{
int fd;
vlong 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; /* 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;
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;
int needundo;
};
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 textstretchsel(Text*, uint*, uint*, int);
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, int);
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);
enum
{
SPACESINDENT = 0,
AUTOINDENT,
NINDENT,
};
struct Window
{
QLock;
Ref;
Text tag;
Text body;
Rectangle r;
uchar isdir;
uchar isscratch;
uchar filemenu;
uchar dirty;
uchar indent[NINDENT];
uchar showdel;
int id;
Range addr;
Range limit;
uchar nopen[QMAX];
uchar nomark;
uchar noscroll;
Range wrselrange;
int rdselfd;
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;
int tagsafe; /* taglines is correct */
int tagexpand;
int taglines;
Rectangle tagtop;
};
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, 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*);
char *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;
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*);
int 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];
vlong logoff; // for putlog
};
struct Xfid
{
void *arg; /* args to xfidinit */
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);
void xfidlogopen(Xfid*);
void xfidlogread(Xfid*);
void xfidlogflush(Xfid*);
void xfidlog(Window*, char*);
struct Reffont
{
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;
};
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 globalincref;
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 *user;
char *cputype;
char *objtype;
char *home;
char *fontnames[2];
char acmeerrorfile[128];
Image *tagcols[NCOL];
Image *textcols[NCOL];
int plumbsendfd;
int plumbeditfd;
char wdir[];
int editing;
int messagesize; /* negotiated in 9P version setup */
int globalindent[NINDENT];
Rune *delcmd; /* what command deleted the window. eg, Del, Delete, Delmesg */
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) */
Channel *cwarn; /* chan(void*)[1] (really chan(unit)[1]) */
#define STACK 8192

BIN
disk.6 Normal file

Binary file not shown.

142
disk.c Normal file
View file

@ -0,0 +1,142 @@
#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(), user);
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->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].next = &blist[j+1];
}
b = blist;
blist = b->next;
b->addr = d->addr;
if(d->addr+size < d->addr){
error("temp file overflow");
}
d->addr += size;
}
b->n = n;
return b;
}
void
diskrelease(Disk *d, Block *b)
{
uint i;
ntosize(b->n, &i);
b->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->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->n = n;
}
void
diskread(Disk *d, Block *b, Rune *r, uint n)
{
int tot, nr;
char *p;
if(n > b->n)
error("internal error: diskread");
ntosize(b->n, nil);
n *= sizeof(Rune);
p = (char*)r;
for(tot = 0; tot < n; tot += nr){
nr = pread(d->fd, p+tot, n-tot, b->addr+tot);
if(nr <= 0)
error("read error from temp file");
}
if(tot != n)
error("read error from temp file");
}

BIN
ecmd.6 Normal file

Binary file not shown.

1366
ecmd.c Normal file

File diff suppressed because it is too large Load diff

BIN
edit.6 Normal file

Binary file not shown.

679
edit.c Normal file
View file

@ -0,0 +1,679 @@
#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*)
{
Cmd *cmdp;
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*)
{
elogterm(w->body.file);
}
void
alleditinit(Window *w, void*)
{
textcommit(&w->tag, TRUE);
textcommit(&w->body, TRUE);
w->body.file->editclean = FALSE;
}
void
allupdate(Window *w, void*)
{
Text *t;
int i;
File *f;
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->listptr==0 || l->nalloc==0){
l->nalloc = INCR;
l->listptr = emalloc(INCR*sizeof(void*));
l->nused = 0;
}else if(l->nused == l->nalloc){
l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*));
memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*));
l->nalloc += INCR;
}
}
/*
* Remove the ith element from the list
*/
void
dellist(List *l, int i)
{
l->nused--;
memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-i)*sizeof(void*));
}
/*
* Add a new element, whose position is i, to the list
*/
void
inslist(List *l, int i, void *v)
{
growlist(l);
memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*));
l->ptr[i] = v;
l->nused++;
}
void
listfree(List *l)
{
free(l->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, p);
return p;
}
String*
newstring(int n)
{
String *p;
p = allocstring(n);
inslist(&stringlist, stringlist.nused, p);
return p;
}
Addr*
newaddr(void)
{
Addr *p;
p = emalloc(sizeof(Addr));
inslist(&addrlist, addrlist.nused, p);
return p;
}
void
freecmd(void)
{
int i;
while(cmdlist.nused > 0)
free(cmdlist.ucharptr[--cmdlist.nused]);
while(addrlist.nused > 0)
free(addrlist.ucharptr[--addrlist.nused]);
while(stringlist.nused>0){
i = --stringlist.nused;
freestring(stringlist.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';
s->n -= 2;
}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.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.text = newstring(0);
getrhs(cmd.text, c, 's');
if(nextc() == c){
getch();
if(nextc() == 'g')
cmd.flag = getch();
}
}
}
}
if(ct->addr && (cmd.mtaddr=simpleaddr())==0)
editerror("bad address");
if(ct->defcmd){
if(cmdskipbl() == '\n'){
getch();
cmd.cmd = newcmd();
cmd.cmd->cmdc = ct->defcmd;
}else if((cmd.cmd = parsecmd(nest))==0)
error("defcmd");
}else if(ct->text)
cmd.text = collecttext();
else if(ct->token)
cmd.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.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.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.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.left = simpleaddr();
if((addr.type = cmdskipbl())!=',' && addr.type!=';')
return addr.left;
getch();
next = addr.next = compoundaddr();
if(next && (next->type==',' || next->type==';') && next->left==0)
editerror("bad address syntax");
ap = newaddr();
*ap = addr;
return ap;
}

99
edit.h Normal file
View file

@ -0,0 +1,99 @@
#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 ; */
};
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 */
};
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
{
int nalloc;
int nused;
union{
void *listptr;
void* *ptr;
uchar* *ucharptr;
String* *stringptr;
};
};
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);

BIN
elog.6 Normal file

Binary file not shown.

353
elog.c Normal file
View file

@ -0,0 +1,353 @@
#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, 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 && f->elog.nr+nr<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 tq0, tq1;
Buffer *log;
Text *t;
int owner;
elogflush(f);
log = f->elogbuf;
t = f->curtext;
buf = fbufalloc();
mod = FALSE;
owner = 0;
if(t->w){
owner = t->w->owner;
if(owner == 0)
t->w->owner = 'E';
}
/*
* The edit commands have already updated the selection in t->q0, t->q1,
* but using coordinates relative to the unmodified buffer. As we apply the log,
* we have to update the coordinates to be relative to the modified buffer.
* Textinsert and textdelete will do this for us; our only work is to apply the
* convention that an insertion at t->q0==t->q1 is intended to select the
* inserted text.
*/
/*
* 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 (%d %d)\n",
b.q0, b.q0+b.nd, t->q0, t->q1);
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);
}
if(t->q0 == b.q0 && t->q1 == b.q0)
t->q1 += b.nr;
break;
case Delete:
if(tracelog)
warning(nil, "elog delete %d %d (%d %d)\n",
b.q0, b.q0+b.nd, t->q0, t->q1);
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 (%d %d)\n",
b.q0, b.q0+b.nr, t->q0, t->q1);
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);
}
if(t->q0 == b.q0 && t->q1 == b.q0)
t->q1 += b.nr;
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);
elogterm(f);
/*
* Bad addresses will cause bufload to crash, so double check.
* If changes were out of order, we expect problems so don't complain further.
*/
if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){
if(!warned)
warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc);
t->q1 = min(t->q1, f->nc);
t->q0 = min(t->q0, t->q1);
}
if(t->w)
t->w->owner = owner;
}

BIN
exec.6 Normal file

Binary file not shown.

1499
exec.c Normal file

File diff suppressed because it is too large Load diff

BIN
file.6 Normal file

Binary file not shown.

310
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->nc)
error("internal error: fileinsert");
if(f->seq > 0)
fileuninsert(f, &f->delta, p0, ns);
bufinsert(f, 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->nc && p1<=f->nc))
error("internal error: filedelete");
if(f->seq > 0)
fileundelete(f, &f->delta, p0, p1);
bufdelete(f, 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, 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, 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, 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, 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);
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;
}

96
fns.h Normal file
View file

@ -0,0 +1,96 @@
#pragma varargck argpos warning 2
void warning(Mntdir*, char*, ...);
#define fbufalloc() emalloc(BUFSIZE)
#define fbuffree(x) free(x)
void plumblook(Plumbmsg*m);
void plumbshow(Plumbmsg*m);
void putsnarf(void);
void getsnarf(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);
char* getname(Text*, Text*, Rune*, int, int);
void scrsleep(uint);
void savemouse(Window*);
int restoremouse(Window*);
void clearmouse(void);
void allwindows(void(*)(Window*, void*), void*);
uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*);
void movetodel(Window*);
Window* errorwin(Mntdir*, int);
Window* errorwinforwin(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 isspace(Rune);
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 fsysincid(Mntdir*);
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);
void flushwarnings(void);
long nlcount(Text*, long, long, long*);
long nlcounttopos(Text*, long, long, long);
#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))

BIN
fsys.6 Normal file

Binary file not shown.

749
fsys.c Normal file
View file

@ -0,0 +1,749 @@
#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 cfd;
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 },
{ "log", QTFILE, Qlog, 0400 },
{ "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 },
{ "errors", QTFILE, QWerrors, 0200 },
{ "event", QTFILE, QWevent, 0600 },
{ "rdsel", QTFILE, QWrdsel, 0400 },
{ "wrsel", QTFILE, QWwrsel, 0200 },
{ "tag", QTAPPEND, QWtag, 0600|DMAPPEND },
{ "xdata", QTFILE, QWxdata, 0600 },
{ nil, }
};
typedef struct Mnt Mnt;
struct Mnt
{
QLock;
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";
int clockfd;
static int closing = 0;
int messagesize = Maxblock+IOHDRSZ; /* good start */
void fsysproc(void *);
void
fsysinit(void)
{
int p[2];
if(pipe(p) < 0)
error("can't create pipe");
cfd = p[0];
sfd = p[1];
fmtinstall('F', fcallfmt);
clockfd = open("/dev/time", OREAD|OCEXEC);
user = getuser();
proccreate(fsysproc, nil, STACK);
}
void
fsysproc(void *)
{
int n;
Xfid *x;
Fid *f;
Fcall t;
uchar *buf;
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) != n)
error("convert error in convM2S");
if(DEBUG)
fprint(2, "%F\n", &x->Fcall);
if(fcall[x->type] == nil)
x = respond(x, &t, "bad fcall type");
else{
switch(x->type){
case Tversion:
case Tauth:
case Tflush:
f = nil;
break;
case Tattach:
f = newfid(x->fid);
break;
default:
f = newfid(x->fid);
if(!f->busy){
x->f = f;
x = respond(x, &t, "fid not in use");
continue;
}
break;
}
x->f = f;
x = (*fcall[x->type])(x, f);
}
}
}
Mntdir*
fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
{
Mntdir *m;
int id;
qlock(&mnt);
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);
return m;
}
void
fsysincid(Mntdir *m)
{
qlock(&mnt);
m->ref++;
qunlock(&mnt);
}
void
fsysdelid(Mntdir *idm)
{
Mntdir *m, *prev;
int i;
char buf[64];
if(idm == nil)
return;
qlock(&mnt);
if(--idm->ref > 0){
qunlock(&mnt);
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);
return;
}
prev = m;
}
qunlock(&mnt);
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)
{
char buf[256];
Mntdir *m;
/* close server side so don't hang if acme is half-exited */
close(sfd);
m = fsysaddid(dir, ndir, incl, nincl);
sprint(buf, "%d", m->id);
if(mount(cfd, -1, "/mnt/acme", MREPL, buf) == -1){
fsysdelid(m);
return nil;
}
bind("/mnt/acme", "/mnt/wsys", MREPL);
if(bind("/mnt/acme", "/dev", MBEFORE) == -1){
fsysdelid(m);
return nil;
}
return m;
}
void
fsysclose(void)
{
closing = 1;
close(cfd);
close(sfd);
}
Xfid*
respond(Xfid *x, Fcall *t, char *err)
{
int n;
if(err){
t->type = Rerror;
t->ename = err;
}else
t->type = x->type+1;
t->fid = x->fid;
t->tag = x->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*)
{
Fcall t;
if(x->msize < 256)
return respond(x, &t, "version: message size too small");
messagesize = x->msize;
t.msize = messagesize;
t.version = "9P2000";
if(strncmp(x->version, "9P", 2) != 0)
t.version = "unknown";
return respond(x, &t, nil);
}
static
Xfid*
fsysauth(Xfid *x, Fid*)
{
Fcall t;
return respond(x, &t, "acme: authentication not required");
}
static
Xfid*
fsysflush(Xfid *x, Fid*)
{
sendp(x->c, xfidflush);
return nil;
}
static
Xfid*
fsysattach(Xfid *x, Fid *f)
{
Fcall t;
int id;
Mntdir *m;
if(strcmp(x->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->aname);
qlock(&mnt);
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);
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->fid != x->newfid){
nf = newfid(x->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);
f = nf; /* walk f */
}
t.nwqid = 0;
err = nil;
dir = nil;
id = WIN(f->qid);
q = f->qid;
if(x->nwname > 0){
for(i=0; i<x->nwname; i++){
if((q.type & QTDIR) == 0){
err = Enotdir;
break;
}
if(strcmp(x->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->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->wname[i]);
qlock(&row);
w = lookid(id, FALSE);
if(w == nil){
qunlock(&row);
break;
}
incref(w); /* we'll drop reference at end if there's an error */
path = Qdir;
type = QTDIR;
qunlock(&row);
dir = dirtabw;
goto Accept;
Regular:
// if(FILE(f->qid) == Qacme) /* empty directory */
// break;
if(strcmp(x->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);
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->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->nwname){
if(nf){
nf->busy = FALSE;
fsysdelid(nf->mntdir);
}
}else if(t.nwqid == x->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->mode &= ~(OTRUNC|OCEXEC);
/* can't execute or remove anything */
if(x->mode==OEXEC || (x->mode&ORCLOSE))
goto Deny;
switch(x->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*)
{
Fcall t;
return respond(x, &t, Eperm);
}
static
int
idcmp(void *a, 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->offset;
e = x->offset+x->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->count-n, clock);
if(len <= BIT16SZ)
break;
if(i >= o)
n += len;
d++;
}
if(id == 0){
qlock(&row);
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);
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->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*)
{
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*)
{
Fcall t;
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*)
{
Fcall t;
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()
{
char buf[32];
buf[0] = '\0';
pread(clockfd, buf, sizeof buf, 0);
return atoi(buf);
}
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);
}

BIN
logf.6 Normal file

Binary file not shown.

202
logf.c Normal file
View file

@ -0,0 +1,202 @@
#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 <libsec.h>
#include "dat.h"
#include "fns.h"
// State for global log file.
typedef struct Log Log;
struct Log
{
QLock lk;
Rendez r;
vlong start; // msg[0] corresponds to 'start' in the global sequence of events
// queued events (nev=entries in ev, mev=capacity of p)
char **ev;
int nev;
int mev;
// open acme/put files that need to read events
Fid **f;
int nf;
int mf;
// active (blocked) reads waiting for events
Xfid **read;
int nread;
int mread;
};
static Log eventlog;
void
xfidlogopen(Xfid *x)
{
qlock(&eventlog.lk);
if(eventlog.nf >= eventlog.mf) {
eventlog.mf = eventlog.mf*2;
if(eventlog.mf == 0)
eventlog.mf = 8;
eventlog.f = erealloc(eventlog.f, eventlog.mf*sizeof eventlog.f[0]);
}
eventlog.f[eventlog.nf++] = x->f;
x->f->logoff = eventlog.start + eventlog.nev;
qunlock(&eventlog.lk);
}
void
xfidlogclose(Xfid *x)
{
int i;
qlock(&eventlog.lk);
for(i=0; i<eventlog.nf; i++) {
if(eventlog.f[i] == x->f) {
eventlog.f[i] = eventlog.f[--eventlog.nf];
break;
}
}
qunlock(&eventlog.lk);
}
void
xfidlogread(Xfid *x)
{
char *p;
int i;
Fcall fc;
qlock(&eventlog.lk);
if(eventlog.nread >= eventlog.mread) {
eventlog.mread = eventlog.mread*2;
if(eventlog.mread == 0)
eventlog.mread = 8;
eventlog.read = erealloc(eventlog.read, eventlog.mread*sizeof eventlog.read[0]);
}
eventlog.read[eventlog.nread++] = x;
if(eventlog.r.l == nil)
eventlog.r.l = &eventlog.lk;
x->flushed = FALSE;
while(x->f->logoff >= eventlog.start+eventlog.nev && !x->flushed)
rsleep(&eventlog.r);
for(i=0; i<eventlog.nread; i++) {
if(eventlog.read[i] == x) {
eventlog.read[i] = eventlog.read[--eventlog.nread];
break;
}
}
if(x->flushed) {
qunlock(&eventlog.lk);
return;
}
i = x->f->logoff - eventlog.start;
p = estrdup(eventlog.ev[i]);
x->f->logoff++;
qunlock(&eventlog.lk);
fc.data = p;
fc.count = strlen(p);
respond(x, &fc, nil);
free(p);
}
void
xfidlogflush(Xfid *x)
{
int i;
Xfid *rx;
qlock(&eventlog.lk);
for(i=0; i<eventlog.nread; i++) {
rx = eventlog.read[i];
if(rx->tag == x->oldtag) {
rx->flushed = TRUE;
rwakeupall(&eventlog.r);
}
}
qunlock(&eventlog.lk);
}
/*
* add a log entry for op on w.
* expected calls:
*
* op == "new" for each new window
* - caller of coladd or makenewwindow responsible for calling
* xfidlog after setting window name
* - exception: zerox
*
* op == "zerox" for new window created via zerox
* - called from zeroxx
*
* op == "get" for Get executed on window
* - called from get
*
* op == "put" for Put executed on window
* - called from put
*
* op == "del" for deleted window
* - called from winclose
*
* op == "focus" for window focus change
* - called from mousethread
*/
void
xfidlog(Window *w, char *op)
{
int i, n;
vlong min;
File *f;
char *name;
qlock(&eventlog.lk);
if(eventlog.nev >= eventlog.mev) {
// Remove and free any entries that all readers have read.
min = eventlog.start + eventlog.nev;
for(i=0; i<eventlog.nf; i++) {
if(min > eventlog.f[i]->logoff)
min = eventlog.f[i]->logoff;
}
if(min > eventlog.start) {
n = min - eventlog.start;
for(i=0; i<n; i++)
free(eventlog.ev[i]);
eventlog.nev -= n;
eventlog.start += n;
memmove(eventlog.ev, eventlog.ev+n, eventlog.nev*sizeof eventlog.ev[0]);
}
// Otherwise grow.
if(eventlog.nev >= eventlog.mev) {
eventlog.mev = eventlog.mev*2;
if(eventlog.mev == 0)
eventlog.mev = 8;
eventlog.ev = erealloc(eventlog.ev, eventlog.mev*sizeof eventlog.ev[0]);
}
}
f = w->body.file;
name = runetobyte(f->name, f->nname);
if(name == nil)
name = estrdup("");
eventlog.ev[eventlog.nev++] = smprint("%d %s %s\n", w->id, op, name);
free(name);
if(eventlog.r.l == nil)
eventlog.r.l = &eventlog.lk;
rwakeupall(&eventlog.r);
qunlock(&eventlog.lk);
}

BIN
look.6 Normal file

Binary file not shown.

737
look.c Normal file
View file

@ -0,0 +1,737 @@
#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"
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.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, 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.at->file, e.a0, r+e.nname+1, e.a1-e.a0);
}
}else{
n = e.q1 - e.q0;
r = runemalloc(n);
bufread(t->file, 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->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, 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.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, e.q0, r, n);
if(search(ct, r, n) && e.jump)
moveto(mousectl, addpt(frptofchar(ct, ct->p0), Pt(4, ct->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>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.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.ar = bytetorune(addr, &e.a1);
e.agetc = plumbgetc;
}
openfile(nil, &e);
free(e.name);
free(e.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->nc, w->tag.file->nc);
xfidlog(w, "new");
}
int
search(Text *ct, Rune *r, uint n)
{
uint q, nb, maxn;
int around;
Rune *s, *b, *c;
if(n==0 || n>ct->file->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->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->nc-q){
nb = ct->file->nc-q;
if(nb >= maxn)
nb = maxn-1;
bufread(ct->file, 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;
}
--nb;
b++;
q++;
if(around && q>=ct->q1)
break;
}
fbuffree(s);
return FALSE;
}
int
isfilec(Rune r)
{
if(isalnum(r))
return TRUE;
if(runestrchr(L".-+/:@", r))
return TRUE;
return FALSE;
}
/* Runestr wrapper for cleanname */
Runestr
cleanrname(Runestr rs)
{
char *s;
int nb, nulls;
s = runetobyte(rs.r, rs.nr);
cleanname(s);
cvttorunes(s, strlen(s), rs.r, &nb, &rs.nr, &nulls);
free(s);
return rs;
}
Runestr
includefile(Rune *dir, Rune *file, int nfile)
{
int m, n;
char *a;
Rune *r;
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, L"/", 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];
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(L"/sys/include", 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->nc;
if(nt == 0)
goto Rescue;
if(n>=1 && r[0]=='/')
goto Rescue;
b = runemalloc(nt+n+1);
bufread(t->w->tag.file, 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->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->nc-1 && isaddrc(textreadc(t, colon+1))){
q1 = colon+1;
while(q1<t->file->nc && isaddrc(textreadc(t, q1)))
q1++;
}
}
if(q1 > q0)
if(colon >= 0){ /* stop at white space */
for(amax=colon+1; amax<t->file->nc; amax++)
if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n')
break;
}else
amax = t->file->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, 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->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->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->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->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->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->nc, t->w->tag.file->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);
}
for(i=0; i < NINDENT; i++)
w->indent[i] = ow->indent[i];
}else
for(i=0; i < NINDENT; i++)
w->indent[i] = globalindent[i];
xfidlog(w, "new");
}
if(e->a1 == e->a0)
eval = FALSE;
else{
eval = TRUE;
r = address(nil, t, (Range){-1, -1}, (Range){t->q0, t->q1}, e->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, t->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;
Window *w;
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) {
w = coladd(et->col, nil, nil, -1);
winsettag(w);
xfidlog(w, "new");
}
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
mkfile Normal file
View file

@ -0,0 +1,41 @@
</$objtype/mkfile
BIN=$home/bin/$objtype
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\
logf.$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\
</sys/src/cmd/mkone
$O.out: /$objtype/lib/libframe.a /$objtype/lib/libdraw.a /$objtype/lib/libthread.a
edit.$O ecmd.$O elog.$O: edit.h
syms:V:
$CC -a acme.c > syms
for(i in ????.c) $CC -aa $i >> syms

BIN
regx.6 Normal file

Binary file not shown.

839
regx.c Normal file
View file

@ -0,0 +1,839 @@
#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; /* <= Runemax+1 ==> literal, otherwise action */
union {
int sid;
int subid;
int class;
Inst *other;
Inst *right;
};
union{
Inst *left;
Inst *next;
};
};
#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 127
Ilist *tl, *nl; /* This list, next list */
Ilist list[2][NLIST+1]; /* +1 for trailing null */
static Rangeset sempty;
/*
* Actions and Tokens
*
* 0x100xx are operators, value == precedence
* 0x200xx are tokens, i.e. operands for operators
*/
enum {
OPERATOR = Runemask+1, /* Bitmask of all operators */
START = OPERATOR, /* Start, used for marker on stack */
RBRA, /* Right bracket, ) */
LBRA, /* Left bracket, ( */
OR, /* Alternation, | */
CAT, /* Concatentation, implicit operator */
STAR, /* Closure, * */
PLUS, /* a+ == aa* */
QUEST, /* a? == a|nothing, i.e. 0 or 1 a's */
ANY = OPERATOR<<1, /* Any character but newline, . */
NOP, /* No operation, internal use only */
BOL, /* Beginning of line, ^ */
EOL, /* End of line, $ */
CCLASS, /* Character class, [] */
NCCLASS, /* Negated character class, [^] */
END, /* Terminate: match found */
ISATOR = OPERATOR,
ISAND = OPERATOR<<1,
};
/*
* 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;
int 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->left = nil;
progp->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->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->subid = *subidp;
op1->last->next = inst2;
inst1 = newinst(LBRA);
inst1->subid = *subidp;
inst1->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->next = inst2;
op1->last->next = inst2;
inst1 = newinst(OR);
inst1->right = op1->first;
inst1->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->next = op2->first;
pushand(op1->first, op2->last);
break;
case STAR:
op2 = popand('*');
inst1 = newinst(OR);
op2->last->next = inst1;
inst1->right = op2->first;
pushand(inst1, inst1);
break;
case PLUS:
op2 = popand('+');
inst1 = newinst(OR);
op2->last->next = inst1;
inst1->right = op2->first;
pushand(op2->first, inst1);
break;
case QUEST:
op2 = popand('?');
inst1 = newinst(OR);
inst2 = newinst(NOP);
inst1->left = inst2;
inst1->right = op2->first;
op2->last->next = inst2;
pushand(inst1, inst2);
break;
}
}
}
void
optimize(Inst *start)
{
Inst *inst, *target;
for(inst=start; inst->type!=END; inst++){
target = inst->next;
while(target->type == NOP)
target = target->next;
inst->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++|(Runemask+1);
}
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] = Runemax;
classp[n+1] = c1 & Runemask;
classp[n+2] = c2 & Runemask;
n += 3;
}else
classp[n++] = c1 & Runemask;
}
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 == Runemax){
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.
*/
int
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 0; /* It's already there */
}
}
p->inst = inst;
p->se= *sep;
(p+1)->inst = nil;
return 1;
}
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->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 */
sempty.r[0].q0 = p;
if(addinst(tl, startinst, &sempty))
if(++ntl >= NLIST){
Overflow:
warning(nil, "regexp list overflow\n");
sel.r[0].q0 = -1;
goto Return;
}
}
/* 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(addinst(nl, inst->next, &tlp->se))
if(++nnl >= NLIST)
goto Overflow;
}
break;
case LBRA:
if(inst->subid>=0)
tlp->se.r[inst->subid].q0 = p;
inst = inst->next;
goto Switchstmt;
case RBRA:
if(inst->subid>=0)
tlp->se.r[inst->subid].q1 = p;
inst = inst->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->next;
goto Switchstmt;
}
break;
case EOL:
if(c == '\n')
goto Step;
break;
case CCLASS:
if(c>=0 && classmatch(inst->class, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>=0 && classmatch(inst->class, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(addinst(tlp, inst->right, &tlp->se))
if(++ntl >= NLIST)
goto Overflow;
/* efficiency: advance and re-evaluate */
inst = inst->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->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 */
/* the minus is so the optimizations in addinst work */
sempty.r[0].q0 = -p;
if(addinst(tl, bstartinst, &sempty))
if(++ntl >= NLIST){
Overflow:
warning(nil, "regexp list overflow\n");
sel.r[0].q0 = -1;
goto Return;
}
}
/* 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(addinst(nl, inst->next, &tlp->se))
if(++nnl >= NLIST)
goto Overflow;
}
break;
case LBRA:
if(inst->subid>=0)
tlp->se.r[inst->subid].q0 = p;
inst = inst->next;
goto Switchstmt;
case RBRA:
if(inst->subid >= 0)
tlp->se.r[inst->subid].q1 = p;
inst = inst->next;
goto Switchstmt;
case ANY:
if(c != '\n')
goto Addinst;
break;
case BOL:
if(c=='\n' || p==0){
Step:
inst = inst->next;
goto Switchstmt;
}
break;
case EOL:
if(p<t->file->nc && textreadc(t, p)=='\n')
goto Step;
break;
case CCLASS:
if(c>0 && classmatch(inst->class, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>0 && classmatch(inst->class, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(addinst(tl, inst->right, &tlp->se))
if(++ntl >= NLIST)
goto Overflow;
/* efficiency: advance and re-evaluate */
inst = inst->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;
}
}

BIN
rows.6 Normal file

Binary file not shown.

736
rows.c Normal file
View file

@ -0,0 +1,736 @@
#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"
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, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
textsetselect(t, t->file->nc, t->file->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.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);
}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, TRUE);
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)
{
Rectangle r;
int i, b, x;
Point p, op;
Column *d;
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:
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;
}
if(i == 0)
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);
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);
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, start, 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->nc, fontname);
}
free(a);
winctlprint(w, buf, 0);
Bwrite(b, buf, strlen(buf));
m = min(RBUFSIZE, w->tag.file->nc);
bufread(w->tag.file, 0, r, m);
n = 0;
while(n<m) {
start = n;
while(n<m && r[n]!='\n')
n++;
Bprint(b, "%.*S", n-start, r+start);
if(n<m) {
Bputc(b, 0xff); // \n in tag becomes 0xff byte (invalid UTF)
n++;
}
}
Bprint(b, "\n");
if(dumped){
q0 = 0;
q1 = t->file->nc;
while(q0 < q1){
n = q1 - q0;
if(n > BUFSIZE/UTFmax)
n = BUFSIZE/UTFmax;
bufread(t->file, 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){
free(fontnames[i]);
fontnames[i] = estrdup(l);
}
}
Return:
Bterm(b);
}
int
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, 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;
/* convert 0xff in multiline tag back to \n */
for(i=0; l[i]!=0; i++)
if((uchar)l[i] == 0xff)
l[i] = '\n';
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->nc, r+n+1, nr-(n+1), TRUE);
if(ndumped >= 0){
/* simplest thing is to put it in a file and load that */
sprint(buf, "/tmp/d%d.%.4sacme", getpid(), user);
fd = create(buf, OWRITE|ORCLOSE, 0600);
if(fd < 0){
free(r);
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){
free(r);
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);
}
free(r);
if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1)
q0 = q1 = 0;
textshow(&w->body, q0, q1, 1);
w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
xfidlog(w, "new");
}
Bterm(b);
fbuffree(buf);
return TRUE;
Rescue2:
warning(nil, "bad load file %s:%d\n", file, line);
Bterm(b);
Rescue1:
fbuffree(buf);
return FALSE;
}
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);
}
}

BIN
scrl.6 Normal file

Binary file not shown.

157
scrl.c Normal file
View file

@ -0,0 +1,157 @@
#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, 3*Dy(screen->r)), 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->nchars, t->file->nc);
if(!eqrect(r2, t->lastsr)){
t->lastsr = r2;
/* move r1, r2 to (0,0) to avoid clipping */
r2 = rectsubpt(r2, r1.min);
r1 = rectsubpt(r1, r1.min);
draw(b, r1, t->cols[BORD], nil, ZP);
r2.max.x++;
draw(b, r2, t->cols[TEXT], nil, ZP);
r2.min.x = r2.max.x-1;
draw(b, r2, t->cols[BORD], nil, ZP);
draw(t->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->Mouse;
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 y, my, h, first;
s = insetrect(t->scrollr, 1);
h = s.max.y-s.min.y;
oldp0 = ~0;
first = TRUE;
do{
flushimage(display, 1);
my = mouse->xy.y;
if(my < s.min.y)
my = s.min.y;
if(my >= s.max.y)
my = s.max.y;
if(but == 2){
y = my;
p0 = (vlong)t->file->nc*(y-s.min.y)/h;
if(p0 >= t->q1)
p0 = textbacknl(t, p0, 2);
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->font->height);
else
p0 = t->org+frcharofpt(t, 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->Mouse);
first = FALSE;
}
scrsleep(80);
}while(mouse->buttons & (1<<(but-1)));
while(mouse->buttons)
readmouse(mousectl);
}

BIN
text.6 Normal file

Binary file not shown.

1484
text.c Normal file

File diff suppressed because it is too large Load diff

BIN
time.6 Normal file

Binary file not shown.

120
time.c Normal file
View file

@ -0,0 +1,120 @@
#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*)
{
int i, nt, na, dt, del;
Timer **t, *x;
uint old, new;
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;
}

BIN
util.6 Normal file

Binary file not shown.

485
util.c Normal file
View file

@ -0,0 +1,485 @@
#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);
remove(acmeerrorfile);
abort();
}
Window*
errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
{
Window *w;
Rune *r;
int i, n;
r = runemalloc(ndir+8);
if(n = ndir){ /* assign = */
runemove(r, dir, ndir);
r[n++] = L'/';
}
runemove(r+n, L"+Errors", 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);
xfidlog(w, "new");
}
free(r);
for(i=nincl; --i>=0; ){
n = runestrlen(incl[i]);
r = runemalloc(n);
runemove(r, incl[i], n);
winaddincl(w, r, n);
}
for(i=0; i<NINDENT; i++)
w->indent[i] = globalindent[i];
return w;
}
/* make new window, if necessary; return with it locked */
Window*
errorwin(Mntdir *md, int owner)
{
Window *w;
for(;;){
if(md == nil)
w = errorwin1(nil, 0, nil, 0);
else
w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
winlock(w, owner);
if(w->col != nil)
break;
/* window was deleted too fast */
winunlock(w);
}
return w;
}
/*
* Incoming window should be locked.
* It will be unlocked and returned window
* will be locked in its place.
*/
Window*
errorwinforwin(Window *w)
{
int i, n, nincl, owner;
Rune **incl;
Runestr dir;
Text *t;
t = &w->body;
dir = dirname(t, nil, 0);
if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
free(dir.r);
dir.r = nil;
dir.nr = 0;
}
incl = nil;
nincl = w->nincl;
if(nincl > 0){
incl = emalloc(nincl*sizeof(Rune*));
for(i=0; i<nincl; i++){
n = runestrlen(w->incl[i]);
incl[i] = runemalloc(n+1);
runemove(incl[i], w->incl[i], n);
}
}
owner = w->owner;
winunlock(w);
for(;;){
w = errorwin1(dir.r, dir.nr, incl, nincl);
winlock(w, owner);
if(w->col != nil)
break;
/* window deleted too fast */
winunlock(w);
}
return w;
}
typedef struct Warning Warning;
struct Warning{
Mntdir *md;
Buffer buf;
Warning *next;
};
static Warning *warnings;
static
void
addwarningtext(Mntdir *md, Rune *r, int nr)
{
Warning *warn;
for(warn = warnings; warn; warn=warn->next){
if(warn->md == md){
bufinsert(&warn->buf, warn->buf.nc, r, nr);
return;
}
}
warn = emalloc(sizeof(Warning));
warn->next = warnings;
warn->md = md;
if(md)
fsysincid(md);
warnings = warn;
bufinsert(&warn->buf, 0, r, nr);
nbsendp(cwarn, 0);
}
/* called while row is locked */
void
flushwarnings(void)
{
Warning *warn, *next;
Window *w;
Text *t;
int owner, nr, q0, n;
Rune *r;
for(warn=warnings; warn; warn=next) {
w = errorwin(warn->md, 'E');
t = &w->body;
owner = w->owner;
if(owner == 0)
w->owner = 'E';
wincommit(w, t);
/*
* Most commands don't generate much output. For instance,
* Edit ,>cat goes through /dev/cons and is already in blocks
* because of the i/o system, but a few can. Edit ,p will
* put the entire result into a single hunk. So it's worth doing
* this in blocks (and putting the text in a buffer in the first
* place), to avoid a big memory footprint.
*/
r = fbufalloc();
q0 = t->file->nc;
for(n = 0; n < warn->buf.nc; n += nr){
nr = warn->buf.nc - n;
if(nr > RBUFSIZE)
nr = RBUFSIZE;
bufread(&warn->buf, n, r, nr);
textbsinsert(t, t->file->nc, r, nr, TRUE, &nr);
}
textshow(t, q0, t->file->nc, 1);
free(r);
winsettag(t->w);
textscrdraw(t);
w->owner = owner;
w->dirty = FALSE;
winunlock(w);
bufclose(&warn->buf);
next = warn->next;
if(warn->md)
fsysdelid(warn->md);
free(warn);
}
warnings = nil;
}
void
warning(Mntdir *md, char *s, ...)
{
Rune *r;
va_list arg;
va_start(arg, s);
r = runevsmprint(s, arg);
va_end(arg);
if(r == nil)
error("runevsmprint failed");
addwarningtext(md, r, runestrlen(r));
free(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
isspace(Rune c)
{
return c == 0 || c == ' ' || c == '\t' ||
c == '\n' || c == '\r' || c == '\v';
}
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.
*
* Treat 0xA0 (non-breaking space) as a special alphanumeric
* character [sape]
*/
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->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;
}
int
restoremouse(Window *w)
{
int did;
did = 0;
if(mousew!=nil && mousew==w) {
moveto(mousectl, prevmouse);
did = 1;
}
mousew = nil;
return did;
}
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)
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)
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.maxlines >= bigw->body.maxlines)
bigw = w;
if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
emptyw = w;
}
emptyb = &emptyw->body;
el = emptyb->maxlines-emptyb->nlines;
/* if empty space is big, use it */
if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
y = emptyb->r.min.y+emptyb->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.maxlines < 2)
colgrow(w->col, w, 1);
return w;
}

BIN
wind.6 Normal file

Binary file not shown.

698
wind.c Normal file
View file

@ -0,0 +1,698 @@
#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, i;
w->tag.w = w;
w->taglines = 1;
w->tagexpand = TRUE;
w->body.w = w;
w->id = ++winid;
incref(w);
if(globalincref)
incref(w);
w->ctlfid = ~0;
w->utflastqid = -1;
r1 = r;
r1.max.y = r1.min.y + font->height;
w->tagtop = r;
w->tagtop.max.y = r.min.y + font->height;
incref(&reffont);
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->nc, TRUE);
nc = clone->tag.file->nc;
rp = runemalloc(nc);
bufread(clone->tag.file, 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 += w->taglines*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.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.maxlines;
for(i=0; i<NINDENT; i++)
w->indent[i] = globalindent[i];
if(clone){
w->dirty = clone->dirty;
for(i=0; i<NINDENT; i++)
w->indent[i] = clone->indent[i];
textsetselect(&w->body, clone->body.q0, clone->body.q1);
winsettag(w);
}
}
int
tagrunepos(Window *w, Rune *s)
{
int n;
Rune *r, *rr;
if(s == nil)
return -1;
n = w->tag.file->nc;
r = runemalloc(n+1);
bufread(w->tag.file, 0, r, n);
r[n] = L'\0';
rr = runestrstr(r, s);
if(rr == nil || rr == r)
return -1;
return rr - r;
}
void
movetodel(Window *w)
{
int n;
n = tagrunepos(w, delcmd);
free(delcmd);
delcmd = nil;
if(n < 0)
return;
moveto(mousectl, addpt(frptofchar(&w->tag, n), Pt(4, w->tag.font->height-4)));
}
/*
* Compute number of tag lines required
* to display entire tag text.
*/
int
wintaglines(Window *w, Rectangle r)
{
int n;
Rune rune;
Point p;
if(!w->tagexpand && !w->showdel)
return 1;
w->showdel = FALSE;
textresize(&w->tag, r, TRUE);
w->tagsafe = FALSE;
if(!w->tagexpand) {
/* use just as many lines as needed to show the Del */
n = tagrunepos(w, delcmd);
if(n < 0)
return 1;
p = subpt(frptofchar(&w->tag, n), w->tag.r.min);
return 1 + p.y / w->tag.font->height;
}
/* can't use more than we have */
if(w->tag.nlines >= w->tag.maxlines)
return w->tag.maxlines;
/* if tag ends with \n, include empty line at end for typing */
n = w->tag.nlines;
if(w->tag.file->nc > 0){
bufread(w->tag.file, w->tag.file->nc-1, &rune, 1);
if(rune == '\n')
n++;
}
if(n == 0)
n = 1;
return n;
}
int
winresize(Window *w, Rectangle r, int safe, int fillfringe)
{
int oy, y, mouseintag, mouseinbody;
Point p;
Rectangle r1;
Image *b;
Rectangle br;
mouseintag = ptinrect(mouse->xy, w->tag.all);
mouseinbody = ptinrect(mouse->xy, w->body.all);
/* Tagtop is first line of tag. */
w->tagtop = r;
w->tagtop.max.y = r.min.y+font->height;
r1 = r;
r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
/* If needed, recompute number of lines in tag. */
if(!safe || !w->tagsafe || !eqrect(w->tag.r, r1)){
w->taglines = wintaglines(w, r);
r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
}
if(Dy(r1) < font->height)
r1.max.y = r1.min.y+font->height;
/* If needed, resize & redraw tag. */
y = r1.max.y;
if(!safe || !w->tagsafe || !eqrect(w->tag.r, r1)){
textresize(&w->tag, r1, TRUE);
y = w->tag.r.max.y;
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);
w->tagsafe = TRUE;
/* If mouse is in tag, pull up as tag closes. */
if(mouseintag && !ptinrect(mouse->xy, w->tag.all)){
p = mouse->xy;
p.y = w->tag.all.max.y-3;
moveto(mousectl, p);
}
/* If mouse is in body, push down as tag expands. */
if(mouseinbody && ptinrect(mouse->xy, w->tag.all)){
p = mouse->xy;
p.y = w->tag.all.max.y+3;
moveto(mousectl, p);
}
}
/* If needed, resize & redraw body. */
r1 = r;
r1.min.y = y;
if(!safe || !eqrect(w->body.r, r1)){
oy = y;
if(y+1+w->body.font->height <= r.max.y){ /* room for one line */
r1.min.y = y;
r1.max.y = y+1;
draw(screen, r1, tagcols[BORD], nil, ZP);
y++;
r1.min.y = min(y, r.max.y);
r1.max.y = r.max.y;
}else{
r1.min.y = y;
r1.max.y = y;
}
y = textresize(&w->body, r1, fillfringe);
w->r = r;
w->r.max.y = y;
textscrdraw(&w->body);
w->body.all.min.y = oy;
}
w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
return w->r.max.y;
}
void
winlock1(Window *w, int owner)
{
incref(w);
qlock(w);
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;
/*
* subtle: loop runs backwards to avoid tripping over
* winclose indirectly editing f->text and freeing f
* on the last iteration of the loop.
*/
f = w->body.file;
for(i=f->ntext-1; i>=0; i--){
w = f->text[i]->w;
w->owner = 0;
qunlock(w);
winclose(w);
}
}
void
winmousebut(Window *w)
{
moveto(mousectl, addpt(w->tag.scrollr.min, divpt(Pt(Dx(w->tag.scrollr), font->height), 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) == 0){
xfidlog(w, "del");
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.p0+v->body.org;
v->body.q1 = v->body.p1+v->body.org;
}
}
winsettag(w);
}
void
winsetname(Window *w, Rune *name, int n)
{
Text *t;
Window *v;
int i;
t = &w->body;
if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
return;
w->isscratch = FALSE;
if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6))
w->isscratch = TRUE;
else if(n>=7 && runeeq(L"+Errors", 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 == Tag)
w->tagsafe = FALSE;
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->nc;
r = runemalloc(n);
bufread(w->tag.file, 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;
/* 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->nc+1);
bufread(w->tag.file, 0, old, w->tag.file->nc);
old[w->tag.file->nc] = '\0';
for(i=0; i<w->tag.file->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->nc+1);
bufread(w->tag.file, 0, old, w->tag.file->nc);
old[w->tag.file->nc] = '\0';
w->tagsafe = FALSE;
}
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, L" Del Snarf", 10);
i += 10;
if(w->filemenu){
if(w->body.file->delta.nc>0 || w->body.ncache){
runemove(new+i, L" Undo", 5);
i += 5;
}
if(w->body.file->epsilon.nc > 0){
runemove(new+i, L" Redo", 5);
i += 5;
}
dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
if(!w->isdir && dirty){
runemove(new+i, L" Put", 4);
i += 4;
}
}
if(w->isdir){
runemove(new+i, L" Get", 4);
i += 4;
}
runemove(new+i, L" |", 2);
i += 2;
r = runestrchr(old, '|');
if(r)
k = r-old+1;
else{
k = w->tag.file->nc;
if(w->body.file->seq == 0){
runemove(new+i, L" Look ", 6);
i += 6;
}
}
new[i] = 0;
/* replace tag if the new one is different */
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;
}
}
w->tagsafe = FALSE;
}
free(old);
free(new);
w->tag.file->mod = FALSE;
n = w->tag.file->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);
if(w->tagsafe == FALSE)
winresize(w, w->r, TRUE, TRUE);
}
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.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->nc);
bufread(w->tag.file, 0, r, w->tag.file->nc);
for(i=0; i<w->tag.file->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;
}
if((d->qid.type&QTDIR) == 0){
free(d);
warning(nil, "%s: not a directory\n", a);
free(r);
free(a);
return;
}
free(a);
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)
{
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->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;
}
char*
winctlprint(Window *w, char *buf, int fonts)
{
sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc,
w->body.file->nc, w->isdir, w->dirty);
if(fonts)
return smprint("%s%11d %q %11d " , buf, Dx(w->body.r),
w->body.reffont->f->name, w->body.maxtab);
return buf;
}
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 = erealloc(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);
}
}

BIN
xfid.6 Normal file

Binary file not shown.

1100
xfid.c Normal file

File diff suppressed because it is too large Load diff