This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch make_default_configuration_useful
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git

commit b0bc0617d948acbde06c7c704c2eb3b094c3e7c1
Author: Stephen Webb <[email protected]>
AuthorDate: Fri Aug 15 14:21:04 2025 +1000

    Add support for programmatically providing runtime values for configuration 
option values
---
 src/examples/cpp/CMakeLists.txt                    |  2 +-
 src/examples/cpp/com/foo/config4.cpp               | 62 ++++++++++++++++++++
 .../cpp/com/foo/product_version.h}                 | 20 ++-----
 src/main/cpp/configurator.cpp                      | 29 ++++++++++
 src/main/cpp/defaultconfigurator.cpp               | 67 +++++++++++-----------
 src/main/cpp/domconfigurator.cpp                   |  2 +-
 src/main/cpp/optionconverter.cpp                   |  2 +-
 src/main/cpp/properties.cpp                        | 16 ++++++
 src/main/cpp/propertyconfigurator.cpp              |  2 +-
 src/main/cpp/system.cpp                            | 60 +++++++++++++++++++
 src/main/include/log4cxx/defaultconfigurator.h     | 26 +++++++--
 .../log4cxx/helpers/filesystempath.h}              | 28 ++++-----
 src/main/include/log4cxx/helpers/properties.h      | 14 ++++-
 src/main/include/log4cxx/helpers/system.h          | 23 ++++++--
 src/main/include/log4cxx/spi/configurator.h        | 19 ++++++
 src/site/markdown/example-programs.md              |  5 +-
 16 files changed, 298 insertions(+), 79 deletions(-)

diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index e2487c25..983bb748 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -46,7 +46,7 @@ foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
         target_link_libraries(${PROGRAM_NAME} PRIVATE log4cxx-qt)
     endif()
     if(${exampleName} STREQUAL auto-configured)
