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