ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
path.cpp
1//##################################################################################################
2// ALib C++ Framework
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 "Configuration MACRO 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 ========================================
49
50/// This is the reference documentation of module \alib_system, which exposes it's entities
51/// in this namespace.
52namespace alib::system {
53
54#if !DOXYGEN
55//----------------- two versions to load environment variables into a Path instance ----------------
56namespace {
57
59template<typename TRequires= PathCharType>
60requires std::same_as<TRequires, character>
61bool loadEnvVar( const CString& name, AString& target,
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,
70 String256 buf;
71 auto result= EnvironmentVariables::Get( name, buf, targetData );
72 target.Reset( buf );
73 return result;
74}
76
77} // anonymous namespace
78
79#endif // !DOXYGEN
80
81//##################################################################################################
82// Static variables
83//##################################################################################################
86
87//##################################################################################################
88// Change
89//##################################################################################################
90//! @cond NO_DOX
91namespace {
92void createTempFolderInHomeDir( const PathString& folderName, Path& resultPath,
93 const NString& reasonMsg ) {
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 if( homeTemp.Create() == std::errc(0) ) {
103 exists= true;
104 NAString fileName( homeTemp ); fileName._( DIRECTORY_SEPARATOR )._( "readme.txt" );
105
106 std::ofstream file ( fileName );
107 if ( file.is_open() ) {
109 file << "This folder was created by \"" << pi.CmdLine
110 << "\"" << std::endl
111 << "to be used for temporary files." << std::endl;
112 file.write( reasonMsg.Buffer(), reasonMsg.Length() );
113 file << std::endl;
114 file.close();
115 } } }
116
117 // if existed or got created
118 if( exists )
119 resultPath.Reset( homeTemp );
120}
121} // anonymous namespace
122//! @endcond
123
125 switch( special ) {
126 case SystemFolders::Root: _( DIRECTORY_SEPARATOR );
127 return true;
128
129 case SystemFolders::Current:
130 {
131 Reset();
132 nchar charBuf[FILENAME_MAX];
133
134 #if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || defined(__APPLE__) || defined(__ANDROID_NDK__)
135 if ( ! getcwd( charBuf, sizeof(charBuf ) ) )
136 return false;
137 #elif defined ( _WIN32 )
138 if ( !_getcwd( charBuf, sizeof(charBuf ) ) )
139 return false;
140 #else
141 #pragma message ("Unknown Platform in file: " __FILE__ )
142 return false;
143 #endif
144
145 this ->_( static_cast<const nchar*>( charBuf ) );
146 return true;
147 }
148
149
150 case SystemFolders::Home:
151 {
152 #if defined (__unix__)
153 if ( !loadEnvVar( A_CHAR("HOME"), *this ) ) {
154 struct passwd* pwd = getpwuid(getuid());
155 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
156 }
157 return true;
158
159 #elif defined(__APPLE__)
160 macos::ALIB_APPLE_OC_NSHomeDirectory( *this );
161 if ( IsEmpty() ) {
162 struct passwd* pwd = getpwuid(getuid());
163 Reset( pwd ? NString( pwd->pw_dir ) : "~/" );
164 }
165 return true;
166
167
168 #elif defined(_WIN32)
169 if ( !loadEnvVar( A_CHAR("USERPROFILE"), *this ) || !IsDirectory() ) {
170 loadEnvVar( A_CHAR("HOMEDRIVE"), *this );
171 loadEnvVar( A_CHAR("HOMEPATH" ), *this, lang::CurrentData::Keep );
172 }
173 return true;
174 #else
175 #pragma message ("Unknown Platform in file: " __FILE__ )
176 return false;
177 #endif
178 }
179
180
181 case SystemFolders::HomeConfig:
182 {
183 if( !Change( SystemFolders::Home ) )
184 return false;
185
186 // try ".config" and "AppData/Roaming" subdirectories.
187 #if defined (__unix__)
188 Change( A_PATH(".config") );
189 return true;
190 #elif defined(__APPLE__)
191 Change( A_PATH("Library/Preferences") );
192 return true;
193 #elif defined(_WIN32)
194 Change( Path(A_PATH("AppData")) << DIRECTORY_SEPARATOR << A_PATH("Roaming") );
195 return true;
196 #else
197 #pragma message ("Unknown Platform in file: " __FILE__ )
198 return false;
199 #endif
200 }
201
202 case SystemFolders::Module:
203 {
204 Reset( ProcessInfo::Current().ExecFilePath );
205 return true;
206 }
207
208 case SystemFolders::Temp:
209 {
210 #if ALIB_MONOMEM
212 #endif
213 if ( tempDirEvaluatedOnce.IsNull() ) {
214 #if defined (__unix__)
215 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
216 if ( Path(A_PATH("/tmp") ).IsDirectory() )
217 #if ALIB_MONOMEM
219 #else
220 {
222 tempDirEvaluatedOnce.Allocate(ha, A_PATH("/tmp"));
223 }
224 #endif
225
226
227 #elif defined(__APPLE__)
228 NString reasonMsg= "(The default temporary folder \"/tmp\" could not be found.)";
229 Path temp;
230 macos::ALIB_APPLE_OC_NSTemporaryDirectory( temp );
231 if ( temp.IsNotEmpty() )
232 #if ALIB_MONOMEM
234 #else
235 {
237 tempDirEvaluatedOnce.Allocate(ha, temp);
238 }
239 #endif
240 else {
241 temp.Reset( A_PATH("/tmp") );
242 if ( temp.IsDirectory() )
243 #if ALIB_MONOMEM
245 #else
246 {
248 tempDirEvaluatedOnce.Allocate(ha, temp);
249 }
250 #endif
251 }
252
253
254 #elif defined(_WIN32)
255 NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
256 Path testDir;
257 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
258 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
259 {
260 #if ALIB_MONOMEM
262 #else
263 {
265 tempDirEvaluatedOnce.Allocate(ha, testDir);
266 }
267 #endif
268 }
269 #else
270 #pragma message ("Unknown Platform in file: " __FILE__ )
271 #endif
272
273
274 if( tempDirEvaluatedOnce.IsEmpty() ) {
275 Path homeTemp;
276 createTempFolderInHomeDir( A_PATH(".tmp"), homeTemp, reasonMsg );
277
278 // If this did not work, use home
279 if( homeTemp.IsNotEmpty() ) {
280 #if ALIB_MONOMEM
283 #else
284 {
286 tempDirEvaluatedOnce.Allocate(ha, homeTemp);
287 }
288 #endif
289
290 } else {
291 Change( SystemFolders::Home );
292 {
293 #if ALIB_MONOMEM
296 #else
297 {
299 tempDirEvaluatedOnce.Allocate(ha, *this);
300 }
301 #endif
302 } } } }
303
304 // set path to evaluated dir name
306 return true;
307
308 } // case SystemFolders::Temp:
309
310 case SystemFolders::VarTemp:
311 {
312 #if ALIB_MONOMEM
314 #endif
315 if ( varTempDirEvaluatedOnce.IsNull() ) {
316 #if defined (__unix__)
317 NString reasonMsg= "(The default folder \"/var/tmp\" could not be found.)";
318
319 if ( Path( A_PATH("/var/tmp") ).IsDirectory() )
320 #if ALIB_MONOMEM
322 #else
323 {
325 varTempDirEvaluatedOnce.Allocate(ha, A_PATH("/var/tmp"));
326 }
327 #endif
328
329 #elif defined(__APPLE__)
330 const NString reasonMsg= "(The default folder \"/private/var/tmp\" could not be found.)";
331 Path temp( A_PATH("/private/var/tmp") );
332 if ( temp.IsDirectory() )
333 #if ALIB_MONOMEM
335 #else
336 {
338 varTempDirEvaluatedOnce.Allocate(ha, temp);
339 }
340 #endif
341
342 #elif defined(_WIN32)
343 const NString reasonMsg= "(Environment variables TMP and TEMP either not set or not containing valid paths.)";
344 Path testDir;
345 if ( ( loadEnvVar( A_CHAR("TMP") , testDir ) && testDir.IsDirectory() )
346 || ( loadEnvVar( A_CHAR("TEMP"), testDir ) && testDir.IsDirectory() ) )
347 {
348 #if ALIB_MONOMEM
350 #else
351 {
353 varTempDirEvaluatedOnce.Allocate(ha, testDir);
354 }
355 #endif
356 }
357 #else
358 #pragma message ("Unknown Platform in file: " __FILE__ )
359 #endif
360
361
362 if( varTempDirEvaluatedOnce.IsEmpty() ) {
363 Path varTemp;
364 createTempFolderInHomeDir( A_PATH(".var.tmp"), varTemp, reasonMsg );
365
366 // If this did not work, use home
367 if( varTemp.IsNotEmpty() ) {
368 #if ALIB_MONOMEM
371 #else
372 {
374 varTempDirEvaluatedOnce.Allocate(ha, varTemp);
375 }
376 #endif
377
378 } else {
379 Change( SystemFolders::Home );
380 {
381 #if ALIB_MONOMEM
384 #else
385 {
387 varTempDirEvaluatedOnce.Allocate(ha, *this);
388 }
389 #endif
390 } } }
391
392 }
393 // now path to evaluated dir name
395 return true;
396 }
397
398 default: ALIB_ERROR("SYSTEM", "Illegal switch state.")
399 return false;
400 } // switch ( special )
401}
402
403void Path::AddModuleName( const PathString& extension ) {
406
408
409 #if defined(_WIN32)
410 if( EndsWith( A_PATH(".exe") ) )
411 DeleteEnd( 4 );
412 #endif
413
414 _( extension );
415}
416
417bool Path::Change( const PathString& ppath ) {
418 // absolute addressing
419 Path path(ppath);
420 if( path.CharAtStart() == DIRECTORY_SEPARATOR ) {
421 if( !path.IsDirectory() )
422 return false;
423
424 Reset( path );
425 return true;
426 }
427
428
429 // relative addressing
430 integer origLength= Length();
431 _<NC>( DIRECTORY_SEPARATOR )._( path );
432
433 if( IsDirectory() )
434 return true;
435
436 ShortenTo( origLength );
437 return false;
438}
439
441 integer origLength= Length();
442
443 integer startIdx= length;
444 if ( CharAtEnd() == DIRECTORY_SEPARATOR ) {
445 if ( startIdx == 1 )
446 return false;
447 --startIdx;
448 }
449
450 integer lastDirSep= LastIndexOf(DIRECTORY_SEPARATOR, startIdx);
451 if ( lastDirSep < 0 )
452 return false;
453 if ( lastDirSep == 0 )
454 lastDirSep= 1;
455 ShortenTo(lastDirSep);
456
457 if( IsDirectory() )
458 return true;
459
460 length= origLength;
461 return false;
462}
463
465 #if defined (__GLIBC__) || defined(__APPLE__) || defined(__ANDROID_NDK__)
466 ALIB_STRINGS_TO_NARROW(*this, nPath, 512)
467 DIR* dir= opendir( nPath );
468 if ( dir != nullptr ) {
469 closedir( dir );
470 return true;
471 }
472 return false;
473
474 #elif defined(_WIN32)
475
476 #if !ALIB_PATH_CHARACTERS_WIDE
477 DWORD dwAttrib = GetFileAttributesA( Terminate() );
478 #else
479 DWORD dwAttrib = GetFileAttributesW( Terminate() );
480 #endif
481 if( dwAttrib == INVALID_FILE_ATTRIBUTES )
482 return false;
483 if ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
484 return true;
485 return false;
486
487 #else
488 #pragma message ("Unknown Platform in file: " __FILE__ )
489 #endif
490}
491
493
494 #if (defined(__GLIBCXX__) && !defined(__MINGW32__)) \
495 || defined(__APPLE__) \
496 || defined(__ANDROID_NDK__)
497
498 Path realPath;
499 if(!realpath(Terminate(), realPath.VBuffer() ) )
500 return std::errc(errno);
501
502 realPath.DetectLength();
503 Reset(realPath);
504 return std::errc(0);
505
506 #else
507
508 namespace fs = std::filesystem;
509
510 std::error_code errorCode;
511 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(Buffer(),
512 size_t(Length()))),
513 errorCode);
514 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
515 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
516
517 if(errorCode)
518 return std::errc(errorCode.value());
519
520 Reset(fsRealPath.c_str());
521 return std::errc(0);
522
523 #endif
524}
525
526std::errc Path::Create( const PathString& ppath ) {
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 std::errc(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 std::errc(0);
549
550 return std::errc( GetLastError() );
551 #else
552 #pragma message ("Unknown Platform in file: " __FILE__ )
553 #endif
554}
555
556} // namespace [alib::system]
#define A_CHAR(STR)
Definition alib.inl:1325
#define ALIB_ALLOW_UNUSED_FUNCTION
Definition alib.inl:639
#define ALIB_ERROR(domain,...)
Definition alib.inl:1140
#define ALIB_LOCK_RECURSIVE_WITH(lock)
Definition alib.inl:1414
#define ALIB_POP_ALLOWANCE
Definition alib.inl:673
#define ALIB_DBG(...)
Definition alib.inl:931
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
constexpr const PathCharType * Terminate() const
Definition tastring.inl:620
integer DetectLength(integer offset=0)
Definition tastring.inl:720
TChar * VBuffer() const
Definition tastring.inl:637
constexpr integer Length() const
Definition string.inl:304
constexpr bool IsEmpty() const
Definition string.inl:353
bool EndsWith(const TString &needle) const
Definition string.inl:768
TChar CharAtStart() const
Definition string.inl:421
constexpr bool IsNotEmpty() const
Definition string.inl:357
constexpr const PathCharType * Buffer() const
Definition string.inl:299
integer LastIndexOf(PathCharType needle, integer startIndex=MAX_LEN) const
Definition string.inl:913
std::errc MakeCanonical()
Definition path.cpp:492
static PathString varTempDirEvaluatedOnce
Definition path.inl:180
PathString Name() const
Definition path.inl:288
void AddModuleName(const PathString &extension)
Definition path.cpp:403
int IsAbsolute() const
Definition path.inl:284
bool IsDirectory()
Definition path.cpp:464
bool ChangeToParent()
Definition path.cpp:440
std::errc Create()
Definition path.inl:222
bool Change(const PathString &path)
Definition path.cpp:417
static PathString tempDirEvaluatedOnce
Definition path.inl: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< 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
strings::TString< nchar > NString
Type alias in namespace alib.
Definition string.inl:2181
strings::TCString< character > CString
Type alias in namespace alib.
Definition cstring.inl:399
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
characters::nchar nchar
Type alias in namespace alib.
LocalString< 256 > String256
Type alias name for #"TLocalString;TLocalString<character,256>".
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)