aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/curl.cxx61
-rw-r--r--libbutl/curl.hxx10
2 files changed, 70 insertions, 1 deletions
diff --git a/libbutl/curl.cxx b/libbutl/curl.cxx
index 5649965..ab13d84 100644
--- a/libbutl/curl.cxx
+++ b/libbutl/curl.cxx
@@ -5,10 +5,13 @@
#include <cassert>
#include <utility> // move()
-#include <cstdlib> // strtoul(), size_t
+#include <cstdlib> // strtoul(), exit(), size_t
#include <exception> // invalid_argument
#include <libbutl/utility.hxx>
+#include <libbutl/process.hxx>
+#include <libbutl/optional.hxx>
+#include <libbutl/fdstream.hxx>
using namespace std;
@@ -190,6 +193,62 @@ namespace butl
throw invalid_argument ("unsupported protocol");
}
+ optional<semantic_version> curl::
+ version (const path& prog)
+ {
+ // curl --version prints the version to stdout and exits with 0
+ // status. The first line starts with "curl X.Y.Z"
+ //
+ const char* args[] = {prog.string ().c_str (), "--version", nullptr};
+
+ try
+ {
+ // Redirect stdout to a pipe and stderr to /dev/null.
+ //
+ // Note that curl may print the following line to stderr if built with
+ // the --enable-debug option:
+ //
+ // WARNING: this libcurl is Debug-enabled, do not use in production
+ //
+ process pr (args, 0, -1, -2);
+
+ string l;
+
+ try
+ {
+ ifdstream is (move (pr.in_ofd), fdstream_mode::skip);
+
+ getline (is, l);
+ is.close ();
+
+ if (!pr.wait () || l.compare (0, 5, "curl ") != 0)
+ return nullopt;
+ }
+ catch (const ios_base::failure&)
+ {
+ return nullopt;
+ }
+
+ // Try to extract the version.
+ //
+ // Note that there is some variety across the build configurations:
+ //
+ // curl 8.9.1-DEV (x86_64-pc-linux-gnu) libcurl/8.9.1-DEV OpenSSL/3.1.1 zlib/1.2.13 libpsl/0.21.2 OpenLDAP/2.6.7
+ // curl 8.7.1 (x86_64-pc-linux-gnu) libcurl/8.7.1 OpenSSL/3.2.2 zlib/1.2.11 brotli/1.0.9 zstd/1.5.5 libidn2/2.3.3 libpsl/0.21.2 libssh2/1.11.0 nghttp2/1.50.0 librtmp/2.3 OpenLDAP/2.5.13
+ //
+ return parse_semantic_version (l, 5,
+ semantic_version::allow_build,
+ "" /* build_separators */);
+ }
+ catch (const process_error& e)
+ {
+ if (e.child)
+ std::exit (1);
+
+ return nullopt;
+ }
+ }
+
uint16_t curl::
parse_http_status_code (const string& s)
{
diff --git a/libbutl/curl.hxx b/libbutl/curl.hxx
index ea91807..3a3d8d1 100644
--- a/libbutl/curl.hxx
+++ b/libbutl/curl.hxx
@@ -9,8 +9,10 @@
#include <libbutl/path.hxx>
#include <libbutl/process.hxx>
+#include <libbutl/optional.hxx>
#include <libbutl/fdstream.hxx>
#include <libbutl/small-vector.hxx>
+#include <libbutl/semantic-version.hxx>
#include <libbutl/export.hxx>
@@ -163,6 +165,14 @@ namespace butl
const std::string& url,
A&&... options);
+ // Try to run the specified program with the --version option and parse
+ // its stdout for the curl version. Return nullopt if this program cannot
+ // be found/executed, doesn't exit with the zero code, or its stdout
+ // cannot be parsed.
+ //
+ static optional<semantic_version>
+ version (const path&);
+
// Read the HTTP response status from an input stream.
//
// Specifically, read and parse the HTTP status line, by default skip over