Commit fbbac469 authored by jan.koester's avatar jan.koester
Browse files

test

parent c677f241
Loading
Loading
Loading
Loading
+8 −27
Changes for commands/shell/case.cpp: 8 added lines, 27 removed lines.
Original line number Diff line number Diff line
@@ -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);
@@ -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
+8 −27
Changes for commands/shell/for.cpp: 8 added lines, 27 removed lines.
Original line number Diff line number Diff line
@@ -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() &&
@@ -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
+8 −26
Changes for commands/shell/if.cpp: 8 added lines, 26 removed lines.
Original line number Diff line number Diff line
@@ -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
+5 −65
Changes for commands/shell/while.cpp: 5 added lines, 65 removed lines.
Original line number Diff line number Diff line
@@ -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();
@@ -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();
@@ -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:
+63 −0
Changes for src/shell.cpp: 63 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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