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

sed fixed

parent 80bc39a4
Loading
Loading
Loading
Loading
+80 −3
Changes for commands/editor/sed.cpp: 80 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -113,8 +113,9 @@ namespace fresh { namespace cmd {
    };

    // One parsed command: 's' (substitute), 'p' (print now), 'q' (quit),
    // or '{' (an address-gated group of nested commands -- recursive, same
    // grammar one level in). A std::vector<SedCmd> holding SedCmd itself is
    // 'y' (transliterate), or '{' (an address-gated group of nested
    // commands -- recursive, same grammar one level in). A
    // std::vector<SedCmd> holding SedCmd itself is
    // fine since C++17 (23.2.1): incomplete-type members are permitted for
    // vector/list/forward_list as long as the type is complete by the time
    // any operation actually needs it, which it always is here (this file
@@ -133,6 +134,10 @@ namespace fresh { namespace cmd {
        bool printFlag=false;
        char delimiter='/'; // whatever character followed 's' in this command

        // 'y' data: yFrom[k] is transliterated to yTo[k] for every k --
        // always the same length, checked at parse time.
        std::string yFrom,yTo;

        // '{' data:
        std::vector<SedCmd> block;
    };
@@ -284,6 +289,66 @@ namespace fresh { namespace cmd {
            return true;
        }

        // Parses one "y<delim>SRC<delim>DST<delim>" starting at
        // text[i]=='y' -- same delimiter/escape handling as parseSCommand()
        // (any character may be the delimiter; "\\", "\<delim>" are taken
        // literally), but with no regex/flags: real sed's 'y' is a plain,
        // same-length character transliteration (e.g. scripts/syscallhdr.
        // sh's own "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/"
        // to uppercase a header guard name), and POSIX requires SRC and
        // DST to have exactly the same length.
        static bool parseYCommand(const std::string &text,size_t &i,SedCmd &cmd,std::string &err){
            size_t n=text.length();
            if(i+1>=n){
                err="unterminated y command";
                return false;
            }
            char delim=text[i+1];
            i+=2;

            std::string from,to;
            int delimsSeen=0;
            std::string *target=&from;
            while(i<n && delimsSeen<2){
                char c=text[i];
                if(c=='\\' && i+1<n && text[i+1]=='\\'){
                    *target+='\\';
                    i+=2;
                    continue;
                }
                if(c=='\\' && i+1<n && text[i+1]==delim){
                    *target+=delim;
                    i+=2;
                    continue;
                }
                if(c==delim){
                    ++delimsSeen;
                    target=&to;
                    ++i;
                    continue;
                }
                if(c=='\n'){
                    err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command";
                    return false;
                }
                *target+=c;
                ++i;
            }
            if(delimsSeen<2){
                err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command";
                return false;
            }
            if(from.size()!=to.size()){
                err="strings for 'y' command are different lengths";
                return false;
            }

            cmd.yFrom=from;
            cmd.yTo=to;
            cmd.kind='y';
            return true;
        }

        // Recursive-descent parse of a command list: zero or more
        // (optionally addressed) commands, each 's', 'p', 'q', or a
        // '{'-nested sub-list -- stopping at the matching '}' when
@@ -361,8 +426,14 @@ namespace fresh { namespace cmd {
                    out.push_back(std::move(cmd));
                    continue;
                }
                if(letter=='y'){
                    if(!parseYCommand(text,i,cmd,err))
                        return false;
                    out.push_back(std::move(cmd));
                    continue;
                }

                err="only 's', 'p', 'q', and '{' commands are supported";
                err="only 's', 'p', 'q', 'y', and '{' commands are supported";
                return false;
            }
        }
@@ -496,6 +567,12 @@ namespace fresh { namespace cmd {
                    out << line << "\n";
                }else if(cmd.kind=='q'){
                    return true;
                }else if(cmd.kind=='y'){
                    for(size_t c=0;c<line.size();++c){
                        size_t pos=cmd.yFrom.find(line[c]);
                        if(pos!=std::string::npos)
                            line[c]=cmd.yTo[pos];
                    }
                }else if(cmd.kind=='{'){
                    if(execCommands(cmd.block,line,out))
                        return true;