ALib C++ Library
Library Version: 2511 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
37 import ALib.Strings.StdIOStream;
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,
64 return EnvironmentVariables::Get( name, target, targetData );
65}
66
67template<typename TRequires= PathCharType>
68requires (!std::same_as<TRequires, character>)
69bool loadEnvVar( const CString& name, Path& target,
71 String256 buf;
72 auto result= EnvironmentVariables::Get( name, buf, targetData );
73 target.Reset( buf );
74 return result;
75}
77
78} // anonymous namespace
79
80#endif // !DOXYGEN
81
82//##################################################################################################
83// Static variables
84//##################################################################################################
87
88//##################################################################################################
89// Change
90//##################################################################################################
91//! @cond NO_DOX
92namespace {
93void createTempFolderInHomeDir( const PathString& folderName, Path& resultPath,
94 const NString& reasonMsg ) {
95 // get home directory and set this as fallback result value
96 Path homeTemp( SystemFolders::Home );
97 resultPath.Reset( homeTemp );
98
99 // add given folder name and check if already exists
100 homeTemp._( DIRECTORY_SEPARATOR )._( folderName );
101 bool exists= homeTemp.IsDirectory();
102 if( !exists ) {
103 if( homeTemp.Create() == SystemErrors::OK ) {
104 exists= true;
105 NAString fileName( homeTemp ); fileName._( DIRECTORY_SEPARATOR )._( "readme.txt" );
106
107 std::ofstream file ( fileName );
108 if ( file.is_open() ) {
110 file << "This folder was created by \"" << pi.CmdLine
111 << "\"" << std::endl
112 << "to be used for temporary files." << std::endl;
113 file.write( reasonMsg.Buffer(), reasonMsg.Length() );
114 file << std::endl;
115 file.close();
116 } } }
117
118 // if existed or got created
119 if( exists )
120 resultPath.Reset( homeTemp );
121}
122} // anonymous namespace
123//! @endcond
124
126 switch( special ) {
127 case SystemFolders::Root: _( DIRECTORY_SEPARATOR );
128 return true;
129
130 case SystemFolders::Current:
131 {
132 Reset();
133 nchar charBuf[FILENAME_MAX];
134
135 #if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || defined(__APPLE__) || defined(__ANDROID_NDK__)
136 if ( ! getcwd( charBuf, sizeof(charBuf ) ) )
137 return false;
138 #elif defined ( _WIN32 )
139 if ( !_getcwd( charBuf, sizeof(charBuf ) ) )
140 return false;
141 #else
142 #pragma message ("Unknown Platform in file: " __FILE__ )
143 return false;
144 #endif
145
146 this ->_( static_cast<const nchar*>( charBuf ) );
147 return true;
148 }
149
150
151 case SystemFolders::Home:
152 {
153 #if defined (__unix__)
154 if ( !loadEnvVar( A_CHAR("HOME"), *this ) ) {
155 struct passwd* pwd = getpwuid(getuid());
156 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
157 }
158 return true;
159
160 #elif defined(__APPLE__)
161 macos::ALIB_APPLE_OC_NSHomeDirectory( *this );
162 if ( IsEmpty() ) {
163 struct passwd* pwd = getpwuid(getuid());
164 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
165 }
166 return true;
167
168
169 #elif defined(_WIN32)
170 if ( !loadEnvVar( A_CHAR("USERPROFILE"), *this ) || !IsDirectory() ) {
171 loadEnvVar( A_CHAR("HOMEDRIVE"), *this );
172 loadEnvVar( A_CHAR("HOMEPATH" ), *this, lang::CurrentData::Keep );
173 }
174 return true;
175 #else
176 #pragma message ("Unknown Platform in file: " __FILE__ )
177 return false;
178 #endif
179 }
180
181
182 case SystemFolders::HomeConfig:
183 {
184 if( !Change( SystemFolders::Home ) )
185 return false;
186
187 // try ".config" and "AppData/Roaming" subdirectories.
188 #if defined (__unix__)
189 Change( A_PATH(".config") );
190 return true;
191 #elif defined(__APPLE__)
192 Change( A_PATH("Library/Preferences") );
193 return true;
194 #elif defined(_WIN32)
195 Change( Path(A_PATH("AppData")) << DIRECTORY_SEPARATOR << A_PATH("Roaming") );
196 return true;
197 #else
198 #pragma message ("Unknown Platform in file: " __FILE__ )
199 return false;
200 #endif
201 }
202
203 case SystemFolders::Module:
204 {
205 Reset( ProcessInfo::Current().ExecFilePath );
206 return true;
207 }
208
209 case SystemFolders::Temp:
210 {
211 #if ALIB_MONOMEM
213 #endif
214 if ( tempDirEvaluatedOnce.IsNull() ) {
215 #if defined (__unix__)
216 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
217 if ( Path(A_PATH("/tmp") ).IsDirectory() )
218 #if ALIB_MONOMEM
220 #else
221 {
223 tempDirEvaluatedOnce.Allocate(ha, A_PATH("/tmp"));
224 }
225 #endif
226
227
228 #elif defined(__APPLE__)
229 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
230 Path temp;
231 macos::ALIB_APPLE_OC_NSTemporaryDirectory( temp );
232 if ( temp.IsNotEmpty() )
233 #if ALIB_MONOMEM
235 #else
236 {
238 tempDirEvaluatedOnce.Allocate(ha, temp);
239 }
240 #endif
241 else {
242 temp.Reset( A_PATH("/tmp") );
243 if ( temp.IsDirectory() )
244 #if ALIB_MONOMEM
246 #else
247 {
249 tempDirEvaluatedOnce.Allocate(ha, temp);
250 }
251 #endif
252 }
253
254
255 #elif defined(_WIN32)
256 NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
257 Path testDir;
258 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
259 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
260 {
261 #if ALIB_MONOMEM
263 #else
264 {
266 tempDirEvaluatedOnce.Allocate(ha, testDir);
267 }
268 #endif
269 }
270 #else
271 #pragma message ("Unknown Platform in file: " __FILE__ )
272 #endif
273
274
275 if( tempDirEvaluatedOnce.IsEmpty() ) {
276 Path homeTemp;
277 createTempFolderInHomeDir( A_PATH(".tmp"), homeTemp, reasonMsg );
278
279 // If this did not work, use home
280 if( homeTemp.IsNotEmpty() ) {
281 #if ALIB_MONOMEM
284 #else
285 {
287 tempDirEvaluatedOnce.Allocate(ha, homeTemp);
288 }
289 #endif
290
291 } else {
292 Change( SystemFolders::Home );
293 {
294 #if ALIB_MONOMEM
297 #else
298 {
300 tempDirEvaluatedOnce.Allocate(ha, *this);
301 }
302 #endif
303 } } } }
304
305 // set path to evaluated dir name
307 return true;
308
309 } // case SystemFolders::Temp:
310
311 case SystemFolders::VarTemp:
312 {
313 #if ALIB_MONOMEM
315 #endif
316 if ( varTempDirEvaluatedOnce.IsNull() ) {
317 #if defined (__unix__)
318 NString reasonMsg= "(The default folder \"/var/tmp\" could not be found.)";
319
320 if ( Path( A_PATH("/var/tmp") ).IsDirectory() )
321 #if ALIB_MONOMEM
323 #else
324 {
326 varTempDirEvaluatedOnce.Allocate(ha, A_PATH("/var/tmp"));
327 }
328 #endif
329
330 #elif defined(__APPLE__)
331 const NString reasonMsg= "(The default folder \"/private/var/tmp\" could not be found.)";
332 Path temp( A_PATH("/private/var/tmp") );
333 if ( temp.IsDirectory() )
334 #if ALIB_MONOMEM
336 #else
337 {
339 varTempDirEvaluatedOnce.Allocate(ha, temp);
340 }
341 #endif
342
343 #elif defined(_WIN32)
344 const NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
345 Path testDir;
346 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
347 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
348 {
349 #if ALIB_MONOMEM
351 #else
352 {
354 varTempDirEvaluatedOnce.Allocate(ha, testDir);
355 }
356 #endif
357 }
358 #else
359 #pragma message ("Unknown Platform in file: " __FILE__ )
360 #endif
361
362
363 if( varTempDirEvaluatedOnce.IsEmpty() ) {
364 Path varTemp;
365 createTempFolderInHomeDir( A_PATH(".var.tmp"), varTemp, reasonMsg );
366
367 // If this did not work, use home
368 if( varTemp.IsNotEmpty() ) {
369 #if ALIB_MONOMEM
372 #else
373 {
375 varTempDirEvaluatedOnce.Allocate(ha, varTemp);
376 }
377 #endif
378
379 } else {
380 Change( SystemFolders::Home );
381 {
382 #if ALIB_MONOMEM
385 #else
386 {
388 varTempDirEvaluatedOnce.Allocate(ha, *this);
389 }
390 #endif
391 } } }
392
393 }
394 // now path to evaluated dir name
396 return true;
397 }
398
399 default: ALIB_ERROR("SYSTEM", "Illegal switch state.")
400 return false;
401 } // switch ( special )
402}
403
404void Path::AddModuleName( const PathString& extension ) {
407
409
410 #if defined(_WIN32)
411 if( EndsWith( A_PATH(".exe") ) )
412 DeleteEnd( 4 );
413 #endif
414
415 _( extension );
416}
417
418bool Path::Change( const PathString& ppath ) {
419 // absolute addressing
420 Path path(ppath);
421 if( path.CharAtStart() == DIRECTORY_SEPARATOR ) {
422 if( !path.IsDirectory() )
423 return false;
424
425 Reset( path );
426 return true;
427 }
428
429
430 // relative addressing
431 integer origLength= Length();
432 _<NC>( DIRECTORY_SEPARATOR )._( path );
433
434 if( IsDirectory() )
435 return true;
436
437 ShortenTo( origLength );
438 return false;
439}
440
442 integer origLength= Length();
443
444 integer startIdx= length;
445 if ( CharAtEnd() == DIRECTORY_SEPARATOR ) {
446 if ( startIdx == 1 )
447 return false;
448 --startIdx;
449 }
450
451 integer lastDirSep= LastIndexOf(DIRECTORY_SEPARATOR, startIdx);
452 if ( lastDirSep < 0 )
453 return false;
454 if ( lastDirSep == 0 )
455 lastDirSep= 1;
456 ShortenTo(lastDirSep);
457
458 if( IsDirectory() )
459 return true;
460
461 length= origLength;
462 return false;
463}
464
466 #if defined (__GLIBC__) || defined(__APPLE__) || defined(__ANDROID_NDK__)
467 ALIB_STRINGS_TO_NARROW(*this, nPath, 512)
468 DIR* dir= opendir( nPath );
469 if ( dir != nullptr ) {
470 closedir( dir );
471 return true;
472 }
473 return false;
474
475 #elif defined(_WIN32)
476
477 #if !ALIB_PATH_CHARACTERS_WIDE
478 DWORD dwAttrib = GetFileAttributesA( Terminate() );
479 #else
480 DWORD dwAttrib = GetFileAttributesW( Terminate() );
481 #endif
482 if( dwAttrib == INVALID_FILE_ATTRIBUTES )
483 return false;
484 if ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
485 return true;
486 return false;
487
488 #else
489 #pragma message ("Unknown Platform in file: " __FILE__ )
490 #endif
491}
492
494
495 #if (defined(__GLIBCXX__) && !defined(__MINGW32__)) \
496 || defined(__APPLE__) \
497 || defined(__ANDROID_NDK__)
498
499 Path realPath;
500 if(!realpath(Terminate(), realPath.VBuffer() ) )
501 return SystemErrors(errno);
502
503 realPath.DetectLength();
504 Reset(realPath);
505 return SystemErrors::OK;
506
507 #else
508
509 namespace fs = std::filesystem;
510
511 std::error_code errorCode;
512 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(Buffer(),
513 size_t(Length()))),
514 errorCode);
515 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
516 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
517
518 if(errorCode)
519 return SystemErrors(errorCode.value());
520
521 Reset(fsRealPath.c_str());
522 return SystemErrors::OK;
523
524 #endif
525}
527 if( Path::IsAbsolute(ppath) )
528 Reset( ppath );
529 else
530 (*this)._( DIRECTORY_SEPARATOR )._( ppath );
531
532 #if defined (__GLIBC__) || defined(__APPLE__) || defined(__ANDROID_NDK__)
533 ALIB_STRINGS_TO_NARROW(*this,nPath,512)
534 int errCode= mkdir( nPath, S_IRWXU | S_IRGRP | S_IROTH
535 | S_IXGRP | S_IXOTH );
536
537 return SystemErrors(errCode);
538
539 #elif defined(_WIN32)
540 #if !ALIB_PATH_CHARACTERS_WIDE
541 BOOL result= CreateDirectoryA( Terminate(), NULL );
542 #else
543 BOOL result= CreateDirectoryW( Terminate(), NULL );
544 #endif
545
546
547 if( result )
548 return SystemErrors::OK;
549
550 return SystemErrors( GetLastError() );
551 #else
552 #pragma message ("Unknown Platform in file: " __FILE__ )
553 #endif
554}
555
556} // namespace [alib::system]
constexpr const PathCharType * Terminate() const
Definition tastring.inl:660
integer DetectLength(integer offset=0)
Definition tastring.inl:760
TAString & _(const TAppendable &src)
TChar * VBuffer() const
Definition tastring.inl:677
constexpr integer Length() const
Definition string.inl:316
constexpr bool IsEmpty() const
Definition string.inl:365
bool EndsWith(const TString &needle) const
Definition string.inl:780
TChar CharAtStart() const
Definition string.inl:433
constexpr bool IsNotEmpty() const
Definition string.inl:369
constexpr const PathCharType * Buffer() const
Definition string.inl:311
integer LastIndexOf(PathCharType needle, integer startIndex=MAX_LEN) const
Definition string.inl:925
ALIB_DLL SystemErrors MakeReal()
Definition path.cpp:493
static PathString varTempDirEvaluatedOnce
Definition path.inl:180
ALIB_DLL SystemErrors Create()
Definition path.inl:222
PathString Name() const
Definition path.inl:288
ALIB_DLL void AddModuleName(const PathString &extension)
Definition path.cpp:404
int IsAbsolute() const
Definition path.inl:284
ALIB_DLL bool IsDirectory()
Definition path.cpp:465
ALIB_DLL bool ChangeToParent()
Definition path.cpp:441
ALIB_DLL bool Change(const PathString &path)
Definition path.cpp:418
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:719
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
#define A_PATH(literal)
#define ALIB_LOCK_RECURSIVE_WITH(lock)
Definition alib.inl:1340
#define ALIB_DBG(...)
Definition alib.inl:853
#define ALIB_WARNINGS_IGNORE_UNUSED_FUNCTION
Definition alib.inl:677
@ 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:475
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
strings::TString< nchar > NString
Type alias in namespace alib.
Definition string.inl:2198
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)