Commit 8aa6b07f authored by jan.koester's avatar jan.koester
Browse files

test

parent 84deb24f
Loading
Loading
Loading
Loading
+98 −3
Changes for src/shell.cpp: 98 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -1829,6 +1829,46 @@ std::vector<std::string> fresh::splitPipelineStages(const std::string &segment,
    // '|' check already respects.
    int braceDepth=0;

    // Depth of unclosed if/for/while/case keyword blocks -- e.g. the
    // kernel's own scripts/setlocalversion: "if { git ... || git ...; } |
    // read dummy; then ...; fi". By the time that '|' is reached, the
    // if-CONDITION's own "{...}" has already closed (braceDepth is back to
    // 0), but the enclosing "if...fi" is still very much open -- that pipe
    // is still part of building the if's own condition command, never a
    // boundary between two stages of some OUTER pipeline `runSimple()`
    // itself was asked to split. Without this, that '|' got treated as a
    // real top-level split point, breaking the whole "if...fi" into a
    // bogus multi-stage "pipeline" (stage 0 = "if { ... }", stage 1 =
    // "read dummy; then ...; fi") -- so the "if" builtin never even ran;
    // "then"/"fi" surfaced later as their own unrelated "command not
    // found" statements instead. Word-based (unlike braceDepth just
    // above, which is a plain character count): "if"/"elif"/"while"/
    // "for"/"case" only count as real openers, and "fi"/"done"/"esac"
    // only as real closers, in actual command-name position -- the same
    // boundary set (start of text, or right after ';'/'\n'/'|'/"&&"/
    // "||") parse()'s own per-statement scan and needsMoreInput() gate
    // the identical recognition on (see their own comments) -- so e.g.
    // "echo done" 's own "done" (cat's argument, not a real loop
    // terminator) can never desync this count.
    int keywordDepth=0;
    static const char *kwOpeners[]={"if","elif","while","for","case"};
    static const char *kwClosers[]={"fi","done","esac"};
    auto atKeywordStmtStart=[&](size_t pos)->bool{
        size_t p=pos;
        while(p>0 && (segment[p-1]==' ' || segment[p-1]=='\t'))
            --p;
        if(p==0) return true;
        char c=segment[p-1];
        if(c==';' || c=='\n' || c=='|') return true;
        if(c=='&' && p>=2 && segment[p-2]=='&') return true;
        return false;
    };
    auto isWordBoundary=[&](size_t pos)->bool{
        return pos>=segment.size() || segment[pos]==' ' || segment[pos]=='\t' ||
               segment[pos]=='\n' || segment[pos]=='\r' || segment[pos]==';' ||
               segment[pos]=='|' || segment[pos]=='&';
    };

    auto flushStage=[&](){
        stages.push_back(current);
        current.clear();
@@ -2025,6 +2065,34 @@ std::vector<std::string> fresh::splitPipelineStages(const std::string &segment,
            continue;
        }

        if(atKeywordStmtStart(i)){
            bool matchedKw=false;
            for(const char *kw: kwOpeners){
                size_t klen=strlen(kw);
                if(segment.compare(i,klen,kw)==0 && isWordBoundary(i+klen)){
                    ++keywordDepth;
                    current+=segment.substr(i,klen);
                    i+=klen;
                    matchedKw=true;
                    break;
                }
            }
            if(!matchedKw && keywordDepth>0){
                for(const char *kw: kwClosers){
                    size_t klen=strlen(kw);
                    if(segment.compare(i,klen,kw)==0 && isWordBoundary(i+klen)){
                        --keywordDepth;
                        current+=segment.substr(i,klen);
                        i+=klen;
                        matchedKw=true;
                        break;
                    }
                }
            }
            if(matchedKw)
                continue;
        }

        if(c=='{'){
            ++braceDepth;
            current+=c;
@@ -2038,7 +2106,7 @@ std::vector<std::string> fresh::splitPipelineStages(const std::string &segment,
            continue;
        }

        if(c=='|' && braceDepth==0){
        if(c=='|' && braceDepth==0 && keywordDepth==0){
            flushStage();
            ++i;
            continue;
@@ -3230,9 +3298,25 @@ int fresh::Shell::runSegmentWords(const std::string &input,const std::vector<She
            // argument, not a real while-loop terminator) would desync
            // this depth count and either close the group too early or
            // (as observed) miss the real closing '}' entirely.
            // "if"/"elif"/"while" right before words[i] also opens a real
            // command position -- their own CONDITION can itself be a
            // compound command, e.g. the kernel's own
            // scripts/setlocalversion: "if { git ... || git ...; } | read
            // dummy; then ...; fi" as a function's own body. Without this,
            // that inner "{" was never counted as an opener at all (only
            // ';'/'&&'/'||'/'|' and the position right after THIS group's
            // own opening brace were), yet its own matching "}" (correctly
            // ';'-preceded) still closed -- decrementing `depth` back to 0
            // and making this scan think the WHOLE group ended right there,
            // one "}" (and the entire rest of the real body: "| read
            // dummy; then ...; fi; ...") too early. Same root cause, and
            // same fix, as parse()'s own per-statement scan needed for the
            // identical reason (see its own comment).
            bool atStmtStart=(i==from+1 || words[i-1].text==";" ||
                               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");
            if(atStmtStart && words[i].text=="{") ++depth;
            else if(atStmtStart && words[i].text=="}"){
                if(--depth==0){ closeIdx=i; break; }
@@ -3370,9 +3454,20 @@ void fresh::Shell::collectFunctionDefinitions(const std::string &input,std::vect
    for(size_t i=0;i<words.size();){
        const std::string &w=words[i].text;

        // "if"/"elif"/"while" right before words[i] also opens a real
        // command position (their own CONDITION can itself be a compound
        // command, e.g. "if { ...; } | read x; then") -- same fix, same
        // reason, as this function's own depth counter just below needs it
        // for (see that comment): without it, a nested "{" used as an
        // if/while's own condition was never counted as a real opener here
        // either, letting its matching "}" desync `depth` and mistake some
        // enclosing function's own header/body match at a wrong,
        // coincidentally-depth-0 position.
        bool atStmtStart=(i==0) || words[i-1].text==";" ||
                          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";

        if(depth==0 && atStmtStart){
            std::string name;