Loading commands/editor/sed.cpp +23 −2 Changes for commands/editor/sed.cpp: 23 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -157,14 +157,34 @@ namespace fresh { namespace cmd { return (int)r.sc.regexes.size()-1; } // GNU sed's escapes for control characters, which regcomp() doesn't // know: \t, \f, \v, \r, \a; 0 for any other character static char controlEscape(char e){ switch(e){ case 't': return '\t'; case 'f': return '\f'; case 'v': return '\v'; case 'r': return '\r'; case 'a': return '\a'; } return 0; } // text up to an unescaped delim; "\delim" becomes delim, "\n" a // newline, other escapes are kept for the regex engine. A delimiter // inside a bracket expression doesn't end the regex. // newline, "\t" and the other control escapes (also inside a // bracket expression, as in GNU sed) the character, other escapes // are kept for the regex engine. A delimiter inside a bracket // expression doesn't end the regex. static bool readDelimited(Src &s,char delim,std::string &out,bool isRegex){ bool inBracket=false; while(!s.end()){ char c=s.get(); if(isRegex && inBracket){ if(c=='\\' && !s.end() && (controlEscape(s.peek()) || s.peek()=='n')){ char e=s.get(); out+= e=='n' ? '\n' : controlEscape(e); continue; } out+=c; if(c==']' && !(out.size()>=2 && (out[out.size()-2]=='[' || (out.size()>=3 && out[out.size()-2]=='^' && out[out.size()-3]=='[')))) inBracket=false; Loading @@ -176,6 +196,7 @@ namespace fresh { namespace cmd { if(e==delim && delim!='\n') out+=delim; else if(e=='n') out+='\n'; else if(e=='\n') out+='\n'; else if(isRegex && controlEscape(e)) out+=controlEscape(e); else { out+='\\'; out+=e; } continue; } Loading commands/system/kill.cpp +79 −17 Changes for commands/system/kill.cpp: 79 added lines, 17 removed lines. Original line number Diff line number Diff line Loading @@ -30,49 +30,111 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <signal.h> #include <strings.h> #include <sys/types.h> #include "shell.h" namespace fresh { namespace cmd { struct SignalName { const char *name; int sig; }; static const SignalName signalNames[]={ {"HUP",SIGHUP},{"INT",SIGINT},{"QUIT",SIGQUIT},{"ILL",SIGILL}, {"TRAP",SIGTRAP},{"ABRT",SIGABRT},{"IOT",SIGABRT},{"BUS",SIGBUS}, {"FPE",SIGFPE},{"KILL",SIGKILL},{"USR1",SIGUSR1},{"SEGV",SIGSEGV}, {"USR2",SIGUSR2},{"PIPE",SIGPIPE},{"ALRM",SIGALRM},{"TERM",SIGTERM}, #ifdef SIGSTKFLT {"STKFLT",SIGSTKFLT}, #endif {"CHLD",SIGCHLD},{"CLD",SIGCHLD},{"CONT",SIGCONT},{"STOP",SIGSTOP}, {"TSTP",SIGTSTP},{"TTIN",SIGTTIN},{"TTOU",SIGTTOU},{"URG",SIGURG}, {"XCPU",SIGXCPU},{"XFSZ",SIGXFSZ},{"VTALRM",SIGVTALRM},{"PROF",SIGPROF}, {"WINCH",SIGWINCH},{"IO",SIGIO},{"POLL",SIGIO}, #ifdef SIGPWR {"PWR",SIGPWR}, #endif {"SYS",SIGSYS}, }; // a signal name (with or without SIG, any case) or number; -1 if unknown static int parseSignal(const char *spec){ if(*spec>='0' && *spec<='9'){ for(const char *p=spec; *p; ++p) if(*p<'0' || *p>'9') return -1; int n=atoi(spec); return n>=0 && n<NSIG ? n : -1; } if(strncasecmp(spec,"SIG",3)==0) spec+=3; for(const SignalName &s: signalNames) if(strcasecmp(spec,s.name)==0) return s.sig; return -1; } static const char *signalName(int sig){ for(const SignalName &s: signalNames) if(s.sig==sig) return s.name; return nullptr; } class KillCommand : public ShellCommand { public: KillCommand():ShellCommand("kill"){} int runCommand(int argc,const char **argv){ if(argc<2){ std::cerr << "kill: usage: kill [-SIGNAL|-SIGNUM] pid..." << std::endl; std::cerr << "kill: usage: kill [-s SIGNAL | -SIGNAL | -n NUM] pid... | kill -l [status]" << std::endl; return 1; } int sig=SIGTERM; int firstpid=1; if(argv[1][0]=='-' && argv[1][1]!='\0'){ const char *spec=argv[1]+1; if(strcmp(spec,"TERM")==0) sig=SIGTERM; else if(strcmp(spec,"KILL")==0) sig=SIGKILL; else if(strcmp(spec,"HUP")==0) sig=SIGHUP; else if(strcmp(spec,"INT")==0) sig=SIGINT; else{ bool numeric=true; for(const char *p=spec; *p; ++p){ if(*p<'0' || *p>'9'){ numeric=false; break; // kill -l [exit status]: the signal names, or the one of a status if(strcmp(argv[1],"-l")==0 || strcmp(argv[1],"-L")==0){ if(argc>2){ int n=atoi(argv[2]); if(n>128) n-=128; const char *name=signalName(n); if(!name){ std::cerr << "kill: unknown signal: " << argv[2] << std::endl; return 1; } std::cout << name << std::endl; return 0; } if(!numeric){ std::cerr << "kill: unknown signal: " << spec << std::endl; for(int n=1; n<NSIG; ++n){ const char *name=signalName(n); if(name) std::cout << n << ") SIG" << name << std::endl; } return 0; } if(strcmp(argv[1],"-s")==0 || strcmp(argv[1],"-n")==0){ if(argc<3){ std::cerr << "kill: " << argv[1] << " needs a signal" << std::endl; return 1; } sig=atoi(spec); sig=parseSignal(argv[2]); if(sig<0){ std::cerr << "kill: unknown signal: " << argv[2] << std::endl; return 1; } firstpid=3; }else if(strcmp(argv[1],"--")==0){ firstpid=2; }else if(argv[1][0]=='-' && argv[1][1]!='\0'){ const char *spec=argv[1]+1; sig=parseSignal(spec); if(sig<0){ std::cerr << "kill: unknown signal: " << spec << std::endl; return 1; } firstpid=2; } if(firstpid<argc && strcmp(argv[firstpid],"--")==0) ++firstpid; if(firstpid>=argc){ std::cerr << "kill: no pid specified" << std::endl; Loading commands/text/textutil.h +15 −6 Changes for commands/text/textutil.h: 15 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -44,22 +44,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace fresh { namespace text { inline bool readFd(int fd,std::string &out){ // appends fd's data up to EOF, or up to max bytes if max>=0 (endless // inputs such as /dev/urandom need the limit) inline bool readFd(int fd,std::string &out,long long max=-1){ char buf[65536]; for(;;){ ssize_t r=::read(fd,buf,sizeof buf); size_t want=sizeof buf; if(max>=0){ if(max==0) return true; if((long long)want>max) want=(size_t)max; } ssize_t r=::read(fd,buf,want); if(r<0){ if(errno==EINTR) continue; return false; } if(r==0) return true; out.append(buf,(size_t)r); if(max>=0) max-=r; } } // the whole of path ("-": stdin); false with errno set on failure inline bool readInput(const std::string &path,std::string &out){ if(path=="-") return readFd(0,out); // the whole of path ("-": stdin), or its first max bytes if max>=0; // false with errno set on failure inline bool readInput(const std::string &path,std::string &out,long long max=-1){ if(path=="-") return readFd(0,out,max); int fd=open(path.c_str(),O_RDONLY); if(fd<0) return false; bool ok=readFd(fd,out); bool ok=readFd(fd,out,max); int e=errno; close(fd); errno=e; Loading commands/text/textutils.cpp +5 −1 Changes for commands/text/textutils.cpp: 5 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -133,9 +133,13 @@ namespace fresh { namespace cmd { if(!o.ok) return 1; if(types.empty()) types.push_back({'o',2}); if(o.operands.empty()) o.operands.push_back("-"); // with -N read no more than needed: inputs like /dev/urandom never end long long want=limit>=0 ? skip+limit : -1; std::string data; for(auto &f: o.operands){ if(!text::readInput(f,data)){ std::cerr << "od: " << f << ": " << strerror(errno) << std::endl; return 1; } long long left=want>=0 ? want-(long long)data.size() : -1; if(left==0) break; if(!text::readInput(f,data,left)){ std::cerr << "od: " << f << ": " << strerror(errno) << std::endl; return 1; } } if(skip>0) data=skip<(long long)data.size() ? data.substr((size_t)skip) : std::string(); if(limit>=0 && limit<(long long)data.size()) data.resize((size_t)limit); Loading Loading
commands/editor/sed.cpp +23 −2 Changes for commands/editor/sed.cpp: 23 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -157,14 +157,34 @@ namespace fresh { namespace cmd { return (int)r.sc.regexes.size()-1; } // GNU sed's escapes for control characters, which regcomp() doesn't // know: \t, \f, \v, \r, \a; 0 for any other character static char controlEscape(char e){ switch(e){ case 't': return '\t'; case 'f': return '\f'; case 'v': return '\v'; case 'r': return '\r'; case 'a': return '\a'; } return 0; } // text up to an unescaped delim; "\delim" becomes delim, "\n" a // newline, other escapes are kept for the regex engine. A delimiter // inside a bracket expression doesn't end the regex. // newline, "\t" and the other control escapes (also inside a // bracket expression, as in GNU sed) the character, other escapes // are kept for the regex engine. A delimiter inside a bracket // expression doesn't end the regex. static bool readDelimited(Src &s,char delim,std::string &out,bool isRegex){ bool inBracket=false; while(!s.end()){ char c=s.get(); if(isRegex && inBracket){ if(c=='\\' && !s.end() && (controlEscape(s.peek()) || s.peek()=='n')){ char e=s.get(); out+= e=='n' ? '\n' : controlEscape(e); continue; } out+=c; if(c==']' && !(out.size()>=2 && (out[out.size()-2]=='[' || (out.size()>=3 && out[out.size()-2]=='^' && out[out.size()-3]=='[')))) inBracket=false; Loading @@ -176,6 +196,7 @@ namespace fresh { namespace cmd { if(e==delim && delim!='\n') out+=delim; else if(e=='n') out+='\n'; else if(e=='\n') out+='\n'; else if(isRegex && controlEscape(e)) out+=controlEscape(e); else { out+='\\'; out+=e; } continue; } Loading
commands/system/kill.cpp +79 −17 Changes for commands/system/kill.cpp: 79 added lines, 17 removed lines. Original line number Diff line number Diff line Loading @@ -30,49 +30,111 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <signal.h> #include <strings.h> #include <sys/types.h> #include "shell.h" namespace fresh { namespace cmd { struct SignalName { const char *name; int sig; }; static const SignalName signalNames[]={ {"HUP",SIGHUP},{"INT",SIGINT},{"QUIT",SIGQUIT},{"ILL",SIGILL}, {"TRAP",SIGTRAP},{"ABRT",SIGABRT},{"IOT",SIGABRT},{"BUS",SIGBUS}, {"FPE",SIGFPE},{"KILL",SIGKILL},{"USR1",SIGUSR1},{"SEGV",SIGSEGV}, {"USR2",SIGUSR2},{"PIPE",SIGPIPE},{"ALRM",SIGALRM},{"TERM",SIGTERM}, #ifdef SIGSTKFLT {"STKFLT",SIGSTKFLT}, #endif {"CHLD",SIGCHLD},{"CLD",SIGCHLD},{"CONT",SIGCONT},{"STOP",SIGSTOP}, {"TSTP",SIGTSTP},{"TTIN",SIGTTIN},{"TTOU",SIGTTOU},{"URG",SIGURG}, {"XCPU",SIGXCPU},{"XFSZ",SIGXFSZ},{"VTALRM",SIGVTALRM},{"PROF",SIGPROF}, {"WINCH",SIGWINCH},{"IO",SIGIO},{"POLL",SIGIO}, #ifdef SIGPWR {"PWR",SIGPWR}, #endif {"SYS",SIGSYS}, }; // a signal name (with or without SIG, any case) or number; -1 if unknown static int parseSignal(const char *spec){ if(*spec>='0' && *spec<='9'){ for(const char *p=spec; *p; ++p) if(*p<'0' || *p>'9') return -1; int n=atoi(spec); return n>=0 && n<NSIG ? n : -1; } if(strncasecmp(spec,"SIG",3)==0) spec+=3; for(const SignalName &s: signalNames) if(strcasecmp(spec,s.name)==0) return s.sig; return -1; } static const char *signalName(int sig){ for(const SignalName &s: signalNames) if(s.sig==sig) return s.name; return nullptr; } class KillCommand : public ShellCommand { public: KillCommand():ShellCommand("kill"){} int runCommand(int argc,const char **argv){ if(argc<2){ std::cerr << "kill: usage: kill [-SIGNAL|-SIGNUM] pid..." << std::endl; std::cerr << "kill: usage: kill [-s SIGNAL | -SIGNAL | -n NUM] pid... | kill -l [status]" << std::endl; return 1; } int sig=SIGTERM; int firstpid=1; if(argv[1][0]=='-' && argv[1][1]!='\0'){ const char *spec=argv[1]+1; if(strcmp(spec,"TERM")==0) sig=SIGTERM; else if(strcmp(spec,"KILL")==0) sig=SIGKILL; else if(strcmp(spec,"HUP")==0) sig=SIGHUP; else if(strcmp(spec,"INT")==0) sig=SIGINT; else{ bool numeric=true; for(const char *p=spec; *p; ++p){ if(*p<'0' || *p>'9'){ numeric=false; break; // kill -l [exit status]: the signal names, or the one of a status if(strcmp(argv[1],"-l")==0 || strcmp(argv[1],"-L")==0){ if(argc>2){ int n=atoi(argv[2]); if(n>128) n-=128; const char *name=signalName(n); if(!name){ std::cerr << "kill: unknown signal: " << argv[2] << std::endl; return 1; } std::cout << name << std::endl; return 0; } if(!numeric){ std::cerr << "kill: unknown signal: " << spec << std::endl; for(int n=1; n<NSIG; ++n){ const char *name=signalName(n); if(name) std::cout << n << ") SIG" << name << std::endl; } return 0; } if(strcmp(argv[1],"-s")==0 || strcmp(argv[1],"-n")==0){ if(argc<3){ std::cerr << "kill: " << argv[1] << " needs a signal" << std::endl; return 1; } sig=atoi(spec); sig=parseSignal(argv[2]); if(sig<0){ std::cerr << "kill: unknown signal: " << argv[2] << std::endl; return 1; } firstpid=3; }else if(strcmp(argv[1],"--")==0){ firstpid=2; }else if(argv[1][0]=='-' && argv[1][1]!='\0'){ const char *spec=argv[1]+1; sig=parseSignal(spec); if(sig<0){ std::cerr << "kill: unknown signal: " << spec << std::endl; return 1; } firstpid=2; } if(firstpid<argc && strcmp(argv[firstpid],"--")==0) ++firstpid; if(firstpid>=argc){ std::cerr << "kill: no pid specified" << std::endl; Loading
commands/text/textutil.h +15 −6 Changes for commands/text/textutil.h: 15 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -44,22 +44,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace fresh { namespace text { inline bool readFd(int fd,std::string &out){ // appends fd's data up to EOF, or up to max bytes if max>=0 (endless // inputs such as /dev/urandom need the limit) inline bool readFd(int fd,std::string &out,long long max=-1){ char buf[65536]; for(;;){ ssize_t r=::read(fd,buf,sizeof buf); size_t want=sizeof buf; if(max>=0){ if(max==0) return true; if((long long)want>max) want=(size_t)max; } ssize_t r=::read(fd,buf,want); if(r<0){ if(errno==EINTR) continue; return false; } if(r==0) return true; out.append(buf,(size_t)r); if(max>=0) max-=r; } } // the whole of path ("-": stdin); false with errno set on failure inline bool readInput(const std::string &path,std::string &out){ if(path=="-") return readFd(0,out); // the whole of path ("-": stdin), or its first max bytes if max>=0; // false with errno set on failure inline bool readInput(const std::string &path,std::string &out,long long max=-1){ if(path=="-") return readFd(0,out,max); int fd=open(path.c_str(),O_RDONLY); if(fd<0) return false; bool ok=readFd(fd,out); bool ok=readFd(fd,out,max); int e=errno; close(fd); errno=e; Loading
commands/text/textutils.cpp +5 −1 Changes for commands/text/textutils.cpp: 5 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -133,9 +133,13 @@ namespace fresh { namespace cmd { if(!o.ok) return 1; if(types.empty()) types.push_back({'o',2}); if(o.operands.empty()) o.operands.push_back("-"); // with -N read no more than needed: inputs like /dev/urandom never end long long want=limit>=0 ? skip+limit : -1; std::string data; for(auto &f: o.operands){ if(!text::readInput(f,data)){ std::cerr << "od: " << f << ": " << strerror(errno) << std::endl; return 1; } long long left=want>=0 ? want-(long long)data.size() : -1; if(left==0) break; if(!text::readInput(f,data,left)){ std::cerr << "od: " << f << ": " << strerror(errno) << std::endl; return 1; } } if(skip>0) data=skip<(long long)data.size() ? data.substr((size_t)skip) : std::string(); if(limit>=0 && limit<(long long)data.size()) data.resize((size_t)limit); Loading