From: Fco.J.Ballesteros <nemo@plan9.escet.urjc.es>
To: 9fans@cse.psu.edu
Subject: [9fans] acme: hiding certain files in dir windows
Date: Mon, 5 Nov 2001 09:07:34 +0100 [thread overview]
Message-ID: <20011105080740.64503199B5@mail.cse.psu.edu> (raw)
[-- Attachment #1: Type: text/plain, Size: 566 bytes --]
Hi,
I added a Ignore command to acme to let it ignore
files matching a regexp when listing dir contents. I use
it to keep the window of my kernel source dir with just
the source files;
but I think it's also marginally useful to
hide ugly dot files on windows browsing unix directories.
The usage is simple:
Ignore regexp: ignore matching files
Ignore: don't ignore anything
regexps are those of regexp(6).
I attach the changed files here.
By the way, why doesn't acme use regexp(2)?
Shouldn't we converge to regexp(6)?
Just wondering...
[-- Attachment #2: acme.c --]
[-- Type: text/plain, Size: 19396 bytes --]
#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 <auth.h>
#include <fcall.h>
#include <plumb.h>
#include <regexp.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 plumbproc(void*);
Reffont **fontcache;
int nfontcache;
char wdir[512] = ".";
Reffont *reffonts[2];
int snarffd = -1;
int mainpid;
int plumbsendfd;
int plumbeditfd;
enum{
NSnarf = 1000 /* less than 1024, I/O buffer size */
};
Rune snarfrune[NSnarf+1];
char *fontnames[2] =
{
"/lib/font/bit/lucidasans/euro.8.font",
"/lib/font/bit/lucm/unicode.9.font",
};
Reprog* ignoreregx;
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;
static void *arg[1];
rfork(RFENVG|RFNAMEG);
ncol = -1;
loadfile = nil;
ARGBEGIN{
case 'b':
bartflag = TRUE;
break;
case 'c':
p = ARGF();
if(p == nil)
goto Usage;
ncol = atoi(p);
if(ncol <= 0)
goto Usage;
break;
case 'i':
p = ARGF();
if (p == nil)
goto Usage;
ignoreregx = regcomp(p);
break;
case 'f':
fontnames[0] = ARGF();
if(fontnames[0] == nil)
goto Usage;
break;
case 'F':
fontnames[1] = ARGF();
if(fontnames[1] == nil)
goto Usage;
break;
case 'l':
loadfile = ARGF();
if(loadfile == nil)
goto Usage;
break;
default:
Usage:
fprint(2, "usage: acme -c ncol -f fontname -F fixedwidthfontname -i ignore -l loadfile\n");
exits("usage");
}ARGEND
cputype = getenv("cputype");
objtype = getenv("objtype");
home = getenv("home");
p = getenv("tabstop");
if(p != nil){
maxtab = strtoul(p, nil, 0);
free(p);
}
if(maxtab == 0)
maxtab = 4;
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, nil, "acme", nil, Refnone) < 0){
fprint(2, "acme: can't open display: %r\n");
exits("font");
}
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);
cerr = chancreate(sizeof(char*), 0);
cedit = chancreate(sizeof(int), 0);
cexit = chancreate(sizeof(int), 0);
if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil){
fprint(2, "acme: can't create initial channels: %r\n");
exits("channels");
}
mousectl = initmouse(nil, screen);
if(mousectl == nil){
fprint(2, "acme: can't initialize mouse: %r\n");
exits("mouse");
}
mouse = mousectl;
keyboardctl = initkeyboard(nil);
if(keyboardctl == nil){
fprint(2, "acme: can't initialize keyboard: %r\n");
exits("keyboard");
}
mainpid = getpid();
plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
if(plumbeditfd >= 0){
cplumb = chancreate(sizeof(Plumbmsg*), 0);
proccreate(plumbproc, nil, STACK);
}
plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
fsysinit();
#define WPERCOL 8
disk = diskinit();
if(loadfile)
rowload(&row, loadfile, TRUE);
else{
rowinit(&row, screen->clipr);
if(ncol < 0){
if(argc == 0)
ncol = 2;
else{
ncol = (argc+(WPERCOL-1))/WPERCOL;
if(ncol < 2)
ncol = 2;
}
}
if(ncol == 0)
ncol = 2;
for(i=0; i<ncol; i++){
c = rowadd(&row, nil, -1);
if(c==nil && i==0)
error("initializing columns");
}
c = row.col[row.ncol-1];
if(argc == 0)
readfile(c, wdir);
else
for(i=0; i<argc; i++){
p = utfrrune(argv[i], '/');
if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
readfile(c, argv[i]);
else
readfile(row.col[i/WPERCOL], argv[i]);
}
}
flushimage(display, 1);
acmeerrorinit();
threadcreate(keyboardthread, nil, STACK);
threadcreate(mousethread, nil, STACK);
threadcreate(waitthread, nil, STACK);
threadcreate(xfidallocthread, nil, STACK);
atnotify(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);
}
char *oknotes[] ={
"delete",
"hangup",
"kill",
"kilall",
"exit",
nil
};
int dumping;
void
shutdown1(void*, char *msg)
{
int i;
notify(nil);
killprocs();
if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && strcmp(msg, "kilall")!=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();
}
int
shutdown(void *a, char *msg) /* extra call to get stack trace on 386 */
{
shutdown1(a, msg);
return 1;
}
void
killprocs(void)
{
Command *c;
fsysclose();
if(display)
flushimage(display, 1);
for(c=command; c; c=c->next)
postnote(PNGROUP, c->pid, "hangup");
remove(acmeerrorfile);
}
static int errorfd;
void
acmeerrorproc(void *)
{
char *buf;
int n;
threadsetname("acmeerrorproc");
buf = emalloc(8192+1);
while((n=read(errorfd, buf, 8192)) >= 0){
buf[n] = '\0';
sendp(cerr, estrdup(buf));
}
}
void
acmeerrorinit(void)
{
int fd, pfd[2];
char buf[64];
if(pipe(pfd) < 0)
error("can't create pipe");
sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
fd = create(acmeerrorfile, OWRITE, 0666);
if(fd < 0){
remove(acmeerrorfile);
fd = create(acmeerrorfile, OWRITE, 0666);
if(fd < 0)
error("can't create acmeerror file");
}
sprint(buf, "%d", pfd[0]);
write(fd, buf, strlen(buf));
close(fd);
/* reopen pfd[1] close on exec */
sprint(buf, "/fd/%d", pfd[1]);
errorfd = open(buf, OREAD|OCEXEC);
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)
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;
char *act;
enum { MResize, MMouse, MPlumb, 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;
if(cplumb == nil)
alts[MPlumb].op = CHANNOP;
alts[NMALT].op = CHANEND;
for(;;){
switch(alt(alts)){
case MResize:
if(getwindow(display, Refnone) < 0)
error("attach to window");
scrlresize();
rowresize(&row, screen->clipr);
flushimage(display, 1);
break;
case MPlumb:
if(strcmp(pm->type, "text") == 0){
act = plumblookup(pm->attr, "action");
if(act==nil || strcmp(act, "showfile")==0)
plumblook(pm);
else if(strcmp(act, "showdata")==0)
plumbshow(pm);
}
flushimage(display, 1);
plumbfree(pm);
break;
case MMouse:
qlock(&row);
t = rowwhich(&row, mouse->xy);
if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
winlock(mousetext->w, 'M');
mousetext->eq0 = ~0;
wincommit(mousetext->w, mousetext);
winunlock(mousetext->w);
}
mousetext = t;
if(t == nil)
goto Continue;
w = t->w;
if(t==nil || mouse->buttons==0)
goto Continue;
but = 0;
if(mouse->buttons == 1)
but = 1;
else if(mouse->buttons == 2)
but = 2;
else if(mouse->buttons == 4)
but = 3;
barttext = t;
if(t->what==Body && ptinrect(mouse->xy, t->scrollr)){
if(but){
winlock(w, 'M');
t->eq0 = ~0;
textscroll(t, but);
winunlock(w);
}
goto Continue;
}
if(ptinrect(mouse->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(mouse->buttons){
if(w)
winlock(w, 'M');
t->eq0 = ~0;
if(w)
wincommit(w, t);
else
textcommit(t, TRUE);
if(mouse->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(mouse->buttons & 2){
if(textselect2(t, &q0, &q1, &argt))
execute(t, q0, q1, FALSE, argt);
}else if(mouse->buttons & 4){
if(textselect3(t, &q0, &q1))
look3(t, q0, q1, FALSE);
}
if(w)
winunlock(w);
goto Continue;
}
Continue:
flushimage(display, 1);
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[ERRLEN];
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 = atoi(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);
Freecmd:
if(c){
if(c->iseditcmd)
sendul(cedit, 0);
free(c->text);
free(c->av);
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;
}
}
}
Reffont*
rfget(int fix, int save, int setfont, char *name)
{
Reffont *r;
Font *f;
int i;
r = nil;
if(name == nil){
name = fontnames[fix];
r = reffonts[fix];
}
if(r == nil){
for(i=0; i<nfontcache; i++)
if(strcmp(name, fontcache[i]->f->name) == 0){
r = fontcache[i];
goto Found;
}
f = openfont(display, name);
if(f == nil){
warning(nil, "can't open font file %s: %r\n", name);
return nil;
}
r = emalloc(sizeof(Reffont));
r->f = f;
fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
fontcache[nfontcache++] = r;
}
Found:
if(save){
incref(r);
if(reffonts[fix])
rfclose(reffonts[fix]);
reffonts[fix] = r;
fontnames[fix] = 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;
/* Blue */
tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), display->chan, 1, DPalegreygreen);
tagcols[BORD] = allocimage(display, Rect(0,0,1,1), display->chan, 1, DPurpleblue);
tagcols[TEXT] = display->black;
tagcols[HTEXT] = display->black;
/* Yellow */
textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
textcols[HIGH] = allocimage(display, Rect(0,0,1,1), display->chan, 1, DDarkyellow);
textcols[BORD] = allocimage(display, Rect(0,0,1,1), display->chan, 1, DYellowgreen);
textcols[TEXT] = display->black;
textcols[HTEXT] = display->black;
if(button){
freeimage(button);
freeimage(modbutton);
freeimage(colbutton);
}
r = Rect(0, 0, Scrollwid+2, font->height+1);
button = allocimage(display, r, display->chan, 0, DNofill);
draw(button, r, tagcols[BACK], nil, r.min);
r.max.x -= 2;
border(button, r, 2, tagcols[BORD], ZP);
r = button->r;
modbutton = allocimage(display, r, display->chan, 0, DNofill);
draw(modbutton, r, tagcols[BACK], nil, r.min);
r.max.x -= 2;
border(modbutton, r, 2, tagcols[BORD], ZP);
r = insetrect(r, 2);
tmp = allocimage(display, Rect(0,0,1,1), display->chan, 1, DMedblue);
draw(modbutton, r, tmp, nil, ZP);
freeimage(tmp);
r = button->r;
colbutton = allocimage(display, r, display->chan, 0, DPurpleblue);
but2col = allocimage(display, r, display->chan, 1, 0xAA0000FF);
but3col = allocimage(display, r, display->chan, 1, 0x006600FF);
}
/*
* /dev/snarf updates when the file is closed, so we must open our own
* fd here rather than use snarffd
*/
/* rio truncates larges snarf buffers, so this avoids using the
* service if the string is huge */
#define MAXSNARF 100*1024
void
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);
}
[-- Attachment #3: exec.c --]
[-- Type: text/plain, Size: 28262 bytes --]
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <auth.h>
#include <fcall.h>
#include <plumb.h>
#include <regexp.h>
#include "dat.h"
#include "fns.h"
Buffer snarfbuf;
void del(Text*, Text*, Text*, int, int, Rune*, int);
void delcol(Text*, Text*, Text*, int, int, Rune*, int);
void dump(Text*, Text*, Text*, int, int, Rune*, int);
void edit(Text*, Text*, Text*, int, int, Rune*, int);
void exit(Text*, Text*, Text*, int, int, Rune*, int);
void fontx(Text*, Text*, Text*, int, int, Rune*, int);
void get(Text*, Text*, Text*, int, int, Rune*, int);
void id(Text*, Text*, Text*, int, int, Rune*, int);
void ignore(Text*, Text*, Text*, int, int, Rune*, int);
void incl(Text*, Text*, Text*, int, int, Rune*, int);
void kill(Text*, Text*, Text*, int, int, Rune*, int);
void local(Text*, Text*, Text*, int, int, Rune*, int);
void look(Text*, Text*, Text*, int, int, Rune*, int);
void newcol(Text*, Text*, Text*, int, int, Rune*, int);
void paste(Text*, Text*, Text*, int, int, Rune*, int);
void put(Text*, Text*, Text*, int, int, Rune*, int);
void putall(Text*, Text*, Text*, int, int, Rune*, int);
void sendx(Text*, Text*, Text*, int, int, Rune*, int);
void sort(Text*, Text*, Text*, int, int, Rune*, int);
void tab(Text*, Text*, Text*, int, int, Rune*, int);
void zeroxx(Text*, Text*, Text*, int, int, Rune*, int);
extern Reprog* ignoreregx;
typedef struct Exectab Exectab;
struct Exectab
{
Rune *name;
void (*fn)(Text*, Text*, Text*, int, int, Rune*, int);
int mark;
int flag1;
int flag2;
};
Exectab exectab[] = {
{ L"Cut", cut, TRUE, TRUE, TRUE },
{ L"Del", del, FALSE, FALSE, XXX },
{ L"Delcol", delcol, FALSE, XXX, XXX },
{ L"Delete", del, FALSE, TRUE, XXX },
{ L"Dump", dump, FALSE, TRUE, XXX },
{ L"Edit", edit, FALSE, XXX, XXX },
{ L"Exit", exit, FALSE, XXX, XXX },
{ L"Font", fontx, FALSE, XXX, XXX },
{ L"Get", get, FALSE, TRUE, XXX },
{ L"Ignore", ignore, FALSE, XXX, XXX },
{ L"ID", id, FALSE, XXX, XXX },
{ L"Incl", incl, FALSE, XXX, XXX },
{ L"Kill", kill, FALSE, XXX, XXX },
{ L"Load", dump, FALSE, FALSE, XXX },
{ L"Local", local, FALSE, XXX, XXX },
{ L"Look", look, FALSE, XXX, XXX },
{ L"New", new, FALSE, XXX, XXX },
{ L"Newcol", newcol, FALSE, XXX, XXX },
{ L"Paste", paste, TRUE, TRUE, XXX },
{ L"Put", put, FALSE, XXX, XXX },
{ L"Putall", putall, FALSE, XXX, XXX },
{ L"Redo", undo, FALSE, FALSE, XXX },
{ L"Send", sendx, TRUE, XXX, XXX },
{ L"Snarf", cut, FALSE, TRUE, FALSE },
{ L"Sort", sort, FALSE, XXX, XXX },
{ L"Tab", tab, FALSE, XXX, XXX },
{ L"Undo", undo, FALSE, TRUE, XXX },
{ L"Zerox", zeroxx, FALSE, XXX, XXX },
{ nil, nil, 0, 0, 0 },
};
Exectab*
lookup(Rune *r, int n)
{
Exectab *e;
int nr;
r = skipbl(r, n, &n);
if(n == 0)
return nil;
findbl(r, n, &nr);
nr = n-nr;
for(e=exectab; e->name; e++)
if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE)
return e;
return nil;
}
int
isexecc(int c)
{
if(isfilec(c))
return 1;
return c=='<' || c=='|' || c=='>';
}
void
execute(Text *t, uint aq0, uint aq1, int external, Text *argt)
{
uint q0, q1;
Rune *r, *s;
char *b, *a, *aa;
Exectab *e;
int c, n, f;
Runestr dir;
q0 = aq0;
q1 = aq1;
if(q1 == q0){ /* expand to find word (actually file name) */
/* if in selection, choose selection */
if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
q0 = t->q0;
q1 = t->q1;
}else{
while(q1<t->file->nc && isexecc(c=textreadc(t, q1)) && c!=':')
q1++;
while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':')
q0--;
if(q1 == q0)
return;
}
}
r = runemalloc(q1-q0);
bufread(t->file, q0, r, q1-q0);
e = lookup(r, q1-q0);
if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
f = 0;
if(e)
f |= 1;
if(q0!=aq0 || q1!=aq1){
bufread(t->file, aq0, r, aq1-aq0);
f |= 2;
}
aa = getbytearg(argt, TRUE, TRUE, &a);
if(a){
if(strlen(a) > EVENTSIZE){ /* too big; too bad */
free(aa);
free(a);
warning(nil, "`argument string too long\n");
return;
}
f |= 8;
}
c = 'x';
if(t->what == Body)
c = 'X';
n = aq1-aq0;
if(n <= EVENTSIZE)
winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r);
else
winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n);
if(q0!=aq0 || q1!=aq1){
n = q1-q0;
bufread(t->file, q0, r, n);
if(n <= EVENTSIZE)
winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r);
else
winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n);
}
if(a){
winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a);
if(aa)
winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa);
else
winevent(t->w, "%c0 0 0 0 \n", c);
}
free(r);
free(aa);
free(a);
return;
}
if(e){
if(e->mark && seltext!=nil)
if(seltext->what == Body){
seq++;
filemark(seltext->w->body.file);
}
s = skipbl(r, q1-q0, &n);
s = findbl(s, n, &n);
s = skipbl(s, n, &n);
(*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n);
free(r);
return;
}
b = runetobyte(r, q1-q0);
free(r);
dir = dirname(t, nil, 0);
if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
free(dir.r);
dir.r = nil;
dir.nr = 0;
}
aa = getbytearg(argt, TRUE, TRUE, &a);
if(t->w)
incref(t->w);
run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE);
}
char*
printarg(Text *argt, uint q0, uint q1)
{
char *buf;
if(argt->what!=Body || argt->file->name==nil)
return nil;
buf = emalloc(argt->file->nname+32);
if(q0 == q1)
sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0);
else
sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1);
return buf;
}
char*
getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp)
{
int n;
Expand e;
char *a;
*rp = nil;
*nrp = 0;
if(argt == nil)
return nil;
a = nil;
textcommit(argt, TRUE);
if(expand(argt, argt->q0, argt->q1, &e)){
free(e.bname);
if(e.nname && dofile){
e.name = runerealloc(e.name, e.nname+1);
if(doaddr)
a = printarg(argt, e.q0, e.q1);
*rp = e.name;
*nrp = e.nname;
return a;
}
free(e.name);
}else{
e.q0 = argt->q0;
e.q1 = argt->q1;
}
n = e.q1 - e.q0;
*rp = runemalloc(n+1);
bufread(argt->file, e.q0, *rp, n);
if(doaddr)
a = printarg(argt, e.q0, e.q1);
*nrp = n;
return a;
}
char*
getbytearg(Text *argt, int doaddr, int dofile, char **bp)
{
Rune *r;
int n;
char *aa;
*bp = nil;
aa = getarg(argt, doaddr, dofile, &r, &n);
if(r == nil)
return nil;
*bp = runetobyte(r, n);
free(r);
return aa;
}
void
newcol(Text *et, Text*, Text*, int, int, Rune*, int)
{
Column *c;
c = rowadd(et->row, nil, -1);
if(c)
winsettag(coladd(c, nil, nil, -1));
}
void
delcol(Text *et, Text*, Text*, int, int, Rune*, int)
{
int i;
Column *c;
Window *w;
c = et->col;
if(c==nil || colclean(c)==0)
return;
for(i=0; i<c->nw; i++){
w = c->w[i];
if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata] > 0){
warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name);
return;
}
}
rowclose(et->col->row, et->col, TRUE);
}
void
del(Text *et, Text*, Text*, int flag1, int, Rune*, int)
{
if(et->col==nil || et->w == nil)
return;
if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE))
colclose(et->col, et->w, TRUE);
}
void
sort(Text *et, Text*, Text*, int, int, Rune*, int)
{
if(et->col)
colsort(et->col);
}
uint
seqof(Window *w, int isundo)
{
/* if it's undo, see who changed with us */
if(isundo)
return w->body.file->seq;
/* if it's redo, see who we'll be sync'ed up with */
return fileredoseq(w->body.file);
}
void
undo(Text *et, Text*, Text*, int flag1, int, Rune*, int)
{
int i, j;
Column *c;
Window *w;
uint seq;
if(et==nil || et->w== nil)
return;
seq = seqof(et->w, flag1);
if(seq == 0){
/* nothing to undo */
return;
}
/*
* Undo the executing window first. Its display will update. other windows
* in the same file will not call show() and jump to a different location in the file.
* Simultaneous changes to other files will be chaotic, however.
*/
winundo(et->w, flag1);
for(i=0; i<row.ncol; i++){
c = row.col[i];
for(j=0; j<c->nw; j++){
w = c->w[j];
if(w == et->w)
continue;
if(seqof(w, flag1) == seq)
winundo(w, flag1);
}
}
}
char*
getname(Text *t, Text *argt, Rune *arg, int narg, int isput)
{
char *s;
Rune *r;
int i, n, promote;
Runestr dir;
getarg(argt, FALSE, TRUE, &r, &n);
promote = FALSE;
if(r == nil)
promote = TRUE;
else if(isput){
/* if are doing a Put, want to synthesize name even for non-existent file */
/* best guess is that file name doesn't contain a slash */
promote = TRUE;
for(i=0; i<n; i++)
if(r[i] == '/'){
promote = FALSE;
break;
}
if(promote){
t = argt;
arg = r;
narg = n;
}
}
if(promote){
n = narg;
if(n <= 0){
s = runetobyte(t->file->name, t->file->nname);
return s;
}
/* prefix with directory name if necessary */
dir.r = nil;
dir.nr = 0;
if(n>0 && arg[0]!='/'){
dir = dirname(t, nil, 0);
if(n==1 && dir.r[0]=='.'){ /* sigh */
free(dir.r);
dir.r = nil;
dir.nr = 0;
}
}
if(dir.r){
r = runemalloc(dir.nr+n+1);
runemove(r, dir.r, dir.nr);
free(dir.r);
runemove(r+dir.nr, arg, n);
n += dir.nr;
}else{
r = runemalloc(n+1);
runemove(r, arg, n);
}
}
s = runetobyte(r, n);
free(r);
if(strlen(s) == 0){
free(s);
s = nil;
}
return s;
}
void
zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int)
{
Window *nw;
int c, locked;
locked = FALSE;
if(t!=nil && t->w!=nil && t->w!=et->w){
locked = TRUE;
c = 'M';
if(et->w)
c = et->w->owner;
winlock(t->w, c);
}
if(t == nil)
t = et;
if(t==nil || t->w==nil)
return;
t = &t->w->body;
if(t->w->isdir)
warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name);
else{
nw = coladd(t->w->col, nil, t->w, -1);
/* ugly: fix locks so w->unlock works */
winlock1(nw, t->w->owner);
}
if(locked)
winunlock(t->w);
}
void
get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg)
{
char *name;
Rune *r;
int i, n, dirty, samename;
Window *w;
Text *u;
Dir d;
if(flag1)
if(et==nil || et->w==nil)
return;
if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE)))
return;
w = et->w;
t = &w->body;
name = getname(t, argt, arg, narg, FALSE);
if(name == nil){
warning(nil, "no file name\n");
return;
}
if(t->file->ntext>1 && dirstat(name, &d)==0 && d.qid.path & CHDIR){
warning(nil, "%s is a directory; can't read with multiple windows on it\n", name);
return;
}
r = bytetorune(name, &n);
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
/* second and subsequent calls with zero an already empty buffer, but OK */
textreset(u);
windirfree(u->w);
}
samename = runeeq(r, n, t->file->name, t->file->nname);
textload(t, 0, name, samename);
if(samename){
t->file->mod = FALSE;
dirty = FALSE;
}else{
t->file->mod = TRUE;
dirty = TRUE;
}
for(i=0; i<t->file->ntext; i++)
t->file->text[i]->w->dirty = dirty;
free(name);
free(r);
winsettag(w);
t->file->unread = FALSE;
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc);
textscrdraw(u);
}
}
void
putfile(File *f, int q0, int q1, Rune *namer, int nname)
{
uint n, m;
Rune *r;
char *s, *name;
int i, fd, q;
Dir d;
Window *w;
w = f->curtext->w;
name = runetobyte(namer, nname);
if(runeeq(namer, nname, f->name, f->nname) && dirstat(name, &d)>=0){
if(f->dev!=d.dev || f->qidpath!=d.qid.path || f->mtime<d.mtime){
f->dev = d.dev;
f->qidpath = d.qid.path;
f->mtime = d.mtime;
if(f->unread)
warning(nil, "%s not written; file already exists\n", name);
else
warning(nil, "%s modified since last read\n", name);
goto Rescue1;
}
}
fd = create(name, OWRITE, 0666);
if(fd < 0){
warning(nil, "can't create file %s: %r\n", name);
goto Rescue1;
}
r = fbufalloc();
s = fbufalloc();
if(dirfstat(fd, &d)>=0 && (d.mode&CHAPPEND) && d.length>0){
warning(nil, "%s not written; file is append only\n", name);
goto Rescue2;
}
for(q=q0; q<q1; q+=n){
n = q1 - q;
if(n > BUFSIZE/UTFmax)
n = BUFSIZE/UTFmax;
bufread(f, q, r, n);
m = snprint(s, BUFSIZE+1, "%.*S", n, r);
if(write(fd, s, m) != m){
warning(nil, "can't write file %s: %r\n", name);
goto Rescue2;
}
}
if(runeeq(namer, nname, f->name, f->nname)){
if(q0!=0 || q1!=f->nc){
f->mod = TRUE;
w->dirty = TRUE;
f->unread = TRUE;
}else{
dirfstat(fd, &d); /* ignore error; use old values if we failed */
f->qidpath = d.qid.path;
f->dev = d.dev;
f->mtime = d.mtime;
f->mod = FALSE;
w->dirty = FALSE;
f->unread = FALSE;
}
for(i=0; i<f->ntext; i++){
f->text[i]->w->putseq = f->seq;
f->text[i]->w->dirty = w->dirty;
}
}
fbuffree(s);
fbuffree(r);
free(namer);
free(name);
close(fd);
winsettag(w);
return;
Rescue2:
fbuffree(s);
fbuffree(r);
close(fd);
/* fall through */
Rescue1:
free(namer);
free(name);
}
void
put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
{
int nname;
Rune *namer;
Window *w;
File *f;
char *name;
if(et==nil || et->w==nil || et->w->isdir)
return;
w = et->w;
f = w->body.file;
name = getname(&w->body, argt, arg, narg, TRUE);
if(name == nil){
warning(nil, "no file name\n");
return;
}
namer = bytetorune(name, &nname);
putfile(f, 0, f->nc, namer, nname);
free(name);
}
void
dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg)
{
char *name;
if(narg)
name = runetobyte(arg, narg);
else
getbytearg(argt, FALSE, TRUE, &name);
if(isdump)
rowdump(&row, name);
else
rowload(&row, name, FALSE);
free(name);
}
void
ignore(Text *, Text *, Text *argt, int, int, Rune *arg, int narg)
{
char *exp;
exp = nil;
if(narg)
exp = runetobyte(arg, narg);
else
getbytearg(argt, FALSE, TRUE, &exp);
if (ignoreregx)
free(ignoreregx);
ignoreregx = (exp == nil) ? nil : regcomp(exp);
free(exp);
}
void
cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int)
{
uint q0, q1, n, locked, c;
Rune *r;
/* use current window if snarfing and its selection is non-null */
if(et!=t && dosnarf && et->w!=nil){
if(et->w->body.q1>et->w->body.q0){
t = &et->w->body;
if(docut)
filemark(t->file); /* seq has been incremented by execute */
}else if(et->w->tag.q1>et->w->tag.q0)
t = &et->w->tag;
}
if(t == nil){
/* can only happen if seltext == nil */
return;
}
locked = FALSE;
if(t->w!=nil && et->w!=t->w){
locked = TRUE;
c = 'M';
if(et->w)
c = et->w->owner;
winlock(t->w, c);
}
if(t->q0 == t->q1){
if(locked)
winunlock(t->w);
return;
}
if(dosnarf){
q0 = t->q0;
q1 = t->q1;
bufdelete(&snarfbuf, 0, snarfbuf.nc);
r = fbufalloc();
while(q0 < q1){
n = q1 - q0;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(t->file, q0, r, n);
bufinsert(&snarfbuf, snarfbuf.nc, r, n);
q0 += n;
}
fbuffree(r);
putsnarf();
}
if(docut){
textdelete(t, t->q0, t->q1, TRUE);
textsetselect(t, t->q0, t->q0);
if(t->w){
textscrdraw(t);
winsettag(t->w);
}
}else if(dosnarf) /* Snarf command */
argtext = t;
if(locked)
winunlock(t->w);
}
void
paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int)
{
int c;
uint q, q0, q1, n;
Rune *r;
getsnarf();
if(snarfbuf.nc==0)
return;
/* if(tobody), use body of executing window (Paste or Send command) */
if(tobody && et!=nil && et->w!=nil){
t = &et->w->body;
filemark(t->file); /* seq has been incremented by execute */
}
if(t == nil)
return;
if(t->w!=nil && et->w!=t->w){
c = 'M';
if(et->w)
c = et->w->owner;
winlock(t->w, c);
}
cut(t, t, nil, FALSE, TRUE, nil, 0);
q = 0;
q0 = t->q0;
q1 = t->q0+snarfbuf.nc;
r = fbufalloc();
while(q0 < q1){
n = q1 - q0;
if(n > RBUFSIZE)
n = RBUFSIZE;
if(r == nil)
r = runemalloc(n);
bufread(&snarfbuf, q, r, n);
textinsert(t, q0, r, n, TRUE);
q += n;
q0 += n;
}
fbuffree(r);
if(selectall)
textsetselect(t, t->q0, q1);
else
textsetselect(t, q1, q1);
if(t->w){
textscrdraw(t);
winsettag(t->w);
}
if(t->w!=nil && et->w!=t->w)
winunlock(t->w);
}
void
look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
{
Rune *r;
int n;
if(et && et->w){
t = &et->w->body;
if(narg > 0){
search(t, arg, narg);
return;
}
getarg(argt, FALSE, FALSE, &r, &n);
if(r == nil){
n = t->q1-t->q0;
r = runemalloc(n);
bufread(t->file, t->q0, r, n);
}
search(t, r, n);
free(r);
}
}
void
sendx(Text *et, Text *t, Text*, int, int, Rune*, int)
{
if(et->w==nil)
return;
t = &et->w->body;
if(t->q0 != t->q1)
cut(t, t, nil, TRUE, FALSE, nil, 0);
textsetselect(t, t->file->nc, t->file->nc);
paste(t, t, nil, TRUE, TRUE, nil, 0);
if(textreadc(t, t->file->nc-1) != '\n'){
textinsert(t, t->file->nc, L"\n", 1, TRUE);
textsetselect(t, t->file->nc, t->file->nc);
}
}
void
edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
{
Rune *r;
int len;
if(et == nil)
return;
getarg(argt, FALSE, TRUE, &r, &len);
seq++;
if(r != nil){
editcmd(et, r, len);
free(r);
}else
editcmd(et, arg, narg);
}
void
exit(Text*, Text*, Text*, int, int, Rune*, int)
{
if(rowclean(&row)){
sendul(cexit, 0);
threadexits(nil);
}
}
void
putall(Text*, Text*, Text*, int, int, Rune*, int)
{
int i, j, e;
Window *w;
Column *c;
char *a;
for(i=0; i<row.ncol; i++){
c = row.col[i];
for(j=0; j<c->nw; j++){
w = c->w[j];
if(w->isscratch || w->isdir || w->body.file->nname==0)
continue;
if(w->nopen[QWevent] > 0)
continue;
a = runetobyte(w->body.file->name, w->body.file->nname);
e = access(a, 0);
if(w->body.file->mod || w->body.ncache)
if(e < 0)
warning(nil, "no auto-Put of %s: %r\n", a);
else{
wincommit(w, &w->body);
put(&w->body, nil, nil, XXX, XXX, nil, 0);
}
free(a);
}
}
}
void
id(Text *et, Text*, Text*, int, int, Rune*, int)
{
if(et && et->w)
warning(nil, "/mnt/acme/%d/\n", et->w->id);
}
void
local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
{
char *a, *aa;
Runestr dir;
aa = getbytearg(argt, TRUE, TRUE, &a);
dir = dirname(et, nil, 0);
if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
free(dir.r);
dir.r = nil;
dir.nr = 0;
}
run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE);
}
void
kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg)
{
Rune *a, *cmd, *r;
int na;
getarg(argt, FALSE, FALSE, &r, &na);
if(r)
kill(nil, nil, nil, 0, 0, r, na);
/* loop condition: *arg is not a blank */
for(;;){
a = findbl(arg, narg, &na);
if(a == arg)
break;
cmd = runemalloc(narg-na+1);
runemove(cmd, arg, narg-na);
sendp(ckill, cmd);
arg = skipbl(a, na, &narg);
}
}
void
fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
{
Rune *a, *r, *flag, *file;
int na, nf;
char *aa;
Reffont *newfont;
Dirlist *dp;
int i, fix;
if(et==nil || et->w==nil)
return;
t = &et->w->body;
flag = nil;
file = nil;
/* loop condition: *arg is not a blank */
nf = 0;
for(;;){
a = findbl(arg, narg, &na);
if(a == arg)
break;
r = runemalloc(narg-na+1);
runemove(r, arg, narg-na);
if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){
free(flag);
flag = r;
}else{
free(file);
file = r;
nf = narg-na;
}
arg = skipbl(a, na, &narg);
}
getarg(argt, FALSE, TRUE, &r, &na);
if(r)
if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){
free(flag);
flag = r;
}else{
free(file);
file = r;
nf = na;
}
fix = 1;
if(flag)
fix = runeeq(flag, runestrlen(flag), L"fix", 3);
else if(file == nil){
newfont = rfget(FALSE, FALSE, FALSE, nil);
if(newfont)
fix = strcmp(newfont->f->name, t->font->name)==0;
}
if(file){
aa = runetobyte(file, nf);
newfont = rfget(fix, flag!=nil, FALSE, aa);
free(aa);
}else
newfont = rfget(fix, FALSE, FALSE, nil);
if(newfont){
draw(screen, t->w->r, textcols[BACK], nil, ZP);
rfclose(t->reffont);
t->reffont = newfont;
t->font = newfont->f;
frinittick(t);
if(t->w->isdir){
t->all.min.x++; /* force recolumnation; disgusting! */
for(i=0; i<t->w->ndl; i++){
dp = t->w->dlp[i];
aa = runetobyte(dp->r, dp->nr);
dp->wid = stringwidth(newfont->f, aa);
free(aa);
}
}
/* avoid shrinking of window due to quantization */
colgrow(t->w->col, t->w, -1);
}
free(file);
free(flag);
}
void
incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
{
Rune *a, *r;
Window *w;
int na, n, len;
if(et==nil || et->w==nil)
return;
w = et->w;
n = 0;
getarg(argt, FALSE, TRUE, &r, &len);
if(r){
n++;
winaddincl(w, r, len);
}
/* loop condition: *arg is not a blank */
for(;;){
a = findbl(arg, narg, &na);
if(a == arg)
break;
r = runemalloc(narg-na+1);
runemove(r, arg, narg-na);
n++;
winaddincl(w, r, narg-na);
arg = skipbl(a, na, &narg);
}
if(n==0 && w->nincl){
for(n=w->nincl; --n>=0; )
warning(nil, "%S ", w->incl[n]);
warning(nil, "\n");
}
}
void
tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
{
Rune *a, *r;
Window *w;
int na, len, tab;
char *p;
if(et==nil || et->w==nil)
return;
w = et->w;
getarg(argt, FALSE, TRUE, &r, &len);
tab = 0;
if(r!=nil && len>0){
p = runetobyte(r, len);
if('0'<=p[0] && p[0]<='9')
tab = atoi(p);
free(p);
}else{
a = findbl(arg, narg, &na);
if(a != arg){
p = runetobyte(arg, narg-na);
if('0'<=p[0] && p[0]<='9')
tab = atoi(p);
free(p);
}
}
if(tab > 0){
if(w->body.tabstop != tab){
w->body.tabstop = tab;
winresize(w, w->r, 1);
}
}else
warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop);
}
void
runproc(void *argvp)
{
/* args: */
Window *win;
char *s;
Rune *rdir;
int ndir;
int newns;
char *argaddr;
char *arg;
Command *c;
Channel *cpid;
int iseditcmd;
/* end of args */
char *e, *t, *name, *dir, **av, *news;
Rune r, **incl;
int ac, w, inarg, i, n, fd, nincl, winid;
int pipechar;
char buf[512];
static void *parg[2];
void **argv;
argv = argvp;
win = argv[0];
s = argv[1];
rdir = argv[2];
ndir = (int)argv[3];
newns = (int)argv[4];
argaddr = argv[5];
arg = argv[6];
c = argv[7];
cpid = argv[8];
iseditcmd = (int)argv[9];
free(argv);
t = s;
while(*t==' ' || *t=='\n' || *t=='\t')
t++;
for(e=t; *e; e++)
if(*e==' ' || *e=='\n' || *e=='\t' )
break;
name = emalloc((e-t)+2);
memmove(name, t, e-t);
name[e-t] = 0;
e = utfrrune(name, '/');
if(e)
strcpy(name, e+1);
strcat(name, " "); /* add blank here for ease in waittask */
c->name = bytetorune(name, &c->nname);
free(name);
pipechar = 0;
if(*t=='<' || *t=='|' || *t=='>')
pipechar = *t++;
c->iseditcmd = iseditcmd;
c->text = s;
if(rdir != nil){
dir = runetobyte(rdir, ndir);
chdir(dir); /* ignore error: probably app. window */
free(dir);
}
if(newns){
nincl = 0;
incl = nil;
if(win){
nincl = win->nincl;
if(nincl > 0){
incl = emalloc(nincl*sizeof(Rune*));
for(i=0; i<nincl; i++){
n = runestrlen(win->incl[i]);
incl[i] = runemalloc(n+1);
runemove(incl[i], win->incl[i], n);
}
}
winid = win->id;
winclose(win);
}else{
winid = 0;
if(activewin)
winid = activewin->id;
}
rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG);
c->md = fsysmount(rdir, ndir, incl, nincl);
if(c->md == nil){
threadprint(2, "child: can't mount /dev/cons: %r\n");
threadexits("mount");
}
close(0);
if(winid>0 && (pipechar=='|' || pipechar=='>')){
sprint(buf, "/mnt/acme/%d/rdsel", winid);
open(buf, OREAD);
}else
open("/dev/null", OREAD);
close(1);
if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
if(iseditcmd){
if(winid > 0)
sprint(buf, "/mnt/acme/%d/editout", winid);
else
sprint(buf, "/mnt/acme/editout");
}else
sprint(buf, "/mnt/acme/%d/wrsel", winid);
open(buf, OWRITE);
close(2);
open("/dev/cons", OWRITE);
}else{
open("/dev/cons", OWRITE);
dup(1, 2);
}
}else{
if(win)
winclose(win);
rfork(RFFDG|RFNOTEG);
fsysclose();
close(0);
open("/dev/null", OREAD);
close(1);
open(acmeerrorfile, OWRITE);
dup(1, 2);
}
if(argaddr)
putenv("acmeaddr", argaddr);
if(strlen(t) > sizeof buf-10) /* may need to print into stack */
goto Hard;
inarg = FALSE;
for(e=t; *e; e+=w){
w = chartorune(&r, e);
if(r==' ' || r=='\t')
continue;
if(r < ' ')
goto Hard;
if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r))
goto Hard;
inarg = TRUE;
}
if(!inarg)
goto Fail;
ac = 0;
av = nil;
inarg = FALSE;
for(e=t; *e; e+=w){
w = chartorune(&r, e);
if(r==' ' || r=='\t'){
inarg = FALSE;
*e = 0;
continue;
}
if(!inarg){
inarg = TRUE;
av = realloc(av, (ac+1)*sizeof(char**));
av[ac++] = e;
}
}
av = realloc(av, (ac+2)*sizeof(char**));
av[ac++] = arg;
av[ac] = nil;
c->av = av;
procexec(cpid, av[0], av);
e = av[0];
if(e[0]=='/' || (e[0]=='.' && e[1]=='/'))
goto Fail;
if(cputype){
sprint(buf, "%s/%s", cputype, av[0]);
procexec(cpid, buf, av);
}
sprint(buf, "/bin/%s", av[0]);
procexec(cpid, buf, av);
goto Fail;
Hard:
/*
* ugly: set path = (. $cputype /bin)
* should honor $path if unusual.
*/
if(cputype){
n = 0;
memmove(buf+n, ".", 2);
n += 2;
i = strlen(cputype)+1;
memmove(buf+n, cputype, i);
n += i;
memmove(buf+n, "/bin", 5);
n += 5;
fd = create("/env/path", OWRITE, 0666);
write(fd, buf, n);
close(fd);
}
if(arg){
news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1);
if(news){
sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */
free(s);
t = news;
c->text = news;
}
}
procexecl(cpid, "/bin/rc", "rc", "-c", t, nil);
Fail:
/* procexec hasn't happened, so need to send our own pid */
sendul(cpid, getpid());
threadexits(nil);
}
void
runwaittask(void *v)
{
Command *c;
Channel *cpid;
void **a;
threadsetname("runwaittask");
a = v;
c = a[0];
cpid = a[1];
free(a);
do
c->pid = recvul(cpid);
while(c->pid == ~0);
if(c->pid != 0) /* successful exec */
sendp(ccommand, c);
else{
free(c->name);
free(c);
}
chanfree(cpid);
}
void
run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd)
{
void **arg;
Command *c;
Channel *cpid;
if(s == nil)
return;
arg = emalloc(10*sizeof(void*));
c = emalloc(sizeof *c);
cpid = chancreate(sizeof(ulong), 0);
arg[0] = win;
arg[1] = s;
arg[2] = rdir;
arg[3] = (void*)ndir;
arg[4] = (void*)newns;
arg[5] = argaddr;
arg[6] = xarg;
arg[7] = c;
arg[8] = cpid;
arg[9] = (void*)iseditcmd;
proccreate(runproc, arg, STACK);
/* mustn't block here because must be ready to answer mount() call in run() */
arg = emalloc(2*sizeof(void*));
arg[0] = c;
arg[1] = cpid;
threadcreate(runwaittask, arg, STACK);
}
[-- Attachment #4: rows.c --]
[-- Type: text/plain, Size: 15143 bytes --]
#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 <auth.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;
char *c;
Rune rc[100];
int i;
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);
if (c=getenv("acmetag")){
for (i=0; *c && i<99; i++){
c += chartorune(rc+i, c);
}
rc[i] = 0;
textinsert(t, 0, rc, i, TRUE);
}else
textinsert(t, 0, L"Newcol Kill Putall Dump Exit Ignore ", 36, 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);
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:
if(i == 0)
return;
p = mouse->xy;
if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
return;
if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
/* shuffle */
x = c->r.min.x;
rowclose(row, c, FALSE);
if(rowadd(row, c, p.x) == nil) /* whoops! */
if(rowadd(row, c, x) == nil) /* WHOOPS! */
if(rowadd(row, c, -1)==nil){ /* shit! */
rowclose(row, c, TRUE);
return;
}
colmousebut(c);
return;
}
d = row->col[i-1];
if(p.x < d->r.min.x+80+Scrollwid)
p.x = d->r.min.x+80+Scrollwid;
if(p.x > c->r.max.x-80-Scrollwid)
p.x = c->r.max.x-80-Scrollwid;
r = d->r;
r.max.x = c->r.max.x;
draw(screen, r, display->white, nil, ZP);
r.max.x = p.x;
colresize(d, r);
r = c->r;
r.min.x = p.x;
r.max.x = r.min.x;
r.max.x += Border;
draw(screen, r, display->black, nil, ZP);
r.min.x = r.max.x;
r.max.x = c->r.max.x;
colresize(c, r);
colmousebut(c);
}
void
rowclose(Row *row, Column *c, int dofree)
{
Rectangle r;
int i;
for(i=0; i<row->ncol; i++)
if(row->col[i] == c)
goto Found;
error("can't find column");
Found:
r = c->r;
if(dofree)
colcloseall(c);
memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
row->ncol--;
row->col = realloc(row->col, row->ncol*sizeof(Column*));
if(row->ncol == 0){
draw(screen, r, display->white, nil, ZP);
return;
}
if(i == row->ncol){ /* extend last column right */
c = row->col[i-1];
r.min.x = c->r.min.x;
r.max.x = row->r.max.x;
}else{ /* extend next window left */
c = row->col[i];
r.max.x = c->r.max.x;
}
draw(screen, r, display->white, nil, ZP);
colresize(c, r);
}
Column*
rowwhichcol(Row *row, Point p)
{
int i;
Column *c;
for(i=0; i<row->ncol; i++){
c = row->col[i];
if(ptinrect(p, c->r))
return c;
}
return nil;
}
Text*
rowwhich(Row *row, Point p)
{
Column *c;
if(ptinrect(p, row->tag.all))
return &row->tag;
c = rowwhichcol(row, p);
if(c)
return colwhich(c, p);
return nil;
}
Text*
rowtype(Row *row, Rune r, Point p)
{
Window *w;
Text *t;
clearmouse();
qlock(row);
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, 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(strlen(a) == 0){ /* don't save unnamed windows */
free(a);
continue;
}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);
Bwrite(b, buf, strlen(buf));
m = min(RBUFSIZE, w->tag.file->nc);
bufread(w->tag.file, 0, r, m);
n = 0;
while(n<m && r[n]!='\n')
n++;
r[n++] = '\n';
Bprint(b, "%.*S", n, r);
if(dumped){
q0 = 0;
q1 = t->file->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;
}
void
rowload(Row *row, char *file, int initing)
{
int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd;
Biobuf *b, *bout;
char *buf, *l, *t, *fontname;
Rune *r, rune, *fontr;
Column *c, *c1, *c2;
uint q0, q1;
Rectangle r1, r2;
Window *w;
buf = fbufalloc();
if(file == nil){
if(home == nil){
warning(nil, "can't find file for load: $home not defined\n");
goto Rescue1;
}
sprint(buf, "%s/acme.dump", home);
file = buf;
}
b = Bopen(file, OREAD);
if(b == nil){
warning(nil, "can't open load file %s: %r\n", file);
goto Rescue1;
}
/* current directory */
line = 0;
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(chdir(l) < 0){
warning(nil, "can't chdir %s\n", l);
goto Rescue2;
}
/* global fonts */
for(i=0; i<2; i++){
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(*l && strcmp(l, fontnames[i])!=0)
rfget(i, TRUE, i==0 && initing, estrdup(l));
}
if(initing && row->ncol==0)
rowinit(row, screen->clipr);
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
j = Blinelen(b)/12;
if(j<=0 || j>10)
goto Rescue2;
for(i=0; i<j; i++){
percent = atoi(l+i*12);
if(percent<0 || percent>=100)
goto Rescue2;
x = row->r.min.x+percent*Dx(row->r)/100;
if(i < row->ncol){
if(i == 0)
continue;
c1 = row->col[i-1];
c2 = row->col[i];
r1 = c1->r;
r2 = c2->r;
r1.max.x = x;
r2.min.x = x+Border;
if(Dx(r1) < 50 || Dx(r2) < 50)
continue;
draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
colresize(c1, r1);
colresize(c2, r2);
r2.min.x = x;
r2.max.x = x+Border;
draw(screen, r2, display->black, nil, ZP);
}
if(i >= row->ncol)
rowadd(row, nil, x);
}
for(;;){
l = rdline(b, &line);
if(l == nil)
break;
dumpid = 0;
switch(l[0]){
case 'e':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
l = rdline(b, &line); /* ctl line; ignored */
if(l == nil)
goto Rescue2;
l = rdline(b, &line); /* directory */
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
if(*l == '\0'){
if(home == nil)
r = bytetorune("./", &nr);
else{
t = emalloc(strlen(home)+1+1);
sprint(t, "%s/", home);
r = bytetorune(t, &nr);
free(t);
}
}else
r = bytetorune(l, &nr);
l = rdline(b, &line); /* command */
if(l == nil)
goto Rescue2;
t = emalloc(Blinelen(b)+1);
memmove(t, l, Blinelen(b));
run(nil, t, r, nr, TRUE, nil, nil, FALSE);
/* r is freed in run() */
continue;
case 'f':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
fontname = l+1+5*12;
ndumped = -1;
break;
case 'F':
if(Blinelen(b) < 1+6*12+1)
goto Rescue2;
fontname = l+1+6*12;
ndumped = atoi(l+1+5*12+1);
break;
case 'x':
if(Blinelen(b) < 1+5*12+1)
goto Rescue2;
fontname = l+1+5*12;
ndumped = -1;
dumpid = atoi(l+1+1*12);
break;
default:
goto Rescue2;
}
l[Blinelen(b)-1] = 0;
fontr = nil;
nfontr = 0;
if(*fontname)
fontr = bytetorune(fontname, &nfontr);
i = atoi(l+1+0*12);
j = atoi(l+1+1*12);
q0 = atoi(l+1+2*12);
q1 = atoi(l+1+3*12);
percent = atoi(l+1+4*12);
if(i<0 || i>10)
goto Rescue2;
if(i > row->ncol)
i = row->ncol;
c = row->col[i];
y = c->r.min.y+(percent*Dy(c->r))/100;
if(y<c->r.min.y || y>=c->r.max.y)
y = -1;
if(dumpid == 0)
w = coladd(c, nil, nil, y);
else
w = coladd(c, nil, lookid(dumpid, TRUE), y);
if(w == nil)
continue;
w->dumpid = j;
l = rdline(b, &line);
if(l == nil)
goto Rescue2;
l[Blinelen(b)-1] = 0;
r = bytetorune(l+5*12, &nr);
ns = -1;
for(n=0; n<nr; n++){
if(r[n] == '/')
ns = n;
if(r[n] == ' ')
break;
}
if(dumpid == 0)
winsetname(w, r, n);
for(; n<nr; n++)
if(r[n] == '|')
break;
wincleartag(w);
textinsert(&w->tag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE);
free(r);
if(ndumped >= 0){
/* simplest thing is to put it in a file and load that */
sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser());
fd = create(buf, OWRITE|ORCLOSE, 0600);
if(fd < 0){
warning(nil, "can't create temp file: %r\n");
goto Rescue2;
}
bout = emalloc(sizeof(Biobuf));
Binit(bout, fd, OWRITE);
for(n=0; n<ndumped; n++){
rune = Bgetrune(b);
if(rune == '\n')
line++;
if(rune == Beof){
Bterm(bout);
free(bout);
close(fd);
goto Rescue2;
}
Bputrune(bout, rune);
}
Bterm(bout);
free(bout);
textload(&w->body, 0, buf, 1);
close(fd);
w->body.file->mod = TRUE;
for(n=0; n<w->body.file->ntext; n++)
w->body.file->text[n]->w->dirty = TRUE;
winsettag(w);
}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
get(&w->body, nil, nil, FALSE, XXX, nil, 0);
if(fontr){
fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
free(fontr);
}
if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1)
q0 = q1 = 0;
textshow(&w->body, q0, q1);
w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
}
Bterm(b);
Rescue1:
fbuffree(buf);
return;
Rescue2:
warning(nil, "bad load file %s:%d\n", file, line);
Bterm(b);
goto Rescue1;
}
void
allwindows(void (*f)(Window*, void*), void *arg)
{
int i, j;
Column *c;
for(i=0; i<row.ncol; i++){
c = row.col[i];
for(j=0; j<c->nw; j++)
(*f)(c->w[j], arg);
}
}
[-- Attachment #5: text.c --]
[-- Type: text/plain, Size: 24802 bytes --]
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <auth.h>
#include <fcall.h>
#include <plumb.h>
#include <regexp.h>
#include "dat.h"
#include "fns.h"
extern Reprog* ignoreregx;
Image *tagcols[NCOL];
Image *textcols[NCOL];
enum{
TABDIR = 3 /* width of tabs in directory windows */
};
void
textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
{
t->file = f;
t->all = r;
t->scrollr = r;
t->scrollr.max.x = r.min.x+Scrollwid;
t->lastsr = nullrect;
r.min.x += Scrollwid+Scrollgap;
t->eq0 = ~0;
t->ncache = 0;
t->reffont = rf;
t->tabstop = maxtab;
memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
textredraw(t, r, rf->f, screen, -1);
}
void
textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
{
int maxt;
Rectangle rr;
frinit(t, r, f, b, t->Frame.cols);
rr = t->r;
rr.min.x -= Scrollwid; /* back fill to scroll bar */
draw(t->b, rr, t->cols[BACK], nil, ZP);
/* use no wider than 3-space tabs in a directory */
maxt = maxtab;
if(t->what == Body){
if(t->w->isdir)
maxt = min(TABDIR, maxtab);
else
maxt = t->tabstop;
}
t->maxtab = maxt*stringwidth(f, "0");
if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
if(t->maxlines > 0){
textreset(t);
textcolumnate(t, t->w->dlp, t->w->ndl);
textshow(t, 0, 0);
}
}else{
textfill(t);
textsetselect(t, t->q0, t->q1);
}
}
int
textresize(Text *t, Rectangle r)
{
int odx;
if(Dy(r) > 0)
r.max.y -= Dy(r)%t->font->height;
else
r.max.y = r.min.y;
odx = Dx(t->all);
t->all = r;
t->scrollr = r;
t->scrollr.max.x = r.min.x+Scrollwid;
t->lastsr = nullrect;
r.min.x += Scrollwid+Scrollgap;
frclear(t, 0);
textredraw(t, r, t->font, t->b, odx);
return r.max.y;
}
void
textclose(Text *t)
{
free(t->cache);
frclear(t, 1);
filedeltext(t->file, t);
t->file = nil;
rfclose(t->reffont);
if(argtext == t)
argtext = nil;
if(typetext == t)
typetext = nil;
if(seltext == t)
seltext = nil;
if(mousetext == t)
mousetext = nil;
if(barttext == t)
barttext = nil;
}
int
dircmp(void *a, void *b)
{
Dirlist *da, *db;
int i, n;
da = *(Dirlist**)a;
db = *(Dirlist**)b;
n = min(da->nr, db->nr);
i = memcmp(da->r, db->r, n*sizeof(Rune));
if(i)
return i;
return da->nr - db->nr;
}
void
textcolumnate(Text *t, Dirlist **dlp, int ndl)
{
int i, j, w, colw, mint, maxt, ncol, nrow;
Dirlist *dl;
uint q1;
if(t->file->ntext > 1)
return;
mint = stringwidth(t->font, "0");
/* go for narrower tabs if set more than 3 wide */
t->maxtab = min(maxtab, TABDIR)*mint;
maxt = t->maxtab;
colw = 0;
for(i=0; i<ndl; i++){
dl = dlp[i];
w = dl->wid;
if(maxt-w%maxt < mint)
w += mint;
if(w % maxt)
w += maxt-(w%maxt);
if(w > colw)
colw = w;
}
if(colw == 0)
ncol = 1;
else
ncol = max(1, Dx(t->r)/colw);
nrow = (ndl+ncol-1)/ncol;
q1 = 0;
for(i=0; i<nrow; i++){
for(j=i; j<ndl; j+=nrow){
dl = dlp[j];
fileinsert(t->file, q1, dl->r, dl->nr);
q1 += dl->nr;
if(j+nrow >= ndl)
break;
w = dl->wid;
if(maxt-w%maxt < mint){
fileinsert(t->file, q1, L"\t", 1);
q1++;
w += mint;
}
do{
fileinsert(t->file, q1, L"\t", 1);
q1++;
w += maxt-(w%maxt);
}while(w < colw);
}
fileinsert(t->file, q1, L"\n", 1);
q1++;
}
}
uint
textload(Text *t, uint q0, char *file, int setqid)
{
Rune *rp;
Dirlist *dl, **dlp;
int fd, i, n, ndl, nulls;
uint q, q1;
Dir d, *dbuf;
char tmp[NAMELEN+1];
Text *u;
if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body || (t->w->isdir && t->file->nname==0))
error("text.load");
fd = open(file, OREAD);
if(fd < 0){
warning(nil, "can't open %s: %r\n", file);
return 0;
}
if(dirfstat(fd, &d) < 0){
warning(nil, "can't fstat %s: %r\n", file);
goto Rescue;
}
nulls = FALSE;
if(d.qid.path & CHDIR){
/* this is checked in get() but it's possible the file changed underfoot */
if(t->file->ntext > 1){
warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
goto Rescue;
}
t->w->isdir = TRUE;
t->w->filemenu = FALSE;
if(t->file->name[t->file->nname-1] != '/'){
rp = runemalloc(t->file->nname+1);
runemove(rp, t->file->name, t->file->nname);
rp[t->file->nname] = '/';
winsetname(t->w, rp, t->file->nname+1);
free(rp);
}
dlp = nil;
ndl = 0;
dbuf = (Dir*)fbufalloc();
while((n=dirread(fd, dbuf, BUFSIZE-(BUFSIZE)%sizeof(Dir))) > 0){
n /= sizeof(Dir);
for(i=0; i<n; i++){
if (ignoreregx != nil)
if (regexec(ignoreregx, dbuf[i].name, nil, 0))
continue;
dl = emalloc(sizeof(Dirlist));
memmove(tmp, dbuf[i].name, NAMELEN);
if(dbuf[i].mode & CHDIR)
strcat(tmp, "/");
dl->r = bytetorune(tmp, &dl->nr);
dl->wid = stringwidth(t->font, tmp);
ndl++;
dlp = realloc(dlp, ndl*sizeof(Dirlist*));
dlp[ndl-1] = dl;
}
}
fbuffree(dbuf);
qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
t->w->dlp = dlp;
t->w->ndl = ndl;
textcolumnate(t, dlp, ndl);
q1 = t->file->nc;
}else{
t->w->isdir = FALSE;
t->w->filemenu = TRUE;
q1 = q0 + fileload(t->file, q0, fd, &nulls);
}
if(setqid){
t->file->dev = d.dev;
t->file->mtime = d.mtime;
t->file->qidpath = d.qid.path;
}
close(fd);
rp = fbufalloc();
for(q=q0; q<q1; q+=n){
n = q1-q;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(t->file, q, rp, n);
if(q < t->org)
t->org += n;
else if(q <= t->org+t->nchars)
frinsert(t, rp, rp+n, q-t->org);
if(t->lastlinefull)
break;
}
fbuffree(rp);
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
if(u != t){
if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */
u->org = 0;
textresize(u, u->all);
textbacknl(u, u->org, 0); /* go to beginning of line */
}
textsetselect(u, q0, q0);
}
if(nulls)
warning(nil, "%s: NUL bytes elided\n", file);
return q1-q0;
Rescue:
close(fd);
return 0;
}
uint
textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
{
Rune *bp, *tp, *up;
int i, initial;
if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */
Err:
textinsert(t, q0, r, n, tofile);
*nrp = n;
return q0;
}
bp = r;
for(i=0; i<n; i++)
if(*bp++ == '\b'){
--bp;
initial = 0;
tp = runemalloc(n);
runemove(tp, r, i);
up = tp+i;
for(; i<n; i++){
*up = *bp++;
if(*up == '\b')
if(up == tp)
initial++;
else
--up;
else
up++;
}
if(initial){
if(initial > q0)
initial = q0;
q0 -= initial;
textdelete(t, q0, q0+initial, tofile);
}
n = up-tp;
textinsert(t, q0, tp, n, tofile);
free(tp);
*nrp = n;
return q0;
}
goto Err;
}
void
textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
{
int c, i;
Text *u;
if(tofile && t->ncache != 0)
error("text.insert");
if(n == 0)
return;
if(tofile){
fileinsert(t->file, q0, r, n);
if(t->what == Body){
t->w->dirty = TRUE;
t->w->utflastqid = -1;
}
if(t->file->ntext > 1)
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
if(u != t){
u->w->dirty = TRUE; /* always a body */
textinsert(u, q0, r, n, FALSE);
textsetselect(u, u->q0, u->q1);
textscrdraw(u);
}
}
}
if(q0 < t->q1)
t->q1 += n;
if(q0 < t->q0)
t->q0 += n;
if(q0 < t->org)
t->org += n;
else if(q0 <= t->org+t->nchars)
frinsert(t, r, r+n, q0-t->org);
if(t->w){
c = 'i';
if(t->what == Body)
c = 'I';
if(n <= EVENTSIZE)
winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
else
winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
}
}
void
textfill(Text *t)
{
Rune *rp;
int i, n, m, nl;
if(t->lastlinefull || t->nofill)
return;
if(t->ncache > 0){
if(t->w != nil)
wincommit(t->w, t);
else
textcommit(t, TRUE);
}
rp = fbufalloc();
do{
n = t->file->nc-(t->org+t->nchars);
if(n == 0)
break;
if(n > 2000) /* educated guess at reasonable amount */
n = 2000;
bufread(t->file, t->org+t->nchars, rp, n);
/*
* it's expensive to frinsert more than we need, so
* count newlines.
*/
nl = t->maxlines-t->nlines;
m = 0;
for(i=0; i<n; ){
if(rp[i++] == '\n'){
m++;
if(m >= nl)
break;
}
}
frinsert(t, rp, rp+i, t->nchars);
}while(t->lastlinefull == FALSE);
fbuffree(rp);
}
void
textdelete(Text *t, uint q0, uint q1, int tofile)
{
uint n, p0, p1;
int i, c;
Text *u;
if(tofile && t->ncache != 0)
error("text.delete");
n = q1-q0;
if(n == 0)
return;
if(tofile){
filedelete(t->file, q0, q1);
if(t->what == Body){
t->w->dirty = TRUE;
t->w->utflastqid = -1;
}
if(t->file->ntext > 1)
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
if(u != t){
u->w->dirty = TRUE; /* always a body */
textdelete(u, q0, q1, FALSE);
textsetselect(u, u->q0, u->q1);
textscrdraw(u);
}
}
}
if(q0 < t->q0)
t->q0 -= min(n, t->q0-q0);
if(q0 < t->q1)
t->q1 -= min(n, t->q1-q0);
if(q1 <= t->org)
t->org -= n;
else if(q0 < t->org+t->nchars){
p1 = q1 - t->org;
if(p1 > t->nchars)
p1 = t->nchars;
if(q0 < t->org){
t->org = q0;
p0 = 0;
}else
p0 = q0 - t->org;
frdelete(t, p0, p1);
textfill(t);
}
if(t->w){
c = 'd';
if(t->what == Body)
c = 'D';
winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
}
}
Rune
textreadc(Text *t, uint q)
{
Rune r;
if(t->cq0<=q && q<t->cq0+t->ncache)
r = t->cache[q-t->cq0];
else
bufread(t->file, q, &r, 1);
return r;
}
int
textbswidth(Text *t, Rune c)
{
uint q, eq;
Rune r;
int skipping;
/* there is known to be at least one character to erase */
if(c == 0x08) /* ^H: erase character */
return 1;
q = t->q0;
skipping = TRUE;
while(q > 0){
r = textreadc(t, q-1);
if(r == '\n'){ /* eat at most one more character */
if(q == t->q0) /* eat the newline */
--q;
break;
}
if(c == 0x17){
eq = isalnum(r);
if(eq && skipping) /* found one; stop skipping */
skipping = FALSE;
else if(!eq && !skipping)
break;
}
--q;
}
return t->q0-q;
}
void
texttype(Text *t, Rune r)
{
uint q0, q1;
int nnb, nb, n, i;
Text *u;
if(t->what!=Body && r=='\n')
return;
switch(r){
case Kdown:
case Kleft:
case Kright:
n = t->maxlines/2;
q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
textsetorigin(t, q0, FALSE);
return;
case Kup:
n = t->maxlines/2;
q0 = textbacknl(t, t->org, n);
textsetorigin(t, q0, FALSE);
return;
}
if(t->what == Body){
seq++;
filemark(t->file);
}
if(t->q1 > t->q0){
if(t->ncache != 0)
error("text.type");
cut(t, t, nil, TRUE, TRUE, nil, 0);
t->eq0 = ~0;
}
textshow(t, t->q0, t->q0);
switch(r){
case 0x1B:
if(t->eq0 != ~0)
textsetselect(t, t->eq0, t->q0);
if(t->ncache > 0){
if(t->w != nil)
wincommit(t->w, t);
else
textcommit(t, TRUE);
}
return;
case 0x08: /* ^H: erase character */
case 0x15: /* ^U: erase line */
case 0x17: /* ^W: erase word */
if(t->q0 == 0) /* nothing to erase */
return;
nnb = textbswidth(t, r);
q1 = t->q0;
q0 = q1-nnb;
/* if selection is at beginning of window, avoid deleting invisible text */
if(q0 < t->org){
q0 = t->org;
nnb = q1-q0;
}
if(nnb <= 0)
return;
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
u->nofill = TRUE;
nb = nnb;
n = u->ncache;
if(n > 0){
if(q1 != u->cq0+n)
error("text.type backspace");
if(n > nb)
n = nb;
u->ncache -= n;
textdelete(u, q1-n, q1, FALSE);
nb -= n;
}
if(u->eq0==q1 || u->eq0==~0)
u->eq0 = q0;
if(nb && u==t)
textdelete(u, q0, q0+nb, TRUE);
if(u != t)
textsetselect(u, u->q0, u->q1);
else
textsetselect(t, q0, q0);
u->nofill = FALSE;
}
for(i=0; i<t->file->ntext; i++)
textfill(t->file->text[i]);
return;
}
/* otherwise ordinary character; just insert, typically in caches of all texts */
for(i=0; i<t->file->ntext; i++){
u = t->file->text[i];
if(u->eq0 == ~0)
u->eq0 = t->q0;
if(u->ncache == 0)
u->cq0 = t->q0;
else if(t->q0 != u->cq0+u->ncache)
error("text.type cq1");
textinsert(u, t->q0, &r, 1, FALSE);
if(u != t)
textsetselect(u, u->q0, u->q1);
if(u->ncache == u->ncachealloc){
u->ncachealloc += 10;
u->cache = runerealloc(u->cache, u->ncachealloc);
}
u->cache[u->ncache++] = r;
}
textsetselect(t, t->q0+1, t->q0+1);
if(r=='\n' && t->w!=nil)
wincommit(t->w, t);
}
void
textcommit(Text *t, int tofile)
{
if(t->ncache == 0)
return;
if(tofile)
fileinsert(t->file, t->cq0, t->cache, t->ncache);
if(t->what == Body){
t->w->dirty = TRUE;
t->w->utflastqid = -1;
}
t->ncache = 0;
}
static Text *clicktext;
static uint clickmsec;
static Text *selecttext;
static uint selectq;
/*
* called from frame library
*/
void
framescroll(Frame *f, int dl)
{
if(f != &selecttext->Frame)
error("frameselect not right frame");
textframescroll(selecttext, dl);
}
void
textframescroll(Text *t, int dl)
{
uint q0;
if(dl == 0){
scrsleep(100);
return;
}
if(dl < 0){
q0 = textbacknl(t, t->org, -dl);
if(selectq > t->org+t->p0)
textsetselect(t, t->org+t->p0, selectq);
else
textsetselect(t, selectq, t->org+t->p0);
}else{
if(t->org+t->nchars == t->file->nc)
return;
q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
if(selectq > t->org+t->p1)
textsetselect(t, t->org+t->p1, selectq);
else
textsetselect(t, selectq, t->org+t->p1);
}
textsetorigin(t, q0, TRUE);
}
void
textselect(Text *t)
{
uint q0, q1;
int b, x, y;
int state;
selecttext = t;
/*
* To have double-clicking and chording, we double-click
* immediately if it might make sense.
*/
b = mouse->buttons;
q0 = t->q0;
q1 = t->q1;
selectq = t->org+frcharofpt(t, mouse->xy);
if(clicktext==t && mouse->msec-clickmsec<500)
if(q0==q1 && selectq==q0){
textdoubleclick(t, &q0, &q1);
textsetselect(t, q0, q1);
flushimage(display, 1);
x = mouse->xy.x;
y = mouse->xy.y;
/* stay here until something interesting happens */
do
readmouse(mousectl);
while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
mouse->xy.x = x; /* in case we're calling frselect */
mouse->xy.y = y;
q0 = t->q0; /* may have changed */
q1 = t->q1;
selectq = q0;
}
if(mouse->buttons == b){
t->Frame.scroll = framescroll;
frselect(t, mousectl);
/* horrible botch: while asleep, may have lost selection altogether */
if(selectq > t->file->nc)
selectq = t->org + t->p0;
t->Frame.scroll = nil;
if(selectq < t->org)
q0 = selectq;
else
q0 = t->org + t->p0;
if(selectq > t->org+t->nchars)
q1 = selectq;
else
q1 = t->org+t->p1;
}
if(q0 == q1){
if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
textdoubleclick(t, &q0, &q1);
clicktext = nil;
}else{
clicktext = t;
clickmsec = mouse->msec;
}
}else
clicktext = nil;
textsetselect(t, q0, q1);
flushimage(display, 1);
state = 0; /* undo when possible; +1 for cut, -1 for paste */
while(mouse->buttons){
mouse->msec = 0;
b = mouse->buttons;
if(b & 6){
if(state==0 && t->what==Body){
seq++;
filemark(t->w->body.file);
}
if(b & 2){
if(state==-1 && t->what==Body){
winundo(t->w, TRUE);
textsetselect(t, q0, t->q0);
state = 0;
}else if(state != 1){
cut(t, t, nil, TRUE, TRUE, nil, 0);
state = 1;
}
}else{
if(state==1 && t->what==Body){
winundo(t->w, TRUE);
textsetselect(t, q0, t->q1);
state = 0;
}else if(state != -1){
paste(t, t, nil, TRUE, FALSE, nil, 0);
state = -1;
}
}
textscrdraw(t);
clearmouse();
}
flushimage(display, 1);
while(mouse->buttons == b)
readmouse(mousectl);
clicktext = nil;
}
}
void
textshow(Text *t, uint q0, uint q1)
{
int qe;
int nl;
uint q;
if(t->what != Body)
return;
if(t->w!=nil && t->maxlines==0)
colgrow(t->col, t->w, 1);
textsetselect(t, q0, q1);
qe = t->org+t->nchars;
if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->nc+t->ncache)))
textscrdraw(t);
else{
if(t->w->nopen[QWevent] > 0)
nl = 3*t->maxlines/4;
else
nl = t->maxlines/4;
q = textbacknl(t, q0, nl);
/* avoid going backwards if trying to go forwards - long lines! */
if(!(q0>t->org && q<t->org))
textsetorigin(t, q, TRUE);
while(q0 > t->org+t->nchars)
textsetorigin(t, t->org+1, FALSE);
}
}
static
int
region(int a, int b)
{
if(a < b)
return -1;
if(a == b)
return 0;
return 1;
}
void
selrestore(Frame *f, Point pt0, uint p0, uint p1)
{
if(p1<=f->p0 || p0>=f->p1){
/* no overlap */
frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
return;
}
if(p0>=f->p0 && p1<=f->p1){
/* entirely inside */
frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
return;
}
/* they now are known to overlap */
/* before selection */
if(p0 < f->p0){
frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
p0 = f->p0;
pt0 = frptofchar(f, p0);
}
/* after selection */
if(p1 > f->p1){
frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
p1 = f->p1;
}
/* inside selection */
frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
}
void
textsetselect(Text *t, uint q0, uint q1)
{
int p0, p1;
/* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
t->q0 = q0;
t->q1 = q1;
/* compute desired p0,p1 from q0,q1 */
p0 = q0-t->org;
p1 = q1-t->org;
if(p0 < 0)
p0 = 0;
if(p1 < 0)
p1 = 0;
if(p0 > t->nchars)
p0 = t->nchars;
if(p1 > t->nchars)
p1 = t->nchars;
if(p0==t->p0 && p1==t->p1)
return;
/* screen disagrees with desired selection */
if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
/* no overlap or too easy to bother trying */
frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
goto Return;
}
/* overlap; avoid unnecessary painting */
if(p0 < t->p0){
/* extend selection backwards */
frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
}else if(p0 > t->p0){
/* trim first part of selection */
frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
}
if(p1 > t->p1){
/* extend selection forwards */
frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
}else if(p1 < t->p1){
/* trim last part of selection */
frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
}
Return:
t->p0 = p0;
t->p1 = p1;
}
uint;
xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */
{
uint p0, p1, q, tmp;
Point mp, pt0, pt1, qt;
int reg, b;
mp = mc->xy;
b = mc->buttons;
/* remove tick */
if(f->p0 == f->p1)
frtick(f, frptofchar(f, f->p0), 0);
p0 = p1 = frcharofpt(f, mp);
pt0 = frptofchar(f, p0);
pt1 = frptofchar(f, p1);
reg = 0;
frtick(f, pt0, 1);
do{
q = frcharofpt(f, mc->xy);
if(p1 != q){
if(p0 == p1)
frtick(f, pt0, 0);
if(reg != region(q, p0)){ /* crossed starting point; reset */
if(reg > 0)
selrestore(f, pt0, p0, p1);
else if(reg < 0)
selrestore(f, pt1, p1, p0);
p1 = p0;
pt1 = pt0;
reg = region(q, p0);
if(reg == 0)
frdrawsel0(f, pt0, p0, p1, col, display->white);
}
qt = frptofchar(f, q);
if(reg > 0){
if(q > p1)
frdrawsel0(f, pt1, p1, q, col, display->white);
else if(q < p1)
selrestore(f, qt, q, p1);
}else if(reg < 0){
if(q > p1)
selrestore(f, pt1, p1, q);
else
frdrawsel0(f, qt, q, p1, col, display->white);
}
p1 = q;
pt1 = qt;
}
if(p0 == p1)
frtick(f, pt0, 1);
flushimage(f->display, 1);
readmouse(mc);
}while(mc->buttons == b);
if(p1 < p0){
tmp = p0;
p0 = p1;
p1 = tmp;
}
pt0 = frptofchar(f, p0);
if(p0 == p1)
frtick(f, pt0, 0);
selrestore(f, pt0, p0, p1);
/* restore tick */
if(f->p0 == f->p1)
frtick(f, frptofchar(f, f->p0), 1);
flushimage(f->display, 1);
*p1p = p1;
return p0;
}
int
textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
{
uint p0, p1;
int buts;
p0 = xselect(t, mousectl, high, &p1);
buts = mousectl->buttons;
if((buts & mask) == 0){
*q0 = p0+t->org;
*q1 = p1+t->org;
}
while(mousectl->buttons)
readmouse(mousectl);
return buts;
}
int
textselect2(Text *t, uint *q0, uint *q1, Text **tp)
{
int buts;
*tp = nil;
buts = textselect23(t, q0, q1, but2col, 4);
if(buts & 4)
return 0;
if(buts & 1){ /* pick up argument */
*tp = argtext;
return 1;
}
return 1;
}
int
textselect3(Text *t, uint *q0, uint *q1)
{
int h;
h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
return h;
}
static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
static Rune left2[] = { L'\n', 0 };
static Rune left3[] = { L'\'', L'"', L'`', 0 };
static
Rune *left[] = {
left1,
left2,
left3,
nil
};
static
Rune *right[] = {
right1,
left2,
left3,
nil
};
void
textdoubleclick(Text *t, uint *q0, uint *q1)
{
int c, i;
Rune *r, *l, *p;
uint q;
for(i=0; left[i]!=nil; i++){
q = *q0;
l = left[i];
r = right[i];
/* try matching character to left, looking right */
if(q == 0)
c = '\n';
else
c = textreadc(t, q-1);
p = runestrchr(l, c);
if(p != nil){
if(textclickmatch(t, c, r[p-l], 1, &q))
*q1 = q-(c!='\n');
return;
}
/* try matching character to right, looking left */
if(q == t->file->nc)
c = '\n';
else
c = textreadc(t, q);
p = runestrchr(r, c);
if(p != nil){
if(textclickmatch(t, c, l[p-r], -1, &q)){
*q1 = *q0+(*q0<t->file->nc && c=='\n');
*q0 = q;
if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
(*q0)++;
}
return;
}
}
/* try filling out word to right */
while(*q1<t->file->nc && isalnum(textreadc(t, *q1)))
(*q1)++;
/* try filling out word to left */
while(*q0>0 && isalnum(textreadc(t, *q0-1)))
(*q0)--;
}
int
textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
{
Rune c;
int nest;
nest = 1;
for(;;){
if(dir > 0){
if(*q == t->file->nc)
break;
c = textreadc(t, *q);
(*q)++;
}else{
if(*q == 0)
break;
(*q)--;
c = textreadc(t, *q);
}
if(c == cr){
if(--nest==0)
return 1;
}else if(c == cl)
nest++;
}
return cl=='\n' && nest==1;
}
uint
textbacknl(Text *t, uint p, uint n)
{
int i, j;
/* look for start of this line if n==0 */
if(n==0 && p>0 && textreadc(t, p-1)!='\n')
n = 1;
i = n;
while(i-->0 && p>0){
--p; /* it's at a newline now; back over it */
if(p == 0)
break;
/* at 128 chars, call it a line anyway */
for(j=128; --j>0 && p>0; p--)
if(textreadc(t, p-1)=='\n')
break;
}
return p;
}
void
textsetorigin(Text *t, uint org, int exact)
{
int i, a, fixup;
Rune *r;
uint n;
if(org>0 && !exact){
/* org is an estimate of the char posn; find a newline */
/* don't try harder than 256 chars */
for(i=0; i<256 && org<t->file->nc; i++){
if(textreadc(t, org) == '\n'){
org++;
break;
}
org++;
}
}
a = org-t->org;
fixup = 0;
if(a>=0 && a<t->nchars){
frdelete(t, 0, a);
fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
}
else if(a<0 && -a<t->nchars){
n = t->org - org;
r = runemalloc(n);
bufread(t->file, org, r, n);
frinsert(t, r, r+n, 0);
free(r);
}else
frdelete(t, 0, t->nchars);
t->org = org;
textfill(t);
textscrdraw(t);
textsetselect(t, t->q0, t->q1);
if(fixup && t->p1 > t->p0)
frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
}
void
textreset(Text *t)
{
t->file->seq = 0;
t->eq0 = ~0;
/* do t->delete(0, t->nc, TRUE) without building backup stuff */
textsetselect(t, t->org, t->org);
frdelete(t, 0, t->nchars);
t->org = 0;
t->q0 = 0;
t->q1 = 0;
filereset(t->file);
bufreset(t->file);
}
next reply other threads:[~2001-11-05 8:07 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2001-11-05 8:07 Fco.J.Ballesteros [this message]
2001-11-08 4:51 rob pike
2001-11-08 7:49 Fco.J.Ballesteros
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20011105080740.64503199B5@mail.cse.psu.edu \
--to=nemo@plan9.escet.urjc.es \
--cc=9fans@cse.psu.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).