From 92ac709b7e87a82b3eb19f9ea34a0a48abe26d75 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sat, 26 Aug 2023 16:48:28 +0200 Subject: [PATCH] Adds safe versions of fl_filename_*, returning Fl_String --- FL/filename.H | 14 ++++++ src/filename_absolute.cxx | 89 +++++++++++++++++++++++++++++++++++++++ test/unittest_core.cxx | 29 ++++++++++++- 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/FL/filename.H b/FL/filename.H index d9caa70bb..fb2272b43 100644 --- a/FL/filename.H +++ b/FL/filename.H @@ -53,6 +53,20 @@ FL_EXPORT int fl_filename_relative(char *to, int tolen, const char *from); FL_EXPORT int fl_filename_match(const char *name, const char *pattern); FL_EXPORT int fl_filename_isdir(const char *name); +# if defined(__cplusplus) + +class Fl_String; + +FL_EXPORT Fl_String fl_filename_name(const Fl_String &filename); +FL_EXPORT Fl_String fl_filename_path(const Fl_String &filename); +FL_EXPORT Fl_String fl_filename_ext(const Fl_String &filename); +FL_EXPORT Fl_String fl_filename_setext(const Fl_String &filename, const Fl_String &new_extension); +FL_EXPORT Fl_String fl_filename_expand(const Fl_String &from); +FL_EXPORT Fl_String fl_filename_absolute(const Fl_String &from); +FL_EXPORT Fl_String fl_filename_relative(const Fl_String &from); + +# endif + # if defined(__cplusplus) && !defined(FL_DOXYGEN) /* * Under Windows, we include filename.H from numericsort.c; this should probably change... diff --git a/src/filename_absolute.cxx b/src/filename_absolute.cxx index 72bb9f3ff..9bdb723f4 100644 --- a/src/filename_absolute.cxx +++ b/src/filename_absolute.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include "Fl_System_Driver.H" #include @@ -236,3 +237,91 @@ Fl_System_Driver::filename_relative(char *to, // O - Relative filename \} \endcond */ + + +/** + Return a new string that contains the name part of the filename. + \param[in] filename file path and name + \return the name part of a filename + \see fl_filename_name(const char *filename) + */ +Fl_String fl_filename_name(const Fl_String &filename) { + return Fl_String(fl_filename_name(filename.c_str())); +} + +/** + Return a new string that contains the path part of the filename. + \param[in] filename file path and name + \return the path part of a filename without the name + \see fl_filename_name(const char *filename) + */ +Fl_String fl_filename_path(const Fl_String &filename) { + const char *base = filename.c_str(); + const char *name = fl_filename_name(base); + if (name) { + return Fl_String(base, (int)(name-base)); + } else { + return Fl_String(); + } +} + +/** + Return a new string that contains the filename extension. + \param[in] filename file path and name + \return the filename extension including the prepending '.', or an empty + string if the filename has no extension + \see fl_filename_ext(const char *buf) + */ +Fl_String fl_filename_ext(const Fl_String &filename) { + return Fl_String(fl_filename_ext(filename.c_str())); +} + +/** + Return a copy of the old filename with the new extension. + \param[in] filename file path and name + \param[in] new_extension new filename extension, starts with a '.' + \return the new filename + \see fl_filename_setext(char *to, int tolen, const char *ext) + */ +Fl_String fl_filename_setext(const Fl_String &filename, const Fl_String &new_extension) { + char buffer[FL_PATH_MAX]; + fl_strlcpy(buffer, filename.c_str(), FL_PATH_MAX); + fl_filename_setext(buffer, FL_PATH_MAX, new_extension.c_str()); + return Fl_String(buffer); +} + +/** + Expands a filename containing shell variables and tilde (~). + \param[in] filename file path and name + \return the new filename + \see fl_filename_expand(char *to, int tolen, const char *from) +*/ +Fl_String fl_filename_expand(const Fl_String &from) { + char buffer[FL_PATH_MAX]; + fl_filename_expand(buffer, FL_PATH_MAX, from.c_str()); + return Fl_String(buffer); +} + +/** + Makes a filename absolute from a filename relative to the current working directory. + \param[in] filename file path and name + \return the new filename + \see fl_filename_absolute(char *to, int tolen, const char *from) + */ +Fl_String fl_filename_absolute(const Fl_String &from) { + char buffer[FL_PATH_MAX]; + fl_filename_absolute(buffer, FL_PATH_MAX, from.c_str()); + return Fl_String(buffer); +} + +/** + Makes a filename relative to the current working directory. + \param[in] filename file path and name + \return the new filename + \see fl_filename_relative(char *to, int tolen, const char *from) + */ +Fl_String fl_filename_relative(const Fl_String &from) { + char buffer[FL_PATH_MAX]; + fl_filename_relative(buffer, FL_PATH_MAX, from.c_str()); + return Fl_String(buffer); +} diff --git a/test/unittest_core.cxx b/test/unittest_core.cxx index 65fd71630..baccdbd04 100644 --- a/test/unittest_core.cxx +++ b/test/unittest_core.cxx @@ -21,6 +21,8 @@ #include #include #include +#include +#include /* Test Fl_String constructor and assignment. */ TEST(Fl_String, Assignment) { @@ -195,6 +197,29 @@ TEST(Fl_String, Non-Member Functions) { return true; } +/* Test additions to Fl_Preferences. */ +TEST(Fl_String, fl_filename_...) { + const Fl_String ref = "/test/me.txt"; + Fl_String name = fl_filename_name(ref); + EXPECT_STREQ(name.c_str(), "me.txt"); + name = fl_filename_name(Fl_String("/test/")); + EXPECT_STREQ(name.c_str(), ""); + Fl_String path = fl_filename_path(ref); + EXPECT_STREQ(path.c_str(), "/test/"); + Fl_String ext = fl_filename_ext(ref); + EXPECT_STREQ(ext.c_str(), ".txt"); + ext = fl_filename_setext(ref, ".rtf"); + EXPECT_STREQ(ext.c_str(), "/test/me.rtf"); + fl_putenv("FL_UNITTEST=unit/test"); + name = fl_filename_expand(Fl_String("abc/$FL_UNITTEST/xyz")); + EXPECT_STREQ(name.c_str(), "abc/unit/test/xyz"); + Fl_String abs = fl_filename_absolute(Fl_String("./abc/def.txt")); + Fl_String rel = fl_filename_relative(abs); + EXPECT_STREQ(rel.c_str(), "abc/def.txt"); + EXPECT_STREQ(ref.c_str(), "/test/me.txt"); + return true; +} + /* Test additions to Fl_Preferences. */ TEST(Fl_Preferences, Strings) { { @@ -318,9 +343,9 @@ public: // Run one single test and repeat calling this until all tests are done static void timer_cb(void*) { - // Run a test every few miliseconds to visualize the progress + // Run a test every few milliseconds to visualize the progress if (Ut_Suite::run_next_test()) - Fl::repeat_timeout(0.2, timer_cb); + Fl::repeat_timeout(0.15, timer_cb); } // Showing this tab for the first time will trigger the tests