-        target_sources(${PROGRAM_NAME} PRIVATE com/foo/config3.cpp )
+        target_sources(${PROGRAM_NAME} PRIVATE com/foo/config4.cpp )
     endif()
     target_compile_definitions(${PROGRAM_NAME} PRIVATE 
${EXAMPLE_COMPILE_DEFINITIONS} ${LOG4CXX_COMPILE_DEFINITIONS} 
${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS} )
     target_include_directories(${PROGRAM_NAME} PRIVATE 
${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
diff --git a/src/examples/cpp/com/foo/config4.cpp 
b/src/examples/cpp/com/foo/config4.cpp
new file mode 100644
index 00000000..26f79aef
--- /dev/null
+++ b/src/examples/cpp/com/foo/config4.cpp
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "product_version.h"
+#include <log4cxx/logmanager.h>
+#include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/helpers/transcoder.h>
+
+namespace com { namespace foo {
+
+// Retrieve the \c name logger pointer.
+// Configure Log4cxx on the first call.
+auto getLogger(const std::string& name) -> LoggerPtr {
+       using namespace log4cxx;
+       static struct log4cxx_initializer {
+               log4cxx_initializer() {
+                       auto vendorFolder = getVendorFolder();
+                       auto productFolder = getProductFolder();
+                       LOG4CXX_DECODE_CHAR(lsVendorFolder, vendorFolder);
+                       LOG4CXX_DECODE_CHAR(lsProductFolder, productFolder);
+
+                       // Allow expansion of ${CURRENT_VENDOR_FOLDER} and 
${CURRENT_PRODUCT_FOLDER}
+                       // when loading a configuration from a file
+                       auto& props = 
spi::Configurator::configurationProperties();
+                       props.setProperty(LOG4CXX_STR("CURRENT_VENDOR_FOLDER"), 
lsVendorFolder);
+                       
props.setProperty(LOG4CXX_STR("CURRENT_PRODUCT_FOLDER"), lsProductFolder);
+
+                       // Use a configuration file in the current working 
directory
+                       
DefaultConfigurator::setConfigurationFileName(LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.xml"));
+                       if (DefaultConfigurator::tryConfigure() == 
spi::ConfigurationStatus::NotConfigured)
+                       {
+                               // Use a configuration file in the same 
directory as the program
+                               
DefaultConfigurator::setConfigurationFileName(LOG4CXX_STR("${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.xml"));
+                               if (DefaultConfigurator::tryConfigure() == 
spi::ConfigurationStatus::NotConfigured)
+                                       BasicConfigurator::configure(); // Send 
events to the console
+                       }
+               }
+               ~log4cxx_initializer() {
+                       LogManager::shutdown();
+               }
+       } initialiser;
+       return name.empty()
+               ? LogManager::getRootLogger()
+               : LogManager::getLogger(name);
+}
+
+} } // namespace com::foo
diff --git a/src/main/cpp/configurator.cpp 
b/src/examples/cpp/com/foo/product_version.h
similarity index 76%
copy from src/main/cpp/configurator.cpp
copy to src/examples/cpp/com/foo/product_version.h
index 26fa1461..56c51221 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/examples/cpp/com/foo/product_version.h
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/spi/configurator.h>
-#include <assert.h>
-#include <log4cxx/logger.h>
-
-using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::spi;
-
-IMPLEMENT_LOG4CXX_OBJECT(Configurator)
-
-
-
-
-Configurator::Configurator()
+std::string getVendorFolder()
+{
+  return "ApacheSoftwareFoundation";
+}
+std::string getProductFolder()
 {
+  return "Logging";
 }
diff --git a/src/main/cpp/configurator.cpp b/src/main/cpp/configurator.cpp
index 26fa1461..c1603ecd 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/main/cpp/configurator.cpp
@@ -19,6 +19,13 @@
 #include <log4cxx/spi/configurator.h>
 #include <assert.h>
 #include <log4cxx/logger.h>
+#include <log4cxx/helpers/singletonholder.h>
+#include <log4cxx/helpers/system.h>
+
+#if !defined(LOG4CXX)
+       #define LOG4CXX 1
+#endif
+#include <log4cxx/helpers/aprinitializer.h>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::spi;
@@ -26,8 +33,30 @@ using namespace LOG4CXX_NS::spi;
 IMPLEMENT_LOG4CXX_OBJECT(Configurator)
 
 
+namespace
+{
+       struct ConfiguratorTag {};
+       using ConfiguratorProperties = std::pair<ConfiguratorTag, 
helpers::Properties>;
 
+       ConfiguratorProperties& getInstance()
+       {
+               using ConfiguratorObject = 
helpers::SingletonHolder<ConfiguratorProperties>;
+               auto result = 
helpers::APRInitializer::getOrAddUnique<ConfiguratorObject>
+                       ( []() -> helpers::ObjectPtr
+                               { return 
std::make_shared<ConfiguratorObject>(); }
+                       );
+               return result->value();
+       }
+}
 
 Configurator::Configurator()
 {
 }
+
+helpers::Properties& Configurator::configurationProperties()
+{
+       auto& result = getInstance().second;
+       if (result.isEmpty())
+               helpers::System::addProgramFilePathComponents(result);
+       return result;
+}
diff --git a/src/main/cpp/defaultconfigurator.cpp 
b/src/main/cpp/defaultconfigurator.cpp
index 646d50e3..8daac617 100644
--- a/src/main/cpp/defaultconfigurator.cpp
+++ b/src/main/cpp/defaultconfigurator.cpp
@@ -16,46 +16,53 @@
  */
 #include <log4cxx/logstring.h>
 #include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/logmanager.h>
 #include <log4cxx/helpers/pool.h>
 #include <log4cxx/spi/loggerrepository.h>
 #include <log4cxx/file.h>
 #include <log4cxx/helpers/loglog.h>
 #include <log4cxx/helpers/optionconverter.h>
 #include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/system.h>
 #include <log4cxx/xml/domconfigurator.h>
 #include <log4cxx/propertyconfigurator.h>
 
+
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::spi;
 using namespace LOG4CXX_NS::helpers;
 
 namespace
 {
-       LogString DefaultConfiguratorPath;
-       int DefaultConfiguratorWatchSeconds = 0;
+       const LogString CONFIGURATION_FILE_KEY{ 
LOG4CXX_STR("LOG4CXX_CONFIGURATION") };
+       const LogString WATCH_SECONDS_KEY{ 
LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS") };
+       const LogString CONFIGURATOR_CLASS_KEY{ 
LOG4CXX_STR("LOG4CXX_CONFIGURATOR_CLASS") };
 }
 
 void DefaultConfigurator::setConfigurationFileName(const LogString& path)
 {
-       DefaultConfiguratorPath = path;
+       
Configurator::configurationProperties().setProperty(CONFIGURATION_FILE_KEY, 
path);
 }
 
-
 void DefaultConfigurator::setConfigurationWatchSeconds(int seconds)
 {
-       DefaultConfiguratorWatchSeconds = seconds;
+       Pool p;
+       LogString strSeconds;
+       StringHelper::toString(seconds, p, strSeconds);
+       Configurator::configurationProperties().setProperty(WATCH_SECONDS_KEY, 
strSeconds);
 }
 
-static const int MillisecondsPerSecond = 1000;
+spi::ConfigurationStatus DefaultConfigurator::tryConfigure()
+{
+       auto r = LogManager::getLoggerRepository();
+       configure(r);
+       return r->isConfigured() ? spi::ConfigurationStatus::Configured : 
spi::ConfigurationStatus::NotConfigured;
+}
 
 void DefaultConfigurator::configure(LoggerRepositoryPtr repository)
 {
-       repository->setConfigured(true);
-       const LogString configuratorClassName(getConfiguratorClass());
 
-       LogString configurationFileName = DefaultConfiguratorPath;
-       if (configurationFileName.empty())
-               configurationFileName = getConfigurationFileName();
+       LogString configurationFileName = getConfigurationFileName();
        Pool pool;
        File configuration;
 
@@ -92,6 +99,7 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr 
repository)
 
        if (configuration.exists(pool))
        {
+               repository->setConfigured(true);
                if (LogLog::isDebugEnabled())
                {
                        LogString msg(LOG4CXX_STR("Using configuration file 
["));
@@ -103,12 +111,11 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr 
repository)
                LoggerRepositoryPtr repo(repository);
                OptionConverter::selectAndConfigure(
                        configuration,
-                       configuratorClassName,
+                       getConfiguratorClass(),
                        repo,
-                       0 < DefaultConfiguratorWatchSeconds
-                               ? DefaultConfiguratorWatchSeconds * 
MillisecondsPerSecond
-                               : getConfigurationWatchDelay()
+                       getConfigurationWatchDelay()
                        );
+               // TBD: Report a failure
        }
        else if (LogLog::isDebugEnabled())
        {
@@ -129,37 +136,31 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr 
repository)
 
 const LogString DefaultConfigurator::getConfiguratorClass()
 {
-
-       // Use automatic configration to configure the default hierarchy
-       const LogString log4jConfiguratorClassName(
-               
OptionConverter::getSystemProperty(LOG4CXX_STR("log4j.configuratorClass"), 
LOG4CXX_STR("")));
-       const LogString configuratorClassName(
-               
OptionConverter::getSystemProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATOR_CLASS"),
-                       log4jConfiguratorClassName));
-       return configuratorClassName;
+       return System::getProperty(CONFIGURATOR_CLASS_KEY);
 }
 
 
 const LogString DefaultConfigurator::getConfigurationFileName()
 {
-       static const WideLife<LogString> 
LOG4CXX_DEFAULT_CONFIGURATION_KEY(LOG4CXX_STR("LOG4CXX_CONFIGURATION"));
-       static const WideLife<LogString> 
LOG4J_DEFAULT_CONFIGURATION_KEY(LOG4CXX_STR("log4j.configuration"));
-       const LogString log4jConfigurationFileName(
-               
OptionConverter::getSystemProperty(LOG4J_DEFAULT_CONFIGURATION_KEY, 
LOG4CXX_STR("")));
-       const LogString configurationFileName(
-               
OptionConverter::getSystemProperty(LOG4CXX_DEFAULT_CONFIGURATION_KEY,
-                       log4jConfigurationFileName));
-       return configurationFileName;
+       auto& props = Configurator::configurationProperties();
+       LogString configurationFileName = 
props.getProperty(CONFIGURATION_FILE_KEY);
+       if (configurationFileName.empty())
+               configurationFileName = 
System::getProperty(CONFIGURATION_FILE_KEY);
+       return OptionConverter::substVars(configurationFileName, props);
 }
 
 
 int DefaultConfigurator::getConfigurationWatchDelay()
 {
-       static const WideLife<LogString> 
LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"));
-       LogString optionStr = 
OptionConverter::getSystemProperty(LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY, 
LogString());
+       LogString optionStr = 
Configurator::configurationProperties().getProperty(WATCH_SECONDS_KEY);
+       if (optionStr.empty())
+               optionStr = System::getProperty(WATCH_SECONDS_KEY);
        int milliseconds = 0;
        if (!optionStr.empty())
+       {
+               static const int MillisecondsPerSecond = 1000;
                milliseconds = StringHelper::toInt(optionStr) * 
MillisecondsPerSecond;
+       }
        return milliseconds;
 }
 
diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp
index ac448574..6cf90f4a 100644
--- a/src/main/cpp/domconfigurator.cpp
+++ b/src/main/cpp/domconfigurator.cpp
@@ -65,7 +65,7 @@ using namespace LOG4CXX_NS::rolling;
 
 struct DOMConfigurator::DOMConfiguratorPrivate
 {
-       helpers::Properties props;
+       helpers::Properties props = Configurator::configurationProperties();
        spi::LoggerRepositoryPtr repository;
        spi::LoggerFactoryPtr loggerFactory;
 };
diff --git a/src/main/cpp/optionconverter.cpp b/src/main/cpp/optionconverter.cpp
index f70baef3..178e2437 100644
--- a/src/main/cpp/optionconverter.cpp
+++ b/src/main/cpp/optionconverter.cpp
@@ -228,7 +228,7 @@ LogString OptionConverter::findAndSubst(const LogString& 
key, Properties& props)
 LogString OptionConverter::substVars(const LogString& val, Properties& props)
 {
        LogString sbuf;
-       const logchar delimStartArray[] = { 0x24, 0x7B, 0 };
+       const logchar delimStartArray[] = { 0x24, 0x7B, 0 }; // '$', '{'
        const LogString delimStart(delimStartArray);
        const logchar delimStop = 0x7D; // '}';
        const size_t DELIM_START_LEN = 2;
diff --git a/src/main/cpp/properties.cpp b/src/main/cpp/properties.cpp
index 2c06b0ff..dfa72f83 100644
--- a/src/main/cpp/properties.cpp
+++ b/src/main/cpp/properties.cpp
@@ -399,6 +399,18 @@ Properties::Properties() : properties(new PropertyMap())
 {
 }
 
+Properties::Properties(const Properties& other)
+       : properties(new PropertyMap(*other.properties))
+{
+}
+
+Properties& Properties::operator=(const Properties& other)
+{
+       delete this->properties;
+       this->properties = new PropertyMap(*other.properties);
+       return *this;
+}
+
 Properties::~Properties()
 {
        delete properties;
@@ -451,3 +463,7 @@ std::vector<LogString> Properties::propertyNames() const
        return names;
 }
 
+bool Properties::isEmpty() const
+{
+       return properties->empty();
+}
\ No newline at end of file
diff --git a/src/main/cpp/propertyconfigurator.cpp 
b/src/main/cpp/propertyconfigurator.cpp
index 9586a13b..2b56686b 100644
--- a/src/main/cpp/propertyconfigurator.cpp
+++ b/src/main/cpp/propertyconfigurator.cpp
@@ -113,7 +113,7 @@ spi::ConfigurationStatus PropertyConfigurator::doConfigure
        auto hierarchy = repository ? repository : 
LogManager::getLoggerRepository();
        hierarchy->setConfigured(true);
 
-       Properties props;
+       Properties props = Configurator::configurationProperties();
 
        try
        {
diff --git a/src/main/cpp/system.cpp b/src/main/cpp/system.cpp
index ad4a75f6..ad0ecfc9 100644
--- a/src/main/cpp/system.cpp
+++ b/src/main/cpp/system.cpp
@@ -17,13 +17,24 @@
 
 #include <log4cxx/logstring.h>
 #include <log4cxx/helpers/system.h>
+#include <log4cxx/helpers/filesystempath.h>
 
 #include <log4cxx/helpers/transcoder.h>
 #include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/properties.h>
+#include <log4cxx/helpers/loglog.h>
 #include <apr_file_io.h>
 #include <apr_user.h>
 #include <apr_env.h>
 
+#ifdef _WIN32
+#include <windows.h>
+#elif __APPLE__
+#include <mach-o/dyld.h>
+#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+#include <unistd.h> // getpid
+#endif
+#include <sstream>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -120,3 +131,52 @@ LogString System::getProperty(const LogString& lkey)
        return rv;
 }
 
+void System::addProgramFilePathComponents(Properties& props)
+{
+       // Find the executable file name
+       static const int bufSize = 4096;
+       char buf[bufSize+1] = {0}, pathSepar = '/';
+       uint32_t bufCount = 0;
+#if defined(_WIN32)
+       GetModuleFileName(NULL, buf, bufSize);
+       pathSepar = '\\';
+#elif defined(__APPLE__)
+       _NSGetExecutablePath(buf, &bufCount);
+#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+       std::ostringstream exeLink;
+       exeLink << "/proc/" << getpid() << "/exe";
+       bufCount = readlink(exeLink.str().c_str(), buf, bufSize);
+       if (0 < bufCount)
+               buf[bufCount] = 0;
+#else
+       return;
+#endif
+
+       // Add the path to the properties
+       std::string programFileName(buf);
+       LOG4CXX_DECODE_CHAR(lsProgramFileName, programFileName);
+       LogString prefix{ LOG4CXX_STR("PROGRAM_FILE_PATH") };
+       props.setProperty(prefix, lsProgramFileName);
+
+#if LOG4CXX_HAS_FILESYSTEM_PATH
+       // Add the path components to the properties
+       prefix += '.';
+       Path programPath(programFileName);
+       LOG4CXX_DECODE_CHAR(root_name, programPath.root_name().string());
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_NAME"), root_name);
+       LOG4CXX_DECODE_CHAR(root_directory, 
programPath.root_directory().string());
+       props.setProperty(LOG4CXX_STR("ROOT_DIRECTORY"),root_directory);
+       LOG4CXX_DECODE_CHAR(root_path, programPath.root_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_PATH"), root_path);
+       LOG4CXX_DECODE_CHAR(relative_path, 
programPath.relative_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("RELATIVE_PATH"), relative_path);
+       LOG4CXX_DECODE_CHAR(parent_path, programPath.parent_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("PARENT_PATH"), parent_path);
+       LOG4CXX_DECODE_CHAR(filename, programPath.filename().string());
+       props.setProperty(prefix + LOG4CXX_STR("FILENAME"), filename);
+       LOG4CXX_DECODE_CHAR(stem, programPath.stem().string());
+       props.setProperty(prefix + LOG4CXX_STR("STEM"), stem);
+       LOG4CXX_DECODE_CHAR(extension, programPath.extension().string());
+       props.setProperty(prefix + LOG4CXX_STR("EXTENSION"), extension);
+#endif
+}
diff --git a/src/main/include/log4cxx/defaultconfigurator.h 
b/src/main/include/log4cxx/defaultconfigurator.h
index dabe9b1a..f6ff2393 100644
--- a/src/main/include/log4cxx/defaultconfigurator.h
+++ b/src/main/include/log4cxx/defaultconfigurator.h
@@ -36,12 +36,15 @@ class LOG4CXX_EXPORT DefaultConfigurator
 
        public:
                /**
-               Configure \c repository.
+               Configure the default repository.
 
                If the configuration file name has not been provided by a call 
to setConfigurationFileName(),
-               the environment variables "LOG4CXX_CONFIGURATION" and 
"log4j.configuration" are examined.
+               the environment variable "LOG4CXX_CONFIGURATION" value is used,
+               with ${varname} instances expanded using the 
helpers::Properties object
+               provided by DefaultConfigurator::configurationProperties().
+
                Unless a custom configurator is specified using the
-               "LOG4CXX_CONFIGURATOR_CLASS" or "log4j.configuratorClass"
+               "LOG4CXX_CONFIGURATOR_CLASS"
                environment variable, the PropertyConfigurator will be used to
                configure log4cxx unless the file name ends with the ".xml"
                extension, in which case the DOMConfigurator will be used. If a
@@ -58,10 +61,19 @@ class LOG4CXX_EXPORT DefaultConfigurator
                a background thread is started that will periodically check for 
a change to the configuration file
                and apply any configuration changes found.
                */
+               static spi::ConfigurationStatus tryConfigure();
+
+               /**
+               Configure \c repository.
+               */
                static void configure(spi::LoggerRepositoryPtr repository);
 
                /**
                Make \c path the configuration file used by configure().
+
+               Any ${varname} instances in the \c path value are expanded
+               using either a system environment variable value (if found)
+               otherwise using the map provided by 
Configurator::configurationProperties.
                */
                static void setConfigurationFileName(const LogString& path);
 
@@ -96,14 +108,16 @@ class LOG4CXX_EXPORT DefaultConfigurator
                 * @param filenames The names of the files to look for
                 * @return The status of the configuration, and the filename 
loaded(if a file was found).
                 */
-               static 
std::tuple<LOG4CXX_NS::spi::ConfigurationStatus,LogString> 
configureFromFile(const std::vector<LogString>& directories,
-                                                                               
                                                                                
                 const std::vector<LogString>& filenames);
+               static std::tuple<spi::ConfigurationStatus,LogString> 
configureFromFile
+                       ( const std::vector<LogString>& directories
+                       , const std::vector<LogString>& filenames
+                       );
 
        private:
                static const LogString getConfigurationFileName();
                static const LogString getConfiguratorClass();
                static int getConfigurationWatchDelay();
-               static LOG4CXX_NS::spi::ConfigurationStatus tryLoadFile(const 
LogString& filename);
+               static spi::ConfigurationStatus tryLoadFile(const LogString& 
filename);
 
 };      // class DefaultConfigurator
 }  // namespace log4cxx
diff --git a/src/main/cpp/configurator.cpp 
b/src/main/include/log4cxx/helpers/filesystempath.h
similarity index 56%
copy from src/main/cpp/configurator.cpp
copy to src/main/include/log4cxx/helpers/filesystempath.h
index 26fa1461..b09eaa88 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/main/include/log4cxx/helpers/filesystempath.h
@@ -15,19 +15,19 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/spi/configurator.h>
-#include <assert.h>
-#include <log4cxx/logger.h>
+#ifndef LOG4CXX_FILE_SYSTEM_PATH_HDR_
+#define LOG4CXX_FILE_SYSTEM_PATH_HDR_
 
-using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::spi;
+#ifdef __has_include                           // Check if __has_include is 
present
+#  if __has_include(<filesystem>)              // Check for a standard version
+#    include <filesystem>
+#    if defined(__cpp_lib_filesystem)          // C++ >= 17
+namespace LOG4CXX_NS { using Path = std::filesystem::path; }
+#define LOG4CXX_HAS_FILESYSTEM_PATH 1
+#    endif
+#  else                                        // Not found at all
+#define LOG4CXX_HAS_FILESYSTEM_PATH 0
+#  endif
+#endif // __has_include
 
-IMPLEMENT_LOG4CXX_OBJECT(Configurator)
-
-
-
-
-Configurator::Configurator()
-{
-}
+#endif /* LOG4CXX_FILE_SYSTEM_PATH_HDR_ */
diff --git a/src/main/include/log4cxx/helpers/properties.h 
b/src/main/include/log4cxx/helpers/properties.h
index 7992e841..1481d2cc 100644
--- a/src/main/include/log4cxx/helpers/properties.h
+++ b/src/main/include/log4cxx/helpers/properties.h
@@ -34,18 +34,26 @@ class LOG4CXX_EXPORT Properties
        private:
                typedef std::map<LogString, LogString> PropertyMap;
                PropertyMap* properties;
-               Properties(const Properties&);
-               Properties& operator=(const Properties&);
 
-       public:
+       public: // ...structors
                /**
                 *  Create new instance.
                 */
                Properties();
+               Properties(const Properties&);
+               Properties(const Properties&&) = delete;
+               Properties& operator=(const Properties&);
+               Properties& operator=(const Properties&&) = delete;
                /**
                 * Destructor.
                 */
                ~Properties();
+
+       public: // Methods
+               /** Does this contain any key-value pairs?
+                */
+               bool isEmpty() const;
+
                /**
                Reads a property list (key and element pairs) from the input 
stream.
                The stream is assumed to be using the ISO 8859-1 character 
encoding.
diff --git a/src/main/include/log4cxx/helpers/system.h 
b/src/main/include/log4cxx/helpers/system.h
index 37381b7b..5c0ba3c9 100644
--- a/src/main/include/log4cxx/helpers/system.h
+++ b/src/main/include/log4cxx/helpers/system.h
@@ -34,18 +34,33 @@ class LOG4CXX_EXPORT System
 {
        public:
 
+
                /**
-               Gets the system property indicated by the specified key.
+               Add to \c props the currently executing program file path
+               and the 
[std::filesystem::path](https://en.cppreference.com/w/cpp/filesystem/path.html)
+               decomposition of the currently executing program file path, 
using the variable names:
+               - PROGRAM_FILE_PATH
+               - PROGRAM_FILE_PATH.ROOT_NAME
+               - PROGRAM_FILE_PATH.ROOT_DIRECTORY
+               - PROGRAM_FILE_PATH.ROOT_PATH
+               - PROGRAM_FILE_PATH.RELATIVE_PATH
+               - PROGRAM_FILE_PATH.PARENT_PATH
+               - PROGRAM_FILE_PATH.FILENAME
+               - PROGRAM_FILE_PATH.STEM
+               - PROGRAM_FILE_PATH.EXTENSION
+               */
+               static void addProgramFilePathComponents(Properties& props);
+
+               /**
+               The value of the system property associated with \c key.
 
                @param key the name of the system property.
 
-               @return the string value of the system property, or the default 
value if
-               there is no property with that key.
+               @return the string value of the system property.
 
                @throws IllegalArgumentException if key is empty.
                */
                static LogString getProperty(const LogString& key);
-
 };
 } // namespace helpers
 } //  namespace log4cxx
diff --git a/src/main/include/log4cxx/spi/configurator.h 
b/src/main/include/log4cxx/spi/configurator.h
index d4dae180..a0199eb4 100644
--- a/src/main/include/log4cxx/spi/configurator.h
+++ b/src/main/include/log4cxx/spi/configurator.h
@@ -19,6 +19,7 @@
 #define _LOG4CXX_SPI_CONFIGURATOR_H
 
 #include <log4cxx/spi/loggerrepository.h>
+#include <log4cxx/helpers/properties.h>
 
 namespace LOG4CXX_NS
 {
@@ -63,6 +64,24 @@ class LOG4CXX_EXPORT Configurator : virtual public 
helpers::Object
 #endif
                        ) = 0;
 
+               /**
+               The key value pairs used when expanding ${varname} instances in 
a configuration file.
+
+               By default, the map holds the currently executing program file 
path
+               and the 
[std::filesystem::path](https://en.cppreference.com/w/cpp/filesystem/path.html)
+               decomposition of the currently executing program file path, 
using the variable names:
+               - ${PROGRAM_FILE_PATH}
+               - ${PROGRAM_FILE_PATH.ROOT_NAME}
+               - ${PROGRAM_FILE_PATH.ROOT_DIRECTORY}
+               - ${PROGRAM_FILE_PATH.ROOT_PATH}
+               - ${PROGRAM_FILE_PATH.RELATIVE_PATH}
+               - ${PROGRAM_FILE_PATH.PARENT_PATH}
+               - ${PROGRAM_FILE_PATH.FILENAME}
+               - ${PROGRAM_FILE_PATH.STEM}
+               - ${PROGRAM_FILE_PATH.EXTENSION}
+               */
+               static helpers::Properties& configurationProperties();
+
        protected:
                Configurator();
 
diff --git a/src/site/markdown/example-programs.md 
b/src/site/markdown/example-programs.md
index 72992d80..5ad3d527 100644
--- a/src/site/markdown/example-programs.md
+++ b/src/site/markdown/example-programs.md
@@ -115,7 +115,7 @@ This version of *config.cpp* instructs 
[PropertyConfigurator](@ref log4cxx.Prope
 to use the *MyApp.properties* file to configure Log4cxx.
 A more realistic approach would (for example)
 use the current module name to select the configuration file
-(see the \ref com/foo/config3.cpp file for how to do this).
+(see the \ref com/foo/config4.cpp file for how to do this).
 
 Here is a sample *MyApp.properties* configuration file that results in exactly 
same output
 as the previous [BasicConfigurator::configure](@ref 
log4cxx.BasicConfigurator.configure) based example.
@@ -196,3 +196,6 @@ This file is a simplified example of encapsulated Log4cxx 
configuration.
 
 \example com/foo/config3.cpp
 This file is an example of how to use the current module name to select the 
Log4cxx configuration file.
+
+\example com/foo/config4.cpp
+This file is a simpler example of how to use the current module name to select 
the Log4cxx configuration file.
\ No newline at end of file

Reply via email to