ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
path.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14// ====================================== Global Fragment ======================================
18#if !DOXYGEN
19# if defined ( _WIN32 )
20# include <direct.h>
21# elif defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || defined(__APPLE__) || defined(__ANDROID_NDK__)
22# include <unistd.h>
23# include <dirent.h>
24# include <sys/stat.h>
25# include <pwd.h>
26# else
27# pragma message ("Unknown Platform in file: " __FILE__ )
28# endif
29# include <fstream>
30#endif // !DOXYGEN
31// =========================================== Module ==========================================
32#if ALIB_C20_MODULES
33 module ALib.System;
34# if ALIB_STRINGS
35 import ALib.Strings;
36# endif
38# if ALIB_BOXING
39 import ALib.Boxing;
40# endif
41#else
42# include "ALib.Strings.H"
44# include "ALib.Boxing.H"
45# include "ALib.System.H"
46#endif
47// ====================================== Implementation =======================================
50
51/// This is the reference documentation of module \alib_system, which exposes it's entities
52/// in this namespace.
53namespace alib::system {
54
55#if !DOXYGEN
56//--------------- two versions to load environment variables into a Path instance -------------
57namespace {
58
60template<typename TRequires= PathCharType>
61requires std::same_as<TRequires, character>
62bool loadEnvVar( const CString& name, AString& target, lang::CurrentData targetData =lang::CurrentData::Clear ) {
63 return EnvironmentVariables::Get( name, target, targetData );
64}
65
66template<typename TRequires= PathCharType>
67requires (!std::same_as<TRequires, character>)
68bool loadEnvVar( const CString& name, Path& target, lang::CurrentData targetData =lang::CurrentData::Clear ) {
69 String256 buf;
70 auto result= EnvironmentVariables::Get( name, buf, targetData );
71 target.Reset( buf );
72 return result;
73}
75
76} // anonymous namespace
77
78#endif // !DOXYGEN
79
80// #################################################################################################
81// Static variables
82// #################################################################################################
85
86// #################################################################################################
87// Change
88// #################################################################################################
89//! @cond NO_DOX
90namespace {
91void createTempFolderInHomeDir( const PathString& folderName, Path& resultPath,
92 const NString& reasonMsg )
93{
94 // get home directory and set this as fallback result value
95 Path homeTemp( SystemFolders::Home );
96 resultPath.Reset( homeTemp );
97
98 // add given folder name and check if already exists
99 homeTemp._( DIRECTORY_SEPARATOR )._( folderName );
100 bool exists= homeTemp.IsDirectory();
101 if( !exists )
102 {
103 if( homeTemp.Create() == SystemErrors::OK )
104 {
105 exists= true;
106 NAString fileName( homeTemp ); fileName._( DIRECTORY_SEPARATOR )._( "readme.txt" );
107
108 std::ofstream file ( fileName );
109 if ( file.is_open() )
110 {
112 file << "This folder was created by \"" << pi.CmdLine
113 << "\"" << std::endl
114 << "to be used for temporary files." << std::endl;
115 file.write( reasonMsg.Buffer(), reasonMsg.Length() );
116 file << std::endl;
117 file.close();
118 }
119 }
120 }
121
122 // if existed or got created
123 if( exists )
124 resultPath.Reset( homeTemp );
125}
126} // anonymous namespace
127//! @endcond
128
130{
131 switch( special )
132 {
133 case SystemFolders::Root: _( DIRECTORY_SEPARATOR );
134 return true;
135
136 case SystemFolders::Current:
137 {
138 Reset();
139 nchar charBuf[FILENAME_MAX];
140
141 #if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || defined(__APPLE__) || defined(__ANDROID_NDK__)
142 if ( ! getcwd( charBuf, sizeof(charBuf ) ) )
143 return false;
144 #elif defined ( _WIN32 )
145 if ( !_getcwd( charBuf, sizeof(charBuf ) ) )
146 return false;
147 #else
148 #pragma message ("Unknown Platform in file: " __FILE__ )
149 return false;
150 #endif
151
152 this ->_( static_cast<const nchar*>( charBuf ) );
153 return true;
154 }
155
156
157 case SystemFolders::Home:
158 {
159 #if defined (__unix__)
160 if ( !loadEnvVar( A_CHAR("HOME"), *this ) )
161 {
162 struct passwd* pwd = getpwuid(getuid());
163 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
164 }
165 return true;
166
167 #elif defined(__APPLE__)
168 macos::ALIB_APPLE_OC_NSHomeDirectory( *this );
169 if ( IsEmpty() )
170 {
171 struct passwd* pwd = getpwuid(getuid());
172 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
173 }
174 return true;
175
176
177 #elif defined(_WIN32)
178 if ( !loadEnvVar( A_CHAR("USERPROFILE"), *this ) || !IsDirectory() )
179 {
180 loadEnvVar( A_CHAR("HOMEDRIVE"), *this );
181 loadEnvVar( A_CHAR("HOMEPATH" ), *this, lang::CurrentData::Keep );
182 }
183 return true;
184 #else
185 #pragma message ("Unknown Platform in file: " __FILE__ )
186 return false;
187 #endif
188 }
189
190
191 case SystemFolders::HomeConfig:
192 {
193 if( !Change( SystemFolders::Home ) )
194 return false;
195
196 // try ".config" and "AppData/Roaming" subdirectories.
197 #if defined (__unix__)
198 Change( A_PATH(".config") );
199 return true;
200 #elif defined(__APPLE__)
201 Change( A_PATH("Library/Preferences") );
202 return true;
203 #elif defined(_WIN32)
204 Change( Path(A_PATH("AppData")) << DIRECTORY_SEPARATOR << A_PATH("Roaming") );
205 return true;
206 #else
207 #pragma message ("Unknown Platform in file: " __FILE__ )
208 return false;
209 #endif
210 }
211
212 case SystemFolders::Module:
213 {
214 Reset( ProcessInfo::Current().ExecFilePath );
215 return true;
216 }
217
218 case SystemFolders::Temp:
219 {
220 #if ALIB_MONOMEM
222 #endif
223 if ( tempDirEvaluatedOnce.IsNull() )
224 {
225 #if defined (__unix__)
226 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
227 if ( Path(A_PATH("/tmp") ).IsDirectory() )
228 #if ALIB_MONOMEM
230 #else
231 {
233 tempDirEvaluatedOnce.Allocate(ha, A_PATH("/tmp"));
234 }
235 #endif
236
237
238 #elif defined(__APPLE__)
239 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
240 Path temp;
241 macos::ALIB_APPLE_OC_NSTemporaryDirectory( temp );
242 if ( temp.IsNotEmpty() )
243 #if ALIB_MONOMEM
245 #else
246 {
248 tempDirEvaluatedOnce.Allocate(ha, temp);
249 }
250 #endif
251 else
252 {
253 temp.Reset( A_PATH("/tmp") );
254 if ( temp.IsDirectory() )
255 #if ALIB_MONOMEM
257 #else
258 {
260 tempDirEvaluatedOnce.Allocate(ha, temp);
261 }
262 #endif
263 }
264
265
266 #elif defined(_WIN32)
267 NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
268 Path testDir;
269 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
270 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
271 {
272 #if ALIB_MONOMEM
274 #else
275 {
277 tempDirEvaluatedOnce.Allocate(ha, testDir);
278 }
279 #endif
280 }
281 #else
282 #pragma message ("Unknown Platform in file: " __FILE__ )
283 #endif
284
285
286 if( tempDirEvaluatedOnce.IsEmpty() )
287 {
288 Path homeTemp;
289 createTempFolderInHomeDir( A_PATH(".tmp"), homeTemp, reasonMsg );
290
291 // If this did not work, use home
292 if( homeTemp.IsNotEmpty() )
293 {
294 #if ALIB_MONOMEM
297 #else
298 {
300 tempDirEvaluatedOnce.Allocate(ha, homeTemp);
301 }
302 #endif
303
304 }
305 else
306 {
307 Change( SystemFolders::Home );
308 {
309 #if ALIB_MONOMEM
312 #else
313 {
315 tempDirEvaluatedOnce.Allocate(ha, *this);
316 }
317 #endif
318 }
319 }
320 }
321 }
322
323 // set path to evaluated dir name
325 return true;
326
327 } // case SystemFolders::Temp:
328
329 case SystemFolders::VarTemp:
330 {
331 #if ALIB_MONOMEM
333 #endif
334 if ( varTempDirEvaluatedOnce.IsNull() )
335 {
336 #if defined (__unix__)
337 NString reasonMsg= "(The default folder \"/var/tmp\" could not be found.)";
338
339 if ( Path( A_PATH("/var/tmp") ).IsDirectory() )
340 #if ALIB_MONOMEM
342 #else
343 {
345 varTempDirEvaluatedOnce.Allocate(ha, A_PATH("/var/tmp"));
346 }
347 #endif
348
349 #elif defined(__APPLE__)
350 const NString reasonMsg= "(The default folder \"/private/var/tmp\" could not be found.)";
351 Path temp( A_PATH("/private/var/tmp") );
352 if ( temp.IsDirectory() )
353 #if ALIB_MONOMEM
355 #else
356 {
358 varTempDirEvaluatedOnce.Allocate(ha, temp);
359 }
360 #endif
361
362 #elif defined(_WIN32)
363 const NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
364 Path testDir;
365 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
366 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
367 {
368 #if ALIB_MONOMEM
370 #else
371 {
373 varTempDirEvaluatedOnce.Allocate(ha, testDir);
374 }
375 #endif
376 }
377 #else
378 #pragma message ("Unknown Platform in file: " __FILE__ )
379 #endif
380
381
382 if( varTempDirEvaluatedOnce.IsEmpty() )
383 {
384 Path varTemp;
385 createTempFolderInHomeDir( A_PATH(".var.tmp"), varTemp, reasonMsg );
386
387 // If this did not work, use home
388 if( varTemp.IsNotEmpty() )
389 {
390 #if ALIB_MONOMEM
393 #else
394 {
396 varTempDirEvaluatedOnce.Allocate(ha, varTemp);
397 }
398 #endif
399
400 }
401 else
402 {
403 Change( SystemFolders::Home );
404 {
405 #if ALIB_MONOMEM
408 #else
409 {
411 varTempDirEvaluatedOnce.Allocate(ha, *this);
412 }
413 #endif
414 }
415 }
416 }
417
418 }
419 // now path to evaluated dir name
421 return true;
422 }
423
424 default: ALIB_ERROR("SYSTEM", "Illegal switch state.")
425 return false;
426 } // switch ( special )
427}
428
429void Path::AddModuleName( const PathString& extension )
430{
433
435
436 #if defined(_WIN32)
437 if( EndsWith( A_PATH(".exe") ) )
438 DeleteEnd( 4 );
439 #endif
440
441 _( extension );
442}
443
444bool Path::Change( const PathString& ppath )
445{
446 // absolute addressing
447 Path path(ppath);
448 if( path.CharAtStart() == DIRECTORY_SEPARATOR )
449 {
450 if( !path.IsDirectory() )
451 return false;
452
453 Reset( path );
454 return true;
455 }
456
457
458 // relative addressing
459 integer origLength= Length();
460 _<NC>( DIRECTORY_SEPARATOR )._( path );
461
462 if( IsDirectory() )
463 return true;
464
465 ShortenTo( origLength );
466 return false;
467}
468
469
471{
472 #if defined (__GLIBC__) || defined(__APPLE__) || defined(__ANDROID_NDK__)
473 ALIB_STRINGS_TO_NARROW(*this, nPath, 512)
474 DIR* dir= opendir( nPath );
475 if ( dir != nullptr )
476 {
477 closedir( dir );
478 return true;
479 }
480 return false;
481
482 #elif defined(_WIN32)
483
484 #if !ALIB_PATH_CHARACTERS_WIDE
485 DWORD dwAttrib = GetFileAttributesA( Terminate() );
486 #else
487 DWORD dwAttrib = GetFileAttributesW( Terminate() );
488 #endif
489 if( dwAttrib == INVALID_FILE_ATTRIBUTES )
490 return false;
491 if ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
492 return true;
493 return false;
494
495 #else
496 #pragma message ("Unknown Platform in file: " __FILE__ )
497 #endif
498}
499
501
502 #if (defined(__GLIBCXX__) && !defined(__MINGW32__)) \
503 || defined(__APPLE__) \
504 || defined(__ANDROID_NDK__)
505
506 Path realPath;
507 if(!realpath(Terminate(), realPath.VBuffer() ) )
508 return SystemErrors(errno);
509
510 realPath.DetectLength();
511 Reset(realPath);
512 return SystemErrors::OK;
513
514 #else
515
516 namespace fs = std::filesystem;
517
518 std::error_code errorCode;
519 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(Buffer(),
520 size_t(Length()))),
521 errorCode);
522 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
523 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
524
525 if(errorCode)
526 return SystemErrors(errorCode.value());
527
528 Reset(fsRealPath.c_str());
529 return SystemErrors::OK;
530
531 #endif
532}
533SystemErrors Path::Create( const PathString& ppath ) // static
534{
535 if( Path::IsAbsolute(ppath) )
536 Reset( ppath );
537 else
538 (*this)._( DIRECTORY_SEPARATOR )._( ppath );
539
540 #if defined (__GLIBC__) || defined(__APPLE__) || defined(__ANDROID_NDK__)
541 ALIB_STRINGS_TO_NARROW(*this,nPath,512)
542 int errCode= mkdir( nPath, S_IRWXU | S_IRGRP | S_IROTH
543 | S_IXGRP | S_IXOTH );
544
545 return SystemErrors(errCode);
546
547 #elif defined(_WIN32)
548 #if !ALIB_PATH_CHARACTERS_WIDE
549 BOOL result= CreateDirectoryA( Terminate(), NULL );
550 #else
551 BOOL result= CreateDirectoryW( Terminate(), NULL );
552 #endif
553
554
555 if( result )
556 return SystemErrors::OK;
557
558 return SystemErrors( GetLastError() );
559 #else
560 #pragma message ("Unknown Platform in file: " __FILE__ )
561 #endif
562}
563
564} // namespace [alib::system]
constexpr const PathCharType * Terminate() const
Definition tastring.inl:682
TAString & _(const TAppendable &src)
TChar * VBuffer() const
Definition tastring.inl:700
constexpr integer Length() const
Definition string.inl:318
constexpr bool IsEmpty() const
Definition string.inl:367
bool EndsWith(const TString &needle) const
Definition string.inl:805
TChar CharAtStart() const
Definition string.inl:440
constexpr bool IsNotEmpty() const
Definition string.inl:371
constexpr const PathCharType * Buffer() const
Definition string.inl:313
ALIB_DLL SystemErrors MakeReal()
Definition path.cpp:500
static PathString varTempDirEvaluatedOnce
Definition path.inl:180
ALIB_DLL SystemErrors Create()
Definition path.inl:242
PathString Name() const
Definition path.inl:316
ALIB_DLL void AddModuleName(const PathString &extension)
Definition path.cpp:429
int IsAbsolute() const
Definition path.inl:310
ALIB_DLL bool IsDirectory()
Definition path.cpp:470
ALIB_DLL bool Change(const PathString &path)
Definition path.cpp:444
static PathString tempDirEvaluatedOnce
Definition path.inl:170
This class represents process information.
static ALIB_DLL const ProcessInfo & Current()
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
#define A_CHAR(STR)
#define ALIB_WARNINGS_RESTORE
Definition alib.inl:705
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#define A_PATH(literal)
#define ALIB_LOCK_RECURSIVE_WITH(lock)
Definition alib.inl:1323
#define ALIB_DBG(...)
Definition alib.inl:836
#define ALIB_WARNINGS_IGNORE_UNUSED_FUNCTION
Definition alib.inl:667
@ Keep
Chooses not no clear existing data.
@ Clear
Chooses to clear existing data.
ALIB_DLL TMonoAllocator< lang::HeapAllocator > GLOBAL_ALLOCATOR
ALIB_DLL RecursiveLock GLOBAL_ALLOCATOR_LOCK
strings::TString< PathCharType > PathString
The string-type used with this ALib Module.
Definition path.inl:33
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
Definition path.inl:63
LocalString< 256 > String256
Type alias name for TLocalString<character,256>.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
strings::TCString< character > CString
Type alias in namespace alib.
Definition cstring.inl:503
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
strings::TString< nchar > NString
Type alias in namespace alib.
Definition string.inl:2390
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace alib.
characters::nchar nchar
Type alias in namespace alib.
static ALIB_DLL bool Get(const CString &varName, AString &target, lang::CurrentData targetData=lang::CurrentData::Clear)