q
This commit is contained in:
parent
bf427430d3
commit
f4b93c1b79
48 changed files with 0 additions and 14583 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
6.out
|
|
||||||
*.6
|
|
||||||
BIN
6.out
BIN
6.out
Binary file not shown.
BIN
acme.6
BIN
acme.6
Binary file not shown.
955
acme.c
955
acme.c
|
|
@ -1,955 +0,0 @@
|
||||||
#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 = ±
|
|
||||||
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
BIN
addr.6
Binary file not shown.
291
addr.c
291
addr.c
|
|
@ -1,291 +0,0 @@
|
||||||
#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
BIN
buff.6
Binary file not shown.
322
buff.c
322
buff.c
|
|
@ -1,322 +0,0 @@
|
||||||
#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
BIN
cols.6
Binary file not shown.
551
cols.c
551
cols.c
|
|
@ -1,551 +0,0 @@
|
||||||
#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
570
dat.h
|
|
@ -1,570 +0,0 @@
|
||||||
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
BIN
disk.6
Binary file not shown.
142
disk.c
142
disk.c
|
|
@ -1,142 +0,0 @@
|
||||||
#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
BIN
ecmd.6
Binary file not shown.
BIN
edit.6
BIN
edit.6
Binary file not shown.
679
edit.c
679
edit.c
|
|
@ -1,679 +0,0 @@
|
||||||
#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
99
edit.h
|
|
@ -1,99 +0,0 @@
|
||||||
#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
BIN
elog.6
Binary file not shown.
353
elog.c
353
elog.c
|
|
@ -1,353 +0,0 @@
|
||||||
#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
BIN
exec.6
Binary file not shown.
BIN
file.6
BIN
file.6
Binary file not shown.
310
file.c
310
file.c
|
|
@ -1,310 +0,0 @@
|
||||||
#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
96
fns.h
|
|
@ -1,96 +0,0 @@
|
||||||
#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
BIN
fsys.6
Binary file not shown.
749
fsys.c
749
fsys.c
|
|
@ -1,749 +0,0 @@
|
||||||
#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
BIN
logf.6
Binary file not shown.
202
logf.c
202
logf.c
|
|
@ -1,202 +0,0 @@
|
||||||
#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
BIN
look.6
Binary file not shown.
737
look.c
737
look.c
|
|
@ -1,737 +0,0 @@
|
||||||
#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
41
mkfile
|
|
@ -1,41 +0,0 @@
|
||||||
</$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
BIN
regx.6
Binary file not shown.
839
regx.c
839
regx.c
|
|
@ -1,839 +0,0 @@
|
||||||
#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
BIN
rows.6
Binary file not shown.
736
rows.c
736
rows.c
|
|
@ -1,736 +0,0 @@
|
||||||
#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
BIN
scrl.6
Binary file not shown.
157
scrl.c
157
scrl.c
|
|
@ -1,157 +0,0 @@
|
||||||
#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
BIN
text.6
Binary file not shown.
BIN
time.6
BIN
time.6
Binary file not shown.
120
time.c
120
time.c
|
|
@ -1,120 +0,0 @@
|
||||||
#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
BIN
util.6
Binary file not shown.
485
util.c
485
util.c
|
|
@ -1,485 +0,0 @@
|
||||||
#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
BIN
wind.6
Binary file not shown.
698
wind.c
698
wind.c
|
|
@ -1,698 +0,0 @@
|
||||||
#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
BIN
xfid.6
Binary file not shown.
Loading…
Add table
Reference in a new issue