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

bugfix inner sloop

parent 5889b75a
Loading
Loading
Loading
Loading
+80 −0
Changes for src/shell.cpp: 80 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -480,6 +480,86 @@ std::vector<std::string> fresh::tokenizeShellArgs(const std::string &segment){
        // ISO boot-medium scan) into an infinite loop. Must be checked
        // BEFORE the "$(cmd)" branch just below, which explicitly excludes
        // "$((" and would otherwise never get a chance to run first.
        // "${...}" parameter expansion: scanned here as one complete,
        // brace-depth-matched unit (same simple '{'/'}' counting
        // Environment::expand() itself already uses internally for e.g.
        // "${var:-word}") BEFORE the "$(cmd)"/"$((expr))" branches below
        // ever get a look at it. Without this, a "$(...)" nested inside an
        // operator's default word -- e.g. kernel.spec's own
        // "${KERNEL_JOBS:-$(nproc)}" -- gets intercepted by the standalone
        // "$(cmd)" branch mid-scan: it flushPending()s the still-open,
        // syntactically incomplete "${KERNEL_JOBS:-" prefix on its own,
        // Environment::expand() (which has no closing '}' to find) falls
        // back to its own documented "unterminated -- treat as literal"
        // behavior, and the REAL closing '}' -- once the scan reaches it --
        // has nothing left open to close and comes out as a stray literal
        // character. Net effect: "-jKERNEL_JOBS" silently became
        // "-j:-32}"/"-j32:-32}" instead of "-j32", and make -j's malformed
        // value went unnoticed rather than erroring, because *some* text
        // (the genuinely-computed default/value) still came out first.
        // Any "$(...)"/"`...`" found while scanning the span is resolved
        // right here, via the same runCaptured() plumbing the standalone
        // branches below use, so the text finally handed to
        // Environment::expand() is fully substituted already -- its own
        // "${var:-word}" operator handling has no command-substitution
        // support of its own and was never meant to need it.
        if(c=='$' && i+1<n && segment[i+1]=='{'){
            size_t j=i+2;
            int depth=1;
            std::string raw;
            while(j<n && depth>0){
                char cj=segment[j];
                if(cj=='{'){
                    ++depth;
                    raw+=cj;
                    ++j;
                }else if(cj=='}'){
                    --depth;
                    if(depth>0) raw+=cj;
                    ++j;
                }else if(cj=='$' && j+1<n && segment[j+1]=='(' &&
                         !(j+2<n && segment[j+2]=='(')){
                    size_t k=j+2;
                    int pdepth=1;
                    while(k<n && pdepth>0){
                        if(segment[k]=='(') ++pdepth;
                        else if(segment[k]==')') --pdepth;
                        if(pdepth>0) ++k;
                    }
                    raw+=Shell::instance().runCaptured(segment.substr(j+2,k-(j+2)));
                    j=(k<n) ? k+1 : k;
                }else if(cj=='`'){
                    size_t k=j+1;
                    std::string inner;
                    while(k<n && segment[k]!='`'){
                        if(segment[k]=='\\' && k+1<n &&
                           (segment[k+1]=='`' || segment[k+1]=='$' || segment[k+1]=='\\')){
                            inner+=segment[k+1];
                            k+=2;
                            continue;
                        }
                        inner+=segment[k];
                        ++k;
                    }
                    raw+=Shell::instance().runCaptured(inner);
                    j=(k<n) ? k+1 : k;
                }else{
                    raw+=cj;
                    ++j;
                }
            }
            if(depth==0){
                flushPending();
                current+=fresh::Environment::instance().expand("${"+raw+"}");
                have=true;
                i=j;
                continue;
            }
            // unterminated -- fall through, let '$' accumulate as an
            // ordinary character below, same "not full parity" handling
            // every other unterminated-substitution case here already gets.
        }

        // flushPending() first, same as the "$(cmd)" branch, so any plain
        // text accumulated just before this substitution resolves (in the
        // right order) ahead of it.