77#include < matplot/util/common.h>
88#include < matplot/util/popen.h>
99#include < iostream>
10- #include < regex >
10+ #include < charconv >
1111#include < thread>
1212#include < cstring>
1313#include < cstdlib>
@@ -317,13 +317,28 @@ namespace matplot::backend {
317317 }
318318 }
319319
320+ // / returns the next word in text after prefix terminated with white space.
321+ static std::string_view word_after (std::string_view text, std::string_view prefix)
322+ {
323+ auto res = text.substr (0 ,0 );
324+ if (auto b = text.find (prefix); b != std::string_view::npos) {
325+ b += prefix.length ();
326+ while (b < text.length () && std::isspace (text[b]))
327+ ++b; // skip white space before word
328+ auto e = b;
329+ while (e < text.length () && !std::isspace (text[e]))
330+ ++e; // scan until white space or end
331+ res = text.substr (b, e-b);
332+ }
333+ return res;
334+ }
335+
320336 std::string gnuplot::default_terminal_type () {
321337 static std::string terminal_type;
322338 const bool dont_know_term_type = terminal_type.empty ();
323339 if (dont_know_term_type) {
324340 terminal_type = run_and_get_output (" gnuplot -e \" show terminal\" 2>&1" );
325- terminal_type = std::regex_replace (terminal_type,
326- std::regex (" [^]*terminal type is ([^ ]+)[^]*" ), " $1" );
341+ terminal_type = word_after (terminal_type, " terminal type is " );
327342 const bool still_dont_know_term_type = terminal_type.empty ();
328343 if (still_dont_know_term_type) {
329344 terminal_type = " qt" ;
@@ -334,54 +349,45 @@ namespace matplot::backend {
334349
335350 bool gnuplot::terminal_is_available (std::string_view term) {
336351 std::string msg = run_and_get_output (" gnuplot -e \" set terminal " +
337- std::string ( term. data ()) + " \" 2>&1" );
352+ std::string{ term} + " \" 2>&1" );
338353 return msg.empty ();
339354 }
340355
356+ template <typename T>
357+ void convert_to (std::string_view text, T& value) {
358+ std::from_chars (text.data (), text.data () + text.length (), value);
359+ }
360+
341361 std::tuple<int , int , int > gnuplot::gnuplot_version () {
342- static std::tuple<int , int , int > version{0 , 0 , 0 };
343- const bool dont_know_gnuplot_version_yet =
344- version == std::tuple<int , int , int >({0 , 0 , 0 });
345- if (dont_know_gnuplot_version_yet) {
346- std::string version_str =
347- run_and_get_output (" gnuplot --version 2>&1" );
348- std::string version_major = std::regex_replace (
349- version_str,
350- std::regex (" [^]*gnuplot (\\ d+)\\ .\\ d+ patchlevel \\ d+ *" ),
351- " $1" );
352- std::string version_minor = std::regex_replace (
353- version_str,
354- std::regex (" [^]*gnuplot \\ d+\\ .(\\ d+) patchlevel \\ d+ *" ),
355- " $1" );
356- std::string version_patch = std::regex_replace (
357- version_str,
358- std::regex (" [^]*gnuplot \\ d+\\ .\\ d+ patchlevel (\\ d+) *" ),
359- " $1" );
360- try {
361- std::get<0 >(version) = std::stoi (version_major);
362- } catch (...) {
363- std::get<0 >(version) = 0 ;
364- }
365- try {
366- std::get<1 >(version) = std::stoi (version_minor);
367- } catch (...) {
368- std::get<1 >(version) = 0 ;
369- }
370- try {
371- std::get<2 >(version) = std::stoi (version_patch);
372- } catch (...) {
373- std::get<2 >(version) = 0 ;
374- }
375- const bool still_dont_know_gnuplot_version =
376- version == std::tuple<int , int , int >({0 , 0 , 0 });
377- if (still_dont_know_gnuplot_version) {
378- // assume it's 5.2.6 by convention
379- version = std::tuple<int , int , int >({5 , 2 , 6 });
362+ constexpr auto version_zero = std::make_tuple (0 , 0 , 0 );
363+ static auto version = version_zero;
364+ if (version == version_zero) { // unknown version
365+ const auto version_str = run_and_get_output (" gnuplot --version 2>&1" );
366+ // gnuplot version_str example: "5.2 patchlevel 6"
367+ const auto major_minor = word_after (version_str, " gnuplot" ); // "5.2"
368+ const auto minor = word_after (major_minor, " ." ); // "2"
369+ const auto patch = word_after (version_str, " patchlevel" ); // "6"
370+ if (!major_minor.empty () && !minor.empty () && !patch.empty ()) {
371+ convert_to (major_minor, std::get<0 >(version));
372+ convert_to (minor, std::get<1 >(version));
373+ convert_to (patch, std::get<2 >(version));
380374 }
375+ if (version == version_zero) // still unknown
376+ version = {5 , 2 , 6 }; // assume by convention
381377 }
382378 return version;
383379 }
384380
381+ bool gnuplot::gnuplot_includes_legends () {
382+ return gnuplot_version () >= std::make_tuple (5 , 2 , 6 );
383+ }
384+ bool gnuplot::gnuplot_has_wall_option () {
385+ return gnuplot_version () >= std::make_tuple (5 , 5 , 0 );
386+ }
387+ bool gnuplot::gnuplot_supports_keyentry () {
388+ return gnuplot_version () >= std::make_tuple (5 , 2 , 6 );
389+ }
390+
385391 bool gnuplot::terminal_has_title_option (const std::string &t) {
386392 SV_CONSTEXPR std::string_view whitelist[] = {
387393 " qt" , " aqua" , " caca" , " canvas" , " windows" , " wxt" , " x11" };
0 commit comments