📄 ecmd.c
字号:
#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"int Glooping;int nest;char Enoname[] = "no file name given";Address addr;File *menu;Rangeset sel;extern Text* curtext;Rune *collection;int ncollection;int append(File*, Cmd*, long);int pdisplay(File*);void pfilename(File*);void looper(File*, Cmd*, int);void filelooper(Cmd*, int);void linelooper(File*, Cmd*);Address lineaddr(long, Address, int);int filematch(File*, String*);File *tofile(String*);Rune* cmdname(File *f, String *s, int);void runpipe(Text*, int, Rune*, int, int);voidclearcollection(void){ free(collection); collection = nil; ncollection = 0;}voidresetxec(void){ Glooping = nest = 0; clearcollection();}voidmkaddr(Address *a, File *f){ a->r.q0 = f->curtext->q0; a->r.q1 = f->curtext->q1; a->f = f;}intcmdexec(Text *t, Cmd *cp){ int i; Addr *ap; File *f; Window *w; Address dot; if(t == nil) w = nil; else w = t->w; if(w==nil && (cp->addr==0 || cp->addr->type!='"') && !utfrune("bBnqUXY!", cp->cmdc) && !(cp->cmdc=='D' && cp->text)) editerror("no current window"); i = cmdlookup(cp->cmdc); /* will be -1 for '{' */ f = nil; if(t && t->w){ t = &t->w->body; f = t->file; f->curtext = t; } if(i>=0 && cmdtab[i].defaddr != aNo){ if((ap=cp->addr)==0 && cp->cmdc!='\n'){ cp->addr = ap = newaddr(); ap->type = '.'; if(cmdtab[i].defaddr == aAll) ap->type = '*'; }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){ ap->next = newaddr(); ap->next->type = '.'; if(cmdtab[i].defaddr == aAll) ap->next->type = '*'; } if(cp->addr){ /* may be false for '\n' (only) */ static Address none = {0,0,nil}; if(f){ mkaddr(&dot, f); addr = cmdaddress(ap, dot, 0); }else /* a " */ addr = cmdaddress(ap, none, 0); f = addr.f; t = f->curtext; } } switch(cp->cmdc){ case '{': mkaddr(&dot, f); if(cp->addr != nil) dot = cmdaddress(cp->addr, dot, 0); for(cp = cp->cmd; cp; cp = cp->next){ if(dot.r.q1 > t->file->nc) editerror("dot extends past end of buffer during { command"); t->q0 = dot.r.q0; t->q1 = dot.r.q1; cmdexec(t, cp); } break; default: if(i < 0) editerror("unknown command %c in cmdexec", cp->cmdc); i = (*cmdtab[i].fn)(t, cp); return i; } return 1;}char*edittext(Window *w, int q, Rune *r, int nr){ File *f; f = w->body.file; switch(editing){ case Inactive: return "permission denied"; case Inserting: eloginsert(f, q, r, nr); return nil; case Collecting: collection = runerealloc(collection, ncollection+nr+1); runemove(collection+ncollection, r, nr); ncollection += nr; collection[ncollection] = '\0'; return nil; default: return "unknown state in edittext"; }}/* string is known to be NUL-terminated */Rune*filelist(Text *t, Rune *r, int nr){ if(nr == 0) return nil; r = skipbl(r, nr, &nr); if(r[0] != '<') return runestrdup(r); /* use < command to collect text */ clearcollection(); runpipe(t, '<', r+1, nr-1, Collecting); return collection;}inta_cmd(Text *t, Cmd *cp){ return append(t->file, cp, addr.r.q1);}intb_cmd(Text*, Cmd *cp){ File *f; f = tofile(cp->text); if(nest == 0) pfilename(f); curtext = f->curtext; return TRUE;}intB_cmd(Text *t, Cmd *cp){ Rune *list, *r, *s; int nr; list = filelist(t, cp->text->r, cp->text->n); if(list == nil) editerror(Enoname); r = list; nr = runestrlen(r); r = skipbl(r, nr, &nr); if(nr == 0) new(t, t, nil, 0, 0, r, 0); else while(nr > 0){ s = findbl(r, nr, &nr); *s = '\0'; new(t, t, nil, 0, 0, r, runestrlen(r)); if(nr > 0) r = skipbl(s+1, nr-1, &nr); } clearcollection(); return TRUE;}intc_cmd(Text *t, Cmd *cp){ elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n); t->q0 = addr.r.q0; t->q1 = addr.r.q0; return TRUE;}intd_cmd(Text *t, Cmd*){ if(addr.r.q1 > addr.r.q0) elogdelete(t->file, addr.r.q0, addr.r.q1); t->q0 = addr.r.q0; t->q1 = addr.r.q0; return TRUE;}voidD1(Text *t){ if(t->w->body.file->ntext>1 || winclean(t->w, FALSE)) colclose(t->col, t->w, TRUE);}intD_cmd(Text *t, Cmd *cp){ Rune *list, *r, *s, *n; int nr, nn; Window *w; Runestr dir, rs; char buf[128]; list = filelist(t, cp->text->r, cp->text->n); if(list == nil){ D1(t); return TRUE; } dir = dirname(t, nil, 0); r = list; nr = runestrlen(r); r = skipbl(r, nr, &nr); do{ s = findbl(r, nr, &nr); *s = '\0'; /* first time through, could be empty string, meaning delete file empty name */ nn = runestrlen(r); if(r[0]=='/' || nn==0 || dir.nr==0){ rs.r = runestrdup(r); rs.nr = nn; }else{ n = runemalloc(dir.nr+1+nn); runemove(n, dir.r, dir.nr); n[dir.nr] = '/'; runemove(n+dir.nr+1, r, nn); rs = cleanrname((Runestr){n, dir.nr+1+nn}); } w = lookfile(rs.r, rs.nr); if(w == nil){ snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r); free(rs.r); editerror(buf); } free(rs.r); D1(&w->body); if(nr > 0) r = skipbl(s+1, nr-1, &nr); }while(nr > 0); clearcollection(); free(dir.r); return TRUE;}static intreadloader(void *v, uint q0, Rune *r, int nr){ if(nr > 0) eloginsert(v, q0, r, nr); return 0;}inte_cmd(Text *t, Cmd *cp){ Rune *name; File *f; int i, isdir, q0, q1, fd, nulls, samename, allreplaced; char *s, tmp[128]; Dir *d; f = t->file; q0 = addr.r.q0; q1 = addr.r.q1; if(cp->cmdc == 'e'){ if(winclean(t->w, TRUE)==FALSE) editerror(""); /* winclean generated message already */ q0 = 0; q1 = f->nc; } allreplaced = (q0==0 && q1==f->nc); name = cmdname(f, cp->text, cp->cmdc=='e'); if(name == nil) editerror(Enoname); i = runestrlen(name); samename = runeeq(name, i, t->file->name, t->file->nname); s = runetobyte(name, i); free(name); fd = open(s, OREAD); if(fd < 0){ snprint(tmp, sizeof tmp, "can't open %s: %r", s); free(s); editerror(tmp); } d = dirfstat(fd); isdir = (d!=nil && (d->qid.type&QTDIR)); free(d); if(isdir){ close(fd); snprint(tmp, sizeof tmp, "%s is a directory", s); free(s); editerror(tmp); } elogdelete(f, q0, q1); nulls = 0; loadfile(fd, q1, &nulls, readloader, f); free(s); close(fd); if(nulls) warning(nil, "%s: NUL bytes elided\n", s); else if(allreplaced && samename) f->editclean = TRUE; return TRUE;}intf_cmd(Text *t, Cmd *cp){ Rune *name; String *str; String empty; if(cp->text == nil){ empty.n = 0; empty.r = L""; str = ∅ }else str = cp->text; name = cmdname(t->file, str, TRUE); free(name); pfilename(t->file); return TRUE;}intg_cmd(Text *t, Cmd *cp){ if(t->file != addr.f){ warning(nil, "internal error: g_cmd f!=addr.f\n"); return FALSE; } if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in g command"); if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){ t->q0 = addr.r.q0; t->q1 = addr.r.q1; return cmdexec(t, cp->cmd); } return TRUE;}inti_cmd(Text *t, Cmd *cp){ return append(t->file, cp, addr.r.q0);}voidcopy(File *f, Address addr2){ long p; int ni; Rune *buf; buf = fbufalloc(); for(p=addr.r.q0; p<addr.r.q1; p+=ni){ ni = addr.r.q1-p; if(ni > RBUFSIZE) ni = RBUFSIZE; bufread(f, p, buf, ni); eloginsert(addr2.f, addr2.r.q1, buf, ni); } fbuffree(buf);}voidmove(File *f, Address addr2){ if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){ elogdelete(f, addr.r.q0, addr.r.q1); copy(f, addr2); }else if(addr.r.q0 >= addr2.r.q1){ copy(f, addr2); elogdelete(f, addr.r.q0, addr.r.q1); }else error("move overlaps itself");}intm_cmd(Text *t, Cmd *cp){ Address dot, addr2; mkaddr(&dot, t->file); addr2 = cmdaddress(cp->mtaddr, dot, 0); if(cp->cmdc == 'm') move(t->file, addr2); else copy(t->file, addr2); return TRUE;}intp_cmd(Text *t, Cmd*){ return pdisplay(t->file);}ints_cmd(Text *t, Cmd *cp){ int i, j, k, c, m, n, nrp, didsub; long p1, op, delta; String *buf; Rangeset *rp; char *err; Rune *rbuf; n = cp->num; op= -1; if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in s command"); nrp = 0; rp = nil; delta = 0; didsub = FALSE; for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){ if(sel.r[0].q0 == sel.r[0].q1){ /* empty match? */ if(sel.r[0].q0 == op){ p1++; continue; } p1 = sel.r[0].q1+1; }else p1 = sel.r[0].q1; op = sel.r[0].q1; if(--n>0) continue; nrp++; rp = erealloc(rp, nrp*sizeof(Rangeset)); rp[nrp-1] = sel; } rbuf = fbufalloc(); buf = allocstring(0); for(m=0; m<nrp; m++){ buf->n = 0; buf->r[0] = L'\0'; sel = rp[m]; for(i = 0; i<cp->text->n; i++) if((c = cp->text->r[i])=='\\' && i<cp->text->n-1){ c = cp->text->r[++i]; if('1'<=c && c<='9') { j = c-'0'; if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){ err = "replacement string too long"; goto Err; } bufread(t->file, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0); for(k=0; k<sel.r[j].q1-sel.r[j].q0; k++) Straddc(buf, rbuf[k]); }else Straddc(buf, c); }else if(c!='&') Straddc(buf, c); else{ if(sel.r[0].q1-sel.r[0].q0>RBUFSIZE){ err = "right hand side too long in substitution"; goto Err; } bufread(t->file, sel.r[0].q0, rbuf, sel.r[0].q1-sel.r[0].q0); for(k=0; k<sel.r[0].q1-sel.r[0].q0; k++) Straddc(buf, rbuf[k]); } elogreplace(t->file, sel.r[0].q0, sel.r[0].q1, buf->r, buf->n); delta -= sel.r[0].q1-sel.r[0].q0; delta += buf->n; didsub = 1; if(!cp->flag) break; } free(rp); freestring(buf); fbuffree(rbuf); if(!didsub && nest==0) editerror("no substitution"); t->q0 = addr.r.q0; t->q1 = addr.r.q1; return TRUE;Err: free(rp); freestring(buf); fbuffree(rbuf); editerror(err); return FALSE;}intu_cmd(Text *t, Cmd *cp){ int n, oseq, flag; n = cp->num; flag = TRUE; if(n < 0){ n = -n; flag = FALSE; } oseq = -1; while(n-->0 && t->file->seq!=0 && t->file->seq!=oseq){ oseq = t->file->seq; undo(t, nil, nil, flag, 0, nil, 0); } return TRUE;}intw_cmd(Text *t, Cmd *cp){ Rune *r; File *f; f = t->file; if(f->seq == seq) editerror("can't write file with pending modifications"); r = cmdname(f, cp->text, FALSE); if(r == nil) editerror("no name specified for 'w' command"); putfile(f, addr.r.q0, addr.r.q1, r, runestrlen(r)); /* r is freed by putfile */ return TRUE;}intx_cmd(Text *t, Cmd *cp){ if(cp->re) looper(t->file, cp, cp->cmdc=='x'); else linelooper(t->file, cp); return TRUE;}intX_cmd(Text*, Cmd *cp){ filelooper(cp, cp->cmdc=='X'); return TRUE;}voidrunpipe(Text *t, int cmd, Rune *cr, int ncr, int state){ Rune *r, *s; int n; Runestr dir; Window *w; r = skipbl(cr, ncr, &n); if(n == 0) editerror("no command specified for %c", cmd); w = nil; if(state == Inserting){ w = t->w; t->q0 = addr.r.q0; t->q1 = addr.r.q1; if(cmd == '<' || cmd=='|') elogdelete(t->file, t->q0, t->q1); } s = runemalloc(n+2); s[0] = cmd; runemove(s+1, r, n); n++; dir.r = nil; dir.nr = 0; if(t != nil) dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } editing = state; if(t!=nil && t->w!=nil) incref(t->w); /* run will decref */ run(w, runetobyte(s, n), dir.r, dir.nr, TRUE, nil, nil, TRUE); free(s); if(t!=nil && t->w!=nil) winunlock(t->w); qunlock(&row); recvul(cedit); qlock(&row); editing = Inactive; if(t!=nil && t->w!=nil) winlock(t->w, 'M');}intpipe_cmd(Text *t, Cmd *cp){ runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting); return TRUE;}longnlcount(Text *t, long q0, long q1){ long nl; Rune *buf; int i, nbuf; buf = fbufalloc(); nbuf = 0; i = nl = 0; while(q0 < q1){ if(i == nbuf){ nbuf = q1-q0; if(nbuf > RBUFSIZE) nbuf = RBUFSIZE; bufread(t->file, q0, buf, nbuf); i = 0; } if(buf[i++] == '\n') nl++; q0++; } fbuffree(buf); return nl;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -