Ignore scroll/noscroll window setting. Instead, scroll when the write begins in or immediately after the displayed window content. In the new scrolling discipline, executing "Noscroll" is replaced by typing Page Up or using the mouse to scroll higher in the buffer, and executing "Scroll" is replaced by typing End or using the mouse to scroll to the bottom of the buffer. R=r, r2 http://codereview.appspot.com/4433060
851 lines
14 KiB
C
851 lines
14 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <thread.h>
|
|
#include <fcall.h>
|
|
#include <9pclient.h>
|
|
#include "term.h"
|
|
|
|
|
|
#define EVENTSIZE 256
|
|
#define STACK 32768
|
|
|
|
typedef struct Event Event;
|
|
typedef struct Q Q;
|
|
|
|
struct Event
|
|
{
|
|
int c1;
|
|
int c2;
|
|
int q0;
|
|
int q1;
|
|
int flag;
|
|
int nb;
|
|
int nr;
|
|
char b[EVENTSIZE*UTFmax+1];
|
|
Rune r[EVENTSIZE+1];
|
|
};
|
|
|
|
Event blank = {
|
|
'M',
|
|
'X',
|
|
0, 0, 0, 1, 1,
|
|
{ ' ', 0 },
|
|
{ ' ', 0 }
|
|
};
|
|
|
|
struct Q
|
|
{
|
|
QLock lk;
|
|
int p;
|
|
int k;
|
|
};
|
|
|
|
Q q;
|
|
|
|
CFid *eventfd;
|
|
CFid *addrfd;
|
|
CFid *datafd;
|
|
CFid *ctlfd;
|
|
/* int bodyfd; */
|
|
|
|
char *typing;
|
|
int ntypeb;
|
|
int ntyper;
|
|
int ntypebreak;
|
|
int debug;
|
|
int rcfd;
|
|
int cook = 1;
|
|
int password;
|
|
int israw(int);
|
|
|
|
char *name;
|
|
|
|
char **prog;
|
|
Channel *cwait;
|
|
int pid = -1;
|
|
|
|
int label(char*, int);
|
|
void error(char*, ...);
|
|
void stdinproc(void*);
|
|
void stdoutproc(void*);
|
|
void type(Event*, int, CFid*, CFid*);
|
|
void sende(Event*, int, CFid*, CFid*, CFid*, int);
|
|
char *onestring(int, char**);
|
|
int delete(Event*);
|
|
void deltype(uint, uint);
|
|
void sendbs(int, int);
|
|
void runproc(void*);
|
|
|
|
int
|
|
fsfidprint(CFid *fid, char *fmt, ...)
|
|
{
|
|
char buf[256];
|
|
va_list arg;
|
|
int n;
|
|
|
|
va_start(arg, fmt);
|
|
n = vsnprint(buf, sizeof buf, fmt, arg);
|
|
va_end(arg);
|
|
return fswrite(fid, buf, n);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: win cmd args...\n");
|
|
threadexitsall("usage");
|
|
}
|
|
|
|
void
|
|
waitthread(void *v)
|
|
{
|
|
recvp(cwait);
|
|
threadexitsall(nil);
|
|
}
|
|
|
|
void
|
|
hangupnote(void *a, char *msg)
|
|
{
|
|
if(strcmp(msg, "hangup") == 0 && pid != 0){
|
|
postnote(PNGROUP, pid, "hangup");
|
|
noted(NDFLT);
|
|
}
|
|
if(strstr(msg, "child")){
|
|
char buf[128];
|
|
int n;
|
|
|
|
n = awaitnohang(buf, sizeof buf-1);
|
|
if(n > 0){
|
|
buf[n] = 0;
|
|
if(atoi(buf) == pid)
|
|
threadexitsall(0);
|
|
}
|
|
noted(NCONT);
|
|
}
|
|
noted(NDFLT);
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char **argv)
|
|
{
|
|
int fd, id;
|
|
char buf[256];
|
|
char buf1[128];
|
|
CFsys *fs;
|
|
char *dump;
|
|
|
|
dump = onestring(argc, argv);
|
|
|
|
ARGBEGIN{
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'n':
|
|
name = EARGF(usage());
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
prog = argv;
|
|
|
|
if(name == nil){
|
|
if(argc > 0)
|
|
name = argv[0];
|
|
else{
|
|
name = sysname();
|
|
if(name == nil)
|
|
name = "gnot";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* notedisable("sys: write on closed pipe");
|
|
* not okay to disable the note, because that
|
|
* gets inherited by the subshell, so that something
|
|
* as simple as "yes | sed 10q" never exits.
|
|
* call notifyoff instead. (is notedisable ever safe?)
|
|
*/
|
|
notifyoff("sys: write on closed pipe");
|
|
|
|
noteenable("sys: child");
|
|
notify(hangupnote);
|
|
|
|
if((fs = nsmount("acme", "")) == 0)
|
|
sysfatal("nsmount acme: %r");
|
|
ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
|
|
if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12)
|
|
sysfatal("ctl: %r");
|
|
id = atoi(buf);
|
|
snprint(buf, sizeof buf, "%d", id);
|
|
putenv("winid", buf);
|
|
sprint(buf, "%d/tag", id);
|
|
fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
|
|
write(fd, " Send", 1+4);
|
|
close(fd);
|
|
sprint(buf, "%d/event", id);
|
|
eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
|
|
sprint(buf, "%d/addr", id);
|
|
addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
|
|
sprint(buf, "%d/data", id);
|
|
datafd = fsopen(fs, buf, ORDWR|OCEXEC);
|
|
sprint(buf, "%d/body", id);
|
|
/* bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
|
|
if(eventfd==nil || addrfd==nil || datafd==nil)
|
|
sysfatal("data files: %r");
|
|
/*
|
|
if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
|
|
sysfatal("data files: %r");
|
|
*/
|
|
fsunmount(fs);
|
|
|
|
cwait = threadwaitchan();
|
|
threadcreate(waitthread, nil, STACK);
|
|
pid = rcstart(argc, argv, &rcfd, nil);
|
|
if(pid == -1)
|
|
sysfatal("exec failed");
|
|
|
|
getwd(buf1, sizeof buf1);
|
|
sprint(buf, "name %s/-%s\n0\n", buf1, name);
|
|
fswrite(ctlfd, buf, strlen(buf));
|
|
sprint(buf, "dumpdir %s/\n", buf1);
|
|
fswrite(ctlfd, buf, strlen(buf));
|
|
sprint(buf, "dump %s\n", dump);
|
|
fswrite(ctlfd, buf, strlen(buf));
|
|
sprint(buf, "scroll");
|
|
fswrite(ctlfd, buf, strlen(buf));
|
|
|
|
updatewinsize(25, 80, 0, 0);
|
|
proccreate(stdoutproc, nil, STACK);
|
|
stdinproc(nil);
|
|
}
|
|
|
|
void
|
|
error(char *s, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
if(s){
|
|
va_start(arg, s);
|
|
s = vsmprint(s, arg);
|
|
va_end(arg);
|
|
fprint(2, "win: %s: %r\n", s);
|
|
}
|
|
if(pid != -1)
|
|
postnote(PNGROUP, pid, "hangup");
|
|
threadexitsall(s);
|
|
}
|
|
|
|
char*
|
|
onestring(int argc, char **argv)
|
|
{
|
|
char *p;
|
|
int i, n;
|
|
static char buf[1024];
|
|
|
|
if(argc == 0)
|
|
return "";
|
|
p = buf;
|
|
for(i=0; i<argc; i++){
|
|
n = strlen(argv[i]);
|
|
if(p+n+1 >= buf+sizeof buf)
|
|
break;
|
|
memmove(p, argv[i], n);
|
|
p += n;
|
|
*p++ = ' ';
|
|
}
|
|
p[-1] = 0;
|
|
return buf;
|
|
}
|
|
|
|
int
|
|
getec(CFid *efd)
|
|
{
|
|
static char buf[8192];
|
|
static char *bufp;
|
|
static int nbuf;
|
|
|
|
if(nbuf == 0){
|
|
nbuf = fsread(efd, buf, sizeof buf);
|
|
if(nbuf <= 0)
|
|
error(nil);
|
|
bufp = buf;
|
|
}
|
|
--nbuf;
|
|
return *bufp++;
|
|
}
|
|
|
|
int
|
|
geten(CFid *efd)
|
|
{
|
|
int n, c;
|
|
|
|
n = 0;
|
|
while('0'<=(c=getec(efd)) && c<='9')
|
|
n = n*10+(c-'0');
|
|
if(c != ' ')
|
|
error("event number syntax");
|
|
return n;
|
|
}
|
|
|
|
int
|
|
geter(CFid *efd, char *buf, int *nb)
|
|
{
|
|
Rune r;
|
|
int n;
|
|
|
|
r = getec(efd);
|
|
buf[0] = r;
|
|
n = 1;
|
|
if(r < Runeself)
|
|
goto Return;
|
|
while(!fullrune(buf, n))
|
|
buf[n++] = getec(efd);
|
|
chartorune(&r, buf);
|
|
Return:
|
|
*nb = n;
|
|
return r;
|
|
}
|
|
|
|
void
|
|
gete(CFid *efd, Event *e)
|
|
{
|
|
int i, nb;
|
|
|
|
e->c1 = getec(efd);
|
|
e->c2 = getec(efd);
|
|
e->q0 = geten(efd);
|
|
e->q1 = geten(efd);
|
|
e->flag = geten(efd);
|
|
e->nr = geten(efd);
|
|
if(e->nr > EVENTSIZE)
|
|
error("event string too long");
|
|
e->nb = 0;
|
|
for(i=0; i<e->nr; i++){
|
|
e->r[i] = geter(efd, e->b+e->nb, &nb);
|
|
e->nb += nb;
|
|
}
|
|
e->r[e->nr] = 0;
|
|
e->b[e->nb] = 0;
|
|
if(getec(efd) != '\n')
|
|
error("event syntax 2");
|
|
}
|
|
|
|
int
|
|
nrunes(char *s, int nb)
|
|
{
|
|
int i, n;
|
|
Rune r;
|
|
|
|
n = 0;
|
|
for(i=0; i<nb; n++)
|
|
i += chartorune(&r, s+i);
|
|
return n;
|
|
}
|
|
|
|
void
|
|
stdinproc(void *v)
|
|
{
|
|
CFid *cfd = ctlfd;
|
|
CFid *efd = eventfd;
|
|
CFid *dfd = datafd;
|
|
CFid *afd = addrfd;
|
|
int fd0 = rcfd;
|
|
Event e, e2, e3, e4;
|
|
int n;
|
|
|
|
USED(v);
|
|
|
|
for(;;){
|
|
if(debug)
|
|
fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
|
|
gete(efd, &e);
|
|
if(debug)
|
|
fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
|
|
qlock(&q.lk);
|
|
switch(e.c1){
|
|
default:
|
|
Unknown:
|
|
print("unknown message %c%c\n", e.c1, e.c2);
|
|
break;
|
|
|
|
case 'E': /* write to body or tag; can't affect us */
|
|
switch(e.c2){
|
|
case 'I':
|
|
case 'D': /* body */
|
|
if(debug)
|
|
fprint(2, "shift typing %d... ", e.q1-e.q0);
|
|
q.p += e.q1-e.q0;
|
|
break;
|
|
|
|
case 'i':
|
|
case 'd': /* tag */
|
|
break;
|
|
|
|
default:
|
|
goto Unknown;
|
|
}
|
|
break;
|
|
|
|
case 'F': /* generated by our actions; ignore */
|
|
break;
|
|
|
|
case 'K':
|
|
case 'M':
|
|
switch(e.c2){
|
|
case 'I':
|
|
if(e.nr == 1 && e.r[0] == 0x7F) {
|
|
char buf[1];
|
|
fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
|
|
fswrite(datafd, "", 0);
|
|
buf[0] = 0x7F;
|
|
write(fd0, buf, 1);
|
|
break;
|
|
}
|
|
if(e.q0 < q.p){
|
|
if(debug)
|
|
fprint(2, "shift typing %d... ", e.q1-e.q0);
|
|
q.p += e.q1-e.q0;
|
|
}
|
|
else if(e.q0 <= q.p+ntyper){
|
|
if(debug)
|
|
fprint(2, "type... ");
|
|
type(&e, fd0, afd, dfd);
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
n = delete(&e);
|
|
q.p -= n;
|
|
if(israw(fd0) && e.q1 >= q.p+n)
|
|
sendbs(fd0, n);
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
if(e.flag & 2)
|
|
gete(efd, &e2);
|
|
if(e.flag & 8){
|
|
gete(efd, &e3);
|
|
gete(efd, &e4);
|
|
}
|
|
if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
|
|
/* send it straight back */
|
|
fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
|
|
break;
|
|
}
|
|
if(e.q0==e.q1 && (e.flag&2)){
|
|
e2.flag = e.flag;
|
|
e = e2;
|
|
}
|
|
char buf[100];
|
|
snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
|
|
if(cistrcmp(buf, "cook") == 0) {
|
|
cook = 1;
|
|
break;
|
|
}
|
|
if(cistrcmp(buf, "nocook") == 0) {
|
|
cook = 0;
|
|
break;
|
|
}
|
|
if(e.flag & 8){
|
|
if(e.q1 != e.q0){
|
|
sende(&e, fd0, cfd, afd, dfd, 0);
|
|
sende(&blank, fd0, cfd, afd, dfd, 0);
|
|
}
|
|
sende(&e3, fd0, cfd, afd, dfd, 1);
|
|
}else if(e.q1 != e.q0)
|
|
sende(&e, fd0, cfd, afd, dfd, 1);
|
|
break;
|
|
|
|
case 'l':
|
|
case 'L':
|
|
/* just send it back */
|
|
if(e.flag & 2)
|
|
gete(efd, &e2);
|
|
fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
|
|
break;
|
|
|
|
case 'd':
|
|
case 'i':
|
|
break;
|
|
|
|
default:
|
|
goto Unknown;
|
|
}
|
|
}
|
|
qunlock(&q.lk);
|
|
}
|
|
}
|
|
|
|
void
|
|
stdoutproc(void *v)
|
|
{
|
|
int fd1 = rcfd;
|
|
CFid *afd = addrfd;
|
|
CFid *dfd = datafd;
|
|
int n, m, w, npart;
|
|
char *buf, *s, *t;
|
|
Rune r;
|
|
char x[16], hold[UTFmax];
|
|
|
|
USED(v);
|
|
buf = malloc(8192+UTFmax+1);
|
|
npart = 0;
|
|
for(;;){
|
|
/* Let typing have a go -- maybe there's a rubout waiting. */
|
|
yield();
|
|
n = read(fd1, buf+npart, 8192);
|
|
if(n <= 0)
|
|
error(nil);
|
|
|
|
n = echocancel(buf+npart, n);
|
|
if(n == 0)
|
|
continue;
|
|
|
|
n = dropcrnl(buf+npart, n);
|
|
if(n == 0)
|
|
continue;
|
|
|
|
/* squash NULs */
|
|
s = memchr(buf+npart, 0, n);
|
|
if(s){
|
|
for(t=s; s<buf+npart+n; s++)
|
|
if(*t = *s) /* assign = */
|
|
t++;
|
|
n = t-(buf+npart);
|
|
}
|
|
|
|
n += npart;
|
|
|
|
/* hold on to final partial rune */
|
|
npart = 0;
|
|
while(n>0 && (buf[n-1]&0xC0)){
|
|
--n;
|
|
npart++;
|
|
if((buf[n]&0xC0)!=0x80){
|
|
if(fullrune(buf+n, npart)){
|
|
w = chartorune(&r, buf+n);
|
|
n += w;
|
|
npart -= w;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(n > 0){
|
|
memmove(hold, buf+n, npart);
|
|
buf[n] = 0;
|
|
n = label(buf, n);
|
|
buf[n] = 0;
|
|
|
|
// clumsy but effective: notice password
|
|
// prompts so we can disable echo.
|
|
password = 0;
|
|
if(cistrstr(buf, "password")) {
|
|
int i;
|
|
|
|
i = n;
|
|
while(i > 0 && buf[i-1] == ' ')
|
|
i--;
|
|
password = i > 0 && buf[i-1] == ':';
|
|
}
|
|
|
|
qlock(&q.lk);
|
|
m = sprint(x, "#%d", q.p);
|
|
if(fswrite(afd, x, m) != m){
|
|
fprint(2, "stdout writing address %s: %r; resetting\n", x);
|
|
if(fswrite(afd, "$", 1) < 0)
|
|
fprint(2, "reset: %r\n");
|
|
fsseek(afd, 0, 0);
|
|
m = fsread(afd, x, sizeof x-1);
|
|
if(m >= 0){
|
|
x[m] = 0;
|
|
q.p = atoi(x);
|
|
}
|
|
}
|
|
if(fswrite(dfd, buf, n) != n)
|
|
error("stdout writing body");
|
|
/* Make sure acme scrolls to the end of the above write. */
|
|
if(fswrite(dfd, nil, 0) != 0)
|
|
error("stdout flushing body");
|
|
q.p += nrunes(buf, n);
|
|
qunlock(&q.lk);
|
|
memmove(buf, hold, npart);
|
|
}
|
|
}
|
|
}
|
|
|
|
char wdir[512];
|
|
int
|
|
label(char *sr, int n)
|
|
{
|
|
char *sl, *el, *er, *r, *p;
|
|
|
|
er = sr+n;
|
|
for(r=er-1; r>=sr; r--)
|
|
if(*r == '\007')
|
|
break;
|
|
if(r < sr)
|
|
return n;
|
|
|
|
el = r+1;
|
|
if(el-sr > sizeof wdir - strlen(name) - 20)
|
|
sr = el - sizeof wdir - strlen(name) - 20;
|
|
for(sl=el-3; sl>=sr; sl--)
|
|
if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
|
|
break;
|
|
if(sl < sr)
|
|
return n;
|
|
|
|
*r = 0;
|
|
/*
|
|
* add /-sysname if not present
|
|
*/
|
|
snprint(wdir, sizeof wdir, "name %s", sl+3);
|
|
p = strrchr(wdir, '/');
|
|
if(p==nil || *(p+1) != '-'){
|
|
p = wdir+strlen(wdir);
|
|
if(*(p-1) != '/')
|
|
*p++ = '/';
|
|
*p++ = '-';
|
|
strcpy(p, name);
|
|
}
|
|
strcat(wdir, "\n0\n");
|
|
fswrite(ctlfd, wdir, strlen(wdir));
|
|
|
|
memmove(sl, el, er-el);
|
|
n -= (el-sl);
|
|
return n;
|
|
}
|
|
|
|
int
|
|
delete(Event *e)
|
|
{
|
|
uint q0, q1;
|
|
int deltap;
|
|
|
|
q0 = e->q0;
|
|
q1 = e->q1;
|
|
if(q1 <= q.p)
|
|
return e->q1-e->q0;
|
|
if(q0 >= q.p+ntyper)
|
|
return 0;
|
|
deltap = 0;
|
|
if(q0 < q.p){
|
|
deltap = q.p-q0;
|
|
q0 = 0;
|
|
}else
|
|
q0 -= q.p;
|
|
if(q1 > q.p+ntyper)
|
|
q1 = ntyper;
|
|
else
|
|
q1 -= q.p;
|
|
deltype(q0, q1);
|
|
return deltap;
|
|
}
|
|
|
|
void
|
|
addtype(int c, uint p0, char *b, int nb, int nr)
|
|
{
|
|
int i, w;
|
|
Rune r;
|
|
uint p;
|
|
char *b0;
|
|
|
|
for(i=0; i<nb; i+=w){
|
|
w = chartorune(&r, b+i);
|
|
if((r==0x7F||r==3) && c=='K'){
|
|
write(rcfd, "\x7F", 1);
|
|
/* toss all typing */
|
|
q.p += ntyper+nr;
|
|
ntypebreak = 0;
|
|
ntypeb = 0;
|
|
ntyper = 0;
|
|
/* buglet: more than one delete ignored */
|
|
return;
|
|
}
|
|
if(r=='\n' || r==0x04)
|
|
ntypebreak++;
|
|
}
|
|
typing = realloc(typing, ntypeb+nb);
|
|
if(typing == nil)
|
|
error("realloc");
|
|
if(p0 == ntyper)
|
|
memmove(typing+ntypeb, b, nb);
|
|
else{
|
|
b0 = typing;
|
|
for(p=0; p<p0 && b0<typing+ntypeb; p++){
|
|
w = chartorune(&r, b0+i);
|
|
b0 += w;
|
|
}
|
|
if(p != p0)
|
|
error("typing: findrune");
|
|
memmove(b0+nb, b0, (typing+ntypeb)-b0);
|
|
memmove(b0, b, nb);
|
|
}
|
|
ntypeb += nb;
|
|
ntyper += nr;
|
|
}
|
|
|
|
int
|
|
israw(int fd0)
|
|
{
|
|
return (!cook || password) && !isecho(fd0);
|
|
}
|
|
|
|
void
|
|
sendtype(int fd0)
|
|
{
|
|
int i, n, nr, raw;
|
|
|
|
raw = israw(fd0);
|
|
while(ntypebreak || (raw && ntypeb > 0)){
|
|
for(i=0; i<ntypeb; i++)
|
|
if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){
|
|
if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0)
|
|
ntypebreak--;
|
|
n = i+1;
|
|
i++;
|
|
if(!raw)
|
|
echoed(typing, n);
|
|
if(write(fd0, typing, n) != n)
|
|
error("sending to program");
|
|
nr = nrunes(typing, i);
|
|
q.p += nr;
|
|
ntyper -= nr;
|
|
ntypeb -= i;
|
|
memmove(typing, typing+i, ntypeb);
|
|
goto cont2;
|
|
}
|
|
print("no breakchar\n");
|
|
ntypebreak = 0;
|
|
cont2:;
|
|
}
|
|
}
|
|
|
|
void
|
|
sendbs(int fd0, int n)
|
|
{
|
|
char buf[128];
|
|
int m;
|
|
|
|
memset(buf, 0x08, sizeof buf);
|
|
while(n > 0) {
|
|
m = sizeof buf;
|
|
if(m > n)
|
|
m = n;
|
|
n -= m;
|
|
write(fd0, buf, m);
|
|
}
|
|
}
|
|
|
|
void
|
|
deltype(uint p0, uint p1)
|
|
{
|
|
int w;
|
|
uint p, b0, b1;
|
|
Rune r;
|
|
|
|
/* advance to p0 */
|
|
b0 = 0;
|
|
for(p=0; p<p0 && b0<ntypeb; p++){
|
|
w = chartorune(&r, typing+b0);
|
|
b0 += w;
|
|
}
|
|
if(p != p0)
|
|
error("deltype 1");
|
|
/* advance to p1 */
|
|
b1 = b0;
|
|
for(; p<p1 && b1<ntypeb; p++){
|
|
w = chartorune(&r, typing+b1);
|
|
b1 += w;
|
|
if(r=='\n' || r==0x04)
|
|
ntypebreak--;
|
|
}
|
|
if(p != p1)
|
|
error("deltype 2");
|
|
memmove(typing+b0, typing+b1, ntypeb-b1);
|
|
ntypeb -= b1-b0;
|
|
ntyper -= p1-p0;
|
|
}
|
|
|
|
void
|
|
type(Event *e, int fd0, CFid *afd, CFid *dfd)
|
|
{
|
|
int m, n, nr;
|
|
char buf[128];
|
|
|
|
if(e->nr > 0)
|
|
addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
|
|
else{
|
|
m = e->q0;
|
|
while(m < e->q1){
|
|
n = sprint(buf, "#%d", m);
|
|
fswrite(afd, buf, n);
|
|
n = fsread(dfd, buf, sizeof buf);
|
|
nr = nrunes(buf, n);
|
|
while(m+nr > e->q1){
|
|
do; while(n>0 && (buf[--n]&0xC0)==0x80);
|
|
--nr;
|
|
}
|
|
if(n == 0)
|
|
break;
|
|
addtype(e->c1, m-q.p, buf, n, nr);
|
|
m += nr;
|
|
}
|
|
}
|
|
if(israw(fd0)) {
|
|
n = sprint(buf, "#%d,#%d", e->q0, e->q1);
|
|
fswrite(afd, buf, n);
|
|
fswrite(dfd, "", 0);
|
|
q.p -= e->q1 - e->q0;
|
|
}
|
|
sendtype(fd0);
|
|
if(e->nb > 0 && e->b[e->nb-1] == '\n')
|
|
cook = 1;
|
|
}
|
|
|
|
void
|
|
sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl)
|
|
{
|
|
int l, m, n, nr, lastc, end;
|
|
char abuf[16], buf[128];
|
|
|
|
end = q.p+ntyper;
|
|
l = sprint(abuf, "#%d", end);
|
|
fswrite(afd, abuf, l);
|
|
if(e->nr > 0){
|
|
fswrite(dfd, e->b, e->nb);
|
|
addtype(e->c1, ntyper, e->b, e->nb, e->nr);
|
|
lastc = e->r[e->nr-1];
|
|
}else{
|
|
m = e->q0;
|
|
lastc = 0;
|
|
while(m < e->q1){
|
|
n = sprint(buf, "#%d", m);
|
|
fswrite(afd, buf, n);
|
|
n = fsread(dfd, buf, sizeof buf);
|
|
nr = nrunes(buf, n);
|
|
while(m+nr > e->q1){
|
|
do; while(n>0 && (buf[--n]&0xC0)==0x80);
|
|
--nr;
|
|
}
|
|
if(n == 0)
|
|
break;
|
|
l = sprint(abuf, "#%d", end);
|
|
fswrite(afd, abuf, l);
|
|
fswrite(dfd, buf, n);
|
|
addtype(e->c1, ntyper, buf, n, nr);
|
|
lastc = buf[n-1];
|
|
m += nr;
|
|
end += nr;
|
|
}
|
|
}
|
|
if(donl && lastc!='\n'){
|
|
fswrite(dfd, "\n", 1);
|
|
addtype(e->c1, ntyper, "\n", 1, 1);
|
|
}
|
|
fswrite(cfd, "dot=addr", 8);
|
|
sendtype(fd0);
|
|
}
|