From a150899221a5badff9740703b754f901b4f52762 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Thu, 14 Apr 2011 17:27:24 +0000 Subject: fill /acme --- acme/bin/source/win/main.c | 646 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 646 insertions(+) create mode 100644 acme/bin/source/win/main.c (limited to 'acme/bin/source/win/main.c') diff --git a/acme/bin/source/win/main.c b/acme/bin/source/win/main.c new file mode 100644 index 000000000..916f2b05f --- /dev/null +++ b/acme/bin/source/win/main.c @@ -0,0 +1,646 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include "dat.h" + +void mainctl(void*); +void startcmd(char *[], int*); +void stdout2body(void*); + +int debug; +int notepg; +int eraseinput; +int dirty = 0; + +Window *win; /* the main window */ + +void +usage(void) +{ + fprint(2, "usage: win [command]\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + int i, j; + char *dir, *tag, *name; + char buf[1024], **av; + + quotefmtinstall(); + rfork(RFNAMEG); + ARGBEGIN{ + case 'd': + debug = 1; + chatty9p++; + break; + case 'e': + eraseinput = 1; + break; + case 'D': +{extern int _threaddebuglevel; + _threaddebuglevel = 1<<20; +} + }ARGEND + + if(argc == 0){ + av = emalloc(3*sizeof(char*)); + av[0] = "rc"; + av[1] = "-i"; + name = getenv("sysname"); + }else{ + av = argv; + name = utfrrune(av[0], '/'); + if(name) + name++; + else + name = av[0]; + } + + if(getwd(buf, sizeof buf) == 0) + dir = "/"; + else + dir = buf; + dir = estrdup(dir); + tag = estrdup(dir); + tag = eappend(estrdup(tag), "/-", name); + win = newwindow(); + snprint(buf, sizeof buf, "%d", win->id); + putenv("winid", buf); + winname(win, tag); + wintagwrite(win, "Send Noscroll", 5+8); + threadcreate(mainctl, win, STACK); + mountcons(); + threadcreate(fsloop, nil, STACK); + startpipe(); + startcmd(av, ¬epg); + + strcpy(buf, "win"); + j = 3; + for(i=0; ictl, "scroll"); + winsetdump(win, dir, buf); +} + +int +EQUAL(char *s, char *t) +{ + while(tolower(*s) == tolower(*t++)) + if(*s++ == '\0') + return 1; + return 0; +} + +int +command(Window *w, char *s) +{ + while(*s==' ' || *s=='\t' || *s=='\n') + s++; + if(strcmp(s, "Delete")==0 || strcmp(s, "Del")==0){ + windel(w, 1); + threadexitsall(nil); + return 1; + } + if(EQUAL(s, "scroll")){ + ctlprint(w->ctl, "scroll\nshow"); + return 1; + } + if(EQUAL(s, "noscroll")){ + ctlprint(w->ctl, "noscroll"); + return 1; + } + return 0; +} + +static long +utfncpy(char *to, char *from, int n) +{ + char *end, *e; + + e = to+n; + if(to >= e) + return 0; + end = memccpy(to, from, '\0', e - to); + if(end == nil){ + end = e; + if(end[-1]&0x80){ + if(end-2>=to && (end[-2]&0xE0)==0xC0) + return end-to; + if(end-3>=to && (end[-3]&0xF0)==0xE0) + return end-to; + while(end>to && (*--end&0xC0)==0x80) + ; + } + }else + end--; + return end - to; +} + +/* sendinput and fsloop run in the same proc (can't interrupt each other). */ +static Req *q; +static Req **eq; +static int +__sendinput(Window *w, ulong q0, ulong q1) +{ + char *s, *t; + int n, nb, eofchar; + static int partial; + static char tmp[UTFmax]; + Req *r; + Rune rune; + + if(!q) + return 0; + + r = q; + n = 0; + if(partial){ + Partial: + nb = partial; + if(nb > r->ifcall.count) + nb = r->ifcall.count; + memmove(r->ofcall.data, tmp, nb); + if(nb!=partial) + memmove(tmp, tmp+nb, partial-nb); + partial -= nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + r->ofcall.count = nb; + if(debug) + fprint(2, "satisfy read with partial\n"); + respond(r, nil); + return n; + } + if(q0==q1) + return 0; + s = emalloc((q1-q0)*UTFmax+1); + n = winread(w, q0, q1, s); + s[n] = '\0'; + t = strpbrk(s, "\n\004"); + if(t == nil){ + free(s); + return 0; + } + r = q; + eofchar = 0; + if(*t == '\004'){ + eofchar = 1; + *t = '\0'; + }else + *++t = '\0'; + nb = utfncpy((char*)r->ofcall.data, s, r->ifcall.count); + if(nb==0 && sifcall.count > 0){ + partial = utfncpy(tmp, s, UTFmax); + assert(partial > 0); + chartorune(&rune, tmp); + partial = runelen(rune); + free(s); + n = 1; + goto Partial; + } + n = utfnlen(r->ofcall.data, nb); + if(nb==strlen(s) && eofchar) + n++; + r->ofcall.count = nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + if(debug) + fprint(2, "read returns %lud-%lud: %.*q\n", q0, q0+n, n, r->ofcall.data); + respond(r, nil); + return n; +} + +static int +_sendinput(Window *w, ulong q0, ulong *q1) +{ + char buf[32]; + int n; + + n = __sendinput(w, q0, *q1); + if(!n || !eraseinput) + return n; + /* erase q0 to q0+n */ + sprint(buf, "#%lud,#%lud", q0, q0+n); + winsetaddr(w, buf, 0); + write(w->data, buf, 0); + *q1 -= n; + return 0; +} + +int +sendinput(Window *w, ulong q0, ulong *q1) +{ + ulong n; + Req *oq; + + n = 0; + do { + oq = q; + n += _sendinput(w, q0+n, q1); + } while(q != oq); + return n; +} + +Event esendinput; +void +fsloop(void*) +{ + Fsevent e; + Req **l, *r; + + eq = &q; + memset(&esendinput, 0, sizeof esendinput); + esendinput.c1 = 'C'; + for(;;){ + while(recv(fschan, &e) == -1) + ; + r = e.r; + switch(e.type){ + case 'r': + *eq = r; + r->aux = nil; + eq = &r->aux; + /* call sendinput with hostpt and endpt */ + sendp(win->cevent, &esendinput); + break; + case 'f': + for(l=&q; *l; l=&(*l)->aux){ + if(*l == r->oldreq){ + *l = (*l)->aux; + if(*l == nil) + eq = l; + respond(r->oldreq, "interrupted"); + break; + } + } + respond(r, nil); + break; + } + } +} + +void +sendit(char *s) +{ +// char tmp[32]; + + write(win->body, s, strlen(s)); +/* + * RSC: The problem here is that other procs can call sendit, + * so we lose our single-threadedness if we call sendinput. + * In fact, we don't even have the right queue memory, + * I think that we'll get a write event from the body write above, + * and we can do the sendinput then, from our single thread. + * + * I still need to figure out how to test this assertion for + * programs that use /srv/win* + * + winselect(win, "$", 0); + seek(win->addr, 0UL, 0); + if(read(win->addr, tmp, 2*12) == 2*12) + hostpt += sendinput(win, hostpt, atol(tmp), ); + */ +} + +void +execevent(Window *w, Event *e, int (*command)(Window*, char*)) +{ + Event *ea, *e2; + int n, na, len, needfree; + char *s, *t; + + ea = nil; + e2 = nil; + if(e->flag & 2) + e2 = recvp(w->cevent); + if(e->flag & 8){ + ea = recvp(w->cevent); + na = ea->nb; + recvp(w->cevent); + }else + na = 0; + + needfree = 0; + s = e->b; + if(e->nb==0 && (e->flag&2)){ + s = e2->b; + e->q0 = e2->q0; + e->q1 = e2->q1; + e->nb = e2->nb; + } + if(e->nb==0 && e->q0q1){ + /* fetch data from window */ + s = emalloc((e->q1-e->q0)*UTFmax+2); + n = winread(w, e->q0, e->q1, s); + s[n] = '\0'; + needfree = 1; + }else + if(na){ + t = emalloc(strlen(s)+1+na+2); + sprint(t, "%s %s", s, ea->b); + if(needfree) + free(s); + s = t; + needfree = 1; + } + + /* if it's a known command, do it */ + /* if it's a long message, it can't be for us anyway */ + if(!command(w, s) && s[0]!='\0'){ /* send it as typed text */ + /* if it's a built-in from the tag, send it back */ + if(e->flag & 1) + fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); + else{ /* send text to main window */ + len = strlen(s); + if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){ + if(!needfree){ + /* if(needfree), we left room for a newline before */ + t = emalloc(len+2); + strcpy(t, s); + s = t; + needfree = 1; + } + s[len++] = '\n'; + s[len] = '\0'; + } + sendit(s); + } + } + if(needfree) + free(s); +} + +int +hasboundary(Rune *r, int nr) +{ + int i; + + for(i=0; icevent); + if(debug) + fprint(2, "msg: %C %C %d %d %d %d %q\n", + e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b); + switch(e->c1){ + default: + Unknown: + fprint(2, "unknown message %c%c\n", e->c1, e->c2); + break; + + case 'C': /* input needed for /dev/cons */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'S': /* output to stdout */ + sprint(tmp, "#%lud", hostpt); + winsetaddr(w, tmp, 0); + write(w->data, e->b, e->nb); + pendingS += e->nr; + break; + + case 'E': /* write to tag or body; body happens due to sendit */ + delta = e->q1-e->q0; + if(e->c2=='I'){ + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'F': /* generated by our actions (specifically case 'S' above) */ + delta = e->q1-e->q0; + if(e->c2=='D'){ + /* we know about the delete by _sendinput */ + break; + } + if(e->c2=='I'){ + pendingS -= e->q1 - e->q0; + if(pendingS < 0) + fprint(2, "win: pendingS = %d\n", pendingS); + if(e->q0 != hostpt) + fprint(2, "win: insert at %d expected %lud\n", e->q0, hostpt); + endpt += delta; + hostpt += delta; + sendp(writechan, nil); + if(pendingS == 0 && pendingK){ + pendingK = 0; + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'K': + delta = e->q1-e->q0; + switch(e->c2){ + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'I': + delta = e->q1 - e->q0; + endpt += delta; + if(endpt < e->q1) /* just in case */ + endpt = e->q1; + if(e->q0 < hostpt) + hostpt += delta; + if(e->nr>0 && e->r[e->nr-1]==0x7F){ + write(notepg, "interrupt", 9); + hostpt = endpt; + break; + } + if(e->q0 >= hostpt + && hasboundary(e->r, e->nr)){ + /* + * If we are between the S message (which + * we processed by inserting text in the + * window) and the F message notifying us + * that the text has been inserted, then our + * impression of the hostpt and acme's + * may be different. This could be seen if you + * hit enter a bunch of times in a con + * session. To work around the unreliability, + * only send input if we don't have an S pending. + * The same race occurs between when a character + * is typed and when we get notice of it, but + * since characters tend to be typed at the end + * of the buffer, we don't run into it. There's + * no workaround possible for this typing race, + * since we can't tell when the user has typed + * something but we just haven't been notified. + */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + break; + + case 'M': /* mouse */ + delta = e->q1-e->q0; + switch(e->c2){ + case 'x': + case 'X': + execevent(w, e, command); + break; + + case 'l': /* reflect all searches back to acme */ + case 'L': + if(e->flag & 2) + recvp(w->cevent); + winwriteevent(w, e); + break; + + case 'I': + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'd': /* modify away; we don't care */ + case 'i': + break; + + default: + goto Unknown; + } + } + } +} + +enum +{ + NARGS = 100, + NARGCHAR = 8*1024, + EXECSTACK = STACK+(NARGS+1)*sizeof(char*)+NARGCHAR +}; + +struct Exec +{ + char **argv; + Channel *cpid; +}; + +int +lookinbin(char *s) +{ + if(s[0] == '/') + return 0; + if(s[0]=='.' && s[1]=='/') + return 0; + if(s[0]=='.' && s[1]=='.' && s[2]=='/') + return 0; + return 1; +} + +/* adapted from mail. not entirely free of details from that environment */ +void +execproc(void *v) +{ + struct Exec *e; + char *cmd, **av; + Channel *cpid; + + e = v; + rfork(RFCFDG|RFNOTEG); + av = e->argv; + close(0); + open("/dev/cons", OREAD); + close(1); + open("/dev/cons", OWRITE); + dup(1, 2); + cpid = e->cpid; + free(e); + procexec(cpid, av[0], av); + if(lookinbin(av[0])){ + cmd = estrstrdup("/bin/", av[0]); + procexec(cpid, cmd, av); + } + error("can't exec %s: %r", av[0]); +} + +void +startcmd(char *argv[], int *notepg) +{ + struct Exec *e; + Channel *cpid; + char buf[64]; + int pid; + + e = emalloc(sizeof(struct Exec)); + e->argv = argv; + cpid = chancreate(sizeof(ulong), 0); + e->cpid = cpid; + sprint(buf, "/mnt/wsys/%d", win->id); + bind(buf, "/dev/acme", MREPL); + proccreate(execproc, e, EXECSTACK); + do + pid = recvul(cpid); + while(pid == -1); + sprint(buf, "/proc/%d/notepg", pid); + *notepg = open(buf, OWRITE); +} -- cgit v1.2.3