@@ -80,8 +80,43 @@ namespace vix::utils
8080 }
8181 return true ;
8282 }
83+
84+ inline std::string_view trim (std::string_view s) noexcept
85+ {
86+ std::size_t b = 0 ;
87+ std::size_t e = s.size ();
88+
89+ while (b < e && std::isspace (static_cast <unsigned char >(s[b])))
90+ ++b;
91+ while (e > b && std::isspace (static_cast <unsigned char >(s[e - 1 ])))
92+ --e;
93+
94+ return std::string_view{s.data () + b, e - b};
95+ }
8396 } // namespace detail
8497
98+ static inline const char *vix_getenv (const char *name) noexcept
99+ {
100+ #if defined(_WIN32)
101+ // _dupenv_s returns heap memory we must free.
102+ static thread_local std::string value;
103+ value.clear ();
104+
105+ char *buf = nullptr ;
106+ size_t len = 0 ;
107+
108+ if (_dupenv_s (&buf, &len, name) != 0 || !buf)
109+ return nullptr ;
110+
111+ value.assign (buf);
112+ free (buf);
113+
114+ return value.empty () ? nullptr : value.c_str ();
115+ #else
116+ return std::getenv (name);
117+ #endif
118+ }
119+
85120 /* *
86121 * @brief Returns the value of an environment variable, or a default if not found.
87122 *
@@ -95,11 +130,11 @@ namespace vix::utils
95130 */
96131 inline std::string env_or (std::string_view key, std::string_view def = " " )
97132 {
98- if (const char *v = std::getenv (std::string (key).c_str ()))
133+ const std::string k (key); // stable null-terminated key
134+ if (const char *v = vix_getenv (k.c_str ()))
99135 return std::string (v);
100136 return std::string (def);
101137 }
102-
103138 /* *
104139 * @brief Reads an environment variable and interprets it as a boolean.
105140 *
@@ -120,21 +155,13 @@ namespace vix::utils
120155 {
121156 using namespace std ::literals;
122157 const auto s = env_or (key, def ? " 1" sv : " 0" sv);
123-
124- // Trim leading/trailing spaces
125- std::size_t b = 0 , e = s.size ();
126- while (b < e && std::isspace (static_cast <unsigned char >(s[b])))
127- ++b;
128- while (e > b && std::isspace (static_cast <unsigned char >(s[e - 1 ])))
129- --e;
130- const std::string_view v{s.data () + b, e - b};
158+ const std::string_view v = detail::trim (s);
131159
132160 return v == " 1" sv ||
133161 detail::iequals (v, " true" ) ||
134162 detail::iequals (v, " yes" ) ||
135163 detail::iequals (v, " on" );
136164 }
137-
138165 /* *
139166 * @brief Reads an environment variable as a signed integer (base 10).
140167 *
@@ -153,21 +180,13 @@ namespace vix::utils
153180 inline int env_int (std::string_view key, int def = 0 )
154181 {
155182 const auto s = env_or (key);
156- if (s.empty ())
157- return def;
158-
159- const char *first = s.data ();
160- const char *last = s.data () + s.size ();
161- while (first < last && std::isspace (static_cast <unsigned char >(*first)))
162- ++first;
163- while (last > first && std::isspace (static_cast <unsigned char >(*(last - 1 ))))
164- --last;
165- if (first >= last)
183+ const std::string_view v = detail::trim (s);
184+ if (v.empty ())
166185 return def;
167186
168187 int value = def;
169- const auto [ptr, ec] = std::from_chars (first, last , value, 10 );
170- if (ec != std::errc{} || ptr != last )
188+ const auto [ptr, ec] = std::from_chars (v. data (), v. data () + v. size () , value, 10 );
189+ if (ec != std::errc{} || ptr != v. data () + v. size () )
171190 return def;
172191 return value;
173192 }
@@ -189,21 +208,13 @@ namespace vix::utils
189208 inline unsigned env_uint (std::string_view key, unsigned def = 0u )
190209 {
191210 const auto s = env_or (key);
192- if (s.empty ())
193- return def;
194-
195- const char *first = s.data ();
196- const char *last = s.data () + s.size ();
197- while (first < last && std::isspace (static_cast <unsigned char >(*first)))
198- ++first;
199- while (last > first && std::isspace (static_cast <unsigned char >(*(last - 1 ))))
200- --last;
201- if (first >= last)
211+ const std::string_view v = detail::trim (s);
212+ if (v.empty ())
202213 return def;
203214
204215 unsigned value = def;
205- const auto [ptr, ec] = std::from_chars (first, last , value, 10 );
206- if (ec != std::errc{} || ptr != last )
216+ const auto [ptr, ec] = std::from_chars (v. data (), v. data () + v. size () , value, 10 );
217+ if (ec != std::errc{} || ptr != v. data () + v. size () )
207218 return def;
208219 return value;
209220 }
@@ -225,14 +236,18 @@ namespace vix::utils
225236 inline double env_double (std::string_view key, double def = 0.0 )
226237 {
227238 const auto s = env_or (key);
228- if (s.empty ())
239+ const std::string_view v = detail::trim (s);
240+ if (v.empty ())
229241 return def;
230242
243+ const std::string tmp (v); // ensure null-terminated for strtod
231244 char *endp = nullptr ;
232- const double v = std::strtod (s.c_str (), &endp);
245+ const double out = std::strtod (tmp.c_str (), &endp);
246+
233247 if (!endp || *endp != ' \0 ' )
234248 return def;
235- return v;
249+
250+ return out;
236251 }
237252
238253} // namespace Vix::utils
0 commit comments