Loading commands/system/tar.cpp +37 −2 Changes for commands/system/tar.cpp: 37 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <string> #include <sstream> #include <vector> #include <map> #include <set> Loading Loading @@ -246,8 +247,13 @@ namespace fresh { namespace cmd { samePerms=false,sameOwner=true,numericOwner=false; int verboseLevel=0; long strip=0; std::string compress; // gzip, bzip2, xz std::string compress; // gzip, bzip2, xz, or -I's command (with arguments) std::vector<std::string> excludes; // -T/--files-from lists ("-" is stdin), read once all options // are parsed and before -C changes directory, like GNU tar; // --null: NUL- instead of newline-separated std::vector<std::string> filesFrom; bool nullNames=false; std::vector<std::string> members; }; Loading Loading @@ -801,6 +807,10 @@ namespace fresh { namespace cmd { else if(n=="zstd") o.compress="zstd"; else if(n=="strip-components"){ if(!need()) return 2; o.strip=atol(v.c_str()); } else if(n=="exclude"){ if(!need()) return 2; o.excludes.push_back(v); } else if(n=="files-from"){ if(!need()) return 2; o.filesFrom.push_back(v); } else if(n=="null") o.nullNames=true; else if(n=="no-null") o.nullNames=false; else if(n=="use-compress-program"){ if(!need()) return 2; o.compress=v; } else if(n=="owner" || n=="group" || n=="mode" || n=="mtime" || n=="format" || n=="sort" || n=="pax-option" || n=="blocking-factor" || n=="record-size"){ if(!need()) return 2; } else if(n=="no-recursion" || n=="recursion" || n=="warning" || n=="delay-directory-restore" || Loading Loading @@ -836,6 +846,8 @@ namespace fresh { namespace cmd { case 'J': o.compress="xz"; break; case 'f': if(!value(o.archive)) return 2; break; case 'C': if(!value(o.dir)) return 2; break; case 'T': { std::string v; if(!value(v)) return 2; o.filesFrom.push_back(v); break; } case 'I': if(!value(o.compress)) return 2; break; case 'h': case 'P': case 'U': case 'S': case 'W': case 'B': case 'i': break; default: std::cerr << "tar: invalid option -- '" << c << "'" << std::endl; Loading @@ -847,6 +859,23 @@ namespace fresh { namespace cmd { std::cerr << "tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options" << std::endl; return 2; } for(auto &list: o.filesFrom){ FILE *f=list=="-" ? stdin : fopen(list.c_str(),"r"); if(!f){ std::cerr << "tar: " << list << ": Cannot open: " << strerror(errno) << std::endl; return 2; } std::string name; int ch; while((ch=fgetc(f))!=EOF){ if(ch==(o.nullNames ? '\0' : '\n')){ if(!name.empty()) o.members.push_back(name); name.clear(); }else name+=(char)ch; } if(!name.empty()) o.members.push_back(name); if(f!=stdin) fclose(f); } if(o.archive.empty()) o.archive="-"; int fd; Loading @@ -865,7 +894,13 @@ namespace fresh { namespace cmd { pid_t filter=-1; int ioFd=fd; if(!o.compress.empty()){ std::vector<std::string> cmd{o.compress}; // -I "xz -T0": a command with its arguments std::vector<std::string> cmd; { std::istringstream words(o.compress); std::string word; while(words >> word) cmd.push_back(word); } if(o.mode!='c') cmd.push_back("-dc"); else cmd.push_back("-c"); filter=startFilter(cmd,fd,o.mode!='c',ioFd); Loading commands/system/xz.cpp +51 −15 Changes for commands/system/xz.cpp: 51 added lines, 15 removed lines. Original line number Diff line number Diff line Loading @@ -25,8 +25,8 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // CLI entry point for decompress-only "xz": argv parsing and file/stdio // wiring only -- the actual .xz/LZMA2 decoder lives in lib/xz/xz.h, same // CLI entry point for "xz": argv parsing and file/stdio wiring only -- the // actual .xz/LZMA2 decoder and encoder live in lib/xz (xz.h), same // division of labor as lib/awk vs commands/editor/awk.cpp. dpkg-source's // own "3.0 (quilt)" format always ships a *.debian.tar.xz alongside the // *.orig.tar.* -- apt-get source / dpkg-source -x needs to unpack it, and Loading Loading @@ -65,29 +65,58 @@ namespace fresh { namespace cmd { XzCommand():ShellCommand("xz"){} int runCommand(int argc,const char **argv){ // like xz(1): compress unless -d; "xz -c" alone compresses to // stdout (tar -J/-I xz when creating), "-dc" decompresses bool decompressMode=false; bool toStdout=false; bool keep=false; int level=6; std::vector<std::string> files; for(int i=1;i<argc;++i){ std::string arg=argv[i]; if(arg=="-d" || arg=="--decompress" || arg=="-dc" || arg=="-cd"){ if(arg=="-dc" || arg=="-cd") toStdout=true; }else if(arg=="-c" || arg=="--stdout" || arg=="--to-stdout"){ toStdout=true; }else if(arg=="-k" || arg=="--keep"){ keep=true; }else if(arg=="-f" || arg=="--force" || arg=="-q" || arg=="--quiet" || arg=="-v" || arg=="--verbose" || arg=="-T" || arg=="--threads"){ // accepted, no effect needed for this decompress-only build }else if(!arg.empty() && arg[0]=='-' && arg!="-"){ if(arg.size()>2 && arg.compare(0,2,"--")==0){ std::string n=arg.substr(2); size_t eq=n.find('='); if(eq!=std::string::npos) n=n.substr(0,eq); if(n=="decompress" || n=="uncompress") decompressMode=true; else if(n=="compress") decompressMode=false; else if(n=="stdout" || n=="to-stdout") toStdout=true; else if(n=="keep") keep=true; else if(n=="best") level=9; else if(n=="fast") level=0; else if(n=="force" || n=="quiet" || n=="verbose" || n=="extreme" || n=="threads" || n=="check" || n=="format" || n=="memlimit" || n=="no-warn" || n=="no-sparse"){ // accepted: one thread, CRC32, .xz format }else{ std::cerr << "xz: unrecognized option '" << arg << "'" << std::endl; return 1; }else{ } continue; } if(arg.size()<2 || arg[0]!='-'){ files.push_back(arg); continue; } for(size_t k=1;k<arg.size();++k){ char c=arg[k]; if(c=='d') decompressMode=true; else if(c=='z') decompressMode=false; else if(c=='c') toStdout=true; else if(c=='k') keep=true; else if(c>='0' && c<='9') level=c-'0'; else if(c=='f' || c=='q' || c=='v' || c=='e'){} else if(c=='T' || c=='C' || c=='F' || c=='M'){ // value: the rest of this argument or the next one if(k+1==arg.size()) ++i; break; }else{ std::cerr << "xz: invalid option -- '" << c << "'" << std::endl; return 1; } } } (void)keep; if(files.empty()) files.push_back("-"); Loading @@ -110,11 +139,16 @@ namespace fresh { namespace cmd { } std::vector<unsigned char> output; if(decompressMode){ if(!fresh::xz::decompress(input,output)){ std::cerr << "xz: " << path << ": not in xz format or corrupt" << std::endl; failed=1; continue; } }else if(!fresh::xz::compress(input,output,level)){ failed=1; continue; } if(toStdout || path=="-"){ if(!output.empty()) Loading @@ -124,7 +158,9 @@ namespace fresh { namespace cmd { std::string outpath=path; static const char suffix[]=".xz"; size_t sfxlen=sizeof(suffix)-1; if(outpath.size()>sfxlen && outpath.compare(outpath.size()-sfxlen,sfxlen,suffix)==0) if(!decompressMode) outpath+=suffix; else if(outpath.size()>sfxlen && outpath.compare(outpath.size()-sfxlen,sfxlen,suffix)==0) outpath.resize(outpath.size()-sfxlen); else outpath+=".out"; Loading lib/xz/CMakeLists.txt +1 −1 Changes for lib/xz/CMakeLists.txt: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -3,4 +3,4 @@ # and modprobe/depmod (all three already link into fresh via # target_sources()), so a plain target_sources() here is enough -- see # xz.cpp's own header comment. target_sources(fresh PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/xz.cpp) target_sources(fresh PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/xz.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xz_encode.cpp) lib/xz/xz.cpp +3 −4 Changes for lib/xz/xz.cpp: 3 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Decompress-only .xz/LZMA2, moved here from commands/system/xz.cpp (which // The .xz/LZMA2 decoder, moved here from commands/system/xz.cpp (which // is now just the CLI wrapper) so modprobe/depmod can decompress a *.ko.xz // kernel module in memory without shelling out. No compression side (the // real xz encoder) is implemented; only unpacking archives someone else // compressed is ever needed here. Only a single-filter LZMA2 chain is // kernel module in memory without shelling out. The encoder is // xz_encode.cpp (the kernel build's kheaders_data.tar.xz needs one). Only a single-filter LZMA2 chain is // supported (what every plain "xz"/"dpkg-source" invocation, and the // kernel's own CONFIG_MODULE_COMPRESS_XZ, produces) -- BCJ/delta filter // chains are rejected with a clear error rather than silently misdecoded. Loading lib/xz/xz.h +6 −1 Changes for lib/xz/xz.h: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Decompress-only .xz/LZMA2 engine, shared between the "xz" CLI applet // .xz/LZMA2 engine (decoder; the encoder is xz_encode.cpp), shared between the "xz" CLI applet // (commands/system/xz.cpp -- argv parsing and file/stdio wiring only, same // division of labor as lib/awk vs commands/editor/awk.cpp) and anything // else in this binary that needs to decompress an in-memory .xz blob Loading @@ -48,4 +48,9 @@ namespace fresh { namespace xz { // should catch the false return and report it themselves instead). bool decompress(const std::vector<unsigned char> &input,std::vector<unsigned char> &output); // Compresses `input` into one complete .xz stream (one Block, LZMA2, // CRC32 check), appended to `output`; level 0-9 as xz's presets // (dictionary size, match finder effort). See xz_encode.cpp. bool compress(const std::vector<unsigned char> &input,std::vector<unsigned char> &output,int level=6); }} Loading
commands/system/tar.cpp +37 −2 Changes for commands/system/tar.cpp: 37 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <string> #include <sstream> #include <vector> #include <map> #include <set> Loading Loading @@ -246,8 +247,13 @@ namespace fresh { namespace cmd { samePerms=false,sameOwner=true,numericOwner=false; int verboseLevel=0; long strip=0; std::string compress; // gzip, bzip2, xz std::string compress; // gzip, bzip2, xz, or -I's command (with arguments) std::vector<std::string> excludes; // -T/--files-from lists ("-" is stdin), read once all options // are parsed and before -C changes directory, like GNU tar; // --null: NUL- instead of newline-separated std::vector<std::string> filesFrom; bool nullNames=false; std::vector<std::string> members; }; Loading Loading @@ -801,6 +807,10 @@ namespace fresh { namespace cmd { else if(n=="zstd") o.compress="zstd"; else if(n=="strip-components"){ if(!need()) return 2; o.strip=atol(v.c_str()); } else if(n=="exclude"){ if(!need()) return 2; o.excludes.push_back(v); } else if(n=="files-from"){ if(!need()) return 2; o.filesFrom.push_back(v); } else if(n=="null") o.nullNames=true; else if(n=="no-null") o.nullNames=false; else if(n=="use-compress-program"){ if(!need()) return 2; o.compress=v; } else if(n=="owner" || n=="group" || n=="mode" || n=="mtime" || n=="format" || n=="sort" || n=="pax-option" || n=="blocking-factor" || n=="record-size"){ if(!need()) return 2; } else if(n=="no-recursion" || n=="recursion" || n=="warning" || n=="delay-directory-restore" || Loading Loading @@ -836,6 +846,8 @@ namespace fresh { namespace cmd { case 'J': o.compress="xz"; break; case 'f': if(!value(o.archive)) return 2; break; case 'C': if(!value(o.dir)) return 2; break; case 'T': { std::string v; if(!value(v)) return 2; o.filesFrom.push_back(v); break; } case 'I': if(!value(o.compress)) return 2; break; case 'h': case 'P': case 'U': case 'S': case 'W': case 'B': case 'i': break; default: std::cerr << "tar: invalid option -- '" << c << "'" << std::endl; Loading @@ -847,6 +859,23 @@ namespace fresh { namespace cmd { std::cerr << "tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options" << std::endl; return 2; } for(auto &list: o.filesFrom){ FILE *f=list=="-" ? stdin : fopen(list.c_str(),"r"); if(!f){ std::cerr << "tar: " << list << ": Cannot open: " << strerror(errno) << std::endl; return 2; } std::string name; int ch; while((ch=fgetc(f))!=EOF){ if(ch==(o.nullNames ? '\0' : '\n')){ if(!name.empty()) o.members.push_back(name); name.clear(); }else name+=(char)ch; } if(!name.empty()) o.members.push_back(name); if(f!=stdin) fclose(f); } if(o.archive.empty()) o.archive="-"; int fd; Loading @@ -865,7 +894,13 @@ namespace fresh { namespace cmd { pid_t filter=-1; int ioFd=fd; if(!o.compress.empty()){ std::vector<std::string> cmd{o.compress}; // -I "xz -T0": a command with its arguments std::vector<std::string> cmd; { std::istringstream words(o.compress); std::string word; while(words >> word) cmd.push_back(word); } if(o.mode!='c') cmd.push_back("-dc"); else cmd.push_back("-c"); filter=startFilter(cmd,fd,o.mode!='c',ioFd); Loading
commands/system/xz.cpp +51 −15 Changes for commands/system/xz.cpp: 51 added lines, 15 removed lines. Original line number Diff line number Diff line Loading @@ -25,8 +25,8 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // CLI entry point for decompress-only "xz": argv parsing and file/stdio // wiring only -- the actual .xz/LZMA2 decoder lives in lib/xz/xz.h, same // CLI entry point for "xz": argv parsing and file/stdio wiring only -- the // actual .xz/LZMA2 decoder and encoder live in lib/xz (xz.h), same // division of labor as lib/awk vs commands/editor/awk.cpp. dpkg-source's // own "3.0 (quilt)" format always ships a *.debian.tar.xz alongside the // *.orig.tar.* -- apt-get source / dpkg-source -x needs to unpack it, and Loading Loading @@ -65,29 +65,58 @@ namespace fresh { namespace cmd { XzCommand():ShellCommand("xz"){} int runCommand(int argc,const char **argv){ // like xz(1): compress unless -d; "xz -c" alone compresses to // stdout (tar -J/-I xz when creating), "-dc" decompresses bool decompressMode=false; bool toStdout=false; bool keep=false; int level=6; std::vector<std::string> files; for(int i=1;i<argc;++i){ std::string arg=argv[i]; if(arg=="-d" || arg=="--decompress" || arg=="-dc" || arg=="-cd"){ if(arg=="-dc" || arg=="-cd") toStdout=true; }else if(arg=="-c" || arg=="--stdout" || arg=="--to-stdout"){ toStdout=true; }else if(arg=="-k" || arg=="--keep"){ keep=true; }else if(arg=="-f" || arg=="--force" || arg=="-q" || arg=="--quiet" || arg=="-v" || arg=="--verbose" || arg=="-T" || arg=="--threads"){ // accepted, no effect needed for this decompress-only build }else if(!arg.empty() && arg[0]=='-' && arg!="-"){ if(arg.size()>2 && arg.compare(0,2,"--")==0){ std::string n=arg.substr(2); size_t eq=n.find('='); if(eq!=std::string::npos) n=n.substr(0,eq); if(n=="decompress" || n=="uncompress") decompressMode=true; else if(n=="compress") decompressMode=false; else if(n=="stdout" || n=="to-stdout") toStdout=true; else if(n=="keep") keep=true; else if(n=="best") level=9; else if(n=="fast") level=0; else if(n=="force" || n=="quiet" || n=="verbose" || n=="extreme" || n=="threads" || n=="check" || n=="format" || n=="memlimit" || n=="no-warn" || n=="no-sparse"){ // accepted: one thread, CRC32, .xz format }else{ std::cerr << "xz: unrecognized option '" << arg << "'" << std::endl; return 1; }else{ } continue; } if(arg.size()<2 || arg[0]!='-'){ files.push_back(arg); continue; } for(size_t k=1;k<arg.size();++k){ char c=arg[k]; if(c=='d') decompressMode=true; else if(c=='z') decompressMode=false; else if(c=='c') toStdout=true; else if(c=='k') keep=true; else if(c>='0' && c<='9') level=c-'0'; else if(c=='f' || c=='q' || c=='v' || c=='e'){} else if(c=='T' || c=='C' || c=='F' || c=='M'){ // value: the rest of this argument or the next one if(k+1==arg.size()) ++i; break; }else{ std::cerr << "xz: invalid option -- '" << c << "'" << std::endl; return 1; } } } (void)keep; if(files.empty()) files.push_back("-"); Loading @@ -110,11 +139,16 @@ namespace fresh { namespace cmd { } std::vector<unsigned char> output; if(decompressMode){ if(!fresh::xz::decompress(input,output)){ std::cerr << "xz: " << path << ": not in xz format or corrupt" << std::endl; failed=1; continue; } }else if(!fresh::xz::compress(input,output,level)){ failed=1; continue; } if(toStdout || path=="-"){ if(!output.empty()) Loading @@ -124,7 +158,9 @@ namespace fresh { namespace cmd { std::string outpath=path; static const char suffix[]=".xz"; size_t sfxlen=sizeof(suffix)-1; if(outpath.size()>sfxlen && outpath.compare(outpath.size()-sfxlen,sfxlen,suffix)==0) if(!decompressMode) outpath+=suffix; else if(outpath.size()>sfxlen && outpath.compare(outpath.size()-sfxlen,sfxlen,suffix)==0) outpath.resize(outpath.size()-sfxlen); else outpath+=".out"; Loading
lib/xz/CMakeLists.txt +1 −1 Changes for lib/xz/CMakeLists.txt: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -3,4 +3,4 @@ # and modprobe/depmod (all three already link into fresh via # target_sources()), so a plain target_sources() here is enough -- see # xz.cpp's own header comment. target_sources(fresh PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/xz.cpp) target_sources(fresh PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/xz.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xz_encode.cpp)
lib/xz/xz.cpp +3 −4 Changes for lib/xz/xz.cpp: 3 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Decompress-only .xz/LZMA2, moved here from commands/system/xz.cpp (which // The .xz/LZMA2 decoder, moved here from commands/system/xz.cpp (which // is now just the CLI wrapper) so modprobe/depmod can decompress a *.ko.xz // kernel module in memory without shelling out. No compression side (the // real xz encoder) is implemented; only unpacking archives someone else // compressed is ever needed here. Only a single-filter LZMA2 chain is // kernel module in memory without shelling out. The encoder is // xz_encode.cpp (the kernel build's kheaders_data.tar.xz needs one). Only a single-filter LZMA2 chain is // supported (what every plain "xz"/"dpkg-source" invocation, and the // kernel's own CONFIG_MODULE_COMPRESS_XZ, produces) -- BCJ/delta filter // chains are rejected with a clear error rather than silently misdecoded. Loading
lib/xz/xz.h +6 −1 Changes for lib/xz/xz.h: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Decompress-only .xz/LZMA2 engine, shared between the "xz" CLI applet // .xz/LZMA2 engine (decoder; the encoder is xz_encode.cpp), shared between the "xz" CLI applet // (commands/system/xz.cpp -- argv parsing and file/stdio wiring only, same // division of labor as lib/awk vs commands/editor/awk.cpp) and anything // else in this binary that needs to decompress an in-memory .xz blob Loading @@ -48,4 +48,9 @@ namespace fresh { namespace xz { // should catch the false return and report it themselves instead). bool decompress(const std::vector<unsigned char> &input,std::vector<unsigned char> &output); // Compresses `input` into one complete .xz stream (one Block, LZMA2, // CRC32 check), appended to `output`; level 0-9 as xz's presets // (dictionary size, match finder effort). See xz_encode.cpp. bool compress(const std::vector<unsigned char> &input,std::vector<unsigned char> &output,int level=6); }}