summaryrefslogtreecommitdiff
path: root/sys/src/cmd/acme/edit.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/acme/edit.c679
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;
+}