do not use std::getenv on Windows

This commit is contained in:
Laurent Rineau 2025-10-29 16:15:57 +01:00
parent eaea32ee9f
commit 7c4a3c1dad
3 changed files with 76 additions and 163 deletions

View File

@ -554,6 +554,11 @@ namespace cpp11{
/// @}
#include <CGAL/license/lgpl.h>
#ifdef __STDC_LIB_EXT1__
# define __STDC_WANT_LIB_EXT1__ 1
# include <stdlib.h> // for getenv_s
#endif
//----------------------------------------------------------------------//
// Function to define data directory
//----------------------------------------------------------------------//

View File

@ -243,7 +243,7 @@ void test_color_detection() {
// Test stdout color support function
std::cout << "2. Testing stdout_supports_color() function:\n";
if(CGAL::IO::stdout_supports_color()) {
if(CGAL::IO::stream_supports_color(std::cout)) {
std::cout << " stdout supports colors: YES\n";
{
CGAL::IO::Color_stream_guard guard(std::cout, Ansi_color::Green);
@ -258,7 +258,7 @@ void test_color_detection() {
// Test stderr color support
std::cout << "3. Testing stderr_supports_color():\n";
if(CGAL::IO::stderr_supports_color()) {
if(CGAL::IO::stream_supports_color(std::cerr)) {
std::cout << " stderr supports colors: YES\n";
{
CGAL::IO::Color_stream_guard guard(std::cerr, Ansi_color::Yellow);
@ -325,22 +325,6 @@ void test_color_detection() {
std::cerr << " Critical error occurred\n";
std::cout << "\n";
// Environment hints
std::cout << "7. Environment information:\n";
const char* term = std::getenv("TERM");
const char* no_color = std::getenv("NO_COLOR");
std::cout << " TERM variable: " << (term ? term : "(not set)") << "\n";
std::cout << " NO_COLOR variable: " << (no_color ? "SET (colors disabled)" : "(not set)") << "\n";
#ifdef _WIN32
std::cout << " Platform: Windows\n";
const char* ansicon = std::getenv("ANSICON");
std::cout << " ANSICON variable: " << (ansicon ? ansicon : "(not set)") << "\n";
#else
std::cout << " Platform: POSIX (Linux/macOS/Unix)\n";
#endif
std::cout << "\n";
std::cout << " Tip: To disable colors, set the NO_COLOR environment variable:\n";
std::cout << " export NO_COLOR=1\n";

View File

@ -19,7 +19,9 @@
#include <CGAL/config.h>
#include <cstdlib> // for std::getenv
#include <ios>
#include <optional>
#include <streambuf>
#include <string>
#include <tuple>
@ -27,16 +29,19 @@
#include <vector>
#ifdef _WIN32
#include <io.h>
#define CGAL_ISATTY _isatty
#define CGAL_FILENO _fileno
# include <io.h>
# define CGAL_ISATTY _isatty
# define CGAL_FILENO _fileno
#else
#include <unistd.h>
#define CGAL_ISATTY isatty
#define CGAL_FILENO fileno
# include <unistd.h>
# define CGAL_ISATTY isatty
# define CGAL_FILENO fileno
#endif
#include <cstdlib> // for getenv
#if defined(__STDC_LIB_EXT1__) && defined(_WIN32)
# include <stdlib.h> // for getenv_s
# define CGAL_USE_GETENV_S 1
#endif
namespace CGAL {
namespace IO {
@ -124,52 +129,23 @@ private:
bool at_line_start_;
bool colors_enabled_;
/** \brief Detect if the wrapped buffer supports color output. */
static bool detect_color_support(streambuf_type* buf) {
// Check NO_COLOR environment variable first
if(std::getenv("NO_COLOR")) {
return false;
}
// Try to determine if this is stdout or stderr
// by comparing pointers (works for standard streams)
if(buf == std::cout.rdbuf() || buf == std::clog.rdbuf()) {
int fd = CGAL_FILENO(stdout);
if(!CGAL_ISATTY(fd)) {
return false;
}
} else if(buf == std::cerr.rdbuf()) {
int fd = CGAL_FILENO(stderr);
if(!CGAL_ISATTY(fd)) {
return false;
}
static std::optional<std::string> safe_getenv(const char* name) {
#if CGAL_USE_GETENV_S
static constexpr std::size_t buffer_size = 256;
std::array<char, buffer_size> buffer;
std::size_t size = 0;
if(getenv_s(&size, buffer.data(), buffer.size(), name) == 0 && size > 0) {
return std::string(buffer.data());
} else {
// Unknown buffer, conservatively disable colors
return false;
return std::nullopt;
}
#ifdef _WIN32
// Windows: check for ANSICON or assume modern Windows
return std::getenv("ANSICON") || true;
#else
// POSIX: check TERM environment variable
const char* term = std::getenv("TERM");
if(!term) {
return false;
const char* value = std::getenv(name);
if(value) {
return std::string(value);
} else {
return {};
}
std::string term_str(term);
if(term_str == "dumb") {
return false;
}
// Check for common color-capable terminals
return term_str.find("color") != std::string::npos ||
term_str.find("xterm") != std::string::npos ||
term_str.find("rxvt") != std::string::npos ||
term_str.find("screen") != std::string::npos ||
term_str.find("tmux") != std::string::npos ||
term_str.find("linux") != std::string::npos;
#endif
}
@ -307,6 +283,49 @@ public:
/** \brief Get the wrapped streambuf. */
streambuf_type& wrapped_streambuf() const { return *wrapped_buf_; }
/** \brief Detect if the wrapped buffer supports color output. */
static bool detect_color_support(streambuf_type* buf) {
if(safe_getenv("NO_COLOR").value_or("").size() > 0) {
return false;
}
if(safe_getenv("CLICOLOR_FORCE").value_or("").size() > 0) {
return true;
}
// Try to determine if this is stdout or stderr
// by comparing pointers (works for standard streams)
if(buf == std::cout.rdbuf() || buf == std::clog.rdbuf()) {
int fd = CGAL_FILENO(stdout);
if(!CGAL_ISATTY(fd)) {
return false;
}
} else if(buf == std::cerr.rdbuf()) {
int fd = CGAL_FILENO(stderr);
if(!CGAL_ISATTY(fd)) {
return false;
}
} else {
// Unknown buffer, conservatively disable colors
return false;
}
#ifdef _WIN32
// Windows: assume modern Windows
return true;
#endif
// POSIX: check TERM environment variable
const std::string term_str = safe_getenv("TERM").value_or("");
// Check for common color-capable terminals
return term_str.find("color") != std::string::npos ||
term_str.find("xterm") != std::string::npos ||
term_str.find("rxvt") != std::string::npos ||
term_str.find("screen") != std::string::npos ||
term_str.find("tmux") != std::string::npos ||
term_str.find("linux") != std::string::npos;
}
protected:
using traits_type::eof;
using traits_type::not_eof;
@ -589,102 +608,7 @@ auto make_color_guards(const std::vector<Ansi_color>& colors, Streams&... stream
*/
template <typename CharT, typename Traits>
bool stream_supports_color(const std::basic_ostream<CharT, Traits>& stream) {
// Check if NO_COLOR environment variable is set (standard way to disable colors)
if(std::getenv("NO_COLOR")) {
return false;
}
// Try to get the file descriptor for the stream
// This works for std::cout (stdout) and std::cerr (stderr)
const auto* fbuf = dynamic_cast<const std::basic_filebuf<CharT, Traits>*>(stream.rdbuf());
if(!fbuf) {
// If it's not a file buffer, check if it's stdout or stderr directly
const std::basic_ostream<CharT, Traits>* std_stream = &stream;
int fd = -1;
if(std_stream == &std::cout || std_stream == &std::clog) {
fd = CGAL_FILENO(stdout);
} else if(std_stream == &std::cerr) {
fd = CGAL_FILENO(stderr);
}
if(fd == -1) {
return false; // Unknown stream type
}
// Check if the file descriptor is a TTY
if(!CGAL_ISATTY(fd)) {
return false;
}
} else {
// For file buffers, we can't easily get the fd in a portable way
// Conservatively assume no color support for files
return false;
}
#ifdef _WIN32
// On Windows, check for ANSICON or Windows 10+ with VT100 support
// Modern Windows Terminal and ConEmu support ANSI colors
if(std::getenv("ANSICON")) {
return true;
}
// Windows 10+ console supports ANSI by default
// We could check Windows version, but being conservative here
return true; // Modern Windows usually supports colors
#else
// On POSIX systems, check the TERM environment variable
const char* term = std::getenv("TERM");
if(!term) {
return false; // No TERM variable, probably not a color terminal
}
std::string term_str(term);
// Check for common indicators of color support
if(term_str.find("color") != std::string::npos ||
term_str.find("xterm") != std::string::npos ||
term_str.find("rxvt") != std::string::npos ||
term_str.find("screen") != std::string::npos ||
term_str.find("tmux") != std::string::npos ||
term_str.find("linux") != std::string::npos) {
return true;
}
// Check for dumb terminal
if(term_str == "dumb") {
return false;
}
// For other TERM values, conservatively assume color support
// Most modern terminals support colors
return true;
#endif
}
/**
* \ingroup PkgStreamSupportRef
*
* \brief Check if stdout supports color output.
*
* Convenience function equivalent to `stream_supports_color(std::cout)`.
*
* \return `true` if stdout supports color output, `false` otherwise
*/
inline bool stdout_supports_color() {
return stream_supports_color(std::cout);
}
/**
* \ingroup PkgStreamSupportRef
*
* \brief Check if stderr supports color output.
*
* Convenience function equivalent to `stream_supports_color(std::cerr)`.
*
* \return `true` if stderr supports color output, `false` otherwise
*/
inline bool stderr_supports_color() {
return stream_supports_color(std::cerr);
return Basic_color_streambuf<CharT, Traits>::detect_color_support(stream.rdbuf());
}
} // namespace IO