Commit 84deb24f authored by jan.koester's avatar jan.koester
Browse files

test

parent 68ff5a6b
Loading
Loading
Loading
Loading
+45 −0
Changes for src/shell.cpp: 45 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -2290,6 +2290,33 @@ bool fresh::needsMoreInput(const std::string &text){
        char c=text[p-1];
        if(c==';' || c=='\n' || c=='|') return true;
        if(c=='&' && p>=2 && text[p-2]=='&') return true;
        // "if"/"elif"/"while" right before `pos` also opens a real command
        // position: their own CONDITION is a command like any other, and
        // can itself be a compound one, e.g. the kernel's own
        // scripts/setlocalversion: "if { git ... || git ...; } | read
        // dummy; then ...; fi". Without this, that "{" was never
        // recognized as an opener here (only after ';'/'\n'/'|'/"&&"), so
        // `depth` never actually reached 1 for it -- yet its own matching
        // "}" a few characters later (correctly ';'-preceded) still passed
        // this same check and decremented anyway, net-closing the "if"
        // itself far too early. That let needsMoreInput() -- and every
        // caller relying on it, from runScript()'s own top-level loop
        // through parse()'s recursive one -- believe the whole "if...fi"
        // was already complete right after "then", handing "echo ...",
        // "fi", and everything after to the shell as separate, unrelated,
        // top-level statements instead of one construct. Bounded to a
        // whole word (not e.g. "motif {") by requiring ITS OWN left side
        // to also land on a real boundary.
        {
            size_t wordEnd=p;
            size_t wordStart=wordEnd;
            while(wordStart>0 && text[wordStart-1]!=' ' && text[wordStart-1]!='\t' &&
                  text[wordStart-1]!=';' && text[wordStart-1]!='\n' && text[wordStart-1]!='|')
                --wordStart;
            std::string word=text.substr(wordStart,wordEnd-wordStart);
            if(word=="if" || word=="elif" || word=="while")
                return true;
        }
        return false;
    };

@@ -3661,9 +3688,27 @@ int fresh::Shell::parse(const std::string &input,bool applyErrexit){
        // at all, and every ';'/'&&'/'||' inside the group -- starting
        // with the very first one -- was wrongly treated as a top-level
        // segment boundary instead.
        // "if"/"elif"/"while" right before this word also opens a real
        // command position -- their own CONDITION is a command like any
        // other, and can itself be a compound one, e.g. the kernel's own
        // scripts/setlocalversion: "if { git ... || git ...; } | read
        // dummy; then". Without this, that "{" was never recognized as an
        // opener at all (only "|"/"!" and the segment-start position
        // were), yet its OWN matching "}" right after the ";" INSIDE the
        // group still satisfied the ";"-preceded check below and
        // decremented `depth` anyway -- desyncing the count by one and
        // making the outer "if"'s own ";" before "then" look like a
        // top-level statement separator instead of still being inside the
        // if. Splitting the input right there is exactly the "if: expected
        // 'then'" / "then: command not found" / "fi: command not found"
        // failure this caused: "then"/"fi" ran as their own bogus,
        // unrelated top-level commands instead of ever reaching
        // IfCommand/WhileCommand at all.
        bool atStmtStart=(i==segstart || words[i-1].text==";" ||
                           words[i-1].text=="&&" || words[i-1].text=="||" ||
                           words[i-1].text=="|" ||
                           words[i-1].text=="if" || words[i-1].text=="elif" ||
                           words[i-1].text=="while" ||
                           (i==segstart+1 && words[segstart].text=="!"));
        if(atStmtStart && (w=="if" || w=="for" || w=="while" || w=="case" || w=="{"))
            ++depth;