Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- `fwrite`: return also bytes count as second output argument.

- `loadenv`: Load environment variables defined in .env or regular text files.

- `help`: Help for functions in Command Window.

- macOS Tahoe 26: CI setup and platform support.

- Python 3.14 support.

- Fedora 43 support.

- Python 3.14 support.

### Changed

- Help framework reworked for improved performance and usability:
Expand Down
8 changes: 8 additions & 0 deletions THIRDPARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,11 @@ Beautiful and accessible math in all browsers
> <https://github.com/mathjax/MathJax>

> <https://github.com/mathjax/MathJax/blob/master/LICENSE> (Apache 2.0)

- cppenv

cppenv is a header-only C++ library that makes it easy to manage .env files in your projects.

> <https://github.com/Tina-1300/cppenv>

> <https://raw.githubusercontent.com/Tina-1300/cppenv/refs/heads/main/LICENSE> (MIT)
4 changes: 4 additions & 0 deletions modules/os_functions/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ target_include_directories(
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../interpreter/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../types/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../error_manager/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../characters_encoding/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../stream_manager/src/include
${CMAKE_CURRENT_SOURCE_DIR}/../i18n/src/include)
# ==============================================================================
Expand All @@ -66,6 +68,7 @@ target_link_libraries(
nlsError_manager
nlsI18n
nlsInterpreter
nlsCharacters_encoding
nlsTypes)
# ==============================================================================
include(GNUInstallDirs)
Expand Down Expand Up @@ -100,6 +103,7 @@ install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/modules/${module_name}"
FILES_MATCHING
PATTERN "*.env"
PATTERN "*.m")
install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/help
Expand Down
13 changes: 9 additions & 4 deletions modules/os_functions/builtin/c/nlsOs_functions_builtin.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NLSOS_FUNCTIONS_BUILTIN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/characters_encoding/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<OpenMPSupport>true</OpenMPSupport>
<DisableSpecificWarnings>4190</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand All @@ -116,7 +116,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NLSOS_FUNCTIONS_BUILTIN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/characters_encoding/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<OpenMPSupport>true</OpenMPSupport>
<DisableSpecificWarnings>4190</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand All @@ -139,7 +139,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NLSOS_FUNCTIONS_BUILTIN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/characters_encoding/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<OpenMPSupport>true</OpenMPSupport>
<DisableSpecificWarnings>4190</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand All @@ -164,7 +164,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NLSOS_FUNCTIONS_BUILTIN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)modules/os_functions/builtin/include;$(SolutionDir)modules/os_functions/src/include;$(SolutionDir)modules/commons/src/include;$(SolutionDir)modules/interpreter/src/include;$(SolutionDir)modules/characters_encoding/src/include;$(SolutionDir)modules/types/src/include;$(SolutionDir)modules/stream_manager/src/include;$(SolutionDir)modules/overload/src/include;$(SolutionDir)modules/error_manager/src/include;$(SolutionDir)modules/i18n/src/include;$(SolutionDir)../NelSon-thirdparty-$(PlatformName)/Boost</AdditionalIncludeDirectories>
<OpenMPSupport>true</OpenMPSupport>
<DisableSpecificWarnings>4190</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand All @@ -189,6 +189,7 @@
<ClCompile Include="..\cpp\ispcBuiltin.cpp" />
<ClCompile Include="..\cpp\isunixBuiltin.cpp" />
<ClCompile Include="..\cpp\Gateway.cpp" />
<ClCompile Include="..\cpp\loadenvBuiltin.cpp" />
<ClCompile Include="..\cpp\searchenvBuiltin.cpp" />
<ClCompile Include="..\cpp\setenvBuiltin.cpp" />
<ClCompile Include="..\cpp\systemBuiltin.cpp" />
Expand All @@ -198,6 +199,9 @@
<ClCompile Include="..\cpp\dllMain.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\characters_encoding\src\c\nlsCharacters_encoding.vcxproj">
<Project>{a75dc538-eefa-4873-888b-a034a7ee72c5}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\error_manager\src\c\nlsError_manager.vcxproj">
<Project>{d0ef0ceb-a3bd-44ab-91b3-f33db1870f8d}</Project>
</ProjectReference>
Expand Down Expand Up @@ -225,6 +229,7 @@
<ClInclude Include="..\include\ismacBuiltin.hpp" />
<ClInclude Include="..\include\ispcBuiltin.hpp" />
<ClInclude Include="..\include\isunixBuiltin.hpp" />
<ClInclude Include="..\include\loadenvBuiltin.hpp" />
<ClInclude Include="..\include\nlsOs_functions_builtin_exports.h" />
<ClInclude Include="..\include\searchenvBuiltin.hpp" />
<ClInclude Include="..\include\setenvBuiltin.hpp" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<ClCompile Include="..\cpp\dllMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\cpp\loadenvBuiltin.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\..\CMakeLists.txt" />
Expand Down Expand Up @@ -107,6 +110,9 @@
<ClInclude Include="..\include\usernameBuiltin.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\loadenvBuiltin.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\loader.m" />
Expand Down
3 changes: 3 additions & 0 deletions modules/os_functions/builtin/cpp/Gateway.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "isunixBuiltin.hpp"
#include "searchenvBuiltin.hpp"
#include "setenvBuiltin.hpp"
#include "loadenvBuiltin.hpp"
#include "systemBuiltin.hpp"
#include "winopenBuiltin.hpp"
#include "winqueryregBuiltin.hpp"
Expand All @@ -33,6 +34,8 @@ static const nlsGateway gateway[] = {
CPP_BUILTIN_WITH_EVALUATOR },
{ "unix", (ptrBuiltin)Nelson::OsFunctionsGateway::systemBuiltin, 2, 1,
CPP_BUILTIN_WITH_EVALUATOR },
{ "loadenv", (ptrBuiltin)Nelson::OsFunctionsGateway::loadenvBuiltin, -1, 1,
CPP_BUILTIN_WITH_EVALUATOR },
{ "getenv", (ptrBuiltin)Nelson::OsFunctionsGateway::getenvBuiltin, 1, 1 },
{ "setenv", (ptrBuiltin)Nelson::OsFunctionsGateway::setenvBuiltin, 1, 2 },
{ "searchenv", (ptrBuiltin)Nelson::OsFunctionsGateway::searchenvBuiltin, 1, 2 },
Expand Down
76 changes: 76 additions & 0 deletions modules/os_functions/builtin/cpp/loadenvBuiltin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//=============================================================================
// Copyright (c) 2016-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
#include "loadenvBuiltin.hpp"
#include "InputOutputArgumentsCheckers.hpp"
#include "LoadEnvironment.hpp"
#include "Error.hpp"
#include "i18n.hpp"
#include "characters_encoding.hpp"
#include <algorithm>
#include <vector>
//=============================================================================
using namespace Nelson;
//=============================================================================
ArrayOfVector
Nelson::OsFunctionsGateway::loadenvBuiltin(Evaluator* eval, int nLhs, const ArrayOfVector& argIn)
{
ArrayOfVector retval;
nargincheck(argIn, 1, 1);
nargoutcheck(nLhs, 0, 1);

std::wstring filename = argIn[0].getContentAsWideString();
bool applyToEnv = (nLhs == 0);

std::wstring errorMessage;
std::vector<std::pair<std::wstring, std::wstring>> result;
try {
result = LoadEnvironment(filename, applyToEnv, errorMessage);
} catch (const std::exception& ex) {
errorMessage = utf8_to_wstring(std::string("Exception: ")) + utf8_to_wstring(ex.what());
} catch (...) {
if (errorMessage.empty())
errorMessage = _W("Unknown error while loading environment");
}

if (!errorMessage.empty()) {
Error(errorMessage);
return retval;
}

if (nLhs == 1) {
wstringVector keys;
wstringVector values;
keys.reserve(result.size());
values.reserve(result.size());
for (const auto& kv : result) {
keys.push_back(kv.first);
values.push_back(kv.second);
}

FunctionDef* funcDef = nullptr;
if (!eval) {
Error(_W("Evaluator is not initialized."));

Copilot AI Nov 1, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After checking if eval is null and calling Error(), execution continues to line 61 where eval->getContext() is called. This can cause a null pointer dereference if eval is null. Add an early return after the Error() call on line 59.

Suggested change
Error(_W("Evaluator is not initialized."));
Error(_W("Evaluator is not initialized."));
return retval;

Copilot uses AI. Check for mistakes.
}
Context* context = eval->getContext();
if (!context) {
Error(_W("Context is not initialized."));

Copilot AI Nov 1, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After checking if context is null and calling Error(), execution continues to line 65 where context->lookupFunction() is called. This can cause a null pointer dereference if context is null. Add an early return after the Error() call on line 63.

Suggested change
Error(_W("Context is not initialized."));
Error(_W("Context is not initialized."));
return retval;

Copilot uses AI. Check for mistakes.
}
if (context->lookupFunction("dictionary", funcDef)) {
ArrayOfVector args;
args << ArrayOf::stringArrayConstructor(keys, Dimensions(keys.size(), 1));
args << ArrayOf::stringArrayConstructor(values, Dimensions(values.size(), 1));
return funcDef->evaluateFunction(eval, args, 1);
} else {
Error(_W("Function 'dictionary' not found in the current context."));
}
}
return retval;
}
//=============================================================================
21 changes: 21 additions & 0 deletions modules/os_functions/builtin/include/loadenvBuiltin.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//=============================================================================
// Copyright (c) 2016-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
#pragma once
//=============================================================================
#include "ArrayOf.hpp"
#include "Evaluator.hpp"
//=============================================================================
namespace Nelson::OsFunctionsGateway {
//=============================================================================
ArrayOfVector
loadenvBuiltin(Evaluator* eval, int nLhs, const ArrayOfVector& argIn);
//=============================================================================
} // namespace Nelson
//=============================================================================
64 changes: 64 additions & 0 deletions modules/os_functions/help/en_US/xml/loadenv.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xmldoc>
<copyright>SAME AS NELSON SOFTWARE</copyright>
<language>en_US</language>
<keyword>loadenv</keyword>
<short_description
>Load environment variables defined in .env or regular text files.</short_description>
<syntax>
<syntax_item>loadenv(filename)</syntax_item>
<syntax_item>D = loadenv(filename)</syntax_item>
</syntax>
<param_input>
<param_input_item>
<param_name>filename</param_name>
<param_description
>string scalar, character vector: environment filename.</param_description>
</param_input_item>
</param_input>
<param_output>
<param_output_item>
<param_name>s</param_name>
<param_description
>dictionary: the environment variables and their values.</param_description>
</param_output_item>
</param_output>
<description>
<p><b
>loadenv(filename)</b> loads environment variables from a .env or plain text file by parsing one key-value pair per line and sets them as environment variables in the Nelson environment.</p>
<p><b
>D = loadenv(filename)</b> returns a dictionary containing the parsed key-value pairs. When an output argument is specified, loadenv does not modify the Nelson environment.</p>
</description>
<used_function />
<bibliography />
<examples>
<example_item>
<example_item_type>nelson</example_item_type>
<example_item_description />
<example_item_data
runnable="cli"
><![CDATA[env_file = [modulepath('os_functions', 'tests'), '/sample.env'];
D = loadenv(env_file)
getenv('Key1')
loadenv(env_file)
getenv('Key1')]]></example_item_data>
</example_item>
</examples>
<see_also>
<see_also_item>
<link linkend="${os_functions}setenv">setenv</link>
</see_also_item>
<see_also_item>
<link linkend="${os_functions}getenv">getenv</link>
</see_also_item>
</see_also>
<history>
<history_item>
<history_version>1.15.0</history_version>
<history_description>initial version</history_description>
</history_item>
</history>
<authors>
<author_item>Allan CORNET</author_item>
</authors>
</xmldoc>
64 changes: 64 additions & 0 deletions modules/os_functions/help/fr_FR/xml/loadenv.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xmldoc>
<copyright>SAME AS NELSON SOFTWARE</copyright>
<language>fr_FR</language>
<keyword>loadenv</keyword>
<short_description
>Charger les variables d'environnement définies dans des fichiers .env ou des fichiers texte ordinaires.</short_description>
<syntax>
<syntax_item>loadenv(filename)</syntax_item>
<syntax_item>D = loadenv(filename)</syntax_item>
</syntax>
<param_input>
<param_input_item>
<param_name>filename</param_name>
<param_description
>chaine de caractères: nom du fichier d'environnement.</param_description>
</param_input_item>
</param_input>
<param_output>
<param_output_item>
<param_name>s</param_name>
<param_description
>dictionnaire: les variables d'environnement et leurs valeurs.</param_description>
</param_output_item>
</param_output>
<description>
<p><b
>loadenv(filename)</b> charge les variables d'environnement à partir d'un fichier .env ou texte brut en analysant une paire clé-valeur par ligne et les définit comme variables d'environnement dans l'environnement Nelson.</p>
<p><b
>D = loadenv(filename)</b> renvoie un dictionnaire contenant les paires clé-valeur analysées. Lorsqu'un argument de sortie est spécifié, loadenv ne modifie pas l'environnement Nelson.</p>
</description>
<used_function />
<bibliography />
<examples>
<example_item>
<example_item_type>nelson</example_item_type>
<example_item_description />
<example_item_data
runnable="cli"
><![CDATA[env_file = [modulepath('os_functions', 'tests'), '/sample.env'];
D = loadenv(env_file)
getenv('Key1')
loadenv(env_file)
getenv('Key1')]]></example_item_data>
</example_item>
</examples>
<see_also>
<see_also_item>
<link linkend="${os_functions}setenv">setenv</link>
</see_also_item>
<see_also_item>
<link linkend="${os_functions}getenv">getenv</link>
</see_also_item>
</see_also>
<history>
<history_item>
<history_version>1.15.0</history_version>
<history_description>initial version</history_description>
</history_item>
</history>
<authors>
<author_item>Allan CORNET</author_item>
</authors>
</xmldoc>
Loading