Loading src/shell.cpp +75 −18 Changes for src/shell.cpp: 75 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -159,6 +159,62 @@ static size_t dqEnd(const std::string &s,size_t from){ return i; } // One past the "))" closing a "$((" at `at` (s.size() if unterminated). static size_t arithEnd(const std::string &s,size_t at){ size_t n=s.size(),j=at+3; int depth=0; while(j<n){ if(s[j]=='$' && j+1<n && s[j+1]=='(' && !(j+2<n && s[j+2]=='(')){ size_t c=cmdSubstClose(s,j+2); j=(c<n) ? c+1 : n; continue; } if(s[j]=='('){ ++depth; ++j; } else if(s[j]==')'){ if(depth==0){ ++j; if(j<n && s[j]==')') ++j; break; } --depth; ++j; }else ++j; } return j; } // "$((expr))": command substitutions inside expr run first (the kernel's // mkcapflags.sh: $(( ( 5*8 - ($PFX_SZ + 1) - $(echo "$NAME" | wc -c) ) / 8 ))), // then the arithmetic is evaluated static std::string expandArith(const std::string &span){ std::string out; size_t n=span.size(),i=0; // keep the leading "$((" and trailing "))" around the substituted body while(i<n){ if(i>0 && span[i]=='$' && i+1<n && span[i+1]=='(' && !(i+2<n && span[i+2]=='(')){ size_t c=cmdSubstClose(span,i+2); std::string r=fresh::Shell::instance().runCaptured(span.substr(i+2,c-(i+2))); while(!r.empty() && (r.back()=='\n' || r.back()==' ' || r.back()=='\t')) r.pop_back(); size_t b=0; while(b<r.size() && (r[b]==' ' || r[b]=='\t')) ++b; out+=r.substr(b); i=(c<n) ? c+1 : n; continue; } if(span[i]=='`'){ size_t e=span.find('`',i+1); if(e==std::string::npos) e=n; std::string r=fresh::Shell::instance().runCaptured(span.substr(i+1,e-i-1)); while(!r.empty() && (r.back()=='\n' || r.back()==' ')) r.pop_back(); out+=r; i=(e<n) ? e+1 : n; continue; } out+=span[i++]; } return fresh::Environment::instance().expand(out); } static void pushGlobExpanded(std::vector<std::string> &tokens,const std::string &word){ if(word.find_first_of("*?[")==std::string::npos){ tokens.push_back(word); Loading Loading @@ -644,25 +700,9 @@ std::vector<std::string> fresh::tokenizeShellArgs(const std::string &segment){ // text accumulated just before this substitution resolves (in the // right order) ahead of it. if(c=='$' && i+2<n && segment[i+1]=='(' && segment[i+2]=='('){ size_t j=i+3; int depth=0; while(j<n){ if(segment[j]=='('){ ++depth; ++j; }else if(segment[j]==')'){ if(depth==0){ ++j; if(j<n && segment[j]==')') ++j; break; } --depth; ++j; }else{ ++j; } } size_t j=arithEnd(segment,i); flushPending(); current+=fresh::Environment::instance().expand(segment.substr(i,j-i)); current+=expandArith(segment.substr(i,j-i)); have=true; i=j; continue; Loading Loading @@ -886,6 +926,13 @@ std::vector<std::string> fresh::tokenizeShellArgs(const std::string &segment){ // sees it intact). Paren-depth tracked only (no quote- // awareness inside the substitution itself) -- same scope // limit the top-level "$(...)" handling already has. if(segment[i]=='$' && i+2<n && segment[i+1]=='(' && segment[i+2]=='('){ size_t j=arithEnd(segment,i); flushRaw(); dqResult+=expandArith(segment.substr(i,j-i)); i=j; continue; } if(segment[i]=='$' && i+1<n && segment[i+1]=='(' && !(i+2<n && segment[i+2]=='(')){ size_t j=cmdSubstClose(segment,i+2); Loading Loading @@ -2040,6 +2087,16 @@ std::vector<std::string> fresh::splitPipelineStages(const std::string &segment, i=j; continue; } // a "( ... )" subshell group is one pipeline stage: a '|' inside it // belongs to the subshell (the kernel's net/wireless Makefile: // "(echo ...; echo | cat - $^ ; echo ...) > $@") if(c=='('){ size_t j=cmdSubstClose(segment,i+1); if(j<n) ++j; current+=segment.substr(i,j-i); i=j; continue; } // "`cmd | filter`" -- same reasoning as "$(...)" above, just for // the older backtick spelling. Backticks don't nest (no depth to Loading Loading
src/shell.cpp +75 −18 Changes for src/shell.cpp: 75 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -159,6 +159,62 @@ static size_t dqEnd(const std::string &s,size_t from){ return i; } // One past the "))" closing a "$((" at `at` (s.size() if unterminated). static size_t arithEnd(const std::string &s,size_t at){ size_t n=s.size(),j=at+3; int depth=0; while(j<n){ if(s[j]=='$' && j+1<n && s[j+1]=='(' && !(j+2<n && s[j+2]=='(')){ size_t c=cmdSubstClose(s,j+2); j=(c<n) ? c+1 : n; continue; } if(s[j]=='('){ ++depth; ++j; } else if(s[j]==')'){ if(depth==0){ ++j; if(j<n && s[j]==')') ++j; break; } --depth; ++j; }else ++j; } return j; } // "$((expr))": command substitutions inside expr run first (the kernel's // mkcapflags.sh: $(( ( 5*8 - ($PFX_SZ + 1) - $(echo "$NAME" | wc -c) ) / 8 ))), // then the arithmetic is evaluated static std::string expandArith(const std::string &span){ std::string out; size_t n=span.size(),i=0; // keep the leading "$((" and trailing "))" around the substituted body while(i<n){ if(i>0 && span[i]=='$' && i+1<n && span[i+1]=='(' && !(i+2<n && span[i+2]=='(')){ size_t c=cmdSubstClose(span,i+2); std::string r=fresh::Shell::instance().runCaptured(span.substr(i+2,c-(i+2))); while(!r.empty() && (r.back()=='\n' || r.back()==' ' || r.back()=='\t')) r.pop_back(); size_t b=0; while(b<r.size() && (r[b]==' ' || r[b]=='\t')) ++b; out+=r.substr(b); i=(c<n) ? c+1 : n; continue; } if(span[i]=='`'){ size_t e=span.find('`',i+1); if(e==std::string::npos) e=n; std::string r=fresh::Shell::instance().runCaptured(span.substr(i+1,e-i-1)); while(!r.empty() && (r.back()=='\n' || r.back()==' ')) r.pop_back(); out+=r; i=(e<n) ? e+1 : n; continue; } out+=span[i++]; } return fresh::Environment::instance().expand(out); } static void pushGlobExpanded(std::vector<std::string> &tokens,const std::string &word){ if(word.find_first_of("*?[")==std::string::npos){ tokens.push_back(word); Loading Loading @@ -644,25 +700,9 @@ std::vector<std::string> fresh::tokenizeShellArgs(const std::string &segment){ // text accumulated just before this substitution resolves (in the // right order) ahead of it. if(c=='$' && i+2<n && segment[i+1]=='(' && segment[i+2]=='('){ size_t j=i+3; int depth=0; while(j<n){ if(segment[j]=='('){ ++depth; ++j; }else if(segment[j]==')'){ if(depth==0){ ++j; if(j<n && segment[j]==')') ++j; break; } --depth; ++j; }else{ ++j; } } size_t j=arithEnd(segment,i); flushPending(); current+=fresh::Environment::instance().expand(segment.substr(i,j-i)); current+=expandArith(segment.substr(i,j-i)); have=true; i=j; continue; Loading Loading @@ -886,6 +926,13 @@ std::vector<std::string> fresh::tokenizeShellArgs(const std::string &segment){ // sees it intact). Paren-depth tracked only (no quote- // awareness inside the substitution itself) -- same scope // limit the top-level "$(...)" handling already has. if(segment[i]=='$' && i+2<n && segment[i+1]=='(' && segment[i+2]=='('){ size_t j=arithEnd(segment,i); flushRaw(); dqResult+=expandArith(segment.substr(i,j-i)); i=j; continue; } if(segment[i]=='$' && i+1<n && segment[i+1]=='(' && !(i+2<n && segment[i+2]=='(')){ size_t j=cmdSubstClose(segment,i+2); Loading Loading @@ -2040,6 +2087,16 @@ std::vector<std::string> fresh::splitPipelineStages(const std::string &segment, i=j; continue; } // a "( ... )" subshell group is one pipeline stage: a '|' inside it // belongs to the subshell (the kernel's net/wireless Makefile: // "(echo ...; echo | cat - $^ ; echo ...) > $@") if(c=='('){ size_t j=cmdSubstClose(segment,i+1); if(j<n) ++j; current+=segment.substr(i,j-i); i=j; continue; } // "`cmd | filter`" -- same reasoning as "$(...)" above, just for // the older backtick spelling. Backticks don't nest (no depth to Loading