12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322 |
- #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);
- void
- clearcollection(void)
- {
- free(collection);
- collection = nil;
- ncollection = 0;
- }
- void
- resetxec(void)
- {
- Glooping = nest = 0;
- clearcollection();
- }
- void
- mkaddr(Address *a, File *f)
- {
- a->r.q0 = f->curtext->q0;
- a->r.q1 = f->curtext->q1;
- a->f = f;
- }
- int
- cmdexec(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:
- w->neditwrsel += nr;
- 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;
- }
- int
- a_cmd(Text *t, Cmd *cp)
- {
- return append(t->file, cp, addr.r.q1);
- }
- int
- b_cmd(Text*, Cmd *cp)
- {
- File *f;
- f = tofile(cp->text);
- if(nest == 0)
- pfilename(f);
- curtext = f->curtext;
- return TRUE;
- }
- int
- B_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;
- }
- int
- c_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+cp->text->n;
- return TRUE;
- }
- int
- d_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;
- }
- void
- D1(Text *t)
- {
- if(t->w->body.file->ntext>1 || winclean(t->w, FALSE))
- colclose(t->col, t->w, TRUE);
- }
- int
- D_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 int
- readloader(void *v, uint q0, Rune *r, int nr)
- {
- if(nr > 0)
- eloginsert(v, q0, r, nr);
- return 0;
- }
- int
- e_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;
- }
- int
- f_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;
- }
- int
- g_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;
- }
- int
- i_cmd(Text *t, Cmd *cp)
- {
- return append(t->file, cp, addr.r.q0);
- }
- void
- copy(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);
- }
- void
- move(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");
- }
- int
- m_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;
- }
- int
- p_cmd(Text *t, Cmd*)
- {
- return pdisplay(t->file);
- }
- int
- s_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+delta;
- return TRUE;
- Err:
- free(rp);
- freestring(buf);
- fbuffree(rbuf);
- editerror(err);
- return FALSE;
- }
- int
- u_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;
- }
- int
- w_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;
- }
- int
- x_cmd(Text *t, Cmd *cp)
- {
- if(cp->re)
- looper(t->file, cp, cp->cmdc=='x');
- else
- linelooper(t->file, cp);
- return TRUE;
- }
- int
- X_cmd(Text*, Cmd *cp)
- {
- filelooper(cp, cp->cmdc=='X');
- return TRUE;
- }
- void
- runpipe(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;
- w->neditwrsel = 0;
- 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');
- if(state == Inserting){
- t->q0 = addr.r.q0;
- t->q1 = addr.r.q0 + t->w->neditwrsel;
- }
- }
- int
- pipe_cmd(Text *t, Cmd *cp)
- {
- runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting);
- return TRUE;
- }
- long
- nlcount(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;
- }
- void
- printposn(Text *t, int charsonly)
- {
- long l1, l2;
- if (t != nil && t->file != nil && t->file->name != nil)
- warning(nil, "%.*S:", t->file->nname, t->file->name);
- if(!charsonly){
- l1 = 1+nlcount(t, 0, addr.r.q0);
- l2 = l1+nlcount(t, addr.r.q0, addr.r.q1);
- /* check if addr ends with '\n' */
- if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n')
- --l2;
- warning(nil, "%lud", l1);
- if(l2 != l1)
- warning(nil, ",%lud", l2);
- warning(nil, "\n");
- return;
- }
- warning(nil, "#%d", addr.r.q0);
- if(addr.r.q1 != addr.r.q0)
- warning(nil, ",#%d", addr.r.q1);
- warning(nil, "\n");
- }
- int
- eq_cmd(Text *t, Cmd *cp)
- {
- int charsonly;
- switch(cp->text->n){
- case 0:
- charsonly = FALSE;
- break;
- case 1:
- if(cp->text->r[0] == '#'){
- charsonly = TRUE;
- break;
- }
- default:
- SET(charsonly);
- editerror("newline expected");
- }
- printposn(t, charsonly);
- return TRUE;
- }
- int
- nl_cmd(Text *t, Cmd *cp)
- {
- Address a;
- File *f;
- f = t->file;
- if(cp->addr == 0){
- /* First put it on newline boundaries */
- mkaddr(&a, f);
- addr = lineaddr(0, a, -1);
- a = lineaddr(0, a, 1);
- addr.r.q1 = a.r.q1;
- if(addr.r.q0==t->q0 && addr.r.q1==t->q1){
- mkaddr(&a, f);
- addr = lineaddr(1, a, 1);
- }
- }
- textshow(t, addr.r.q0, addr.r.q1, 1);
- return TRUE;
- }
- int
- append(File *f, Cmd *cp, long p)
- {
- if(cp->text->n > 0)
- eloginsert(f, p, cp->text->r, cp->text->n);
- f->curtext->q0 = p;
- f->curtext->q1 = p+cp->text->n;
- return TRUE;
- }
- int
- pdisplay(File *f)
- {
- long p1, p2;
- int np;
- Rune *buf;
- p1 = addr.r.q0;
- p2 = addr.r.q1;
- if(p2 > f->nc)
- p2 = f->nc;
- buf = fbufalloc();
- while(p1 < p2){
- np = p2-p1;
- if(np>RBUFSIZE-1)
- np = RBUFSIZE-1;
- bufread(f, p1, buf, np);
- buf[np] = L'\0';
- warning(nil, "%S", buf);
- p1 += np;
- }
- fbuffree(buf);
- f->curtext->q0 = addr.r.q0;
- f->curtext->q1 = addr.r.q1;
- return TRUE;
- }
- void
- pfilename(File *f)
- {
- int dirty;
- Window *w;
- w = f->curtext->w;
- /* same check for dirty as in settag, but we know ncache==0 */
- dirty = !w->isdir && !w->isscratch && f->mod;
- warning(nil, "%c%c%c %.*S\n", " '"[dirty],
- '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
- }
- void
- loopcmd(File *f, Cmd *cp, Range *rp, long nrp)
- {
- long i;
- for(i=0; i<nrp; i++){
- f->curtext->q0 = rp[i].q0;
- f->curtext->q1 = rp[i].q1;
- cmdexec(f->curtext, cp);
- }
- }
- void
- looper(File *f, Cmd *cp, int xy)
- {
- long p, op, nrp;
- Range r, tr;
- Range *rp;
- r = addr.r;
- op= xy? -1 : r.q0;
- nest++;
- if(rxcompile(cp->re->r) == FALSE)
- editerror("bad regexp in %c command", cp->cmdc);
- nrp = 0;
- rp = nil;
- for(p = r.q0; p<=r.q1; ){
- if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */
- if(xy || op>r.q1)
- break;
- tr.q0 = op, tr.q1 = r.q1;
- p = r.q1+1; /* exit next loop */
- }else{
- if(sel.r[0].q0==sel.r[0].q1){ /* empty match? */
- if(sel.r[0].q0==op){
- p++;
- continue;
- }
- p = sel.r[0].q1+1;
- }else
- p = sel.r[0].q1;
- if(xy)
- tr = sel.r[0];
- else
- tr.q0 = op, tr.q1 = sel.r[0].q0;
- }
- op = sel.r[0].q1;
- nrp++;
- rp = erealloc(rp, nrp*sizeof(Range));
- rp[nrp-1] = tr;
- }
- loopcmd(f, cp->cmd, rp, nrp);
- free(rp);
- --nest;
- }
- void
- linelooper(File *f, Cmd *cp)
- {
- long nrp, p;
- Range r, linesel;
- Address a, a3;
- Range *rp;
- nest++;
- nrp = 0;
- rp = nil;
- r = addr.r;
- a3.f = f;
- a3.r.q0 = a3.r.q1 = r.q0;
- a = lineaddr(0, a3, 1);
- linesel = a.r;
- for(p = r.q0; p<r.q1; p = a3.r.q1){
- a3.r.q0 = a3.r.q1;
- if(p!=r.q0 || linesel.q1==p){
- a = lineaddr(1, a3, 1);
- linesel = a.r;
- }
- if(linesel.q0 >= r.q1)
- break;
- if(linesel.q1 >= r.q1)
- linesel.q1 = r.q1;
- if(linesel.q1 > linesel.q0)
- if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){
- a3.r = linesel;
- nrp++;
- rp = erealloc(rp, nrp*sizeof(Range));
- rp[nrp-1] = linesel;
- continue;
- }
- break;
- }
- loopcmd(f, cp->cmd, rp, nrp);
- free(rp);
- --nest;
- }
- struct Looper
- {
- Cmd *cp;
- int XY;
- Window **w;
- int nw;
- } loopstruct; /* only one; X and Y can't nest */
- void
- alllooper(Window *w, void *v)
- {
- Text *t;
- struct Looper *lp;
- Cmd *cp;
- lp = v;
- cp = lp->cp;
- // if(w->isscratch || w->isdir)
- // return;
- t = &w->body;
- /* only use this window if it's the current window for the file */
- if(t->file->curtext != t)
- return;
- // if(w->nopen[QWevent] > 0)
- // return;
- /* no auto-execute on files without names */
- if(cp->re==nil && t->file->nname==0)
- return;
- if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){
- lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*));
- lp->w[lp->nw++] = w;
- }
- }
- void
- alllocker(Window *w, void *v)
- {
- if(v)
- incref(w);
- else
- winclose(w);
- }
- void
- filelooper(Cmd *cp, int XY)
- {
- int i;
- if(Glooping++)
- editerror("can't nest %c command", "YX"[XY]);
- nest++;
- loopstruct.cp = cp;
- loopstruct.XY = XY;
- if(loopstruct.w) /* error'ed out last time */
- free(loopstruct.w);
- loopstruct.w = nil;
- loopstruct.nw = 0;
- allwindows(alllooper, &loopstruct);
- /*
- * add a ref to all windows to keep safe windows accessed by X
- * that would not otherwise have a ref to hold them up during
- * the shenanigans. note this with globalincref so that any
- * newly created windows start with an extra reference.
- */
- allwindows(alllocker, (void*)1);
- globalincref = 1;
- for(i=0; i<loopstruct.nw; i++)
- cmdexec(&loopstruct.w[i]->body, cp->cmd);
- allwindows(alllocker, (void*)0);
- globalincref = 0;
- free(loopstruct.w);
- loopstruct.w = nil;
- --Glooping;
- --nest;
- }
- void
- nextmatch(File *f, String *r, long p, int sign)
- {
- if(rxcompile(r->r) == FALSE)
- editerror("bad regexp in command address");
- if(sign >= 0){
- if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
- editerror("no match for regexp");
- if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){
- if(++p>f->nc)
- p = 0;
- if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
- editerror("address");
- }
- }else{
- if(!rxbexecute(f->curtext, p, &sel))
- editerror("no match for regexp");
- if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){
- if(--p<0)
- p = f->nc;
- if(!rxbexecute(f->curtext, p, &sel))
- editerror("address");
- }
- }
- }
- File *matchfile(String*);
- Address charaddr(long, Address, int);
- Address lineaddr(long, Address, int);
- Address
- cmdaddress(Addr *ap, Address a, int sign)
- {
- File *f = a.f;
- Address a1, a2;
- do{
- switch(ap->type){
- case 'l':
- case '#':
- a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
- break;
- case '.':
- mkaddr(&a, f);
- break;
- case '$':
- a.r.q0 = a.r.q1 = f->nc;
- break;
- case '\'':
- editerror("can't handle '");
- // a.r = f->mark;
- break;
- case '?':
- sign = -sign;
- if(sign == 0)
- sign = -1;
- /* fall through */
- case '/':
- nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign);
- a.r = sel.r[0];
- break;
- case '"':
- f = matchfile(ap->re);
- mkaddr(&a, f);
- break;
- case '*':
- a.r.q0 = 0, a.r.q1 = f->nc;
- return a;
- case ',':
- case ';':
- if(ap->left)
- a1 = cmdaddress(ap->left, a, 0);
- else
- a1.f = a.f, a1.r.q0 = a1.r.q1 = 0;
- if(ap->type == ';'){
- f = a1.f;
- a = a1;
- f->curtext->q0 = a1.r.q0;
- f->curtext->q1 = a1.r.q1;
- }
- if(ap->next)
- a2 = cmdaddress(ap->next, a, 0);
- else
- a2.f = a.f, a2.r.q0 = a2.r.q1 = f->nc;
- if(a1.f != a2.f)
- editerror("addresses in different files");
- a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1;
- if(a.r.q1 < a.r.q0)
- editerror("addresses out of order");
- return a;
- case '+':
- case '-':
- sign = 1;
- if(ap->type == '-')
- sign = -1;
- if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
- a = lineaddr(1L, a, sign);
- break;
- default:
- error("cmdaddress");
- return a;
- }
- }while(ap = ap->next); /* assign = */
- return a;
- }
- struct Tofile{
- File *f;
- String *r;
- };
- void
- alltofile(Window *w, void *v)
- {
- Text *t;
- struct Tofile *tp;
- tp = v;
- if(tp->f != nil)
- return;
- if(w->isscratch || w->isdir)
- return;
- t = &w->body;
- /* only use this window if it's the current window for the file */
- if(t->file->curtext != t)
- return;
- // if(w->nopen[QWevent] > 0)
- // return;
- if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname))
- tp->f = t->file;
- }
- File*
- tofile(String *r)
- {
- struct Tofile t;
- String rr;
- rr.r = skipbl(r->r, r->n, &rr.n);
- t.f = nil;
- t.r = &rr;
- allwindows(alltofile, &t);
- if(t.f == nil)
- editerror("no such file\"%S\"", rr.r);
- return t.f;
- }
- void
- allmatchfile(Window *w, void *v)
- {
- struct Tofile *tp;
- Text *t;
- tp = v;
- if(w->isscratch || w->isdir)
- return;
- t = &w->body;
- /* only use this window if it's the current window for the file */
- if(t->file->curtext != t)
- return;
- // if(w->nopen[QWevent] > 0)
- // return;
- if(filematch(w->body.file, tp->r)){
- if(tp->f != nil)
- editerror("too many files match \"%S\"", tp->r->r);
- tp->f = w->body.file;
- }
- }
- File*
- matchfile(String *r)
- {
- struct Tofile tf;
- tf.f = nil;
- tf.r = r;
- allwindows(allmatchfile, &tf);
- if(tf.f == nil)
- editerror("no file matches \"%S\"", r->r);
- return tf.f;
- }
- int
- filematch(File *f, String *r)
- {
- char *buf;
- Rune *rbuf;
- Window *w;
- int match, i, dirty;
- Rangeset s;
- /* compile expr first so if we get an error, we haven't allocated anything */
- if(rxcompile(r->r) == FALSE)
- editerror("bad regexp in file match");
- buf = fbufalloc();
- w = f->curtext->w;
- /* same check for dirty as in settag, but we know ncache==0 */
- dirty = !w->isdir && !w->isscratch && f->mod;
- snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty],
- '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
- rbuf = bytetorune(buf, &i);
- fbuffree(buf);
- match = rxexecute(nil, rbuf, 0, i, &s);
- free(rbuf);
- return match;
- }
- Address
- charaddr(long l, Address addr, int sign)
- {
- if(sign == 0)
- addr.r.q0 = addr.r.q1 = l;
- else if(sign < 0)
- addr.r.q1 = addr.r.q0 -= l;
- else if(sign > 0)
- addr.r.q0 = addr.r.q1 += l;
- if(addr.r.q0<0 || addr.r.q1>addr.f->nc)
- editerror("address out of range");
- return addr;
- }
- Address
- lineaddr(long l, Address addr, int sign)
- {
- int n;
- int c;
- File *f = addr.f;
- Address a;
- long p;
- a.f = f;
- if(sign >= 0){
- if(l == 0){
- if(sign==0 || addr.r.q1==0){
- a.r.q0 = a.r.q1 = 0;
- return a;
- }
- a.r.q0 = addr.r.q1;
- p = addr.r.q1-1;
- }else{
- if(sign==0 || addr.r.q1==0){
- p = 0;
- n = 1;
- }else{
- p = addr.r.q1-1;
- n = textreadc(f->curtext, p++)=='\n';
- }
- while(n < l){
- if(p >= f->nc)
- editerror("address out of range");
- if(textreadc(f->curtext, p++) == '\n')
- n++;
- }
- a.r.q0 = p;
- }
- while(p < f->nc && textreadc(f->curtext, p++)!='\n')
- ;
- a.r.q1 = p;
- }else{
- p = addr.r.q0;
- if(l == 0)
- a.r.q1 = addr.r.q0;
- else{
- for(n = 0; n<l; ){ /* always runs once */
- if(p == 0){
- if(++n != l)
- editerror("address out of range");
- }else{
- c = textreadc(f->curtext, p-1);
- if(c != '\n' || ++n != l)
- p--;
- }
- }
- a.r.q1 = p;
- if(p > 0)
- p--;
- }
- while(p > 0 && textreadc(f->curtext, p-1)!='\n') /* lines start after a newline */
- p--;
- a.r.q0 = p;
- }
- return a;
- }
- struct Filecheck
- {
- File *f;
- Rune *r;
- int nr;
- };
- void
- allfilecheck(Window *w, void *v)
- {
- struct Filecheck *fp;
- File *f;
- fp = v;
- f = w->body.file;
- if(w->body.file == fp->f)
- return;
- if(runeeq(fp->r, fp->nr, f->name, f->nname))
- warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r);
- }
- Rune*
- cmdname(File *f, String *str, int set)
- {
- Rune *r, *s;
- int n;
- struct Filecheck fc;
- Runestr newname;
- r = nil;
- n = str->n;
- s = str->r;
- if(n == 0){
- /* no name; use existing */
- if(f->nname == 0)
- return nil;
- r = runemalloc(f->nname+1);
- runemove(r, f->name, f->nname);
- return r;
- }
- s = skipbl(s, n, &n);
- if(n == 0)
- goto Return;
- if(s[0] == '/'){
- r = runemalloc(n+1);
- runemove(r, s, n);
- }else{
- newname = dirname(f->curtext, runestrdup(s), n);
- r = newname.r;
- n = newname.nr;
- }
- fc.f = f;
- fc.r = r;
- fc.nr = n;
- allwindows(allfilecheck, &fc);
- if(f->nname == 0)
- set = TRUE;
- Return:
- if(set && !runeeq(r, n, f->name, f->nname)){
- filemark(f);
- f->mod = TRUE;
- f->curtext->w->dirty = TRUE;
- winsetname(f->curtext->w, r, n);
- }
- return r;
- }
|