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