diff options
Diffstat (limited to 'bpkg/archive.cxx')
-rw-r--r-- | bpkg/archive.cxx | 165 |
1 files changed, 135 insertions, 30 deletions
diff --git a/bpkg/archive.cxx b/bpkg/archive.cxx index c096701..c41c4e3 100644 --- a/bpkg/archive.cxx +++ b/bpkg/archive.cxx @@ -31,22 +31,45 @@ namespace bpkg } #endif + // Only the extract ('x') and list ('t') operations are supported. + // static pair<cstrings, size_t> - start_extract (const common_options& co, const path& a) + start (const common_options& co, char op, const path& a) { + assert (op == 'x' || op == 't'); + cstrings args; // On Windows we default to libarchive's bsdtar with auto-decompression // (though there is also bsdcat which we could have used). // - const char* tar (co.tar_specified () - ? co.tar ().string ().c_str () + // OpenBSD tar does not support -O|--to-stdout and so far the best + // solution seems to require bsdtar (libarchive) or gtar (GNU tar). + // + const char* tar; + + if (co.tar_specified ()) + tar = co.tar ().string ().c_str (); + else + { #ifdef _WIN32 - : "bsdtar" + tar = "bsdtar"; +#elif defined(__OpenBSD__) + // A bit wasteful to do this every time (and throw away the result). + // Oh, well, the user can always "optimize" this away by passing + // explicit --tar. + // + if (!process::try_path_search ("bsdtar", true).empty ()) + tar = "bsdtar"; + else if (!process::try_path_search ("gtar", true).empty ()) + tar = "gtar"; + else + fail << "bsdtar or gtar required on OpenBSD for -O|--to-stdout support" + << endf; #else - : "tar" + tar = "tar"; #endif - ); + } // See if we need to decompress. // @@ -91,7 +114,7 @@ namespace bpkg args.push_back ("--force-local"); #endif - args.push_back ("-xf"); + args.push_back (op == 'x' ? "-xf" : "-tf"); args.push_back (i == 0 ? a.string ().c_str () : "-"); return make_pair (move (args), i); @@ -100,7 +123,7 @@ namespace bpkg pair<process, process> start_extract (const common_options& co, const path& a, const dir_path& d) { - pair<cstrings, size_t> args_i (start_extract (co, a)); + pair<cstrings, size_t> args_i (start (co, 'x', a)); cstrings& args (args_i.first); size_t i (args_i.second); @@ -171,31 +194,20 @@ namespace bpkg } } - pair<process, process> - start_extract (const common_options& co, - const path& a, - const path& f, - bool diag) + // Only the extract ('x') and list ('t') operations are supported. + // + static pair<process, process> + start (const common_options& co, + char op, + const path& a, + const cstrings& tar_args, + bool diag) { - assert (!f.empty () && f.relative ()); - - pair<cstrings, size_t> args_i (start_extract (co, a)); + pair<cstrings, size_t> args_i (start (co, op, a)); cstrings& args (args_i.first); size_t i (args_i.second); - // -O/--to-stdout -- extract to stdout. - // - args.push_back ("-O"); - - // On Windows neither MSYS GNU tar nor BSD tar will find the archived file - // if its path is provided in the Windows notation. - // -#ifdef _WIN32 - string fs (f.posix_string ()); - args.push_back (fs.c_str ()); -#else - args.push_back (f.string ().c_str ()); -#endif + args.insert (args.end (), tar_args.begin (), tar_args.end ()); args.push_back (nullptr); args.push_back (nullptr); // Pipe end. @@ -245,6 +257,34 @@ namespace bpkg } } + pair<process, process> + start_extract (const common_options& co, + const path& a, + const path& f, + bool diag) + { + assert (!f.empty () && f.relative ()); + + cstrings args; + args.reserve (2); + + // -O/--to-stdout -- extract to stdout. + // + args.push_back ("-O"); + + // On Windows neither MSYS GNU tar nor BSD tar will find the archived file + // if its path is provided in the Windows notation. + // +#ifdef _WIN32 + string fs (f.posix_string ()); + args.push_back (fs.c_str ()); +#else + args.push_back (f.string ().c_str ()); +#endif + + return start (co, 'x', a, args, diag); + } + string extract (const common_options& o, const path& a, const path& f, bool diag) try @@ -253,7 +293,7 @@ namespace bpkg try { - // Do not throw when eofbit is set (end of stream reached), and + // Do not throw when eofbit is set (end of stream is reached), and // when failbit is set (getline() failed to extract any character). // ifdstream is (move (pr.second.in_ofd), ifdstream::badbit); @@ -290,4 +330,69 @@ namespace bpkg // fail << "unable to extract " << f << " from " << a << ": " << e << endf; } + + paths + archive_contents (const common_options& o, const path& a, bool diag) + try + { + pair<process, process> pr (start (o, 't', a, cstrings (), diag)); + + try + { + paths r; + + // Do not throw when eofbit is set (end of stream reached), and + // when failbit is set (getline() failed to extract any character). + // + ifdstream is (move (pr.second.in_ofd), ifdstream::badbit); + + for (string l; !eof (getline (is, l)); ) + r.emplace_back (move (l)); + + is.close (); + + if (pr.second.wait () && pr.first.wait ()) + return r; + + // Fall through. + } + catch (const invalid_path& e) + { + // Just fall through if the pipeline has failed. + // + if (pr.second.wait () && pr.first.wait ()) + { + if (diag) + error << "unable to obtain contents for " << a + << ": invalid path '" << e.path << "'"; + + throw failed (); + } + + // Fall through. + } + catch (const io_error&) + { + // Child exit status doesn't matter. Just wait for the process + // completion and fall through. + // + pr.second.wait (); pr.first.wait (); // Check throw. + } + + // While it is reasonable to assuming the child process issued diagnostics + // if exited with an error status, tar, specifically, doesn't mention the + // archive name. So print the error message whatever the child exit status + // is, if the diagnostics is requested. + // + if (diag) + error << "unable to obtain contents for " << a; + + throw failed (); + } + catch (const process_error& e) + { + // Note: this is not a tar error, so no diag check. + // + fail << "unable to obtain contents for " << a << ": " << e << endf; + } } |