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