123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350 |
- implement Editcmd;
- include "common.m";
- sys: Sys;
- utils: Utils;
- edit: Edit;
- editlog: Editlog;
- windowm: Windowm;
- look: Look;
- columnm: Columnm;
- bufferm: Bufferm;
- exec: Exec;
- dat: Dat;
- textm: Textm;
- regx: Regx;
- filem: Filem;
- rowm: Rowm;
- Dir: import Sys;
- Allwin, Filecheck, Tofile, Looper, Astring: import Dat;
- aNo, aDot, aAll: import Edit;
- C_nl, C_a, C_b, C_c, C_d, C_B, C_D, C_e, C_f, C_g, C_i, C_k, C_m, C_n, C_p, C_s, C_u, C_w, C_x, C_X, C_pipe, C_eq: import Edit;
- TRUE, FALSE: import Dat;
- Inactive, Inserting, Collecting: import Dat;
- BUFSIZE, Runestr: import Dat;
- Addr, Address, String, Cmd: import Edit;
- Window: import windowm;
- File: import filem;
- NRange, Range, Rangeset: import Dat;
- Text: import textm;
- Column: import columnm;
- Buffer: import bufferm;
- sprint: import sys;
- elogterm, elogclose, eloginsert, elogdelete, elogreplace, elogapply: import editlog;
- cmdtab, allocstring, freestring, Straddc, curtext, editing, newaddr, cmdlookup, editerror: import edit;
- error, stralloc, strfree, warning, skipbl, findbl: import utils;
- lookfile, cleanname, dirname: import look;
- undo, run: import exec;
- Ref, Lock, row, cedit: import dat;
- rxcompile, rxexecute, rxbexecute: import regx;
- allwindows: import rowm;
- init(mods : ref Dat->Mods)
- {
- sys = mods.sys;
- utils = mods.utils;
- edit = mods.edit;
- editlog = mods.editlog;
- windowm = mods.windowm;
- look = mods.look;
- columnm = mods.columnm;
- bufferm = mods.bufferm;
- exec = mods.exec;
- dat = mods.dat;
- textm = mods.textm;
- regx = mods.regx;
- filem = mods.filem;
- rowm = mods.rowm;
- none.r.q0 = none.r.q1 = 0;
- none.f = nil;
- }
- cmdtabexec(i: int, t: ref Text, cp: ref Cmd): int
- {
- case (cmdtab[i].fnc){
- C_nl => i = nl_cmd(t, cp);
- C_a => i = a_cmd(t, cp);
- C_b => i = b_cmd(t, cp);
- C_c => i = c_cmd(t, cp);
- C_d => i = d_cmd(t, cp);
- C_e => i = e_cmd(t, cp);
- C_f => i = f_cmd(t, cp);
- C_g => i = g_cmd(t, cp);
- C_i => i = i_cmd(t, cp);
- C_m => i = m_cmd(t, cp);
- C_p => i = p_cmd(t, cp);
- C_s => i = s_cmd(t, cp);
- C_u => i = u_cmd(t, cp);
- C_w => i = w_cmd(t, cp);
- C_x => i = x_cmd(t, cp);
- C_eq => i = eq_cmd(t, cp);
- C_B => i = B_cmd(t, cp);
- C_D => i = D_cmd(t, cp);
- C_X => i = X_cmd(t, cp);
- C_pipe => i = pipe_cmd(t, cp);
- * => error("bad case in cmdtabexec");
- }
- return i;
- }
- Glooping: int;
- nest: int;
- Enoname := "no file name given";
- addr: Address;
- menu: ref File;
- sel: Rangeset;
- collection: string;
- ncollection: int;
- clearcollection()
- {
- collection = nil;
- ncollection = 0;
- }
- resetxec()
- {
- Glooping = nest = 0;
- clearcollection();
- }
- mkaddr(f: ref File): Address
- {
- a: Address;
- a.r.q0 = f.curtext.q0;
- a.r.q1 = f.curtext.q1;
- a.f = f;
- return a;
- }
- none: Address;
- cmdexec(t: ref Text, cp: ref Cmd): int
- {
- i: int;
- ap: ref Addr;
- f: ref File;
- w: ref Window;
- dot: Address;
- if(t == nil)
- w = nil;
- else
- w = t.w;
- if(w==nil && (cp.addr==nil || cp.addr.typex!='"') &&
- utils->strchr("bBnqUXY!", cp.cmdc) < 0&&
- !(cp.cmdc=='D' && cp.text!=nil))
- editerror("no current window");
- i = cmdlookup(cp.cmdc); # will be -1 for '{'
- f = nil;
- if(t!=nil && t.w!=nil){
- t = t.w.body;
- f = t.file;
- f.curtext = t;
- }
- if(i>=0 && cmdtab[i].defaddr != aNo){
- if((ap=cp.addr)==nil && cp.cmdc!='\n'){
- cp.addr = ap = newaddr();
- ap.typex = '.';
- if(cmdtab[i].defaddr == aAll)
- ap.typex = '*';
- }else if(ap!=nil && ap.typex=='"' && ap.next==nil && cp.cmdc!='\n'){
- ap.next = newaddr();
- ap.next.typex = '.';
- if(cmdtab[i].defaddr == aAll)
- ap.next.typex = '*';
- }
- if(cp.addr!=nil){ # may be false for '\n' (only)
- if(f!=nil){
- dot = mkaddr(f);
- addr = cmdaddress(ap, dot, 0);
- }else # a "
- addr = cmdaddress(ap, none, 0);
- f = addr.f;
- t = f.curtext;
- }
- }
- case(cp.cmdc){
- '{' =>
- dot = mkaddr(f);
- if(cp.addr != nil)
- dot = cmdaddress(cp.addr, dot, 0);
- for(cp = cp.cmd; cp!=nil; cp = cp.next){
- t.q0 = dot.r.q0;
- t.q1 = dot.r.q1;
- cmdexec(t, cp);
- }
- break;
- * =>
- if(i < 0)
- editerror(sprint("unknown command %c in cmdexec", cp.cmdc));
- i = cmdtabexec(i, t, cp);
- return i;
- }
- return 1;
- }
- edittext(f: ref File, q: int, r: string, nr: int): string
- {
- case(editing){
- Inactive =>
- return "permission denied";
- Inserting =>
- eloginsert(f, q, r, nr);
- return nil;
- Collecting =>
- collection += r[0: nr];
- ncollection += nr;
- return nil;
- * =>
- return "unknown state in edittext";
- }
- }
- # string is known to be NUL-terminated
- filelist(t: ref Text, r: string, nr: int): string
- {
- if(nr == 0)
- return nil;
- (r, nr) = skipbl(r, nr);
- if(r[0] != '<')
- return r;
- # use < command to collect text
- clearcollection();
- runpipe(t, '<', r[1:], nr-1, Collecting);
- return collection;
- }
- a_cmd(t: ref Text, cp: ref Cmd): int
- {
- return append(t.file, cp, addr.r.q1);
- }
- b_cmd(nil: ref Text, cp: ref Cmd): int
- {
- f: ref File;
- f = tofile(cp.text);
- if(nest == 0)
- pfilename(f);
- curtext = f.curtext;
- return TRUE;
- }
- B_cmd(t: ref Text, cp: ref Cmd): int
- {
- listx, r, s: string;
- nr: int;
- listx = filelist(t, cp.text.r, cp.text.n);
- if(listx == nil)
- editerror(Enoname);
- r = listx;
- nr = len r;
- (r, nr) = skipbl(r, nr);
- if(nr == 0)
- look->new(t, t, nil, 0, 0, r, 0);
- else while(nr > 0){
- (s, nr) = findbl(r, nr);
- look->new(t, t, nil, 0, 0, r, len r);
- if(nr > 0)
- (r, nr) = skipbl(s[1:], nr-1);
- }
- clearcollection();
- return TRUE;
- }
- c_cmd(t: ref Text, cp: ref Cmd): int
- {
- elogreplace(t.file, addr.r.q0, addr.r.q1, cp.text.r, cp.text.n);
- return TRUE;
- }
- d_cmd(t: ref Text, nil: ref Cmd): int
- {
- if(addr.r.q1 > addr.r.q0)
- elogdelete(t.file, addr.r.q0, addr.r.q1);
- return TRUE;
- }
- D1(t: ref Text)
- {
- if(t.w.body.file.ntext>1 || t.w.clean(FALSE, FALSE))
- t.col.close(t.w, TRUE);
- }
- D_cmd(t: ref Text, cp: ref Cmd): int
- {
- listx, r, s, n: string;
- nr, nn: int;
- w: ref Window;
- dir, rs: Runestr;
- buf: string;
- listx = filelist(t, cp.text.r, cp.text.n);
- if(listx == nil){
- D1(t);
- return TRUE;
- }
- dir = dirname(t, nil, 0);
- r = listx;
- nr = len r;
- (r, nr) = skipbl(r, nr);
- do{
- (s, nr) = findbl(r, nr);
- # first time through, could be empty string, meaning delete file empty name
- nn = len r;
- if(r[0]=='/' || nn==0 || dir.nr==0){
- rs.r = r;
- rs.nr = nn;
- }else{
- n = dir.r + "/" + r;
- rs = cleanname(n, dir.nr+1+nn);
- }
- w = lookfile(rs.r, rs.nr);
- if(w == nil){
- buf = sprint("no such file %s", rs.r);
- rs.r = nil;
- editerror(buf);
- }
- rs.r = nil;
- D1(w.body);
- if(nr > 0)
- (r, nr) = skipbl(s[1:], nr-1);
- }while(nr > 0);
- clearcollection();
- dir.r = nil;
- return TRUE;
- }
- readloader(f: ref File, q0: int, r: string, nr: int): int
- {
- if(nr > 0)
- eloginsert(f, q0, r, nr);
- return 0;
- }
- e_cmd(t: ref Text , cp: ref Cmd): int
- {
- name: string;
- f: ref File;
- i, q0, q1, nulls, samename, allreplaced, ok: int;
- fd: ref Sys->FD;
- s, tmp: string;
- d: Dir;
- f = t.file;
- q0 = addr.r.q0;
- q1 = addr.r.q1;
- if(cp.cmdc == 'e'){
- if(t.w.clean(TRUE, FALSE)==FALSE)
- editerror(""); # winclean generated message already
- q0 = 0;
- q1 = f.buf.nc;
- }
- allreplaced = (q0==0 && q1==f.buf.nc);
- name = cmdname(f, cp.text, cp.cmdc=='e');
- if(name == nil)
- editerror(Enoname);
- i = len name;
- samename = name == t.file.name;
- s = name;
- name = nil;
- fd = sys->open(s, Sys->OREAD);
- if(fd == nil){
- tmp = sprint("can't open %s: %r", s);
- s = nil;
- editerror(tmp);
- }
- (ok, d) = sys->fstat(fd);
- if(ok >=0 && (d.mode&Sys->DMDIR)){
- fd = nil;
- tmp = sprint("%s is a directory", s);
- s = nil;
- editerror(tmp);
- }
- elogdelete(f, q0, q1);
- nulls = 0;
- bufferm->loadfile(fd, q1, Dat->READL, nil, f);
- s = nil;
- fd = nil;
- if(nulls)
- warning(nil, sprint("%s: NUL bytes elided\n", s));
- else if(allreplaced && samename)
- f.editclean = TRUE;
- return TRUE;
- }
- f_cmd(t: ref Text, cp: ref Cmd): int
- {
- name: string;
- name = cmdname(t.file, cp.text, TRUE);
- name = nil;
- pfilename(t.file);
- return TRUE;
- }
- g_cmd(t: ref Text, cp: ref Cmd): int
- {
- ok: int;
- 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");
- (ok, sel) = rxexecute(t, nil, addr.r.q0, addr.r.q1);
- if(ok ^ cp.cmdc=='v'){
- t.q0 = addr.r.q0;
- t.q1 = addr.r.q1;
- return cmdexec(t, cp.cmd);
- }
- return TRUE;
- }
- i_cmd(t: ref Text, cp: ref Cmd): int
- {
- return append(t.file, cp, addr.r.q0);
- }
- # int
- # k_cmd(File *f, Cmd *cp)
- # {
- # USED(cp);
- # f->mark = addr.r;
- # return TRUE;
- # }
- copy(f: ref File, addr2: Address)
- {
- p: int;
- ni: int;
- buf: ref Astring;
- buf = stralloc(BUFSIZE);
- for(p=addr.r.q0; p<addr.r.q1; p+=ni){
- ni = addr.r.q1-p;
- if(ni > BUFSIZE)
- ni = BUFSIZE;
- f.buf.read(p, buf, 0, ni);
- eloginsert(addr2.f, addr2.r.q1, buf.s, ni);
- }
- strfree(buf);
- }
- move(f: ref File, addr2: Address)
- {
- 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 if(addr.r.q0==addr2.r.q0 && addr.r.q1==addr2.r.q1){
- ; # move to self; no-op
- }else
- editerror("move overlaps itself");
- }
- m_cmd(t: ref Text, cp: ref Cmd): int
- {
- dot, addr2: Address;
- dot = mkaddr(t.file);
- addr2 = cmdaddress(cp.mtaddr, dot, 0);
- if(cp.cmdc == 'm')
- move(t.file, addr2);
- else
- copy(t.file, addr2);
- return TRUE;
- }
- # int
- # n_cmd(File *f, Cmd *cp)
- # {
- # int i;
- # USED(f);
- # USED(cp);
- # for(i = 0; i<file.nused; i++){
- # if(file.filepptr[i] == cmd)
- # continue;
- # f = file.filepptr[i];
- # Strduplstr(&genstr, &f->name);
- # filename(f);
- # }
- # return TRUE;
- #}
- p_cmd(t: ref Text, nil: ref Cmd): int
- {
- return pdisplay(t.file);
- }
- s_cmd(t: ref Text, cp: ref Cmd): int
- {
- i, j, k, c, m, n, nrp, didsub, ok: int;
- p1, op, delta: int;
- buf: ref String;
- rp: array of Rangeset;
- err: string;
- rbuf: ref Astring;
- 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; ){
- (ok, sel) = rxexecute(t, nil, p1, addr.r.q1);
- if(!ok)
- break;
- if(sel[0].q0 == sel[0].q1){ # empty match?
- if(sel[0].q0 == op){
- p1++;
- continue;
- }
- p1 = sel[0].q1+1;
- }else
- p1 = sel[0].q1;
- op = sel[0].q1;
- if(--n>0)
- continue;
- nrp++;
- orp := rp;
- rp = array[nrp] of Rangeset;
- rp[0: ] = orp[0:nrp-1];
- rp[nrp-1] = copysel(sel);
- orp = nil;
- }
- rbuf = stralloc(BUFSIZE);
- buf = allocstring(0);
- for(m=0; m<nrp; m++){
- buf.n = 0;
- buf.r = nil;
- 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[j].q1-sel[j].q0>BUFSIZE){
- err = "replacement string too long";
- rp = nil;
- freestring(buf);
- strfree(rbuf);
- editerror(err);
- return FALSE;
- }
- t.file.buf.read(sel[j].q0, rbuf, 0, sel[j].q1-sel[j].q0);
- for(k=0; k<sel[j].q1-sel[j].q0; k++)
- Straddc(buf, rbuf.s[k]);
- }else
- Straddc(buf, c);
- }else if(c!='&')
- Straddc(buf, c);
- else{
- if(sel[0].q1-sel[0].q0>BUFSIZE){
- err = "right hand side too long in substitution";
- rp = nil;
- freestring(buf);
- strfree(rbuf);
- editerror(err);
- return FALSE;
- }
- t.file.buf.read(sel[0].q0, rbuf, 0, sel[0].q1-sel[0].q0);
- for(k=0; k<sel[0].q1-sel[0].q0; k++)
- Straddc(buf, rbuf.s[k]);
- }
- elogreplace(t.file, sel[0].q0, sel[0].q1, buf.r, buf.n);
- delta -= sel[0].q1-sel[0].q0;
- delta += buf.n;
- didsub = 1;
- if(!cp.flag)
- break;
- }
- rp = nil;
- freestring(buf);
- strfree(rbuf);
- if(!didsub && nest==0)
- editerror("no substitution");
- t.q0 = addr.r.q0;
- t.q1 = addr.r.q1+delta;
- return TRUE;
- }
- u_cmd(t: ref Text, cp: ref Cmd): int
- {
- n, oseq, flag: int;
- 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;
- warning(nil, sprint("seq %d\n", t.file.seq));
- undo(t, flag);
- }
- return TRUE;
- }
- w_cmd(t: ref Text, cp: ref Cmd): int
- {
- r: string;
- f: ref File;
- f = t.file;
- if(f.seq == dat->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");
- exec->putfile(f, addr.r.q0, addr.r.q1, r);
- # r is freed by putfile
- return TRUE;
- }
- x_cmd(t: ref Text, cp: ref Cmd): int
- {
- if(cp.re!=nil)
- looper(t.file, cp, cp.cmdc=='x');
- else
- linelooper(t.file, cp);
- return TRUE;
- }
- X_cmd(nil: ref Text, cp: ref Cmd): int
- {
- filelooper(cp, cp.cmdc=='X');
- return TRUE;
- }
- runpipe(t: ref Text, cmd: int, cr: string, ncr: int, state: int)
- {
- r, s: string;
- n: int;
- dir: Runestr;
- w: ref Window;
- (r, n) = skipbl(cr, ncr);
- if(n == 0)
- editerror("no command specified for >");
- 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);
- }
- tmps := "z";
- tmps[0] = cmd;
- s = tmps + r;
- n++;
- dir.r = nil;
- dir.nr = 0;
- if(t != nil)
- dir = dirname(t, nil, 0);
- if(dir.nr==1 && dir.r[0]=='.'){ # sigh
- dir.r = nil;
- dir.nr = 0;
- }
- editing = state;
- if(t!=nil && t.w!=nil)
- t.w.refx.inc(); # run will decref
- spawn run(w, s, dir.r, dir.nr, TRUE, nil, nil, TRUE);
- s = nil;
- if(t!=nil && t.w!=nil)
- t.w.unlock();
- row.qlock.unlock();
- <- cedit;
- row.qlock.lock();
- editing = Inactive;
- if(t!=nil && t.w!=nil)
- t.w.lock('M');
- }
- pipe_cmd(t: ref Text, cp: ref Cmd): int
- {
- runpipe(t, cp.cmdc, cp.text.r, cp.text.n, Inserting);
- return TRUE;
- }
- nlcount(t: ref Text, q0: int, q1: int): int
- {
- nl: int;
- buf: ref Astring;
- i, nbuf: int;
- buf = stralloc(BUFSIZE);
- nbuf = 0;
- i = nl = 0;
- while(q0 < q1){
- if(i == nbuf){
- nbuf = q1-q0;
- if(nbuf > BUFSIZE)
- nbuf = BUFSIZE;
- t.file.buf.read(q0, buf, 0, nbuf);
- i = 0;
- }
- if(buf.s[i++] == '\n')
- nl++;
- q0++;
- }
- strfree(buf);
- return nl;
- }
- printposn(t: ref Text, charsonly: int)
- {
- l1, l2: int;
- if(t != nil && t.file != nil && t.file.name != nil)
- warning(nil, 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 && t.readc(addr.r.q1-1)=='\n')
- --l2;
- warning(nil, sprint("%ud", l1));
- if(l2 != l1)
- warning(nil, sprint(",%ud", l2));
- warning(nil, "\n");
- # warning(nil, "; ");
- return;
- }
- warning(nil, sprint("#%d", addr.r.q0));
- if(addr.r.q1 != addr.r.q0)
- warning(nil, sprint(",#%d", addr.r.q1));
- warning(nil, "\n");
- }
- eq_cmd(t: ref Text, cp: ref Cmd): int
- {
- charsonly: int;
- case(cp.text.n){
- 0 =>
- charsonly = FALSE;
- break;
- 1 =>
- if(cp.text.r[0] == '#'){
- charsonly = TRUE;
- break;
- }
- * =>
- charsonly = TRUE;
- editerror("newline expected");
- }
- printposn(t, charsonly);
- return TRUE;
- }
- nl_cmd(t: ref Text, cp: ref Cmd): int
- {
- a: Address;
- f: ref File;
- f = t.file;
- if(cp.addr == nil){
- # First put it on newline boundaries
- a = mkaddr(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){
- a = mkaddr(f);
- addr = lineaddr(1, a, 1);
- }
- }
- t.show(addr.r.q0, addr.r.q1, TRUE);
- return TRUE;
- }
- append(f: ref File, cp: ref Cmd, p: int): int
- {
- if(cp.text.n > 0)
- eloginsert(f, p, cp.text.r, cp.text.n);
- return TRUE;
- }
- pdisplay(f: ref File): int
- {
- p1, p2: int;
- np: int;
- buf: ref Astring;
- p1 = addr.r.q0;
- p2 = addr.r.q1;
- if(p2 > f.buf.nc)
- p2 = f.buf.nc;
- buf = stralloc(BUFSIZE);
- while(p1 < p2){
- np = p2-p1;
- if(np>BUFSIZE-1)
- np = BUFSIZE-1;
- f.buf.read(p1, buf, 0, np);
- warning(nil, sprint("%s", buf.s[0:np]));
- p1 += np;
- }
- strfree(buf);
- f.curtext.q0 = addr.r.q0;
- f.curtext.q1 = addr.r.q1;
- return TRUE;
- }
- pfilename(f: ref File)
- {
- dirty: int;
- w: ref Window;
- 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, sprint("%c%c%c %s\n", " '"[dirty],
- '+', " ."[curtext!=nil && curtext.file==f], f.name));
- }
- loopcmd(f: ref File, cp: ref Cmd, rp: array of Range, nrp: int)
- {
- i: int;
- for(i=0; i<nrp; i++){
- f.curtext.q0 = rp[i].q0;
- f.curtext.q1 = rp[i].q1;
- cmdexec(f.curtext, cp);
- }
- }
- looper(f: ref File, cp: ref Cmd, xy: int)
- {
- p, op, nrp, ok: int;
- r, tr: Range;
- rp: array of Range;
- r = addr.r;
- if(xy)
- op = -1;
- else
- op = r.q0;
- nest++;
- if(rxcompile(cp.re.r) == FALSE)
- editerror(sprint("bad regexp in %c command", cp.cmdc));
- nrp = 0;
- rp = nil;
- for(p = r.q0; p<=r.q1; ){
- (ok, sel) = rxexecute(f.curtext, nil, p, r.q1);
- if(!ok){ # 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[0].q0==sel[0].q1){ # empty match?
- if(sel[0].q0==op){
- p++;
- continue;
- }
- p = sel[0].q1+1;
- }else
- p = sel[0].q1;
- if(xy)
- tr = sel[0];
- else{
- tr.q0 = op;
- tr.q1 = sel[0].q0;
- }
- }
- op = sel[0].q1;
- nrp++;
- orp := rp;
- rp = array[nrp] of Range;
- rp[0: ] = orp[0: nrp-1];
- rp[nrp-1] = tr;
- orp = nil;
- }
- loopcmd(f, cp.cmd, rp, nrp);
- rp = nil;
- --nest;
- }
- linelooper(f: ref File, cp: ref Cmd)
- {
- nrp, p: int;
- r, linesel: Range;
- a, a3: Address;
- rp: array of Range;
- 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++;
- orp := rp;
- rp = array[nrp] of Range;
- rp[0: ] = orp[0: nrp-1];
- rp[nrp-1] = linesel;
- orp = nil;
- continue;
- }
- break;
- }
- loopcmd(f, cp.cmd, rp, nrp);
- rp = nil;
- --nest;
- }
- loopstruct: ref Looper;
- alllooper(w: ref Window, lp: ref Looper)
- {
- t: ref Text;
- cp: ref Cmd;
- 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.name==nil)
- return;
- if(cp.re==nil || filematch(t.file, cp.re)==lp.XY){
- olpw := lp.w;
- lp.w = array[lp.nw+1] of ref Window;
- lp.w[0: ] = olpw[0: lp.nw];
- lp.w[lp.nw++] = w;
- olpw = nil;
- }
- }
- filelooper(cp: ref Cmd, XY: int)
- {
- i: int;
- if(Glooping++)
- editerror(sprint("can't nest %c command", "YX"[XY]));
- nest++;
- if(loopstruct == nil)
- loopstruct = ref Looper;
- loopstruct.cp = cp;
- loopstruct.XY = XY;
- if(loopstruct.w != nil) # error'ed out last time
- loopstruct.w = nil;
- loopstruct.w = nil;
- loopstruct.nw = 0;
- aw := ref Allwin.LP(loopstruct);
- allwindows(Edit->ALLLOOPER, aw);
- aw = nil;
- for(i=0; i<loopstruct.nw; i++)
- cmdexec(loopstruct.w[i].body, cp.cmd);
- loopstruct.w = nil;
- --Glooping;
- --nest;
- }
- nextmatch(f: ref File, r: ref String, p: int, sign: int)
- {
- ok: int;
- if(rxcompile(r.r) == FALSE)
- editerror("bad regexp in command address");
- if(sign >= 0){
- (ok, sel) = rxexecute(f.curtext, nil, p, 16r7FFFFFFF);
- if(!ok)
- editerror("no match for regexp");
- if(sel[0].q0==sel[0].q1 && sel[0].q0==p){
- if(++p>f.buf.nc)
- p = 0;
- (ok, sel) = rxexecute(f.curtext, nil, p, 16r7FFFFFFF);
- if(!ok)
- editerror("address");
- }
- }else{
- (ok, sel) = rxbexecute(f.curtext, p);
- if(!ok)
- editerror("no match for regexp");
- if(sel[0].q0==sel[0].q1 && sel[0].q1==p){
- if(--p<0)
- p = f.buf.nc;
- (ok, sel) = rxbexecute(f.curtext, p);
- if(!ok)
- editerror("address");
- }
- }
- }
- cmdaddress(ap: ref Addr, a: Address, sign: int): Address
- {
- f := a.f;
- a1, a2: Address;
- do{
- case(ap.typex){
- 'l' or
- '#' =>
- if(ap.typex == '#')
- a = charaddr(ap.num, a, sign);
- else
- a = lineaddr(ap.num, a, sign);
- break;
- '.' =>
- a = mkaddr(f);
- break;
- '$' =>
- a.r.q0 = a.r.q1 = f.buf.nc;
- break;
- '\'' =>
- editerror("can't handle '");
- # a.r = f.mark;
- break;
- '?' =>
- sign = -sign;
- if(sign == 0)
- sign = -1;
- if(sign >= 0)
- v := a.r.q1;
- else
- v = a.r.q0;
- nextmatch(f, ap.re, v, sign);
- a.r = sel[0];
- break;
- '/' =>
- if(sign >= 0)
- v := a.r.q1;
- else
- v = a.r.q0;
- nextmatch(f, ap.re, v, sign);
- a.r = sel[0];
- break;
- '"' =>
- f = matchfile(ap.re);
- a = mkaddr(f);
- break;
- '*' =>
- a.r.q0 = 0;
- a.r.q1 = f.buf.nc;
- return a;
- ',' or
- ';' =>
- if(ap.left!=nil)
- a1 = cmdaddress(ap.left, a, 0);
- else{
- a1.f = a.f;
- a1.r.q0 = a1.r.q1 = 0;
- }
- if(ap.typex == ';'){
- f = a1.f;
- a = a1;
- f.curtext.q0 = a1.r.q0;
- f.curtext.q1 = a1.r.q1;
- }
- if(ap.next!=nil)
- a2 = cmdaddress(ap.next, a, 0);
- else{
- a2.f = a.f;
- a2.r.q0 = a2.r.q1 = f.buf.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;
- '+' or
- '-' =>
- sign = 1;
- if(ap.typex == '-')
- sign = -1;
- if(ap.next==nil || ap.next.typex=='+' || ap.next.typex=='-')
- a = lineaddr(1, a, sign);
- break;
- * =>
- error("cmdaddress");
- return a;
- }
- }while((ap = ap.next)!=nil); # assign =
- return a;
- }
- alltofile(w: ref Window, tp: ref Tofile)
- {
- t: ref Text;
- 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(tp.r.r == t.file.name)
- tp.f = t.file;
- }
- tofile(r: ref String): ref File
- {
- t: ref Tofile;
- rr: String;
- (rr.r, r.n) = skipbl(r.r, r.n);
- t = ref Tofile;
- t.f = nil;
- t.r = ref String;
- *t.r = rr;
- aw := ref Allwin.FF(t);
- allwindows(Edit->ALLTOFILE, aw);
- aw = nil;
- if(t.f == nil)
- editerror(sprint("no such file\"%s\"", rr.r));
- return t.f;
- }
- allmatchfile(w: ref Window, tp: ref Tofile)
- {
- t: ref Text;
- 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(sprint("too many files match \"%s\"", tp.r.r));
- tp.f = w.body.file;
- }
- }
- matchfile(r: ref String): ref File
- {
- tf: ref Tofile;
- tf = ref Tofile;
- tf.f = nil;
- tf.r = r;
- aw := ref Allwin.FF(tf);
- allwindows(Edit->ALLMATCHFILE, aw);
- aw = nil;
- if(tf.f == nil)
- editerror(sprint("no file matches \"%s\"", r.r));
- return tf.f;
- }
- filematch(f: ref File, r: ref String): int
- {
- buf: string;
- w: ref Window;
- match, i, dirty: int;
- s: Rangeset;
- # 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");
- w = f.curtext.w;
- # same check for dirty as in settag, but we know ncache==0
- dirty = !w.isdir && !w.isscratch && f.mod;
- buf = sprint("%c%c%c %s\n", " '"[dirty],
- '+', " ."[curtext!=nil && curtext.file==f], f.name);
- (match, s) = rxexecute(nil, buf, 0, i);
- buf = nil;
- return match;
- }
- charaddr(l: int, addr: Address, sign: int): Address
- {
- 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.buf.nc)
- editerror("address out of range");
- return addr;
- }
- lineaddr(l: int, addr: Address, sign: int): Address
- {
- n: int;
- c: int;
- f := addr.f;
- a: Address;
- p: int;
- 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 = f.curtext.readc(p++)=='\n';
- }
- while(n < l){
- if(p >= f.buf.nc)
- editerror("address out of range");
- if(f.curtext.readc(p++) == '\n')
- n++;
- }
- a.r.q0 = p;
- }
- while(p < f.buf.nc && f.curtext.readc(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 = f.curtext.readc(p-1);
- if(c != '\n' || ++n != l)
- p--;
- }
- }
- a.r.q1 = p;
- if(p > 0)
- p--;
- }
- while(p > 0 && f.curtext.readc(p-1)!='\n') # lines start after a newline
- p--;
- a.r.q0 = p;
- }
- return a;
- }
- allfilecheck(w: ref Window, fp: ref Filecheck)
- {
- f: ref File;
- f = w.body.file;
- if(w.body.file == fp.f)
- return;
- if(fp.r == f.name)
- warning(nil, sprint("warning: duplicate file name \"%s\"\n", fp.r));
- }
- cmdname(f: ref File, str: ref String , set: int): string
- {
- r, s: string;
- n: int;
- fc: ref Filecheck;
- newname: Runestr;
- r = nil;
- n = str.n;
- s = str.r;
- if(n == 0){
- # no name; use existing
- if(f.name == nil)
- return nil;
- return f.name;
- }
- (s, n) = skipbl(s, n);
- if(n == 0)
- ;
- else{
- if(s[0] == '/'){
- r = s;
- }else{
- newname = dirname(f.curtext, s, n);
- r = newname.r;
- n = newname.nr;
- }
- fc = ref Filecheck;
- fc.f = f;
- fc.r = r;
- fc.nr = n;
- aw := ref Allwin.FC(fc);
- allwindows(Edit->ALLFILECHECK, aw);
- aw = nil;
- if(f.name == nil)
- set = TRUE;
- }
- if(set && r[0: n] != f.name){
- f.mark();
- f.mod = TRUE;
- f.curtext.w.dirty = TRUE;
- f.curtext.w.setname(r, n);
- }
- return r;
- }
- copysel(rs: Rangeset): Rangeset
- {
- nrs := array[NRange] of Range;
- for(i := 0; i < NRange; i++)
- nrs[i] = rs[i];
- return nrs;
- }
|