ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
file.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 ========================================
16#if !defined ( _WIN32 )
17# include <pwd.h>
18# include <grp.h>
19#endif
20
21//============================================== Module ============================================
22#if ALIB_C20_MODULES
23 module ALib.Files;
24 import ALib.Lang;
25 import ALib.Characters.Functions;
26 import ALib.Strings;
27 import ALib.Strings.Calendar;
28# if ALIB_EXPRESSIONS
29 import ALib.Expressions;
30# endif
31# if ALIB_DEBUG
32# include "ALib.Format.H"
33# endif
34#else
35# include "ALib.Lang.H"
37# include "ALib.Strings.H"
39# include "ALib.Expressions.H"
40# if ALIB_DEBUG
41# include "ALib.Format.H"
42# endif
43# include "ALib.Files.H"
44#endif
45//========================================== Implementation ========================================
46using namespace alib::system;
47namespace alib::files {
48
49strings::TAString<system::PathCharType>&
51 lang::Inclusion includeFilename ) const {
52
53 static constexpr int STACK_SIZE= 32;
54 FTree::ConstCursorHandle nStack[STACK_SIZE];
55
56 File childNode= (*this);
57 if(includeFilename == lang::Inclusion::Exclude)
58 childNode= childNode.GetSymbolicParent();
59
60 // if child and maxParent are the same, we do nothing
61 if (childNode.IsRoot())
62 return target;
63
64 // build stack
65 int sp =0;
66 while( !childNode.IsRoot() ) {
67 if(!childNode.HasSymbolicParent()) {
68 // local stack full? -> let a recursive call do the rest
69 if(sp == STACK_SIZE) {
70 File(childNode).AssembleSymbolicPath( target, lang::Inclusion::Include );
71 break;
72 }
73 nStack[sp++]= childNode.Export();
74 childNode= childNode.Parent();
75 }
76 else
77 childNode= childNode.GetSymbolicParent();
78 }
79
80 // unroll stack now from top to bottom
81 while( --sp >= 0) {
82 // todo: we have a problem with const. I think in general accross many ALib types.
83 // Here, I just use the raw value...
84 FTree::CursorHandle ch;
85 ch.value= (nStack[sp]).value;
86 Cursor actual= GetFTree().ImportCursor(ch);
87 if( !actual.IsRoot() ) {
90 target << actual.Name();
91 }
92 else
94 }
95
96 return target;
97}
98
100 int perms= int(Value().Perms());
101 // This is a "hard-coded" optimized approach. It only works unless the values of enum
102 // Permissions are as they have been since decades...
103 static_assert( int(FInfo::Permissions::OWNER_READ ) == 0400 &&
104 int(FInfo::Permissions::OWNER_WRITE ) == 0200 &&
105 int(FInfo::Permissions::OWNER_EXEC ) == 0100 &&
106 int(FInfo::Permissions::GROUP_READ ) == 040 &&
107 int(FInfo::Permissions::GROUP_WRITE ) == 020 &&
108 int(FInfo::Permissions::GROUP_EXEC ) == 010 &&
112 "This method is not compatible due to changes in the permission enumeration." );
113
114 target._<CHK>(""); // ensure valid target
115 std::array<char, 9> result;
116 std::array<char, 3> chars = {'r', 'w', 'x'};
117 int bit = 0400;
118 size_t charIdx = 0;
119 while( bit ) {
120 result[charIdx]= perms & bit ? chars[charIdx % 3] : '-';
121 charIdx++;
122 bit >>= 1;
123 }
124 target << result;
125
126 // This is the naive version that would not need the assertion above
127 // target << ( (perms & FInfo::Permissions::OWNER_READ ) == FInfo::Permissions::OWNER_READ ? 'r' : '-' )
128 // << ( (perms & FInfo::Permissions::OWNER_WRITE ) == FInfo::Permissions::OWNER_WRITE ? 'w' : '-' )
129 // << ( (perms & FInfo::Permissions::OWNER_EXEC ) == FInfo::Permissions::OWNER_EXEC ? 'x' : '-' )
130 // << ( (perms & FInfo::Permissions::GROUP_READ ) == FInfo::Permissions::GROUP_READ ? 'r' : '-' )
131 // << ( (perms & FInfo::Permissions::GROUP_WRITE ) == FInfo::Permissions::GROUP_WRITE ? 'w' : '-' )
132 // << ( (perms & FInfo::Permissions::GROUP_EXEC ) == FInfo::Permissions::GROUP_EXEC ? 'x' : '-' )
133 // << ( (perms & FInfo::Permissions::OTHERS_READ ) == FInfo::Permissions::OTHERS_READ ? 'r' : '-' )
134 // << ( (perms & FInfo::Permissions::OTHERS_WRITE) == FInfo::Permissions::OTHERS_WRITE ? 'w' : '-' )
135 // << ( (perms & FInfo::Permissions::OTHERS_EXEC ) == FInfo::Permissions::OTHERS_EXEC ? 'x' : '-' )
136 // ;
137 return target;
138}
139
141 if(nf == nullptr )
143
144 if ( targetData == lang::CurrentData::Clear )
145 target.Reset();
146
147 // this ensures that target is not nulled, as all other appends are NC-versions
148 target._("");
149
150 while ( format.IsNotEmpty() ) {
151 Box toBeAdded; // A box that most probably is set during the switch below. It will
152 // be added potentially embedded in a TField.
153 bool isUpper=false; // if set during run, the result string will be converted to upper case
154 AString strBuffer; // A string that might be filled and assigned to the result box (toBeAdded).
155 system::Path pathBuffer; // A path that might be filled and assigned to the result box (toBeAdded).
156
157 // read n equal characters
158 int n= 1;
159 character c= format.ConsumeChar();
160 while ( format.ConsumeChar(c) )
161 ++n;
162
163 if( isupper(c) ) {
164 c= character( tolower(c) );
165 isUpper= true;
166 }
167 integer previousLength= target.Length();
168
169 auto& value= Value();
170 switch (c) {
171 // path, name, stem, extension
172 case 'n':
173 {
174 // read next character
175 c= character(tolower(format.ConsumeChar()));
176 switch(c) {
177 case 'a' : toBeAdded= Name(); break;
178 case 's' : toBeAdded= Stem(); break;
179 case 'e' : toBeAdded= Extension(); break;
180 case 'p' :
181 case 'f' : AssembleSymbolicPath( pathBuffer, c == 'f'
184 toBeAdded= pathBuffer;
185 break;
186 case 'r' :
187 case 'x' : AssembleRealPath ( pathBuffer, c == 'x'
190 toBeAdded= pathBuffer;
191 break;
192
193 default:
194 {
195 ALIB_WARNING( "ALIB", "Format Error: Token 'n' followed by unknown "
196 "specifier '{}' in File::Format.", c )
197 target << "Format Error: Token 'n' followed by unknown specifier '" << c
198 << "' in File::Format.";
199 return target;
200 } } }
201 break;
202
203
204 case 'a':
205 FormatAccessRights(strBuffer);
206 toBeAdded= strBuffer;
207 break;
208
209 case 't': // Type
210 if( n == 1 ) toBeAdded= FInfo::TypeNames1Letter (value.Type());
211 else if( n == 2 ) toBeAdded= FInfo::TypeNames2Letters(value.Type());
212 else if( n == 3 ) toBeAdded= FInfo::TypeNames3Letters(value.Type());
213 else toBeAdded= value.Type();
214 break;
215
216 case 'l': // Symlink information
217 {
218 String4K symlinkInfo;
219 if( ( value.Type() == FInfo::Types::SYMBOLIC_LINK
220 || value.Type() == FInfo::Types::SYMBOLIC_LINK_DIR )
221 && value.ScanState() >= FInfo::ScanStates::RESOLVED )
222 {
223 strBuffer << " -> " << value.GetLinkTarget();
224 if( value.GetRealLinkTarget().IsNotEmpty()
225 && !value.GetLinkTarget().Equals( value.GetRealLinkTarget()) )
226 strBuffer << " (" << value.GetRealLinkTarget() << ")";
227 toBeAdded= strBuffer;
228 }
229 break;
230 }
231 case 'b': // Symbolic parent
232 {
233 if(value.symParent != 0) {
234 const File symbolicParent= GetSymbolicParent();
235 if constexpr (std::is_same_v<character, PathCharType>) {
236 strBuffer << " <- ";
237 symbolicParent.AssembleSymbolicPath(strBuffer, lang::Inclusion::Include);
238 } else {
239 Path parentPath;
240 symbolicParent.AssembleSymbolicPath(parentPath, lang::Inclusion::Include);
241 strBuffer << " <- " << parentPath;
242 }
243 toBeAdded= strBuffer;
244 }
245 break;
246 }
247
248 case 'f': // IsCrossingFS() / IsArtificialFS()
249 switch(c= character(tolower(format.ConsumeChar()))) {
250 case 'x' : toBeAdded= (value.IsCrossingFS() ? 'm' : '-') ; break;
251 case 'a' : toBeAdded= (value.IsArtificialFS() ? 'm' : '-') ; break;
252 default:
253 {
254 ALIB_WARNING( "ALIB", "Format Error: Unknown character {} after "
255 "token 'f' in File::Format.", c )
256 target << "Format Error: Unknown character '" << c
257 << "' after token 'f' in File::Format.";
258 return target;
259 } }
260 break;
261
262 case 'h': // Quantity of hard links
263 toBeAdded= value.QtyHardLinks();
264 break;
265
266 case 'q': // ScanState
267 if( n == 3 ) toBeAdded= FInfo::ScanStates3Letters(value.ScanState());
268 else toBeAdded= value.ScanState();
269 break;
270
271 case 'd': // date
272 {
273 CalendarDateTime date;
274 switch(c= character(tolower(format.ConsumeChar()))) {
275 case 'm' : date= value.MDate(); break;
276 case 'b' : date= value.BDate(); break;
277 case 'c' : date= value.CDate(); break;
278 case 'a' : date= value.ADate(); break;
279 default:
280 {
281 ALIB_WARNING( "ALIB",
282 "Format Error: Unknown character {} after token 'd' "
283 "in File::Format.", c )
284 target << "Format Error: Unknown character '" << c
285 << "' after token 'd' in File::Format.";
286 return target;
287 } }
288
289 String dateFormat= format.ConsumeField('{', '}' );
290 if( dateFormat.IsEmpty() )
291 dateFormat= A_CHAR("dd. MMM yyyy HH:mm");
292 date.Format( dateFormat, strBuffer );
293 toBeAdded= strBuffer;
294 break;
295 }
296
297 case 's': // size
298 {
299 bool automaticMode = true;
300 auto unit = ByteSizeUnits::IEC;
301
302 // entity specified in braces?
303 if( format.CharAtStart() == '(' ) {
304 format.ConsumeChar();
305 if( format.StartsWith<CHK,lang::Case::Ignore>(A_CHAR("SI"))) {
306 unit= ByteSizeUnits::SI;
307 format.ConsumeChars(2);
308 }
309 else if( format.StartsWith<CHK,lang::Case::Ignore>(A_CHAR("IEC"))) {
310 format.ConsumeChars(3);
311 } else {
312 enumrecords::Parse( format, unit );
313 automaticMode= false;
314 }
315
316 if( format.ConsumeChar() != ')' ) {
317 ALIB_WARNING( "ALIB",
318 "Format Error: Expected closing brace ')' after unit specification with token 's'." )
319 target << "Format Error: Expected closing brace ')' after unit specification with token 's'.";
320 return target;
321 } }
322
323 auto* ftreeNF= &GetFTree().GetNumberFormat();
324 if( !automaticMode ) {
325 // convert to given unit and output either a double or an integral.
326 ByteSizeIEC bs( value.Size() );
327 auto dval= bs.ConvertTo(unit);
328 if( unit==ByteSizeUnits::B || unit ==ByteSizeUnits::B_SI )
329 strBuffer << alib::Dec( uinteger(dval), 0, ftreeNF);
330 else
331 strBuffer << alib::Dec( dval , 0, ftreeNF);
332 } else {
333 // automatic output (automatically determine magnitude)
334 format::FormatByteSize( strBuffer, value.Size(), 900, 0, unit, *ftreeNF );
335 }
336 toBeAdded= strBuffer;
337 break;
338 }
339
340 case 'o': // owner
341 case 'g': // group
342 {
343 bool isOwner= c== 'o';
344 c= format.ConsumeChar();
345
346 if( c != 'i' && c != 'n' ) {
347 ALIB_WARNING( "ALIB",
348 "Format Error: Expected 'i' or 'n' specifier after token 'o' and 'g'."
349 " Given: '{}'", n )
350 target << "Format Error: Expected 'i' or 'n' specifier after token 'o' and 'g'."
351 " Given: '" << c << "'";
352 return target;
353 }
354 bool isName= (c == 'n');
355
356 if( isName ) {
357 auto& resolver= GetFTree().GetOGResolver();
358 toBeAdded= isOwner ? resolver.GetOwnerName(value)
359 : resolver.GetGroupName(value);
360 } else {
361 strBuffer << (isOwner ? value.Owner() : value.Group());
362 toBeAdded= strBuffer;
363 }
364 break;
365 }
366
367 // Extended directory info: sub-dirs, sub-files, access error, broken links
368 case 'r':
369 {
370 // read next character
371 c= character(tolower(format.ConsumeChar()));
372 if( !value.IsDirectory()
373 || value.ScanState() < FInfo::ScanStates::RECURSIVE )
374 {
375 toBeAdded= 0;
376 break;
377 }
378
379 FInfo::DirectorySums& dirInfo= value.Sums();
380 switch(c) {
381 case 'd' : toBeAdded= dirInfo.CountDirectories(); break;
382 case 'f' : toBeAdded= dirInfo.CountNonDirectories(); break;
383 case 'e' : toBeAdded= dirInfo.QtyErrsAccess; break;
384 case 'b' : toBeAdded= dirInfo.QtyErrsBrokenLink; break;
385 default:
386 {
387 ALIB_WARNING( "ALIB",
388 "Format Error: Token 'r' followed by unknown specifier '{}' "
389 "in File::Format", c )
390 target << "Format Error: Token 'r' followed by unknown specifier '" << c
391 << "'in File::Format";
392 return target;
393 } } }
394 break;
395
396
397 //--------------------------- single quotes and other characters -------------------------
398 case '\'':
399 {
400 // one or more pairs of single quotes?
401 if ( n > 1 ) {
402 int pairs= n / 2;
403 target.InsertChars<NC>( '\'', pairs );
404 n-= (pairs * 2);
405 }
406
407 // one single quote?
408 if ( n == 1 ) {
409 // search end
410 integer end= format.IndexOf( '\'' );
411 if ( end < 1 ) {
412 ALIB_WARNING( "ALIB", "Format Error: Missing single Quote" )
413 target << "Format Error: Missing closing single quote character <'>" ;
414 return target;
415 }
416
417 target._<NC>( format, 0, end );
418 format.ConsumeChars<NC>( end + 1 );
419 } }
420 break;
421
422
423 default: // otherwise: copy what was in
424 target.InsertChars<NC>( c, n );
425 break;
426 } // switch(c)
427
428 // field width, alignment specified in braces?
429 int width= -1;
431 if( format.CharAtStart() == '{' ) {
432 format.ConsumeChar();
433 format.ConsumeInt( width, &GetFTree().GetNumberFormat() );
434 format.ConsumeChar(',');
435 enumrecords::Parse( format, alignment );
436 if( format.ConsumeChar() != '}' ) {
437 ALIB_WARNING( "ALIB",
438 "Format Error: Expected closing brace '}' with field specifier {width/alignment}." )
439 target << "Format Error: Expected closing brace '}' with field specifier {width/alignment}.";
440 return target;
441 }
442 target << Field( toBeAdded, width, alignment );
443 }
444 else
445 target << toBeAdded;
446
447 // upper case conversion
448 if( isUpper )
449 target.ToUpper(previousLength);
450 }
451
452 return target;
453} // File::Format
454
455void FFormat_File( const Box& box, const String& formatSpec, NumberFormat& nf, AString& target ) {
456 box.Unbox<File>().Format( formatSpec.IsNotEmpty() ? formatSpec
457 : FILES.GetResource("FFMT"),
458 target,
460 &nf );
461}
462
463} // namespace alib::files
464
#define A_CHAR(STR)
Definition alib.inl:1325
#define ALIB_WARNING(domain,...)
Definition alib.inl:1141
Cursor ImportCursor(CursorHandle handle)
@ RECURSIVE
Follow symlink target strings.
Definition finfo.inl:137
@ RESOLVED
Read symlink target strings.
Definition finfo.inl:129
@ GROUP_READ
Posix S_IRGRP: The file's user group has read permission.
Definition finfo.inl:102
@ GROUP_EXEC
Posix S_IXGRP: The file's user group has execute/search permission.
Definition finfo.inl:104
@ OTHERS_EXEC
Posix S_IXOTH: Other users have execute/search permission.
Definition finfo.inl:109
@ GROUP_WRITE
Posix S_IWGRP: The file's user group has write permission.
Definition finfo.inl:103
@ OWNER_READ
Posix S_IRUSR: File owner has read permission.
Definition finfo.inl:97
@ OWNER_EXEC
Posix S_IXUSR: File owner has execute/search permission.
Definition finfo.inl:99
@ OWNER_WRITE
Posix S_IWUSR: File owner has write permission.
Definition finfo.inl:98
@ OTHERS_READ
Posix S_IROTH: Other users have read permission.
Definition finfo.inl:107
@ OTHERS_WRITE
Posix S_IWOTH: Other users have write permission.
Definition finfo.inl:108
const OwnerAndGroupResolver & GetOGResolver() const
Definition ftree.inl:301
NumberFormat & GetNumberFormat()
Definition ftree.inl:293
AString & Format(Substring format, AString &target, lang::CurrentData targetData=lang::CurrentData::Keep, NumberFormat *numberFormat=nullptr) const
Definition file.cpp:140
FTree & GetFTree() const
Definition ftree.inl:653
FTree::Cursor Cursor
Definition ftree.inl:645
File()=default
Defaulted default constructor.
system::PathString Extension() const
Definition ftree.inl:781
AString & FormatAccessRights(AString &target) const
Definition file.cpp:99
strings::TAString< system::PathCharType > & AssembleSymbolicPath(strings::TAString< system::PathCharType > &target, lang::Inclusion includeFilename) const
Definition file.cpp:50
File GetSymbolicParent()
Definition ftree.inl:744
strings::TAString< system::PathCharType > & AssembleRealPath(strings::TAString< system::PathCharType > &target, lang::Inclusion includeFilename) const
Definition ftree.inl:796
bool HasSymbolicParent()
Definition ftree.inl:736
system::PathString Stem() const
Definition ftree.inl:770
TAString & InsertChars(TChar c, integer qty)
TAString & ToUpper(integer regionStart=0, integer regionLength=MAX_LEN)
TAString & _(const TAppendable &src)
constexpr integer Length() const
Definition string.inl:304
constexpr bool IsEmpty() const
Definition string.inl:353
constexpr bool IsNotEmpty() const
Definition string.inl:357
TChar CharAtEnd() const
Definition string.inl:440
AString & Format(Substring format, AString &target, lang::CurrentData targetData=lang::CurrentData::Keep) const
Definition calendar.cpp:293
bool Parse(strings::TSubstring< TChar > &input, TEnum &result)
void FFormat_File(const Box &box, const String &formatSpec, NumberFormat &nf, AString &target)
Definition file.cpp:455
void FormatByteSize(AString &target, uinteger byteSize, uint16_t magnitudeThreshold, char unitSeparator, ByteSizeUnits unit, NumberFormat &nf)
Alignment
Denotes Alignments.
@ Right
Chooses right alignment.
@ Keep
Chooses not no clear existing data.
@ Clear
Chooses to clear existing data.
Inclusion
Denotes how members of a set something should be taken into account.
@ Exclude
Chooses exclusion.
@ Include
Chooses inclusion.
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
Definition path.inl:63
strings::TNumberFormat< character > NumberFormat
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
boxing::Box Box
Type alias in namespace alib.
Definition box.inl:1135
strings::TField< character > Field
Type alias in namespace alib.
LocalString< 4096 > String4K
Type alias name for #"TLocalString;TLocalString<character,4096>".
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2172
system::Path Path
Type alias in namespace alib.
Definition path.inl:375
strings::TDec< character > Dec
Type alias in namespace alib.
Definition format.inl:541
strings::TSubstring< character > Substring
Type alias in namespace alib.
strings::util::CalendarDateTime CalendarDateTime
Type alias in namespace alib.
Definition calendar.inl:512
files::FilesCamp FILES
The singleton instance of ALib Camp class #"FilesCamp".
Definition filescamp.cpp:47
format::ByteSizeIEC ByteSizeIEC
Type alias in namespace alib.
Definition bytesize.inl:214
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
characters::character character
Type alias in namespace alib.
lang::uinteger uinteger
Type alias in namespace alib.
Definition integers.inl:152
See sibling type #"NC".
Definition chk_nc.inl:33
Recursively accumulated values for directories.
Definition finfo.inl:189
uint32_t QtyErrsAccess
Number of access errors in the folder and subfolders.
Definition finfo.inl:192
uint32_t CountNonDirectories() const noexcept
Definition finfo.inl:279
uint32_t QtyErrsBrokenLink
Number of broken symbolic links in the directory and its subfolders.
Definition finfo.inl:193
uint32_t CountDirectories() const noexcept
Definition finfo.inl:270
double ConvertTo(ByteSizeUnits unit)