Files
The Util.Files
package provides various utility operations around files
to help in reading, writing, searching for files in a path.
To use the operations described here, use the following GNAT project:
with "utilada_base";
Reading and writing
To easily get the full content of a file, the Read_File
procedure can be
used. A first form exists that populates a Unbounded_String
or a vector
of strings. A second form exists with a procedure that is called with each
line while the file is read. These different forms simplify the reading of
files as it is possible to write:
Content : Ada.Strings.Unbounded.Unbounded_String;
Util.Files.Read_File ("config.txt", Content);
or
List : Util.Strings.Vectors.Vector;
Util.Files.Read_File ("config.txt", List);
or
procedure Read_Line (Line : in String) is ...
Util.Files.Read_File ("config.txt", Read_Line'Access);
Similarly, writing a file when you have a string or an Unbounded_String
is easily written by using Write_File
as follows:
Util.Files.Write_File ("config.txt", "full content");
Searching files
Searching for a file in a list of directories can be accomplished by using
the Iterate_Path
, Iterate_Files_Path
or Find_File_Path
.
The Find_File_Path
function is helpful to find a file in some PATH
search list. The function looks in each search directory for the given
file name and it builds and returns the computed path of the first file
found in the search list. For example:
Path : String := Util.Files.Find_File_Path ("ls",
"/bin:/usr/bin",
':');
This will return /usr/bin/ls
on most Unix systems.
Rolling file manager
The Util.Files.Rolling
package provides a simple support to roll a file
based on some rolling policy. Such rolling is traditionally used for file
logs to move files to another place when they reach some size limit or when
some date conditions are met (such as a day change). The file manager uses
a file path and a pattern. The file path is used to define the default
or initial file. The pattern is used when rolling occurs to decide how
to reorganize files.
The file manager defines a triggering policy represented by Policy_Type
.
It controls when the file rolling must be made.
No_Policy
: no policy, the rolling must be triggered manually.Size_Policy
: size policy, the rolling is triggered when the file reaches a given size.Time_Policy
: time policy, the rolling is made when the date/time pattern no longer applies to the active file; theInterval
configuration defines the period to check for time changes,Size_Time_Policy
: combines the size and time policy, the rolling is triggered when either the file reaches a given size or the date/time pattern no longer applies to the active file.
To control how the rolling is made, the Strategy_Type
defines the behavior
of the rolling.
Rollover_Strategy
:Direct_Strategy
:
To use the file manager, the first step is to create an instance and configure the default file, pattern, choose the triggering policy and strategy:
Manager : Util.Files.Rolling.File_Manager;
Manager.Initialize ("dynamo.log", "dynamo-%i.log",
Policy => (Size_Policy, 100_000),
Strategy => (Rollover_Strategy, 1, 10));
After the initialization, the current file is retrieved by using the
Get_Current_Path
function and you should call Is_Rollover_Necessary
before writing content on the file. When it returns True
, it means you
should call the Rollover
procedure that will perform roll over according
to the rolling strategy.
Path filters
The generic package Util.Files.Filters
implements a path filter mechanism
that emulates the classical .gitignore
path filter. It defines the
Filter_Type
tagged type to represent and control a set of path patterns
associated with values represented by the Element_Type
generic type.
The package must be instantiated with the type representing values
associated to path patterns. A typical instantation for an inclusion,
exclusion filter such as .gitignore
could be:
type Filter_Mode is (Not_Found, Included, Excluded);
package Path_Filter is
new Util.Files.Filters (Filter_Mode);
The Filter_Type
provides one operation to add a pattern in the filter
and associate it with a value. A pattern can contain fixed paths, wildcards
or regular expressions. When inserting a filter, you can indicate
whether the filter is to be applied locally on recursively
(this is similar to the .gitignore
rules, with a pattern that starts
with a /
or without). Example of pattern setup:
Filter : Path_Filter.Filter_Type;
...
Filter.Insert ("*.o", Recursive => True, Value => Excluded);
Filter.Insert ("alire/", Recursive => False, Value => Excluded);
Filter.Insert ("docs/*", Recursive => False, Value => Included);
The Match
function looks in the filter for a match and it indicates
either that there is a match with a value (Found
), a match witout
a value (No_Value
) or no match at all (Not_Found
). When there is
a match Found
, the associated value is retrieved by using Get_Value
.
The Match
operation is called as follows:
Result : Path_Filter.Filter_Result := Filter.Match ("test.o");
...
if Result.Match = Path_Filter.Found then
...
end if;
The table below gives results found for several paths and with the filters defined above:
Operation | Result.Match | Path_Filter.Get_Value |
---|---|---|
Filter.Match ("test.o") | Found | Excluded |
Filter.Match ("test.a") | Not_Found | |
Filter.Match ("docs/test.o") | Found | Included |
Filter.Match ("alire/") | Found | Included |
Filter.Match ("test/alire") | Not_Found |
It is also possible to use the generic package as a mapping framework to implement a mapping of a path to a string, for example:
package Language_Mappers is
new Util.Files.Filters (Element_Type => String);
And filters could be populated as follows:
Filter : Language_Mappers.Filter_Type;
...
Filter.Add ("*.c", True, "C");
Filter.Add ("*.adb", True, "Ada");
Filter.Add ("*.ads", True, "Ada");
Filter.Add ("Makefile", True, "Make");
Directory tree walk
It is sometimes necessary to walk a directory tree while taking into
account some inclusion or exclusion patterns or more complex ignore lists.
The Util.Files.Walk
package provides a support to walk such directory
tree while taking into account some possible ignore lists such as the
.gitignore
file. The package defines the Filter_Type
tagged type
to represent and control the exclusion or inclusion filters and a second
tagged type Walker_Type
to walk the directory tree.
The Filter_Type
provides two operations to add patterns in the filter
and one operation to check against a path whether it matches a pattern.
A pattern can contain fixed paths, wildcards or regular expressions.
Similar to .gitignore
rules, a pattern which starts with a /
will
define a pattern that must match the complete path. Otherwise, the pattern
is a recursive pattern. Example of pattern setup:
Filter : Util.Files.Walk.Filter_Type;
...
Filter.Exclude ("*.o");
Filter.Exclude ("/alire/");
Filter.Include ("/docs/*");
The Match
function looks in the filter for a match. The path could be
included, excluded or not found. For example, the following paths will
match:
Operation | Result |
---|---|
Filter.Match ("test.o") | Walk.Excluded |
Filter.Match ("test.a") | Walk.Not_Found |
Filter.Match ("docs/test.o") | Walk.Included |
Filter.Match ("alire/") | Walk.Included |
Filter.Match ("test/alire") | Walk.Not_Found |
To scan a directory tree, the Walker_Type
must have some of its operations
overriden:
- The
Scan_File
should be overriden to be notified when a file is found and handle it. - The
Scan_Directory
should be overriden to be notified when a directory is entered. - The
Get_Ignore_Path
is called when entering a new directory. It can be overriden to indicate a path of a file which contains some patterns to be ignored (ex: the.gitignore
file).
See the tree.adb
example.