Loading commands/editor/sed.cpp +50 −30 Changes for commands/editor/sed.cpp: 50 added lines, 30 removed lines. Original line number Diff line number Diff line Loading @@ -118,6 +118,7 @@ namespace fresh { namespace cmd { std::map<std::string,std::unique_ptr<std::ofstream>> wstreams; std::map<std::string,std::unique_ptr<std::ifstream>> rstreams; // R command readers std::ostream *out=&std::cout; bool missingNl=false; // the last line written lacked its newline int lastRe=-1; std::string error; int status=0; Loading Loading @@ -413,7 +414,7 @@ namespace fresh { namespace cmd { // after a command: optional spaces, then ';', newline, '}' or '#' s.ws(); if(!s.end() && s.peek()!=';' && s.peek()!='\n' && s.peek()!='}' && s.peek()!='#' && c.name!='}' && c.name!='a' && c.name!='i' && c.name!='c') c.name!='{' && c.name!='}' && c.name!='a' && c.name!='i' && c.name!='c') return fail(r,std::string("extra characters after command")); r.sc.cmds.push_back(std::move(c)); } Loading Loading @@ -528,7 +529,8 @@ namespace fresh { namespace cmd { std::string ps,hs; long lineNo=0; bool isLast=false; bool lastHadNl=true; bool psNl=true; // the pattern space ends a newline-terminated line bool hsNl=true; bool subst=false; // for t/T std::string appendQ; // a/r/R output after the cycle std::string file; Loading @@ -536,11 +538,6 @@ namespace fresh { namespace cmd { int exitCode=0; }; static void writeLine(std::ostream &o,const std::string &s,bool nl,char delim){ o << s; if(nl) o << delim; } bool matchRe(Run &r,int idx,const std::string &s,size_t nmatch=0,regmatch_t *m=nullptr,int eflags=0){ if(idx<0) idx=r.lastRe; if(idx<0){ fail(r,"no previous regular expression"); return false; } Loading Loading @@ -655,11 +652,21 @@ namespace fresh { namespace cmd { long count=0; bool did=false; regmatch_t m[10]; size_t prevEnd=std::string::npos; while(pos<=src.size()){ int ef=pos>0 ? REG_NOTBOL : 0; if(regexec(re,src.c_str()+pos,10,m,ef)!=0) break; ++count; size_t so=pos+m[0].rm_so,eo=pos+m[0].rm_eo; if(so==eo && so==prevEnd){ // an empty match right where the previous one ended // doesn't count (GNU: "six" s/x*/-/g -> "-s-i-") if(so>=src.size()) break; out+=src[so]; pos=so+1; continue; } prevEnd=eo; ++count; bool replaceThis=count>=c.nth && (c.global || count==c.nth); out.append(src,pos,so-pos); if(replaceThis){ Loading Loading @@ -721,19 +728,29 @@ namespace fresh { namespace cmd { return out+line+"$"; } // every write to the main output: first the newline a previous, // unterminated last line still owes (GNU sed does the same) void emit(Run &r,const std::string &s){ if(r.missingNl){ *r.out << (r.nulData ? '\0' : '\n'); r.missingNl=false; } *r.out << s; } void flushAppend(Run &r,State &st){ if(!st.appendQ.empty()){ *r.out << st.appendQ; emit(r,st.appendQ); st.appendQ.clear(); } } void output(Run &r,State &st,const std::string &s){ writeLine(*r.out,s,st.lastHadNl || !st.isLast,r.nulData ? '\0' : '\n'); void output(Run &r,State &st,const std::string &s,bool terminated){ emit(r,s); if(terminated) *r.out << (r.nulData ? '\0' : '\n'); else r.missingNl=true; (void)st; } void writeTo(Run &r,const std::string &file,const std::string &s,char delim){ if(file=="/dev/stdout"){ *r.out << s << delim; return; } if(file=="/dev/stdout"){ emit(r,s+std::string(1,delim)); return; } if(file=="/dev/stderr"){ std::cerr << s << delim; return; } std::ofstream *f=wstream(r,file); if(f){ *f << s << delim; f->flush(); } Loading @@ -757,12 +774,12 @@ namespace fresh { namespace cmd { } switch(c.name){ case '{': break; case '=': *r.out << st.lineNo << '\n'; break; case '=': emit(r,std::to_string(st.lineNo)+"\n"); break; case 'a': st.appendQ+=c.text+"\n"; break; case 'i': *r.out << c.text << '\n'; break; case 'i': emit(r,c.text+"\n"); break; case 'c': // in a range: the text once, at its end if(c.a2.type==Addr::None || c.negate || !c.active) *r.out << c.text << '\n'; if(c.a2.type==Addr::None || c.negate || !c.active) emit(r,c.text+"\n"); st.ps.clear(); autoPrint=false; pc=cmds.size(); Loading Loading @@ -793,17 +810,19 @@ namespace fresh { namespace cmd { } case 'e': if(c.text.empty()) st.ps=runShell(st.ps); else *r.out << runShell(c.text) << '\n'; else emit(r,runShell(c.text)+"\n"); break; case 'F': *r.out << (st.file.empty() ? "-" : st.file) << '\n'; break; case 'g': st.ps=st.hs; break; case 'G': st.ps+="\n"+st.hs; break; case 'h': st.hs=st.ps; break; case 'H': st.hs+="\n"+st.ps; break; case 'x': std::swap(st.ps,st.hs); break; case 'l': *r.out << escapeL(st.ps,c.lwidth<0 ? 70 : c.lwidth) << '\n'; break; case 'F': emit(r,(st.file.empty() ? std::string("-") : st.file)+"\n"); break; case 'g': st.ps=st.hs; st.psNl=st.hsNl; break; // appending: the appended part decides whether the // result still owes a final newline (as in GNU sed) case 'G': st.ps+="\n"+st.hs; st.psNl=st.hsNl; break; case 'h': st.hs=st.ps; st.hsNl=st.psNl; break; case 'H': st.hs+="\n"+st.ps; st.hsNl=st.psNl; break; case 'x': std::swap(st.ps,st.hs); std::swap(st.psNl,st.hsNl); break; case 'l': emit(r,escapeL(st.ps,c.lwidth<0 ? 70 : c.lwidth)+"\n"); break; case 'n': if(r.quiet ? false : true) output(r,st,st.ps); if(r.quiet ? false : true) output(r,st,st.ps,st.psNl); flushAppend(r,st); if(!nextLine(r,st,in,false)){ autoPrint=false; st.quit=true; continue; } break; Loading @@ -818,10 +837,10 @@ namespace fresh { namespace cmd { } break; } case 'p': output(r,st,st.ps); break; case 'p': output(r,st,st.ps,st.psNl); break; case 'P': { size_t p=st.ps.find('\n'); *r.out << st.ps.substr(0,p) << nl; emit(r,st.ps.substr(0,p)+std::string(1,nl)); break; } case 'q': Loading Loading @@ -851,7 +870,7 @@ namespace fresh { namespace cmd { case 's': if(substitute(r,c,st)){ st.subst=true; if(c.print) output(r,st,st.ps); if(c.print) output(r,st,st.ps,st.psNl); if(!c.wfile.empty()) writeTo(r,c.wfile,st.ps,nl); } if(!r.error.empty()) return; Loading @@ -866,7 +885,7 @@ namespace fresh { namespace cmd { } ++pc; } if(autoPrint) output(r,st,st.ps); if(autoPrint) output(r,st,st.ps,st.psNl); flushAppend(r,st); } Loading @@ -878,7 +897,7 @@ namespace fresh { namespace cmd { if(!in.read(line,hadNl,isLast,sf,file)) return false; ++st.lineNo; st.isLast=isLast; st.lastHadNl=hadNl; st.psNl=hadNl; st.file=file; if(append) st.ps+="\n"+line; else st.ps=line; Loading @@ -902,7 +921,7 @@ namespace fresh { namespace cmd { ++st.lineNo; st.ps=line; st.isLast=isLast; st.lastHadNl=hadNl; st.psNl=hadNl; st.file=file; cycle(r,st,in); if(!r.error.empty()){ Loading Loading @@ -946,6 +965,7 @@ namespace fresh { namespace cmd { { std::ofstream o(tmp.c_str(),std::ios::out|std::ios::trunc|std::ios::binary); r.out=&o; r.missingNl=false; for(auto &c: r.sc.cmds) c.active=false; int rc=process(r,{f}); o.flush(); Loading Loading
commands/editor/sed.cpp +50 −30 Changes for commands/editor/sed.cpp: 50 added lines, 30 removed lines. Original line number Diff line number Diff line Loading @@ -118,6 +118,7 @@ namespace fresh { namespace cmd { std::map<std::string,std::unique_ptr<std::ofstream>> wstreams; std::map<std::string,std::unique_ptr<std::ifstream>> rstreams; // R command readers std::ostream *out=&std::cout; bool missingNl=false; // the last line written lacked its newline int lastRe=-1; std::string error; int status=0; Loading Loading @@ -413,7 +414,7 @@ namespace fresh { namespace cmd { // after a command: optional spaces, then ';', newline, '}' or '#' s.ws(); if(!s.end() && s.peek()!=';' && s.peek()!='\n' && s.peek()!='}' && s.peek()!='#' && c.name!='}' && c.name!='a' && c.name!='i' && c.name!='c') c.name!='{' && c.name!='}' && c.name!='a' && c.name!='i' && c.name!='c') return fail(r,std::string("extra characters after command")); r.sc.cmds.push_back(std::move(c)); } Loading Loading @@ -528,7 +529,8 @@ namespace fresh { namespace cmd { std::string ps,hs; long lineNo=0; bool isLast=false; bool lastHadNl=true; bool psNl=true; // the pattern space ends a newline-terminated line bool hsNl=true; bool subst=false; // for t/T std::string appendQ; // a/r/R output after the cycle std::string file; Loading @@ -536,11 +538,6 @@ namespace fresh { namespace cmd { int exitCode=0; }; static void writeLine(std::ostream &o,const std::string &s,bool nl,char delim){ o << s; if(nl) o << delim; } bool matchRe(Run &r,int idx,const std::string &s,size_t nmatch=0,regmatch_t *m=nullptr,int eflags=0){ if(idx<0) idx=r.lastRe; if(idx<0){ fail(r,"no previous regular expression"); return false; } Loading Loading @@ -655,11 +652,21 @@ namespace fresh { namespace cmd { long count=0; bool did=false; regmatch_t m[10]; size_t prevEnd=std::string::npos; while(pos<=src.size()){ int ef=pos>0 ? REG_NOTBOL : 0; if(regexec(re,src.c_str()+pos,10,m,ef)!=0) break; ++count; size_t so=pos+m[0].rm_so,eo=pos+m[0].rm_eo; if(so==eo && so==prevEnd){ // an empty match right where the previous one ended // doesn't count (GNU: "six" s/x*/-/g -> "-s-i-") if(so>=src.size()) break; out+=src[so]; pos=so+1; continue; } prevEnd=eo; ++count; bool replaceThis=count>=c.nth && (c.global || count==c.nth); out.append(src,pos,so-pos); if(replaceThis){ Loading Loading @@ -721,19 +728,29 @@ namespace fresh { namespace cmd { return out+line+"$"; } // every write to the main output: first the newline a previous, // unterminated last line still owes (GNU sed does the same) void emit(Run &r,const std::string &s){ if(r.missingNl){ *r.out << (r.nulData ? '\0' : '\n'); r.missingNl=false; } *r.out << s; } void flushAppend(Run &r,State &st){ if(!st.appendQ.empty()){ *r.out << st.appendQ; emit(r,st.appendQ); st.appendQ.clear(); } } void output(Run &r,State &st,const std::string &s){ writeLine(*r.out,s,st.lastHadNl || !st.isLast,r.nulData ? '\0' : '\n'); void output(Run &r,State &st,const std::string &s,bool terminated){ emit(r,s); if(terminated) *r.out << (r.nulData ? '\0' : '\n'); else r.missingNl=true; (void)st; } void writeTo(Run &r,const std::string &file,const std::string &s,char delim){ if(file=="/dev/stdout"){ *r.out << s << delim; return; } if(file=="/dev/stdout"){ emit(r,s+std::string(1,delim)); return; } if(file=="/dev/stderr"){ std::cerr << s << delim; return; } std::ofstream *f=wstream(r,file); if(f){ *f << s << delim; f->flush(); } Loading @@ -757,12 +774,12 @@ namespace fresh { namespace cmd { } switch(c.name){ case '{': break; case '=': *r.out << st.lineNo << '\n'; break; case '=': emit(r,std::to_string(st.lineNo)+"\n"); break; case 'a': st.appendQ+=c.text+"\n"; break; case 'i': *r.out << c.text << '\n'; break; case 'i': emit(r,c.text+"\n"); break; case 'c': // in a range: the text once, at its end if(c.a2.type==Addr::None || c.negate || !c.active) *r.out << c.text << '\n'; if(c.a2.type==Addr::None || c.negate || !c.active) emit(r,c.text+"\n"); st.ps.clear(); autoPrint=false; pc=cmds.size(); Loading Loading @@ -793,17 +810,19 @@ namespace fresh { namespace cmd { } case 'e': if(c.text.empty()) st.ps=runShell(st.ps); else *r.out << runShell(c.text) << '\n'; else emit(r,runShell(c.text)+"\n"); break; case 'F': *r.out << (st.file.empty() ? "-" : st.file) << '\n'; break; case 'g': st.ps=st.hs; break; case 'G': st.ps+="\n"+st.hs; break; case 'h': st.hs=st.ps; break; case 'H': st.hs+="\n"+st.ps; break; case 'x': std::swap(st.ps,st.hs); break; case 'l': *r.out << escapeL(st.ps,c.lwidth<0 ? 70 : c.lwidth) << '\n'; break; case 'F': emit(r,(st.file.empty() ? std::string("-") : st.file)+"\n"); break; case 'g': st.ps=st.hs; st.psNl=st.hsNl; break; // appending: the appended part decides whether the // result still owes a final newline (as in GNU sed) case 'G': st.ps+="\n"+st.hs; st.psNl=st.hsNl; break; case 'h': st.hs=st.ps; st.hsNl=st.psNl; break; case 'H': st.hs+="\n"+st.ps; st.hsNl=st.psNl; break; case 'x': std::swap(st.ps,st.hs); std::swap(st.psNl,st.hsNl); break; case 'l': emit(r,escapeL(st.ps,c.lwidth<0 ? 70 : c.lwidth)+"\n"); break; case 'n': if(r.quiet ? false : true) output(r,st,st.ps); if(r.quiet ? false : true) output(r,st,st.ps,st.psNl); flushAppend(r,st); if(!nextLine(r,st,in,false)){ autoPrint=false; st.quit=true; continue; } break; Loading @@ -818,10 +837,10 @@ namespace fresh { namespace cmd { } break; } case 'p': output(r,st,st.ps); break; case 'p': output(r,st,st.ps,st.psNl); break; case 'P': { size_t p=st.ps.find('\n'); *r.out << st.ps.substr(0,p) << nl; emit(r,st.ps.substr(0,p)+std::string(1,nl)); break; } case 'q': Loading Loading @@ -851,7 +870,7 @@ namespace fresh { namespace cmd { case 's': if(substitute(r,c,st)){ st.subst=true; if(c.print) output(r,st,st.ps); if(c.print) output(r,st,st.ps,st.psNl); if(!c.wfile.empty()) writeTo(r,c.wfile,st.ps,nl); } if(!r.error.empty()) return; Loading @@ -866,7 +885,7 @@ namespace fresh { namespace cmd { } ++pc; } if(autoPrint) output(r,st,st.ps); if(autoPrint) output(r,st,st.ps,st.psNl); flushAppend(r,st); } Loading @@ -878,7 +897,7 @@ namespace fresh { namespace cmd { if(!in.read(line,hadNl,isLast,sf,file)) return false; ++st.lineNo; st.isLast=isLast; st.lastHadNl=hadNl; st.psNl=hadNl; st.file=file; if(append) st.ps+="\n"+line; else st.ps=line; Loading @@ -902,7 +921,7 @@ namespace fresh { namespace cmd { ++st.lineNo; st.ps=line; st.isLast=isLast; st.lastHadNl=hadNl; st.psNl=hadNl; st.file=file; cycle(r,st,in); if(!r.error.empty()){ Loading Loading @@ -946,6 +965,7 @@ namespace fresh { namespace cmd { { std::ofstream o(tmp.c_str(),std::ios::out|std::ios::trunc|std::ios::binary); r.out=&o; r.missingNl=false; for(auto &c: r.sc.cmds) c.active=false; int rc=process(r,{f}); o.flush(); Loading