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

test

parent 8cbbb731
Loading
Loading
Loading
Loading
+84 −18
Changes for commands/shell/case.cpp: 84 added lines, 18 removed lines.
Original line number Diff line number Diff line
@@ -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){
@@ -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)