Loading commands/shell/case.cpp +8 −27 Changes for commands/shell/case.cpp: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -152,27 +152,13 @@ namespace fresh { namespace cmd { std::string trailing=shellWordsRawText(remainder,words,esacIdx+1,words.size()); // "esac <<WORD" -- a here-doc redirect on the whole case // construct (the matched clause's own body may contain a "read" // meant to consume it), not more statements after it (this // builtin's other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- must be // applied before the clause-matching loop below runs, same // reasoning as while/for/if. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "esac > out", "esac < in", "esac <<WORD" (the matched // clause may contain a "read" meant to consume it): // redirections on the whole case, applied before the clauses // run; more statements after them stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; for(size_t c=0;c<clauses.size() && !matched && shell.running();++c){ std::string clauseText=shellWordsRawText(remainder,words,clauses[c].first,clauses[c].second); Loading Loading @@ -230,12 +216,7 @@ namespace fresh { namespace cmd { status=shell.parse(body); } if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" (see Shell::returning()/ // breaking()/continuing()) must skip any trailing command on Loading commands/shell/for.cpp +8 −27 Changes for commands/shell/for.cpp: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -95,27 +95,13 @@ namespace fresh { namespace cmd { // still correctly reports 0, matching real shells. int status=shell.running() ? 0 : shell.exitCode(); // "done <<WORD" -- a here-doc redirect on the loop as a whole // (its body may well contain a "read" meant to consume it, one // line per iteration), not more statements after it (this // builtin's other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- same fix, // same reasoning, just here since "for" is exactly as exposed // to it as "while" is. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "done > out 2>&1", "done < in", "done <<WORD" (its body may // well contain a "read" meant to consume it, one line per // iteration): redirections on the loop as a whole, not more // statements after it -- those stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; shell.enterLoop(); for(size_t i=0; i<items.size() && shell.running() && !shell.returning() && Loading Loading @@ -149,12 +135,7 @@ namespace fresh { namespace cmd { } shell.exitLoop(); if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" must skip any trailing // command on the same input too, so it can keep unwinding up Loading commands/shell/if.cpp +8 −26 Changes for commands/shell/if.cpp: 8 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -119,36 +119,18 @@ namespace fresh { namespace cmd { std::string trailing=shellWordsRawText(remainder,words,fiIdx+1,words.size()); // "fi <<WORD" -- a here-doc redirect on the whole if/elif/else // construct (the taken branch may contain a "read" meant to // consume it), not more statements after it (this builtin's // other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- must be // applied before `branch` runs, same reasoning as while/for. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "fi > out", "fi < in", "fi <<WORD" (the taken branch may // contain a "read" meant to consume it): redirections on the // whole if/elif/else, applied before `branch` runs; more // statements after them stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; if(!branch.empty()) status=shell.parse(branch); if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" (see Shell::returning()/ // breaking()/continuing()) must skip any trailing command on Loading commands/shell/while.cpp +5 −65 Changes for commands/shell/while.cpp: 5 added lines, 65 removed lines. Original line number Diff line number Diff line Loading @@ -69,34 +69,11 @@ namespace fresh { namespace cmd { Shell &shell=Shell::instance(); int savedStdin=-1,savedStdout=-1,savedStderr=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } trailing.clear(); } // the remaining trailing words are redirections for the loop // "done > out", "done < in", "done <<EOF": for the whole loop CompoundRedirect redirect; std::string trailingCommand; if(!trailing.empty()){ Redirection redir; std::vector<std::string> rest=extractRedirections(tokenizeShellArgs(trailing),redir); if(!rest.empty()){ trailingCommand=trailing; // not redirections: run it after the loop, as before }else if(!applyRedirections(redir,savedStdin,savedStdout,savedStderr)){ restore(savedStdin,savedStdout,savedStderr); if(!redirect.apply(trailing,trailingCommand)) return 1; } } int status=0; shell.enterLoop(); Loading @@ -121,11 +98,7 @@ namespace fresh { namespace cmd { } shell.exitLoop(); std::cout.flush(); std::cerr.flush(); restore(savedStdin,savedStdout,savedStderr); if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); if(!shell.running()) status=shell.exitCode(); Loading @@ -139,40 +112,7 @@ namespace fresh { namespace cmd { private: bool _until; const char *_name; static bool redirect(int target,int fd,int &saved){ if(fd<0) return false; if(saved<0) saved=dup(target); dup2(fd,target); close(fd); return true; } bool applyRedirections(const Redirection &r,int &in,int &out,int &err){ if(r.hasStdin && !redirect(0,open(r.stdinFile.c_str(),O_RDONLY),in)){ std::cerr << "fresh: " << r.stdinFile << ": " << strerror(errno) << std::endl; return false; } if(r.hasStdout && !redirect(1,open(r.stdoutFile.c_str(),O_WRONLY|O_CREAT|(r.append ? O_APPEND : O_TRUNC),0644),out)){ std::cerr << "fresh: " << r.stdoutFile << ": " << strerror(errno) << std::endl; return false; } if(r.hasStderr && !redirect(2,open(r.stderrFile.c_str(),O_WRONLY|O_CREAT|(r.stderrAppend ? O_APPEND : O_TRUNC),0644),err)){ std::cerr << "fresh: " << r.stderrFile << ": " << strerror(errno) << std::endl; return false; } if(r.stderrToStdout) redirect(2,dup(1),err); if(r.stdoutToStderr) redirect(1,dup(2),out); return true; } static void restore(int in,int out,int err){ if(in>=0){ dup2(in,0); close(in); } if(out>=0){ dup2(out,1); close(out); } if(err>=0){ dup2(err,2); close(err); } } }; const char *_name; }; class WhileCommand : public LoopCommand { public: Loading src/shell.cpp +63 −0 Changes for src/shell.cpp: 63 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2829,6 +2829,69 @@ bool fresh::extractHeredocSpec(std::string &text,fresh::HeredocSpec &spec){ return false; } bool fresh::CompoundRedirect::redirect(int target,int fd,int &saved){ if(fd<0) return false; if(saved<0) saved=dup(target); dup2(fd,target); close(fd); return true; } bool fresh::CompoundRedirect::apply(const std::string &trailing,std::string &rest){ rest=trailing; // the redirections: the words up to the first statement separator std::vector<ShellWord> words=tokenizeShellWords(trailing); size_t sep=0; while(sep<words.size() && words[sep].text!=";" && words[sep].text!="&&" && words[sep].text!="||" && words[sep].text!="&") ++sep; if(sep==0) return true; std::string text=shellWordsRawText(trailing,words,0,sep); std::string after=sep<words.size() ? trailing.substr(words[sep].start) : std::string(); HeredocSpec heredoc; bool hasHeredoc=extractHeredocSpec(text,heredoc); Redirection r; if(!extractRedirections(tokenizeShellArgs(text),r).empty()) return true; // not (only) redirections: leave it all to the caller, as before // what was printed so far goes where it was meant to std::cout.flush(); std::cerr.flush(); if(hasHeredoc){ _heredocTemp=Shell::instance().collectHeredocBody(heredoc); if(!_heredocTemp.empty()){ r.hasStdin=true; r.stdinFile=_heredocTemp; } } auto fail=[&](const std::string &file){ std::cerr << "fresh: " << file << ": " << strerror(errno) << std::endl; restore(); return false; }; if(r.hasStdin && !redirect(0,open(r.stdinFile.c_str(),O_RDONLY),_in)) return fail(r.stdinFile); if(r.hasStdout && !redirect(1,open(r.stdoutFile.c_str(),O_WRONLY|O_CREAT|(r.append ? O_APPEND : O_TRUNC),0644),_out)) return fail(r.stdoutFile); if(r.hasStderr && !redirect(2,open(r.stderrFile.c_str(),O_WRONLY|O_CREAT|(r.stderrAppend ? O_APPEND : O_TRUNC),0644),_err)) return fail(r.stderrFile); if(r.stderrToStdout) redirect(2,dup(1),_err); if(r.stdoutToStderr) redirect(1,dup(2),_out); rest=after; return true; } void fresh::CompoundRedirect::restore(){ std::cout.flush(); std::cerr.flush(); if(_in>=0){ dup2(_in,0); close(_in); _in=-1; } if(_out>=0){ dup2(_out,1); close(_out); _out=-1; } if(_err>=0){ dup2(_err,2); close(_err); _err=-1; } if(!_heredocTemp.empty()){ unlink(_heredocTemp.c_str()); _heredocTemp.clear(); } } std::string fresh::Shell::unescapeHeredocLine(const std::string &line,bool &continues){ continues=false; std::string out; Loading Loading
commands/shell/case.cpp +8 −27 Changes for commands/shell/case.cpp: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -152,27 +152,13 @@ namespace fresh { namespace cmd { std::string trailing=shellWordsRawText(remainder,words,esacIdx+1,words.size()); // "esac <<WORD" -- a here-doc redirect on the whole case // construct (the matched clause's own body may contain a "read" // meant to consume it), not more statements after it (this // builtin's other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- must be // applied before the clause-matching loop below runs, same // reasoning as while/for/if. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "esac > out", "esac < in", "esac <<WORD" (the matched // clause may contain a "read" meant to consume it): // redirections on the whole case, applied before the clauses // run; more statements after them stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; for(size_t c=0;c<clauses.size() && !matched && shell.running();++c){ std::string clauseText=shellWordsRawText(remainder,words,clauses[c].first,clauses[c].second); Loading Loading @@ -230,12 +216,7 @@ namespace fresh { namespace cmd { status=shell.parse(body); } if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" (see Shell::returning()/ // breaking()/continuing()) must skip any trailing command on Loading
commands/shell/for.cpp +8 −27 Changes for commands/shell/for.cpp: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -95,27 +95,13 @@ namespace fresh { namespace cmd { // still correctly reports 0, matching real shells. int status=shell.running() ? 0 : shell.exitCode(); // "done <<WORD" -- a here-doc redirect on the loop as a whole // (its body may well contain a "read" meant to consume it, one // line per iteration), not more statements after it (this // builtin's other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- same fix, // same reasoning, just here since "for" is exactly as exposed // to it as "while" is. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "done > out 2>&1", "done < in", "done <<WORD" (its body may // well contain a "read" meant to consume it, one line per // iteration): redirections on the loop as a whole, not more // statements after it -- those stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; shell.enterLoop(); for(size_t i=0; i<items.size() && shell.running() && !shell.returning() && Loading Loading @@ -149,12 +135,7 @@ namespace fresh { namespace cmd { } shell.exitLoop(); if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" must skip any trailing // command on the same input too, so it can keep unwinding up Loading
commands/shell/if.cpp +8 −26 Changes for commands/shell/if.cpp: 8 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -119,36 +119,18 @@ namespace fresh { namespace cmd { std::string trailing=shellWordsRawText(remainder,words,fiIdx+1,words.size()); // "fi <<WORD" -- a here-doc redirect on the whole if/elif/else // construct (the taken branch may contain a "read" meant to // consume it), not more statements after it (this builtin's // other, "; more..." use of `trailing`, below). See // while.cpp's matching comment for the full story -- must be // applied before `branch` runs, same reasoning as while/for. int savedStdin=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } } // "fi > out", "fi < in", "fi <<WORD" (the taken branch may // contain a "read" meant to consume it): redirections on the // whole if/elif/else, applied before `branch` runs; more // statements after them stay in `trailing` CompoundRedirect redirect; if(!redirect.apply(std::string(trailing),trailing)) return 1; if(!branch.empty()) status=shell.parse(branch); if(savedStdin>=0){ dup2(savedStdin,0); close(savedStdin); } if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); // A pending "return"/"break"/"continue" (see Shell::returning()/ // breaking()/continuing()) must skip any trailing command on Loading
commands/shell/while.cpp +5 −65 Changes for commands/shell/while.cpp: 5 added lines, 65 removed lines. Original line number Diff line number Diff line Loading @@ -69,34 +69,11 @@ namespace fresh { namespace cmd { Shell &shell=Shell::instance(); int savedStdin=-1,savedStdout=-1,savedStderr=-1; std::string heredocTempFile; HeredocSpec heredocSpec; if(extractHeredocSpec(trailing,heredocSpec)){ heredocTempFile=shell.collectHeredocBody(heredocSpec); if(!heredocTempFile.empty()){ int fd=open(heredocTempFile.c_str(),O_RDONLY); if(fd>=0){ savedStdin=dup(0); dup2(fd,0); close(fd); } } trailing.clear(); } // the remaining trailing words are redirections for the loop // "done > out", "done < in", "done <<EOF": for the whole loop CompoundRedirect redirect; std::string trailingCommand; if(!trailing.empty()){ Redirection redir; std::vector<std::string> rest=extractRedirections(tokenizeShellArgs(trailing),redir); if(!rest.empty()){ trailingCommand=trailing; // not redirections: run it after the loop, as before }else if(!applyRedirections(redir,savedStdin,savedStdout,savedStderr)){ restore(savedStdin,savedStdout,savedStderr); if(!redirect.apply(trailing,trailingCommand)) return 1; } } int status=0; shell.enterLoop(); Loading @@ -121,11 +98,7 @@ namespace fresh { namespace cmd { } shell.exitLoop(); std::cout.flush(); std::cerr.flush(); restore(savedStdin,savedStdout,savedStderr); if(!heredocTempFile.empty()) unlink(heredocTempFile.c_str()); redirect.restore(); if(!shell.running()) status=shell.exitCode(); Loading @@ -139,40 +112,7 @@ namespace fresh { namespace cmd { private: bool _until; const char *_name; static bool redirect(int target,int fd,int &saved){ if(fd<0) return false; if(saved<0) saved=dup(target); dup2(fd,target); close(fd); return true; } bool applyRedirections(const Redirection &r,int &in,int &out,int &err){ if(r.hasStdin && !redirect(0,open(r.stdinFile.c_str(),O_RDONLY),in)){ std::cerr << "fresh: " << r.stdinFile << ": " << strerror(errno) << std::endl; return false; } if(r.hasStdout && !redirect(1,open(r.stdoutFile.c_str(),O_WRONLY|O_CREAT|(r.append ? O_APPEND : O_TRUNC),0644),out)){ std::cerr << "fresh: " << r.stdoutFile << ": " << strerror(errno) << std::endl; return false; } if(r.hasStderr && !redirect(2,open(r.stderrFile.c_str(),O_WRONLY|O_CREAT|(r.stderrAppend ? O_APPEND : O_TRUNC),0644),err)){ std::cerr << "fresh: " << r.stderrFile << ": " << strerror(errno) << std::endl; return false; } if(r.stderrToStdout) redirect(2,dup(1),err); if(r.stdoutToStderr) redirect(1,dup(2),out); return true; } static void restore(int in,int out,int err){ if(in>=0){ dup2(in,0); close(in); } if(out>=0){ dup2(out,1); close(out); } if(err>=0){ dup2(err,2); close(err); } } }; const char *_name; }; class WhileCommand : public LoopCommand { public: Loading
src/shell.cpp +63 −0 Changes for src/shell.cpp: 63 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2829,6 +2829,69 @@ bool fresh::extractHeredocSpec(std::string &text,fresh::HeredocSpec &spec){ return false; } bool fresh::CompoundRedirect::redirect(int target,int fd,int &saved){ if(fd<0) return false; if(saved<0) saved=dup(target); dup2(fd,target); close(fd); return true; } bool fresh::CompoundRedirect::apply(const std::string &trailing,std::string &rest){ rest=trailing; // the redirections: the words up to the first statement separator std::vector<ShellWord> words=tokenizeShellWords(trailing); size_t sep=0; while(sep<words.size() && words[sep].text!=";" && words[sep].text!="&&" && words[sep].text!="||" && words[sep].text!="&") ++sep; if(sep==0) return true; std::string text=shellWordsRawText(trailing,words,0,sep); std::string after=sep<words.size() ? trailing.substr(words[sep].start) : std::string(); HeredocSpec heredoc; bool hasHeredoc=extractHeredocSpec(text,heredoc); Redirection r; if(!extractRedirections(tokenizeShellArgs(text),r).empty()) return true; // not (only) redirections: leave it all to the caller, as before // what was printed so far goes where it was meant to std::cout.flush(); std::cerr.flush(); if(hasHeredoc){ _heredocTemp=Shell::instance().collectHeredocBody(heredoc); if(!_heredocTemp.empty()){ r.hasStdin=true; r.stdinFile=_heredocTemp; } } auto fail=[&](const std::string &file){ std::cerr << "fresh: " << file << ": " << strerror(errno) << std::endl; restore(); return false; }; if(r.hasStdin && !redirect(0,open(r.stdinFile.c_str(),O_RDONLY),_in)) return fail(r.stdinFile); if(r.hasStdout && !redirect(1,open(r.stdoutFile.c_str(),O_WRONLY|O_CREAT|(r.append ? O_APPEND : O_TRUNC),0644),_out)) return fail(r.stdoutFile); if(r.hasStderr && !redirect(2,open(r.stderrFile.c_str(),O_WRONLY|O_CREAT|(r.stderrAppend ? O_APPEND : O_TRUNC),0644),_err)) return fail(r.stderrFile); if(r.stderrToStdout) redirect(2,dup(1),_err); if(r.stdoutToStderr) redirect(1,dup(2),_out); rest=after; return true; } void fresh::CompoundRedirect::restore(){ std::cout.flush(); std::cerr.flush(); if(_in>=0){ dup2(_in,0); close(_in); _in=-1; } if(_out>=0){ dup2(_out,1); close(_out); _out=-1; } if(_err>=0){ dup2(_err,2); close(_err); _err=-1; } if(!_heredocTemp.empty()){ unlink(_heredocTemp.c_str()); _heredocTemp.clear(); } } std::string fresh::Shell::unescapeHeredocLine(const std::string &line,bool &continues){ continues=false; std::string out; Loading