Loading commands/filesystem/find.cpp +49 −83 Changes for commands/filesystem/find.cpp: 49 added lines, 83 removed lines. Original line number Diff line number Diff line Loading @@ -48,15 +48,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace fresh { namespace cmd { // POSIX find with GNU extensions build scripts rely on. // POSIX find with the GNU extensions build scripts rely on (rpm's // brp-* scripts: -print0, -inum, -regex, "!", "\( ... -o ... \)"). // The expression is parsed into a tree of Node once, then evaluated per // visited file with the usual short-circuit semantics; an expression // without any action gets an implicit "-print" around it. class FindCommand : public ShellCommand { public: FindCommand():ShellCommand("find"){} private: struct Entry { std::string path; // start path + relative part std::string name; // last path component std::string path; // as printed (start path + relative part) std::string name; // last component struct stat st; int depth; }; Loading @@ -71,21 +75,18 @@ namespace fresh { namespace cmd { struct Exec; enum SymlinkMode { DEREF_NEVER, DEREF_COMMAND_LINE, DEREF_ALWAYS }; struct State { SymlinkMode symlinkMode = DEREF_NEVER; bool followLinks=false; bool depthFirst=false; bool xdev=false; int maxDepth=-1; int minDepth=0; bool quit=false; bool prune = false; bool prune=false; // set by -prune for the current entry int status=0; dev_t startDev=0; time_t now=0; std::vector<Exec*> batchExecs; std::vector<Exec*> batchExecs; // "-exec ... {} +" to flush at the end }; // ---- expression nodes ---- Loading @@ -94,22 +95,18 @@ namespace fresh { namespace cmd { NodePtr l,r; bool eval(Entry &e,State &s) override { return l->eval(e,s) && r->eval(e,s); } }; struct Or : Node { NodePtr l,r; bool eval(Entry &e,State &s) override { return l->eval(e,s) || r->eval(e,s); } }; struct Comma : Node { NodePtr l,r; bool eval(Entry &e,State &s) override { l->eval(e,s); return r->eval(e,s); } }; struct Not : Node { NodePtr n; bool eval(Entry &e,State &s) override { return !n->eval(e,s); } }; struct Const : Node { bool v; explicit Const(bool b):v(b){} Loading @@ -123,17 +120,20 @@ namespace fresh { namespace cmd { bool eval(Entry &e,State &) override { std::string subject=wholePath ? e.path : e.name; std::string p=pat; int flags = icase ? FNM_CASEFOLD : 0; return fnmatch(p.c_str(), subject.c_str(), flags) == 0; if(icase){ for(auto &c: subject) c=(char)tolower((unsigned char)c); for(auto &c: p) c=(char)tolower((unsigned char)c); } return fnmatch(p.c_str(),subject.c_str(),0)==0; } }; // GNU find matches a regex against the WHOLE path struct Regex : Node { regex_t re; bool ok=false; Regex(const std::string &p,bool icase){ int flags = REG_EXTENDED | (icase ? REG_ICASE : 0); ok = (regcomp(&re, p.c_str(), flags) == 0); ok=regcomp(&re,p.c_str(),icase ? REG_ICASE : 0)==0; } ~Regex(){ if(ok) regfree(&re); } bool eval(Entry &e,State &) override { Loading @@ -145,7 +145,7 @@ namespace fresh { namespace cmd { }; struct Type : Node { std::string types; std::string types; // "f", "d", "f,d" ... explicit Type(const std::string &t):types(t){} bool eval(Entry &e,State &) override { char t='?'; Loading @@ -161,9 +161,9 @@ namespace fresh { namespace cmd { } }; // numeric comparison: "N" exact, "+N" greater, "-N" less struct Num { int cmp = 0; long long v = 0; int cmp=0; long long v=0; bool parse(const std::string &s){ std::string t=s; if(!t.empty() && t[0]=='+'){ cmp=1; t=t.substr(1); } Loading Loading @@ -193,28 +193,21 @@ namespace fresh { namespace cmd { bool eval(Entry &e,State &) override { return n.test((long long)e.st.st_gid); } }; // -size N[ckMGwb], default unit 512-byte blocks, rounded up struct Size : Node { Num n; long long unit = 512; Num n; long long unit=512; bool eval(Entry &e,State &) override { long long sz=(long long)e.st.st_size; return n.test((sz+unit-1)/unit); } }; enum TimeType { TIME_ACCESS, TIME_CHANGE, TIME_MODIFY }; // -mtime N (days) / -mmin N (minutes), rounded down like GNU find struct Age : Node { Num n; long long unit; TimeType type; Age(long long u, TimeType t = TIME_MODIFY):unit(u), type(t){} Num n; long long unit; explicit Age(long long u):unit(u){} bool eval(Entry &e,State &s) override { time_t target = e.st.st_mtime; if(type == TIME_ACCESS) target = e.st.st_atime; else if(type == TIME_CHANGE) target = e.st.st_ctime; long long age = (long long)(s.now - target); long long age=(long long)(s.now-e.st.st_mtime); if(age<0) age=0; return n.test(age/unit); } Loading Loading @@ -244,9 +237,9 @@ namespace fresh { namespace cmd { } }; // -perm MODE (exact), -perm -MODE (all bits), -perm /MODE (any bit) struct Perm : Node { char kind = 0; mode_t mode = 0; char kind=0; mode_t mode=0; bool eval(Entry &e,State &) override { mode_t m=e.st.st_mode & 07777; if(kind=='-') return (m & mode)==mode; Loading @@ -256,8 +249,7 @@ namespace fresh { namespace cmd { }; struct Owner : Node { bool isUser; unsigned id; bool isUser; unsigned id; Owner(bool u,unsigned i):isUser(u),id(i){} bool eval(Entry &e,State &) override { return (isUser ? e.st.st_uid : e.st.st_gid)==id; } }; Loading Loading @@ -290,10 +282,11 @@ namespace fresh { namespace cmd { } }; // -exec cmd {} ; -exec cmd {} + -execdir ... -ok ... struct Exec : Node { std::vector<std::string> argv; std::vector<std::string> argv; // "{}" marks the path bool batch=false,inDir=false,ask=false; std::vector<std::string> pending; std::vector<std::string> pending; // batched paths State *state=nullptr; static int run(const std::vector<std::string> &args,const std::string &dir){ Loading Loading @@ -348,8 +341,8 @@ namespace fresh { namespace cmd { args.push_back(x); } if(ask){ std::cerr << "< " << args[0]; for(size_t i = 1; i < args.size(); ++i) std::cerr << " " << args[i]; std::cerr << "< "; for(auto &a: args) std::cerr << a << " "; std::cerr << "> ? "; std::string answer; if(!std::getline(std::cin,answer) || answer.empty() || Loading @@ -373,7 +366,7 @@ namespace fresh { namespace cmd { bool more() const { return pos<toks.size(); } const std::string &peek() const { return toks[pos]; } NodePtr parseExpr(){ NodePtr parseExpr(){ // lowest precedence: "," NodePtr l=parseOr(); while(l && more() && peek()==","){ ++pos; Loading Loading @@ -467,12 +460,8 @@ namespace fresh { namespace cmd { if(t=="-links") return num(std::make_unique<Links>()); if(t=="-uid") return num(std::make_unique<Uid>()); if(t=="-gid") return num(std::make_unique<Gid>()); if(t == "-mtime") return num(std::make_unique<Age>(86400, TIME_MODIFY)); if(t == "-atime") return num(std::make_unique<Age>(86400, TIME_ACCESS)); if(t == "-ctime") return num(std::make_unique<Age>(86400, TIME_CHANGE)); if(t == "-mmin") return num(std::make_unique<Age>(60, TIME_MODIFY)); if(t == "-amin") return num(std::make_unique<Age>(60, TIME_ACCESS)); if(t == "-cmin") return num(std::make_unique<Age>(60, TIME_CHANGE)); if(t=="-mtime") return num(std::make_unique<Age>(86400)); if(t=="-mmin") return num(std::make_unique<Age>(60)); if(t=="-size"){ auto n=std::make_unique<Size>(); if(!arg(v)) return nullptr; Loading @@ -480,7 +469,6 @@ namespace fresh { namespace cmd { switch(v.back()){ case 'c': n->unit=1; break; case 'w': n->unit=2; break; case 'b': n->unit = 512; break; case 'k': n->unit=1024; break; case 'M': n->unit=1024*1024; break; case 'G': n->unit=1024LL*1024*1024; break; Loading Loading @@ -540,6 +528,7 @@ namespace fresh { namespace cmd { while(more() && peek()!=";" && peek()!="+") x->argv.push_back(toks[pos++]); if(!more()){ error="missing argument to '"+t+"'"; return nullptr; } // "+" only terminates when right after a lone "{}" if(peek()=="+" && !x->argv.empty() && x->argv.back()=="{}" && !x->ask && !x->inDir){ x->batch=true; s.batchExecs.push_back(x.get()); Loading @@ -551,6 +540,7 @@ namespace fresh { namespace cmd { if(x->argv.empty()){ error="missing command for '"+t+"'"; return nullptr; } return x; } // options: position-independent, always true if(t=="-maxdepth" || t=="-mindepth"){ if(!arg(v)) return nullptr; (t=="-maxdepth" ? s.maxDepth : s.minDepth)=atoi(v.c_str()); Loading @@ -558,7 +548,7 @@ namespace fresh { namespace cmd { } if(t=="-depth" || t=="-d"){ s.depthFirst=true; return std::make_unique<Const>(true); } if(t=="-xdev" || t=="-mount"){ s.xdev=true; return std::make_unique<Const>(true); } if(t == "-follow"){ s.symlinkMode = DEREF_ALWAYS; return std::make_unique<Const>(true); } if(t=="-follow"){ s.followLinks=true; return std::make_unique<Const>(true); } if(t=="-noleaf" || t=="-ignore_readdir_race" || t=="-nowarn" || t=="-warn") return std::make_unique<Const>(true); error="unknown predicate '"+t+"'"; Loading @@ -575,9 +565,7 @@ namespace fresh { namespace cmd { e.depth=depth; size_t slash=path.find_last_of('/'); e.name=(slash==std::string::npos || path.size()==1) ? path : path.substr(slash+1); bool follow = (s.symlinkMode == DEREF_ALWAYS) || (s.symlinkMode == DEREF_COMMAND_LINE && depth == 0); bool follow=s.followLinks; if((follow ? stat(path.c_str(),&e.st) : lstat(path.c_str(),&e.st))!=0){ if(lstat(path.c_str(),&e.st)!=0){ std::cerr << "find: '" << path << "': " << strerror(errno) << std::endl; Loading Loading @@ -624,17 +612,14 @@ namespace fresh { namespace cmd { State s; s.now=time(nullptr); int i=1; // Handle positional symlink options // leading -H/-L/-P options for(; i<argc; ++i){ std::string a=argv[i]; if(a == "-L") s.symlinkMode = DEREF_ALWAYS; else if(a == "-H") s.symlinkMode = DEREF_COMMAND_LINE; else if(a == "-P") s.symlinkMode = DEREF_NEVER; if(a=="-L") s.followLinks=true; else if(a=="-H" || a=="-P") s.followLinks=false; else break; } std::vector paths; std::vector<std::string> paths; for(; i<argc; ++i){ std::string a=argv[i]; if(a=="!" || a=="(" || (a.size()>1 && a[0]=='-')) break; Loading @@ -643,30 +628,11 @@ namespace fresh { namespace cmd { if(paths.empty()) paths.push_back("."); Parser p(s); for(; i < argc; ++i) { std::string arg = argv[i]; // Split concatenated leading '(' (e.g. "(-name" -> "(" "-name") while (arg.size() > 1 && arg[0] == '(') { p.toks.push_back("("); arg = arg.substr(1); } // Split concatenated trailing ')' (e.g. "*.S)" -> "*.S" ")") std::size_t trailing_cnt = 0; while (arg.size() > 1 && arg.back() == ')') { trailing_cnt++; arg.pop_back(); } if (!arg.empty()) { p.toks.push_back(arg); } while (trailing_cnt--) { p.toks.push_back(")"); } } for(; i<argc; ++i) p.toks.push_back(argv[i]); NodePtr expr; if(p.toks.empty()){ expr = std::make_unique(true); expr=std::make_unique<Const>(true); }else{ expr=p.parseExpr(); if(expr && p.more()){ p.error="unexpected '"+p.peek()+"'"; expr.reset(); } Loading @@ -675,11 +641,11 @@ namespace fresh { namespace cmd { return 1; } } // no action anywhere: ( expr ) -print if(!p.hasAction){ auto a = std::make_unique(); auto a=std::make_unique<And>(); a->l=std::move(expr); a->r = std::make_unique('\n'); a->r=std::make_unique<Print>('\n'); expr=std::move(a); } Loading Loading
commands/filesystem/find.cpp +49 −83 Changes for commands/filesystem/find.cpp: 49 added lines, 83 removed lines. Original line number Diff line number Diff line Loading @@ -48,15 +48,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace fresh { namespace cmd { // POSIX find with GNU extensions build scripts rely on. // POSIX find with the GNU extensions build scripts rely on (rpm's // brp-* scripts: -print0, -inum, -regex, "!", "\( ... -o ... \)"). // The expression is parsed into a tree of Node once, then evaluated per // visited file with the usual short-circuit semantics; an expression // without any action gets an implicit "-print" around it. class FindCommand : public ShellCommand { public: FindCommand():ShellCommand("find"){} private: struct Entry { std::string path; // start path + relative part std::string name; // last path component std::string path; // as printed (start path + relative part) std::string name; // last component struct stat st; int depth; }; Loading @@ -71,21 +75,18 @@ namespace fresh { namespace cmd { struct Exec; enum SymlinkMode { DEREF_NEVER, DEREF_COMMAND_LINE, DEREF_ALWAYS }; struct State { SymlinkMode symlinkMode = DEREF_NEVER; bool followLinks=false; bool depthFirst=false; bool xdev=false; int maxDepth=-1; int minDepth=0; bool quit=false; bool prune = false; bool prune=false; // set by -prune for the current entry int status=0; dev_t startDev=0; time_t now=0; std::vector<Exec*> batchExecs; std::vector<Exec*> batchExecs; // "-exec ... {} +" to flush at the end }; // ---- expression nodes ---- Loading @@ -94,22 +95,18 @@ namespace fresh { namespace cmd { NodePtr l,r; bool eval(Entry &e,State &s) override { return l->eval(e,s) && r->eval(e,s); } }; struct Or : Node { NodePtr l,r; bool eval(Entry &e,State &s) override { return l->eval(e,s) || r->eval(e,s); } }; struct Comma : Node { NodePtr l,r; bool eval(Entry &e,State &s) override { l->eval(e,s); return r->eval(e,s); } }; struct Not : Node { NodePtr n; bool eval(Entry &e,State &s) override { return !n->eval(e,s); } }; struct Const : Node { bool v; explicit Const(bool b):v(b){} Loading @@ -123,17 +120,20 @@ namespace fresh { namespace cmd { bool eval(Entry &e,State &) override { std::string subject=wholePath ? e.path : e.name; std::string p=pat; int flags = icase ? FNM_CASEFOLD : 0; return fnmatch(p.c_str(), subject.c_str(), flags) == 0; if(icase){ for(auto &c: subject) c=(char)tolower((unsigned char)c); for(auto &c: p) c=(char)tolower((unsigned char)c); } return fnmatch(p.c_str(),subject.c_str(),0)==0; } }; // GNU find matches a regex against the WHOLE path struct Regex : Node { regex_t re; bool ok=false; Regex(const std::string &p,bool icase){ int flags = REG_EXTENDED | (icase ? REG_ICASE : 0); ok = (regcomp(&re, p.c_str(), flags) == 0); ok=regcomp(&re,p.c_str(),icase ? REG_ICASE : 0)==0; } ~Regex(){ if(ok) regfree(&re); } bool eval(Entry &e,State &) override { Loading @@ -145,7 +145,7 @@ namespace fresh { namespace cmd { }; struct Type : Node { std::string types; std::string types; // "f", "d", "f,d" ... explicit Type(const std::string &t):types(t){} bool eval(Entry &e,State &) override { char t='?'; Loading @@ -161,9 +161,9 @@ namespace fresh { namespace cmd { } }; // numeric comparison: "N" exact, "+N" greater, "-N" less struct Num { int cmp = 0; long long v = 0; int cmp=0; long long v=0; bool parse(const std::string &s){ std::string t=s; if(!t.empty() && t[0]=='+'){ cmp=1; t=t.substr(1); } Loading Loading @@ -193,28 +193,21 @@ namespace fresh { namespace cmd { bool eval(Entry &e,State &) override { return n.test((long long)e.st.st_gid); } }; // -size N[ckMGwb], default unit 512-byte blocks, rounded up struct Size : Node { Num n; long long unit = 512; Num n; long long unit=512; bool eval(Entry &e,State &) override { long long sz=(long long)e.st.st_size; return n.test((sz+unit-1)/unit); } }; enum TimeType { TIME_ACCESS, TIME_CHANGE, TIME_MODIFY }; // -mtime N (days) / -mmin N (minutes), rounded down like GNU find struct Age : Node { Num n; long long unit; TimeType type; Age(long long u, TimeType t = TIME_MODIFY):unit(u), type(t){} Num n; long long unit; explicit Age(long long u):unit(u){} bool eval(Entry &e,State &s) override { time_t target = e.st.st_mtime; if(type == TIME_ACCESS) target = e.st.st_atime; else if(type == TIME_CHANGE) target = e.st.st_ctime; long long age = (long long)(s.now - target); long long age=(long long)(s.now-e.st.st_mtime); if(age<0) age=0; return n.test(age/unit); } Loading Loading @@ -244,9 +237,9 @@ namespace fresh { namespace cmd { } }; // -perm MODE (exact), -perm -MODE (all bits), -perm /MODE (any bit) struct Perm : Node { char kind = 0; mode_t mode = 0; char kind=0; mode_t mode=0; bool eval(Entry &e,State &) override { mode_t m=e.st.st_mode & 07777; if(kind=='-') return (m & mode)==mode; Loading @@ -256,8 +249,7 @@ namespace fresh { namespace cmd { }; struct Owner : Node { bool isUser; unsigned id; bool isUser; unsigned id; Owner(bool u,unsigned i):isUser(u),id(i){} bool eval(Entry &e,State &) override { return (isUser ? e.st.st_uid : e.st.st_gid)==id; } }; Loading Loading @@ -290,10 +282,11 @@ namespace fresh { namespace cmd { } }; // -exec cmd {} ; -exec cmd {} + -execdir ... -ok ... struct Exec : Node { std::vector<std::string> argv; std::vector<std::string> argv; // "{}" marks the path bool batch=false,inDir=false,ask=false; std::vector<std::string> pending; std::vector<std::string> pending; // batched paths State *state=nullptr; static int run(const std::vector<std::string> &args,const std::string &dir){ Loading Loading @@ -348,8 +341,8 @@ namespace fresh { namespace cmd { args.push_back(x); } if(ask){ std::cerr << "< " << args[0]; for(size_t i = 1; i < args.size(); ++i) std::cerr << " " << args[i]; std::cerr << "< "; for(auto &a: args) std::cerr << a << " "; std::cerr << "> ? "; std::string answer; if(!std::getline(std::cin,answer) || answer.empty() || Loading @@ -373,7 +366,7 @@ namespace fresh { namespace cmd { bool more() const { return pos<toks.size(); } const std::string &peek() const { return toks[pos]; } NodePtr parseExpr(){ NodePtr parseExpr(){ // lowest precedence: "," NodePtr l=parseOr(); while(l && more() && peek()==","){ ++pos; Loading Loading @@ -467,12 +460,8 @@ namespace fresh { namespace cmd { if(t=="-links") return num(std::make_unique<Links>()); if(t=="-uid") return num(std::make_unique<Uid>()); if(t=="-gid") return num(std::make_unique<Gid>()); if(t == "-mtime") return num(std::make_unique<Age>(86400, TIME_MODIFY)); if(t == "-atime") return num(std::make_unique<Age>(86400, TIME_ACCESS)); if(t == "-ctime") return num(std::make_unique<Age>(86400, TIME_CHANGE)); if(t == "-mmin") return num(std::make_unique<Age>(60, TIME_MODIFY)); if(t == "-amin") return num(std::make_unique<Age>(60, TIME_ACCESS)); if(t == "-cmin") return num(std::make_unique<Age>(60, TIME_CHANGE)); if(t=="-mtime") return num(std::make_unique<Age>(86400)); if(t=="-mmin") return num(std::make_unique<Age>(60)); if(t=="-size"){ auto n=std::make_unique<Size>(); if(!arg(v)) return nullptr; Loading @@ -480,7 +469,6 @@ namespace fresh { namespace cmd { switch(v.back()){ case 'c': n->unit=1; break; case 'w': n->unit=2; break; case 'b': n->unit = 512; break; case 'k': n->unit=1024; break; case 'M': n->unit=1024*1024; break; case 'G': n->unit=1024LL*1024*1024; break; Loading Loading @@ -540,6 +528,7 @@ namespace fresh { namespace cmd { while(more() && peek()!=";" && peek()!="+") x->argv.push_back(toks[pos++]); if(!more()){ error="missing argument to '"+t+"'"; return nullptr; } // "+" only terminates when right after a lone "{}" if(peek()=="+" && !x->argv.empty() && x->argv.back()=="{}" && !x->ask && !x->inDir){ x->batch=true; s.batchExecs.push_back(x.get()); Loading @@ -551,6 +540,7 @@ namespace fresh { namespace cmd { if(x->argv.empty()){ error="missing command for '"+t+"'"; return nullptr; } return x; } // options: position-independent, always true if(t=="-maxdepth" || t=="-mindepth"){ if(!arg(v)) return nullptr; (t=="-maxdepth" ? s.maxDepth : s.minDepth)=atoi(v.c_str()); Loading @@ -558,7 +548,7 @@ namespace fresh { namespace cmd { } if(t=="-depth" || t=="-d"){ s.depthFirst=true; return std::make_unique<Const>(true); } if(t=="-xdev" || t=="-mount"){ s.xdev=true; return std::make_unique<Const>(true); } if(t == "-follow"){ s.symlinkMode = DEREF_ALWAYS; return std::make_unique<Const>(true); } if(t=="-follow"){ s.followLinks=true; return std::make_unique<Const>(true); } if(t=="-noleaf" || t=="-ignore_readdir_race" || t=="-nowarn" || t=="-warn") return std::make_unique<Const>(true); error="unknown predicate '"+t+"'"; Loading @@ -575,9 +565,7 @@ namespace fresh { namespace cmd { e.depth=depth; size_t slash=path.find_last_of('/'); e.name=(slash==std::string::npos || path.size()==1) ? path : path.substr(slash+1); bool follow = (s.symlinkMode == DEREF_ALWAYS) || (s.symlinkMode == DEREF_COMMAND_LINE && depth == 0); bool follow=s.followLinks; if((follow ? stat(path.c_str(),&e.st) : lstat(path.c_str(),&e.st))!=0){ if(lstat(path.c_str(),&e.st)!=0){ std::cerr << "find: '" << path << "': " << strerror(errno) << std::endl; Loading Loading @@ -624,17 +612,14 @@ namespace fresh { namespace cmd { State s; s.now=time(nullptr); int i=1; // Handle positional symlink options // leading -H/-L/-P options for(; i<argc; ++i){ std::string a=argv[i]; if(a == "-L") s.symlinkMode = DEREF_ALWAYS; else if(a == "-H") s.symlinkMode = DEREF_COMMAND_LINE; else if(a == "-P") s.symlinkMode = DEREF_NEVER; if(a=="-L") s.followLinks=true; else if(a=="-H" || a=="-P") s.followLinks=false; else break; } std::vector paths; std::vector<std::string> paths; for(; i<argc; ++i){ std::string a=argv[i]; if(a=="!" || a=="(" || (a.size()>1 && a[0]=='-')) break; Loading @@ -643,30 +628,11 @@ namespace fresh { namespace cmd { if(paths.empty()) paths.push_back("."); Parser p(s); for(; i < argc; ++i) { std::string arg = argv[i]; // Split concatenated leading '(' (e.g. "(-name" -> "(" "-name") while (arg.size() > 1 && arg[0] == '(') { p.toks.push_back("("); arg = arg.substr(1); } // Split concatenated trailing ')' (e.g. "*.S)" -> "*.S" ")") std::size_t trailing_cnt = 0; while (arg.size() > 1 && arg.back() == ')') { trailing_cnt++; arg.pop_back(); } if (!arg.empty()) { p.toks.push_back(arg); } while (trailing_cnt--) { p.toks.push_back(")"); } } for(; i<argc; ++i) p.toks.push_back(argv[i]); NodePtr expr; if(p.toks.empty()){ expr = std::make_unique(true); expr=std::make_unique<Const>(true); }else{ expr=p.parseExpr(); if(expr && p.more()){ p.error="unexpected '"+p.peek()+"'"; expr.reset(); } Loading @@ -675,11 +641,11 @@ namespace fresh { namespace cmd { return 1; } } // no action anywhere: ( expr ) -print if(!p.hasAction){ auto a = std::make_unique(); auto a=std::make_unique<And>(); a->l=std::move(expr); a->r = std::make_unique('\n'); a->r=std::make_unique<Print>('\n'); expr=std::move(a); } Loading