diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/acme/edit.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/acme/edit.c')
-rwxr-xr-x | sys/src/cmd/acme/edit.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/sys/src/cmd/acme/edit.c b/sys/src/cmd/acme/edit.c new file mode 100755 index 000000000..977b21a7a --- /dev/null +++ b/sys/src/cmd/acme/edit.c @@ -0,0 +1,679 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include "dat.h" +#include "edit.h" +#include "fns.h" + +static char linex[]="\n"; +static char wordx[]=" \t\n"; +struct cmdtab cmdtab[]={ +/* cmdc text regexp addr defcmd defaddr count token fn */ + '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd, + 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd, + 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd, + 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd, + 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd, + 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd, + 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd, + 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd, + 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd, + 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd, + 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd, + 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd, + 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd, + 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd, + 'B', 0, 0, 0, 0, aNo, 0, linex, B_cmd, + 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd, + 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + '<', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, + '|', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, + '>', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, +/* deliberately unimplemented: + 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd, + 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd, + 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd, + '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, + */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +Cmd *parsecmd(int); +Addr *compoundaddr(void); +Addr *simpleaddr(void); +void freecmd(void); +void okdelim(int); + +Rune *cmdstartp; +Rune *cmdendp; +Rune *cmdp; +Channel *editerrc; + +String *lastpat; +int patset; + +List cmdlist; +List addrlist; +List stringlist; +Text *curtext; +int editing = Inactive; + +String* newstring(int); + +void +editthread(void*) +{ + Cmd *cmdp; + + threadsetname("editthread"); + while((cmdp=parsecmd(0)) != 0){ +// ocurfile = curfile; +// loaded = curfile && !curfile->unread; + if(cmdexec(curtext, cmdp) == 0) + break; + freecmd(); + } + sendp(editerrc, nil); +} + +void +allelogterm(Window *w, void*) +{ + elogterm(w->body.file); +} + +void +alleditinit(Window *w, void*) +{ + textcommit(&w->tag, TRUE); + textcommit(&w->body, TRUE); + w->body.file->editclean = FALSE; +} + +void +allupdate(Window *w, void*) +{ + Text *t; + int i; + File *f; + + t = &w->body; + f = t->file; + if(f->curtext != t) /* do curtext only */ + return; + if(f->elog.type == Null) + elogterm(f); + else if(f->elog.type != Empty){ + elogapply(f); + if(f->editclean){ + f->mod = FALSE; + for(i=0; i<f->ntext; i++) + f->text[i]->w->dirty = FALSE; + } + } + textsetselect(t, t->q0, t->q1); + textscrdraw(t); + winsettag(w); +} + +void +editerror(char *fmt, ...) +{ + va_list arg; + char *s; + + va_start(arg, fmt); + s = vsmprint(fmt, arg); + va_end(arg); + freecmd(); + allwindows(allelogterm, nil); /* truncate the edit logs */ + sendp(editerrc, s); + threadexits(nil); +} + +void +editcmd(Text *ct, Rune *r, uint n) +{ + char *err; + + if(n == 0) + return; + if(2*n > RBUFSIZE){ + warning(nil, "string too long\n"); + return; + } + + allwindows(alleditinit, nil); + if(cmdstartp) + free(cmdstartp); + cmdstartp = runemalloc(n+2); + runemove(cmdstartp, r, n); + if(r[n] != '\n') + cmdstartp[n++] = '\n'; + cmdstartp[n] = '\0'; + cmdendp = cmdstartp+n; + cmdp = cmdstartp; + if(ct->w == nil) + curtext = nil; + else + curtext = &ct->w->body; + resetxec(); + if(editerrc == nil){ + editerrc = chancreate(sizeof(char*), 0); + lastpat = allocstring(0); + } + threadcreate(editthread, nil, STACK); + err = recvp(editerrc); + editing = Inactive; + if(err != nil){ + if(err[0] != '\0') + warning(nil, "Edit: %s\n", err); + free(err); + } + + /* update everyone whose edit log has data */ + allwindows(allupdate, nil); +} + +int +getch(void) +{ + if(*cmdp == *cmdendp) + return -1; + return *cmdp++; +} + +int +nextc(void) +{ + if(*cmdp == *cmdendp) + return -1; + return *cmdp; +} + +void +ungetch(void) +{ + if(--cmdp < cmdstartp) + error("ungetch"); +} + +long +getnum(int signok) +{ + long n; + int c, sign; + + n = 0; + sign = 1; + if(signok>1 && nextc()=='-'){ + sign = -1; + getch(); + } + if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */ + return sign; + while('0'<=(c=getch()) && c<='9') + n = n*10 + (c-'0'); + ungetch(); + return sign*n; +} + +int +cmdskipbl(void) +{ + int c; + do + c = getch(); + while(c==' ' || c=='\t'); + if(c >= 0) + ungetch(); + return c; +} + +/* + * Check that list has room for one more element. + */ +void +growlist(List *l) +{ + if(l->listptr==0 || l->nalloc==0){ + l->nalloc = INCR; + l->listptr = emalloc(INCR*sizeof(void*)); + l->nused = 0; + }else if(l->nused == l->nalloc){ + l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*)); + memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*)); + l->nalloc += INCR; + } +} + +/* + * Remove the ith element from the list + */ +void +dellist(List *l, int i) +{ + memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-(i+1))*sizeof(void*)); + l->nused--; +} + +/* + * Add a new element, whose position is i, to the list + */ +void +inslist(List *l, int i, void *v) +{ + growlist(l); + memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*)); + l->ptr[i] = v; + l->nused++; +} + +void +listfree(List *l) +{ + free(l->listptr); + free(l); +} + +String* +allocstring(int n) +{ + String *s; + + s = emalloc(sizeof(String)); + s->n = n; + s->nalloc = n+10; + s->r = emalloc(s->nalloc*sizeof(Rune)); + s->r[n] = '\0'; + return s; +} + +void +freestring(String *s) +{ + free(s->r); + free(s); +} + +Cmd* +newcmd(void){ + Cmd *p; + + p = emalloc(sizeof(Cmd)); + inslist(&cmdlist, cmdlist.nused, p); + return p; +} + +String* +newstring(int n) +{ + String *p; + + p = allocstring(n); + inslist(&stringlist, stringlist.nused, p); + return p; +} + +Addr* +newaddr(void) +{ + Addr *p; + + p = emalloc(sizeof(Addr)); + inslist(&addrlist, addrlist.nused, p); + return p; +} + +void +freecmd(void) +{ + int i; + + while(cmdlist.nused > 0) + free(cmdlist.ucharptr[--cmdlist.nused]); + while(addrlist.nused > 0) + free(addrlist.ucharptr[--addrlist.nused]); + while(stringlist.nused>0){ + i = --stringlist.nused; + freestring(stringlist.stringptr[i]); + } +} + +void +okdelim(int c) +{ + if(c=='\\' || ('a'<=c && c<='z') + || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) + editerror("bad delimiter %c\n", c); +} + +void +atnl(void) +{ + int c; + + cmdskipbl(); + c = getch(); + if(c != '\n') + editerror("newline expected (saw %C)", c); +} + +void +Straddc(String *s, int c) +{ + if(s->n+1 >= s->nalloc){ + s->nalloc += 10; + s->r = erealloc(s->r, s->nalloc*sizeof(Rune)); + } + s->r[s->n++] = c; + s->r[s->n] = '\0'; +} + +void +getrhs(String *s, int delim, int cmd) +{ + int c; + + while((c = getch())>0 && c!=delim && c!='\n'){ + if(c == '\\'){ + if((c=getch()) <= 0) + error("bad right hand side"); + if(c == '\n'){ + ungetch(); + c='\\'; + }else if(c == 'n') + c='\n'; + else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */ + Straddc(s, '\\'); + } + Straddc(s, c); + } + ungetch(); /* let client read whether delimiter, '\n' or whatever */ +} + +String * +collecttoken(char *end) +{ + String *s = newstring(0); + int c; + + while((c=nextc())==' ' || c=='\t') + Straddc(s, getch()); /* blanks significant for getname() */ + while((c=getch())>0 && utfrune(end, c)==0) + Straddc(s, c); + if(c != '\n') + atnl(); + return s; +} + +String * +collecttext(void) +{ + String *s; + int begline, i, c, delim; + + s = newstring(0); + if(cmdskipbl()=='\n'){ + getch(); + i = 0; + do{ + begline = i; + while((c = getch())>0 && c!='\n') + i++, Straddc(s, c); + i++, Straddc(s, '\n'); + if(c < 0) + goto Return; + }while(s->r[begline]!='.' || s->r[begline+1]!='\n'); + s->r[s->n-2] = '\0'; + s->n -= 2; + }else{ + okdelim(delim = getch()); + getrhs(s, delim, 'a'); + if(nextc()==delim) + getch(); + atnl(); + } + Return: + return s; +} + +int +cmdlookup(int c) +{ + int i; + + for(i=0; cmdtab[i].cmdc; i++) + if(cmdtab[i].cmdc == c) + return i; + return -1; +} + +Cmd* +parsecmd(int nest) +{ + int i, c; + struct cmdtab *ct; + Cmd *cp, *ncp; + Cmd cmd; + + cmd.next = cmd.cmd = 0; + cmd.re = 0; + cmd.flag = cmd.num = 0; + cmd.addr = compoundaddr(); + if(cmdskipbl() == -1) + return 0; + if((c=getch())==-1) + return 0; + cmd.cmdc = c; + if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */ + getch(); /* the 'd' */ + cmd.cmdc='c'|0x100; + } + i = cmdlookup(cmd.cmdc); + if(i >= 0){ + if(cmd.cmdc == '\n') + goto Return; /* let nl_cmd work it all out */ + ct = &cmdtab[i]; + if(ct->defaddr==aNo && cmd.addr) + editerror("command takes no address"); + if(ct->count) + cmd.num = getnum(ct->count); + if(ct->regexp){ + /* x without pattern -> .*\n, indicated by cmd.re==0 */ + /* X without pattern is all files */ + if((ct->cmdc!='x' && ct->cmdc!='X') || + ((c = nextc())!=' ' && c!='\t' && c!='\n')){ + cmdskipbl(); + if((c = getch())=='\n' || c<0) + editerror("no address"); + okdelim(c); + cmd.re = getregexp(c); + if(ct->cmdc == 's'){ + cmd.text = newstring(0); + getrhs(cmd.text, c, 's'); + if(nextc() == c){ + getch(); + if(nextc() == 'g') + cmd.flag = getch(); + } + + } + } + } + if(ct->addr && (cmd.mtaddr=simpleaddr())==0) + editerror("bad address"); + if(ct->defcmd){ + if(cmdskipbl() == '\n'){ + getch(); + cmd.cmd = newcmd(); + cmd.cmd->cmdc = ct->defcmd; + }else if((cmd.cmd = parsecmd(nest))==0) + error("defcmd"); + }else if(ct->text) + cmd.text = collecttext(); + else if(ct->token) + cmd.text = collecttoken(ct->token); + else + atnl(); + }else + switch(cmd.cmdc){ + case '{': + cp = 0; + do{ + if(cmdskipbl()=='\n') + getch(); + ncp = parsecmd(nest+1); + if(cp) + cp->next = ncp; + else + cmd.cmd = ncp; + }while(cp = ncp); + break; + case '}': + atnl(); + if(nest==0) + editerror("right brace with no left brace"); + return 0; + default: + editerror("unknown command %c", cmd.cmdc); + } + Return: + cp = newcmd(); + *cp = cmd; + return cp; +} + +String* +getregexp(int delim) +{ + String *buf, *r; + int i, c; + + buf = allocstring(0); + for(i=0; ; i++){ + if((c = getch())=='\\'){ + if(nextc()==delim) + c = getch(); + else if(nextc()=='\\'){ + Straddc(buf, c); + c = getch(); + } + }else if(c==delim || c=='\n') + break; + if(i >= RBUFSIZE) + editerror("regular expression too long"); + Straddc(buf, c); + } + if(c!=delim && c) + ungetch(); + if(buf->n > 0){ + patset = TRUE; + freestring(lastpat); + lastpat = buf; + }else + freestring(buf); + if(lastpat->n == 0) + editerror("no regular expression defined"); + r = newstring(lastpat->n); + runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */ + return r; +} + +Addr * +simpleaddr(void) +{ + Addr addr; + Addr *ap, *nap; + + addr.next = 0; + addr.left = 0; + switch(cmdskipbl()){ + case '#': + addr.type = getch(); + addr.num = getnum(1); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + addr.num = getnum(1); + addr.type='l'; + break; + case '/': case '?': case '"': + addr.re = getregexp(addr.type = getch()); + break; + case '.': + case '$': + case '+': + case '-': + case '\'': + addr.type = getch(); + break; + default: + return 0; + } + if(addr.next = simpleaddr()) + switch(addr.next->type){ + case '.': + case '$': + case '\'': + if(addr.type!='"') + case '"': + editerror("bad address syntax"); + break; + case 'l': + case '#': + if(addr.type=='"') + break; + /* fall through */ + case '/': + case '?': + if(addr.type!='+' && addr.type!='-'){ + /* insert the missing '+' */ + nap = newaddr(); + nap->type='+'; + nap->next = addr.next; + addr.next = nap; + } + break; + case '+': + case '-': + break; + default: + error("simpleaddr"); + } + ap = newaddr(); + *ap = addr; + return ap; +} + +Addr * +compoundaddr(void) +{ + Addr addr; + Addr *ap, *next; + + addr.left = simpleaddr(); + if((addr.type = cmdskipbl())!=',' && addr.type!=';') + return addr.left; + getch(); + next = addr.next = compoundaddr(); + if(next && (next->type==',' || next->type==';') && next->left==0) + editerror("bad address syntax"); + ap = newaddr(); + *ap = addr; + return ap; +} |