Loading commands/shell/case.cpp +84 −18 Changes for commands/shell/case.cpp: 84 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -50,21 +50,94 @@ namespace fresh { namespace cmd { // A case pattern never undergoes filesystem glob expansion (unlike // an ordinary command argument) - its "*"/"?"/"[...]" are match // syntax for fnmatch(), not something to expand against real // files. Quote handling stays at this codebase's usual scope // limit: a pattern entirely wrapped in one pair of quotes is // dequoted and taken as a literal fnmatch() pattern; anything // else just gets $VAR-expanded. // files. POSIX quoting: an unquoted part is $VAR-expanded and stays // pattern syntax (a backslash escapes the next character); a // single-quoted part is literal, a double-quoted one is expanded // and then literal -- their "*"/"?"/"[" are escaped for fnmatch(). // So "*" matches only a '*', and *" $m "* a word containing $m // between spaces. static void appendLiteral(std::string &out,const std::string &s){ for(char c: s){ if(c=='*' || c=='?' || c=='[' || c==']' || c=='\\') out+='\\'; out+=c; } } static std::string extractPattern(const std::string &raw){ size_t s=raw.find_first_not_of(" \t\r\n"); if(s==std::string::npos) return ""; size_t e=raw.find_last_not_of(" \t\r\n"); std::string trimmed=raw.substr(s,e-s+1); if(trimmed.size()>=2 && ((trimmed.front()=='\'' && trimmed.back()=='\'') || (trimmed.front()=='"' && trimmed.back()=='"'))) return trimmed.substr(1,trimmed.size()-2); return Environment::instance().expand(trimmed); std::string t=raw.substr(s,e-s+1); Environment &env=Environment::instance(); std::string out,run; auto flushRun=[&](){ if(!run.empty()){ out+=env.expand(run); run.clear(); } }; for(size_t i=0;i<t.size();++i){ char c=t[i]; if(c=='\\' && i+1<t.size()){ flushRun(); out+='\\'; out+=t[++i]; }else if(c=='\''){ flushRun(); size_t end=t.find('\'',i+1); if(end==std::string::npos) end=t.size(); appendLiteral(out,t.substr(i+1,end-i-1)); i=end; }else if(c=='"'){ flushRun(); // \$ \` \" \\ are the escapes inside double quotes std::string seg; size_t j=i+1; for(;j<t.size() && t[j]!='"';++j){ if(t[j]=='\\' && j+1<t.size() && (t[j+1]=='$' || t[j+1]=='`' || t[j+1]=='"' || t[j+1]=='\\')){ appendLiteral(out,env.expand(seg)); seg.clear(); appendLiteral(out,std::string(1,t[++j])); continue; } seg+=t[j]; } appendLiteral(out,env.expand(seg)); i=j; }else{ run+=c; } } flushRun(); return out; } // The patterns of "PAT1|PAT2|...": '|' inside quotes or escaped // is part of a pattern static std::vector<std::string> splitPatterns(const std::string &text){ std::vector<std::string> out; std::string cur; char quote=0; for(size_t i=0;i<text.size();++i){ char c=text[i]; if(quote){ if(c==quote) quote=0; else if(quote=='"' && c=='\\' && i+1<text.size()){ cur+=c; c=text[++i]; } }else if(c=='\'' || c=='"'){ quote=c; }else if(c=='\\' && i+1<text.size()){ cur+=c; c=text[++i]; }else if(c=='|'){ out.push_back(cur); cur.clear(); continue; } cur+=c; } out.push_back(cur); return out; } int runCommand(int argc,const char **argv){ Loading Loading @@ -192,19 +265,12 @@ namespace fresh { namespace cmd { patternText.erase(ps,1); bool clauseMatches=false; size_t start=0; while(start<=patternText.size()){ size_t bar=patternText.find('|',start); std::string oneRaw=(bar==std::string::npos) ? patternText.substr(start) : patternText.substr(start,bar-start); for(const auto &oneRaw: splitPatterns(patternText)){ std::string pattern=extractPattern(oneRaw); if(fnmatch(pattern.c_str(),word.c_str(),0)==0){ clauseMatches=true; break; } if(bar==std::string::npos) break; start=bar+1; } if(!clauseMatches) Loading Loading
commands/shell/case.cpp +84 −18 Changes for commands/shell/case.cpp: 84 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -50,21 +50,94 @@ namespace fresh { namespace cmd { // A case pattern never undergoes filesystem glob expansion (unlike // an ordinary command argument) - its "*"/"?"/"[...]" are match // syntax for fnmatch(), not something to expand against real // files. Quote handling stays at this codebase's usual scope // limit: a pattern entirely wrapped in one pair of quotes is // dequoted and taken as a literal fnmatch() pattern; anything // else just gets $VAR-expanded. // files. POSIX quoting: an unquoted part is $VAR-expanded and stays // pattern syntax (a backslash escapes the next character); a // single-quoted part is literal, a double-quoted one is expanded // and then literal -- their "*"/"?"/"[" are escaped for fnmatch(). // So "*" matches only a '*', and *" $m "* a word containing $m // between spaces. static void appendLiteral(std::string &out,const std::string &s){ for(char c: s){ if(c=='*' || c=='?' || c=='[' || c==']' || c=='\\') out+='\\'; out+=c; } } static std::string extractPattern(const std::string &raw){ size_t s=raw.find_first_not_of(" \t\r\n"); if(s==std::string::npos) return ""; size_t e=raw.find_last_not_of(" \t\r\n"); std::string trimmed=raw.substr(s,e-s+1); if(trimmed.size()>=2 && ((trimmed.front()=='\'' && trimmed.back()=='\'') || (trimmed.front()=='"' && trimmed.back()=='"'))) return trimmed.substr(1,trimmed.size()-2); return Environment::instance().expand(trimmed); std::string t=raw.substr(s,e-s+1); Environment &env=Environment::instance(); std::string out,run; auto flushRun=[&](){ if(!run.empty()){ out+=env.expand(run); run.clear(); } }; for(size_t i=0;i<t.size();++i){ char c=t[i]; if(c=='\\' && i+1<t.size()){ flushRun(); out+='\\'; out+=t[++i]; }else if(c=='\''){ flushRun(); size_t end=t.find('\'',i+1); if(end==std::string::npos) end=t.size(); appendLiteral(out,t.substr(i+1,end-i-1)); i=end; }else if(c=='"'){ flushRun(); // \$ \` \" \\ are the escapes inside double quotes std::string seg; size_t j=i+1; for(;j<t.size() && t[j]!='"';++j){ if(t[j]=='\\' && j+1<t.size() && (t[j+1]=='$' || t[j+1]=='`' || t[j+1]=='"' || t[j+1]=='\\')){ appendLiteral(out,env.expand(seg)); seg.clear(); appendLiteral(out,std::string(1,t[++j])); continue; } seg+=t[j]; } appendLiteral(out,env.expand(seg)); i=j; }else{ run+=c; } } flushRun(); return out; } // The patterns of "PAT1|PAT2|...": '|' inside quotes or escaped // is part of a pattern static std::vector<std::string> splitPatterns(const std::string &text){ std::vector<std::string> out; std::string cur; char quote=0; for(size_t i=0;i<text.size();++i){ char c=text[i]; if(quote){ if(c==quote) quote=0; else if(quote=='"' && c=='\\' && i+1<text.size()){ cur+=c; c=text[++i]; } }else if(c=='\'' || c=='"'){ quote=c; }else if(c=='\\' && i+1<text.size()){ cur+=c; c=text[++i]; }else if(c=='|'){ out.push_back(cur); cur.clear(); continue; } cur+=c; } out.push_back(cur); return out; } int runCommand(int argc,const char **argv){ Loading Loading @@ -192,19 +265,12 @@ namespace fresh { namespace cmd { patternText.erase(ps,1); bool clauseMatches=false; size_t start=0; while(start<=patternText.size()){ size_t bar=patternText.find('|',start); std::string oneRaw=(bar==std::string::npos) ? patternText.substr(start) : patternText.substr(start,bar-start); for(const auto &oneRaw: splitPatterns(patternText)){ std::string pattern=extractPattern(oneRaw); if(fnmatch(pattern.c_str(),word.c_str(),0)==0){ clauseMatches=true; break; } if(bar==std::string::npos) break; start=bar+1; } if(!clauseMatches) Loading