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