Ada Programming/Libraries/Ada.Directories

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Ada. Time-tested, safe and secure.
Ada. Time-tested, safe and secure.

This language feature is only available from Ada 2005 on.

Ada.Directories is a unit of the Predefined Language Environment since Ada 2005. It allows various manipulations and queries of the filesystem directories.

Introduction

[edit | edit source]

If the environment supports the notion of files and directories, it is possible to manipulate these in a way that is fairly OS agnostic using the Ada.Directories.

This is a very implementation dependent package and, as such, the information that can be extracted about files and directories is kept very basic. If further information is needed (owner, group, and so on), a child package should be created for this, as can be read in the implementation advice from the Ada Reference Manual [1]:

If other information about a file (such as the owner or creation date) is available in a directory entry, the implementation should provide functions in a child package Directories.Information to retrieve it.

Short Use Tutorial

[edit | edit source]

This section gives a quick overview on the use of the Ada.Directories package. If it is not detailed enough, you can also follow the Detailed Use Tutorial.

Traversing Directories

[edit | edit source]

Changing the current working directory is done with Set_Directory. The remaining functions are fairly self-explanatory with the exception of Containing_Directory, which returns the name of the directory containing the given directory.

   function Current_Directory return String;
   procedure Set_Directory (Directory : in String);
   function Exists (Name : in String) return Boolean;
   function Containing_Directory (Name : in String) return String;

Enumerating Directory Entries

[edit | edit source]

This example enumerates the all the entries in the current working directory, that end in ".gpr". The Start_Search procedure is used to start enumerating a directory. It will To remove the filter criteria you can pass an empty string. Iterating through the directory involves calling Get_Next_Entry to return the next directory entry and More_Entries to check for more entries. Once the search is complete, call End_Search.

   type Search_Type is limited private;
   type Directory_Entry_Type is limited private;

   procedure Start_Search (Search    : in out Search_Type;
                          Directory : in String;
                          Pattern   : in String;
                          Filter    : in Filter_Type := (others => True));
   procedure Get_Next_Entry (Search : in out Search_Type; Directory_Entry : out Directory_Entry_Type);
   function More_Entries (Search : in Search_Type) return Boolean;
   procedure End_Search (Search : in out Search_Type);

The resulting Directory_Entry_Type has a full name, simple name, size, kind, and modification type.

  function Simple_Name (Directory_Entry : in Directory_Entry_Type) return String;
  function Full_Name (Directory_Entry : in Directory_Entry_Type) return String;
  function Kind (Directory_Entry : in Directory_Entry_Type) return File_Kind; --  Directory, Ordinary_File, Special_File
  function Size (Directory_Entry : in Directory_Entry_Type) return File_Size;
  function Modification_Time (Directory_Entry : in Directory_Entry_Type) return Ada.Calendar.Time;
with Ada.Text_IO;
with Ada.Directories;
use Ada.Text_IO;
use Ada.Directories;
    
procedure Main is
  Dir : Directory_Entry_Type;
  Dir_Search : Search_Type;
  
  Curr_Dir : string := Current_Directory;
begin
  Put("Current Directory: ");
  Put_Line(Curr_Dir);
   
  Start_Search(Search => Dir_Search,
               Directory => Curr_Dir,
               Pattern => "*.gpr");
  loop
     Get_Next_Entry(Dir_Search, Dir);
     
     Put(Full_Name(Dir));
     Set_Col(50);
     
     if Kind(Dir) = Ordinary_File then
        Put(Size(Dir)'Image);
     end if;
     Set_Col(60);
     
    Put_Line(Kind(Dir)'Image);
    
     exit when not More_Entries(Dir_Search);
  end loop;
  
  End_Search(Dir_Search);
  
end Main;

Directory Manipulation

[edit | edit source]

Creating a directory is done with Create_Directory. Create_Path creates all the directories in the path. The directory path on GNAT Ada can contain either "\" or "/" characters. Rename renames a directory or file within a directory. <Delete_Tree> deletes an entire hierarchy of directories and files. Delete_Directory deletes an empty directory (non-empty directories throw a Use_Error exception. Delete_File deletes an ordinary file in a directory. Note that the Form parameters is implementation specific. On the version of GNAT Ada I'm using, it can only be used to set the encoding.

   procedure Create_Directory (New_Directory : in String; Form : in String := "");
   procedure Create_Path (New_Directory : in String; Form : in String := "");
   procedure Rename (Old_Name, New_Name : in String);
   procedure Delete_Tree (Directory : in String);
   procedure Delete_Directory (Directory : in String);
   procedure Delete_File (Name : in String);

Below is a simple example that creates a set of directories and iterates through those directories. Note that it runs on both Windows and Linux with the "/" path separator. The returned path separators are appropriate to the filesystem (e.g. Windows return "\").

procedure Main is
  Dir : Directory_Entry_Type;
  Dir_Search : Search_Type;   
begin   
  Start_Search(Search => Dir_Search, Directory => Curr_Dir, Pattern => "");
  
  Create_Path("Foo");
  Create_Path("Bar/Baz/Qux");
  
  loop
     Get_Next_Entry(Dir_Search, Dir);
     Put_Line(Full_Name(Dir));
     exit when not More_Entries(Dir_Search);
  end loop;
  
  End_Search(Dir_Search);
  
  Delete_Directory("Foo");
  Delete_Tree("Bar");
  
end Main;

Exceptions

[edit | edit source]

Status_Error is raised if a directory entry is invalid or when enumerating directories past the last directory. Name_Error is usually thrown if the directory or filename is not able to identify a file or directory (either non-existent or invalid depending on context). Use_Error is thrown if directories are not supported or directories are not traversable.

  Status_Error : exception renames Ada.IO_Exceptions.Status_Error;
  Name_Error   : exception renames Ada.IO_Exceptions.Name_Error;
  Use_Error    : exception renames Ada.IO_Exceptions.Use_Error;
  Device_Error : exception renames Ada.IO_Exceptions.Device_Error;

Detailed Use Tutorial

[edit | edit source]

This article will be based on a small program, aDir, to which we will add functionality as we progress through the various functions, procedures, and types. In its most basic form, all it does is output the current default directory:

with Ada.Text_IO;
with Ada.Directories;

procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
end aDir;

When running the above program, you should see output somewhat similar to this:

 Starting default directory: /home/thomas/wiki_examples/aDir

Rather than the above path (/home/thomas/wiki_examples/aDir), you should see the path of the directory from which you launched aDir on your system.

Directory and File Operations

[edit | edit source]

When working with files and directories, some basic functionality is required. We must be able to move to a specific directory; we must be able to copy, rename, and delete both files and directories; and we must be able to create new directories. Luckily, this is exactly what the next function and procedures enable us to do.

Current_Directory

[edit | edit source]

The specification for Current_Directory looks like this:

function Current_Directory return String;

We've already seen how Current_Directory works, but there are a few things yet that are worth mentioning about Current_Directory

  • Current_Directory returns the full directory name for the current default directory. This does not equal the directory where the program itself resides.
  • The directory name returned is suitable for use by the Set_Directory procedure.
  • The exception Use_Error is propagated if a default directory is not supported by the operating system/environment.

When we executed the basic aDir program above, the output was the path to where the aDir program resides on my system. But lets see what happens if we invoke the program from somewhere else:

 $ pwd
 /home/thomas/wiki_examples/aDir
 $ ./adir
 Starting default directory: /home/thomas/wiki_examples/aDir
 $ cd && pwd
 /home/thomas
 $ wiki_examples/aDir/adir
 Starting default directory: /home/thomas

As you can see, Current_Directory does not equal the path to the actual program. Instead it returns the current default directory, which is entirely implementation dependent. In the above example, it varies depending on where from the program is invoked. So unless you've specifically set the default directory using Set_Directory, you cannot be 100% sure what path Current_Directory will return.

The second bullet-point is self-explanatory and the third is impossible for me to test, as I currently don't own a system that does not support the notion of a directory.

Ada.Directories.Current_Directory Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
end aDir;

Set_Directory

[edit | edit source]

The specification for Set_Directory looks like this:

procedure Set_Directory (Directory : String);

With Set_Directory we're able to change the current default directory. Add this little bit of code to the body of the aDir program to see how it works:

D.Set_Directory (Directory => "/home/thomas");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);

If we execute the program now, we get this:

 Starting default directory: /home/thomas/wiki_examples/aDir
 New default directory: /home/thomas

If the directory you're trying to set doesn't exist, a Name_Error is raised. Lets see how that works:

D.Set_Directory (Directory => "/home/does/not/exist");
   
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Directory does not exist.");

If you add the above snippet to the core aDir program, you will get output that looks something like this:

 Starting default directory: /home/thomas/wiki_examples/aDir
 Directory does not exist.

Since the directory set by the Set_Directory does not exist, the Name_Error exception is raised and caught. You should not, however, habitually use Set_Directory to check if a given directory exists. For this simple task we have the Exists function.

Ada.Directories.Set_Directory Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Set_Directory (Directory => "/home/thomas");
   IO.Put ("New default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
end aDir;
with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Set_Directory (Directory => "/home/does/not/exist");
 
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Directory does not exist.");
end aDir;

Create_Directory

[edit | edit source]

The specification for Create_Directory looks like this:

procedure Create_Directory
  (New_Directory : String;
   Form          : String := "");

With this procedure we can, to nobody's surprise, create new directories. But before we try it out, we will first deal with the Form parameter. The RM has this to say about it:

The Form parameter can be used to give system-dependent characteristics of the directory; the interpretation of the Form parameter is implementation-defined. A null string for Form specifies the use of the default options of the implementation of the new directory.

For my specific environment (Slackware 12.1 and GNATMAKE GPL 2008 compiler) the Form parameter does precisely nothing. This might not be the case for other implementations, but I'm fairly certain that for most standard systems (Unix, Linux, BSD, Windows) Form is not used. Thus, Form should be a null string, unless you're on a platform where the implementation defines the use of Form.

But enough about that - lets add some code to aDir to see how Create_Directory works:

D.Create_Directory (New_Directory => "some_dir");

With that one line of code added to the program, we can see how the program by doing a little test:

 $ ls -l
 -rw-rw-rw- 1 thomas users    502 2009-09-24 22:09 Proj.gpr
 -rwxr-xr-x 1 thomas users 578199 2009-09-29 22:03 adir*
 -rw-rw-rw- 1 thomas users    517 2009-09-29 22:03 adir.adb
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:03 objects/
 $ ./adir
 Starting default directory: /home/thomas/wiki_examples/aDir
 $ ls  -l
 -rw-rw-rw- 1 thomas users    502 2009-09-24 22:09 Proj.gpr
 -rwxr-xr-x 1 thomas users 578199 2009-09-29 22:03 adir*
 -rw-rw-rw- 1 thomas users    517 2009-09-29 22:03 adir.adb
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:03 objects/
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:23 some_dir/
 $ cd some_dir/
 $ ../adir
 Starting default directory: /home/thomas/wiki_examples/aDir/some_dir
 $ ls -l
 drwxr-xr-x 2 thomas users 0 2009-09-29 22:25 some_dir/

As you can see from the above, Create_Directory creates the new directory in the current default directory which, in this case, is the directory from where we invoke adir. In case we want to create the some_dir directory in a directory other than the current default, we're going to have to add another line of code prior to the Create_Directory line:

D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");

You should of course substitute the above path with something that fits your system.

Next we delete the already created some_dir directories, and then we try the same chain of commands as before:

 $ ls -l
 -rw-rw-rw- 1 thomas users    502 2009-09-24 22:09 Proj.gpr
 -rwxr-xr-x 1 thomas users 578224 2009-09-29 22:32 adir*
 -rw-rw-rw- 1 thomas users    635 2009-09-29 22:32 adir.adb
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:32 objects/
 $ ./adir
 Starting default directory: /home/thomas/wiki_examples/aDir
 $ ls  -l
 -rw-rw-rw- 1 thomas users    502 2009-09-24 22:09 Proj.gpr
 -rwxr-xr-x 1 thomas users 578224 2009-09-29 22:32 adir*
 -rw-rw-rw- 1 thomas users    635 2009-09-29 22:32 adir.adb
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:32 objects/
 drwxr-xr-x 2 thomas users      0 2009-09-29 22:40 some_dir/
 $ cd some_dir/
 $ ../adir
 Starting default directory: /home/thomas/wiki_examples/aDir/some_dir
 raised ADA.IO_EXCEPTIONS.USE_ERROR : creation of new directory "some_dir" failed

Our program fails at creating the some_dir directory the second time, because it already exists, proving that the program no longer creates some_dir wherever we invoke the program. This is exactly what we wanted, though we should probably add some code to catch the exception, instead of just letting the program crash:

exception
   when D.Use_Error =>
      IO.Put_Line ("Directory cannot be created.");

Now lets try that last command again:

 $ ../adir
 Starting default directory: /home/thomas/wiki_examples/aDir/some_dir
 Directory cannot be created.

Much better!

Besides Use_Error, there's also Name_Error, which is raised if the name given isn't a valid directory. Let's try and change some_dir to something that is obviously not going to cut it as a directory name: An empty string. We just need to change one line in the program:

D.Create_Directory (New_Directory => "some_dir");

To

D.Create_Directory (New_Directory => "");

Now, running the program we get:

 Starting default directory: /home/thomas/wiki_examples/aDir/some_dir
 raised ADA.IO_EXCEPTIONS.NAME_ERROR : invalid new directory path name ""

We can catch Name_Error by adding one more exception handler:

exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Directory cannot be created.");
   when D.Name_Error =>
      IO.Put_Line (Item => "Directory is not valid.");

Running the program we now get this:

 Starting default directory: /home/thomas/wiki_examples/aDir
 Directory is not valid.

Perfect.


Ada.Directories.Create_Directory Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
   D.Create_Directory (New_Directory => "some_dir");
   --  Running the program multiple times will raise the Use_Error.
   --  Changing some_dir to a null string will raise the Name_Error.
exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Directory cannot be created.");
   when D.Name_Error =>
      IO.Put_Line (Item => "Directory is not valid.");
end aDir;

Delete_Directory

[edit | edit source]

The specification for Delete_Directory looks like this:

procedure Delete_Directory (Directory : String);

With Delete_Directory you can delete a directory which is empty and for which the program has sufficient permissions. The Directory parameter accepts both a single named directory or the full path to a directory. In the case of a single name, the current default directory is prepended to create a full path.

Add these lines to the basic aDir program to see it in action:

D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
   
D.Create_Directory (New_Directory => "some_dir");
if D.Exists (Name => "some_dir") then
   IO.Put_Line ("some_dir exists.");
end if;
D.Delete_Directory (Directory => "some_dir");
   
D.Create_Directory (New_Directory => "some_dir2");
if D.Exists (Name => "some_dir2") then
   IO.Put_Line ("some_dir2 exists.");
end if;
D.Delete_Directory (Directory => "/home/thomas/wiki_examples/aDir/some_dir2");

Executing this program, we get the following output:

 $ ./adir
 Starting default directory: /home/thomas/wiki_examples/aDir
 New default directory: /home/thomas/wiki_examples/aDir
 some_dir exists.
 some_dir2 exists.
 $ ls -l
 -rw-rw-rw- 1 thomas users    502 2009-09-24 22:09 Proj.gpr
 -rwxr-xr-x 1 thomas users 578224 2009-09-30 16:13 adir*
 -rw-rw-rw- 1 thomas users    973 2009-09-30 16:14 adir.adb
 drwxr-xr-x 2 thomas users      0 2009-09-30 16:13 objects/

The example shows that Delete_Directory does indeed work with both the full path to the directory and with just the directory name. The two Exists calls are just there to help visualize the fact that the directories are, in fact, created.

The Name_Error exception is raised if the Directory string does not match an existing directory and the Use_Error exception is raised if the directory cannot be deleted or if it isn't empty. Add this code to the core aDir program:

Delete_Non_Existing :
declare
begin
   D.Delete_Directory (Directory => "does_not_exist");  
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Directory not found.");
end Delete_Non_Existing;
     
Delete_With_Contents :
declare  
begin
   D.Delete_Directory (Directory => "dir_with_contents");    
exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Cannot delete non-empty directory.");
end Delete_With_Contents;

And the output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 Directory not found.
 Cannot delete non-empty directory.

As expected. In order to delete non-empty directories, you will have to use the Delete_Tree procedure.


Ada.Directories.Delete_Directory Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
   IO.Put ("New default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Create_Directory (New_Directory => "some_dir");
   if D.Exists (Name => "some_dir") then
      IO.Put_Line ("some_dir exists.");
   end if;
   D.Delete_Directory (Directory => "some_dir");
 
   D.Create_Directory (New_Directory => "some_dir2");
   if D.Exists (Name => "some_dir2") then
      IO.Put_Line ("some_dir2 exists.");
   end if;
   D.Delete_Directory (Directory => "/home/thomas/wiki_examples/aDir/some_dir2");
end Adir;
with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   Delete_Non_Existing :
   declare
   begin
      D.Delete_Directory (Directory => "does_not_exist");  
   exception
      when D.Name_Error =>
         IO.Put_Line (Item => "Directory not found.");
   end Delete_Non_Existing;
 
   Delete_With_Contents :
   declare  
   begin
      D.Delete_Directory (Directory => "dir_with_contents");    
   exception
      when D.Use_Error =>
         IO.Put_Line (Item => "Cannot delete non-empty directory.");
   end Delete_With_Contents;
end aDir;


Create_Path

[edit | edit source]

The specification for Create_Path looks like this:

procedure Create_Path
  (New_Directory : String;
   Form          : String := "");

With Create_Path it is possible to create nested directories, even if the parent directories do not exist. It is much like issuing a mkdir -p /some/path/to/new/dir command, where the entire path is created, all the way down to the dir directory.

Just as with its sibling Create_Directory, the Form parameter is used for implementation specific characteristics. A null string specifies the use of the implementation's default options for creating new directories.

To create the directory this/is/a/new/directory all we have to add to the aDir program is this:

D.Create_Path (New_Directory => "this/is/a/new/directory");
if D.Exists (Name => "this/is/a/new/directory") then
   IO.Put_Line (Item => "this/is/a/new/directory exists!");
end if;

And the output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 this/is/a/new/directory exists!

If the path already exists when calling Create_Path, the procedure simply do nothing.

Create_Path works relative to the current default directory, unless the Directory given describes a path starting from the root of a filesystem, i.e. / for *nix systems and c:/ for a Windows system. To create the this/is/a/new/directory in /tmp, all we have to do is add /tmp to the Directory string:

D.Create_Path (New_Directory => "/tmp/this/is/a/new/directory");
if D.Exists (Name => "/tmp/this/is/a/new/directory") then
   IO.Put_Line (Item => "/tmp/this/is/a/new/directory exists!");
end if;

So if the path is relative, the current default directory is used as the base, but if the path is absolute, the current default directory is ignored and the path is created entirely from the parameter.

The two exceptions, Name_Error and Use_Error, are raised under the same circumstances as Create_Directory Name_Error if the New_Directory string does not identify a directory, and Use_Error if the program is not able to create any one of the directories, for example due to permission issues.

Ada.Directories.Create_Path Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Create_Path (New_Directory => "this/is/a/new/directory");
   if D.Exists (Name => "this/is/a/new/directory") then
      IO.Put_Line (Item => "this/is/a/new/directory exists!");
   end if;
end aDir;

Delete_Tree

[edit | edit source]

The specification for Delete_Tree looks like this:

procedure Delete_Tree (Directory : String);

Delete_Tree enables us to delete directories and their contents in one fell swoop, so if we build on the example from Create_Path, we can delete the this/is/a/new/directory tree by adding this to the program:

D.Delete_Tree (Directory => "this/");
if not D.Exists (Name => "this/") then
   IO.Put_Line (Item => "this/is/a/new/directory does NOT exist!");
end if;

And the output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 this/is/a/new/directory exists!
 this/is/a/new/directory does NOT exist!

The two exceptions Name_Error and Use_Error are also available to us. They work as expected, with Name_Error being raised if the Directory doesn't exist and Use_Error being raised if Directory can't be deleted for some reason or another. There's an important note about this behavior in the RM:

The exception Use_Error is propagated if the external environment does not support the deletion of the directory or some portion of its contents with the given name (in the absence of Name_Error). If Use_Error is propagated, it is unspecified whether a portion of the contents of the directory is deleted.

It is important to pay special attention to the last sentence: it is unspecified whether a portion of the contents of the directory is deleted.

What this means, is that a Use_Error can be raised, but that wont necessarily mean that the directory and its contents are 100% intact. Some of it may very well have been deleted.

Finally, it should be mentioned that Delete_Tree looks for Directory in the current default directory, unless an absolute path is given.

Ada.Directories.Delete_Tree Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Create_Path (New_Directory => "this/is/a/new/directory");
   if D.Exists (Name => "this/is/a/new/directory") then
      IO.Put_Line (Item => "this/is/a/new/directory exists!");
   end if;
 
   D.Delete_Tree (Directory => "this/");
   if not D.Exists (Name => "this/") then
      IO.Put_Line (Item => "this/is/a/new/directory does NOT exist!");
   end if;
end aDir;

Delete_File

[edit | edit source]

The specification for Delete_File looks like this:

procedure Delete_File (Name : String);

Delete_File works much the same as Delete_Directory, it just deletes files instead of directories. Files, in the context of Ada.Directories can be either an ordinary file or a special file:

External files may be classified as directories, special files, or ordinary files. A directory is an external file that is a container for files on the target system. A special file is an external file that cannot be created or read by a predefined Ada input-output package. External files that are not special files or directories are called ordinary files.

The usual exceptions also apply to Delete_File.

Lets create a new file:

 $ touch some_file

And now add this to the basic aDir program:

if D.Exists (Name => "some_file") then
   IO.Put_Line (Item => "some_file exists");   
end if;
   
D.Delete_File (Name => "some_file");
   
if not D.Exists (Name => "some_file") then
   IO.Put_Line (Item => "some_file does NOT exist");   
end if;

The output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 some_file exists
 some_file does NOT exist

And the some_file file is gone.

Here's an example where we (A) don't have the proper permissions to delete the some_file file and (B) try to delete a non-existent file:

if D.Exists (Name => "some_file") then
   IO.Put_Line (Item => "some_file exists");   
end if;
   
declare
begin
   D.Delete_File (Name => "some_file");
exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Cannot delete some_file");
end;
   
declare
begin
   D.Delete_File (Name => "some_wrong_filename");
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "File does not exist");
end;
   
if D.Exists (Name => "some_file") then
   IO.Put_Line (Item => "some_file still exists");   
end if;

The output should look like this:

 Starting default directory: /home/thomas/wiki_examples/aDir
 some_file exists
 Cannot delete some_file
 File does not exist
 some_file still exists

And with that, we conclude our look at Delete_File.


Ada.Directories.Delete_File Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   if D.Exists (Name => "some_file") then
      IO.Put_Line (Item => "some_file exists");   
   end if;
 
   D.Delete_File (Name => "some_file");
 
   if not D.Exists (Name => "some_file") then
      IO.Put_Line (Item => "some_file does NOT exist");   
   end if;
end aDir;
with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   if D.Exists (Name => "some_file") then
      IO.Put_Line (Item => "some_file exists");   
   end if;
 
   declare
   begin
      D.Delete_File (Name => "some_file");
   exception
      when D.Use_Error =>
         IO.Put_Line (Item => "Cannot delete some_file");
   end;
 
   declare
   begin
      D.Delete_File (Name => "some_wrong_filename");
   exception
      when D.Name_Error =>
         IO.Put_Line (Item => "File does not exist");
   end;
 
   if D.Exists (Name => "some_file") then
      IO.Put_Line (Item => "some_file still exists");   
   end if;
end aDir;

Rename

[edit | edit source]

The specification for Rename looks like this:

procedure Rename (Old_Name, New_Name : String);

The Rename procedures renames both directories and files, providing that the New_Name doesn't already exist. The Name_Error exception is raised if Old_Name does not match a directory or file, and Use_Error is raised if the directory/file cannot be renamed. In the following example, we will assume the existence of the directory some_dir and the file some_file.

First we will add a procedure to the declarative part of the aDir program:

procedure Do_We_Exist (A_Name : String) is
begin
   if D.Exists (Name => A_Name) then
      IO.Put_Line (Item => "Yes, " & A_Name & " exists");
   else 
      IO.Put_Line (Item => "No, " & A_Name & " does not exist");
   end if;
end Do_We_Exist;

This procedure is just a bit of sugar to the program. It's mainly here to avoid having to repeat the if block over and over.

Next we'll add a few lines to the body of the program:

Do_We_Exist (A_Name => "some_new_dir"); 
Do_We_Exist (A_Name => "some_new_file");
   
D.Rename (Old_Name => "some_dir",
          New_Name => "some_new_dir");
D.Rename (Old_Name => "some_file",
          New_Name => "some_new_file");
   
Do_We_Exist (A_Name => "some_new_dir"); 
Do_We_Exist (A_Name => "some_new_file");
   
D.Rename (Old_Name => "some_new_dir",
          New_Name => "some_dir");
D.Rename (Old_Name => "some_new_file",
          New_Name => "some_file");

The output from this is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 No, some_new_dir does not exist
 No, some_new_file does not exist
 Yes, some_new_dir exists
 Yes, some_new_file exists

Renaming files and directories just doesn't get any easier than this.

Ada.Directories.Rename Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
   procedure Do_We_Exist (A_Name : String) is
   begin
      if D.Exists (Name => A_Name) then
         IO.Put_Line (Item => "Yes, " & A_Name & " exists");
      else 
         IO.Put_Line (Item => "No, " & A_Name & " does not exist");
      end if;
   end Do_We_Exist;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   Do_We_Exist (A_Name => "some_new_dir"); 
   Do_We_Exist (A_Name => "some_new_file");
 
   D.Rename (Old_Name => "some_dir",
             New_Name => "some_new_dir");
   D.Rename (Old_Name => "some_file",
             New_Name => "some_new_file");
 
   Do_We_Exist (A_Name => "some_new_dir"); 
   Do_We_Exist (A_Name => "some_new_file");
 
   D.Rename (Old_Name => "some_new_dir",
             New_Name => "some_dir");
   D.Rename (Old_Name => "some_new_file",
             New_Name => "some_file");
end aDir;

Copy_File

[edit | edit source]

The specification for Copy_File looks like this:

procedure Copy_File
  (Source_Name   : String;
   Target_Name   : String;
   Form          : String := "");

Copy_File copies the contents of an existing file, Source_Name, to a new file named Target_Name. The result is a duplicate of the source file. Copy_File does not copy directories. Only ordinary files are copied. As noted earlier, the Form parameter is only used if the implementation requires it. Here's what the RM has to say about Form:

The Form parameter can be used to give system-dependent characteristics of the resulting external file; the interpretation of the Form parameter is implementation-defined.

In order to copy some_file to some_new_file, all we have to do is add this line to the body of the aDir program:

D.Copy_File (Source_Name => "some_file",
             Target_Name => "some_new_file");

Please note that if some_new_file already exists, it will be overwritten. The usual exceptions, Name_Error and Use_Error, also apply to Copy_File

declare
begin
   D.Copy_File (Source_Name => "some_non_existant_file",
                Target_Name => "");
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Source and/or Target Name_Error");
end;
   
declare
begin
   D.Copy_File (Source_Name => "some_file",
                Target_Name => "/root/illegal_file_location");
exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Source and/or Target Use_Error");
end;

Name_Error is raised if a Source_Name or Target_Name does not properly identify a file. Use_Error is raised if the action fails; the files are there, but for some other reason, copying is not possible.


Ada.Directories.Copy_File Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   D.Copy_File (Source_Name => "some_file",
                Target_Name => "some_new_file");
end aDir;
with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   declare
   begin
      D.Copy_File (Source_Name => "some_non_existant_file",
                   Target_Name => "");
   exception
      when D.Name_Error =>
         IO.Put_Line (Item => "Source and/or Target Name_Error");
   end;
 
   declare
   begin
      D.Copy_File (Source_Name => "some_file",
                   Target_Name => "/root/illegal_file_location");
   exception
      when D.Use_Error =>
         IO.Put_Line (Item => "Source and/or Target Use_Error");
   end;
end aDir;

Name Operations

[edit | edit source]

The following six functions do not interact with the file system at all. They just manipulate strings. Armed with these functions, we are able to build file path strings, extract file names from paths, extract file extensions and discover the name of the directory containing a file.

Full_Name

[edit | edit source]

The specification for Full_Name looks like this:

function Full_Name (Name : String) return String;

Full_Name returns the full path to a given Name. The Name parameter can be both a single filename, a relative path to a file or an absolute path. Full_Name does not care about whether the file actually exists or not. It is only concerned with returning a proper full path. The RM has this to say about it:

Returns the full name corresponding to the file name specified by Name. The exception Name_Error is propagated if the string given as Name does not allow the identification of an external file (including directories and special files).

When I first read that, I thought that the sentence The exception Name_Error is propagated if the string given as Name does not allow the identification of an external file meant that Name_Error would be raised if the given Name parameter could not be resolved to an actual existing file. This is, however, not the case. Instead it means that Name_Error is raised if Name is malformed.

Lets see how it works:

IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
   
D.Set_Directory (Directory => "/tmp");
   
IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));

IO.Put_Line (Item => D.Full_Name (Name => ""));
   --  Malformed Name parameter
   
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name parameter is malformed");

And the resulting output:

 Starting default directory: /home/thomas/wiki_examples/aDir
 /home/thomas/wiki_examples/aDir/foo
 /home/thomas/wiki_examples/aDir/foo/bar
 /home/thomas/stuff
 New default directory: /tmp
 /tmp/foo
 /tmp/foo/bar
 /home/thomas/stuff
 Name parameter is malformed

As with all the other functions and procedures which depend on the current default directory, the Name parameter is appended to the current default directory if Name is relative, whereas the current default directory is ignored if Name is an absolute path.

Ada.Directories.Full_Name Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Full_Name (Name => "foo"));
   IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
   IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
 
   D.Set_Directory (Directory => "/tmp");
   IO.Put ("New default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Full_Name (Name => "foo"));
   IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
   IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
 
   IO.Put_Line (Item => D.Full_Name (Name => ""));
   --  Malformed Name parameter
 
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name parameter is malformed");
end aDir;

Simple_Name

[edit | edit source]

The specification for Simple_Name looks like this:

function Simple_Name (Name : String) return String;

Where Full_Name returns the entire path to a file, Simple_Name returns the simple name component of a path. So given the Name parameter /home/thomas/foo, it will return foo. The Name_Error exception is raised only if Name is malformed. Lets see an example:

IO.Put_Line (Item => D.Simple_Name (Name => "foo"));
IO.Put_Line (Item => D.Simple_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Simple_Name (Name => "/home/thomas/stuff"));
   
IO.Put_Line (Item => D.Simple_Name (Name => ""));
--  Malformed Name parameter
   
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name parameter is malformed");
end aDir;

And the output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 foo
 bar
 stuff
 Name parameter is malformed

Quite handy.


Ada.Directories.Simple_Name Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Simple_Name (Name => "foo"));
   IO.Put_Line (Item => D.Simple_Name (Name => "foo/bar"));
   IO.Put_Line (Item => D.Simple_Name (Name => "/home/thomas/stuff"));
 
   IO.Put_Line (Item => D.Simple_Name (Name => ""));
   --  Malformed Name parameter
 
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name parameter is malformed");
end aDir;

Containing_Directory

[edit | edit source]

The specification for Containing_Directory looks like this:

function Containing_Directory (Name : String) return String;

Containing_Directory removes the simple name of the Name path given. Lets see it in action:

IO.Put_Line (Item => D.Containing_Directory (Name => "foo"));
IO.Put_Line (Item => D.Containing_Directory (Name => "foo/bar"));
IO.Put_Line (Item => D.Containing_Directory (Name => "/home/thomas/stuff"));
   
declare
begin
   IO.Put_Line (Item => D.Containing_Directory (Name => ""));
   --  Malformed Name parameter
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end;
   
declare
begin
   IO.Put_Line (Item => D.Containing_Directory (Name => "/"));
   --  No parent directory
exception
   when D.Use_Error =>
      IO.Put_Line (Item => "Use_Error raised. No containing directory.");
end;

And the output is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 /home/thomas/wiki_examples/aDir
 foo
 /home/thomas
 Name_Error raised. Malformed Name.
 Use_Error raised. No containing directory.

As expected. Notice that the Use_Error is raised only when the given Name parameter does not have a containing directory.

Ada.Directories.Containing_Directory Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Containing_Directory (Name => "foo"));
   IO.Put_Line (Item => D.Containing_Directory (Name => "foo/bar"));
   IO.Put_Line (Item => D.Containing_Directory (Name => "/home/thomas/stuff"));
 
   declare
   begin
      IO.Put_Line (Item => D.Containing_Directory (Name => ""));
      --  Malformed Name parameter
   exception
      when D.Name_Error =>
         IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
   end;
 
   declare
   begin
      IO.Put_Line (Item => D.Containing_Directory (Name => "/"));
      --  No parent directory
   exception
      when D.Use_Error =>
         IO.Put_Line (Item => "Use_Error raised. No containing directory.");
   end;
end aDir;

Extension

[edit | edit source]

The specification for Extension looks like this:

function Extension (Name : String) return String;

If you want to extract the extension of a file, this function is what you need. Here's an example on usage:

IO.Put_Line (Item => D.Extension (Name => "foo.txt"));
IO.Put_Line (Item => D.Extension (Name => "foo/bar"));
IO.Put_Line (Item => D.Extension (Name => "/home/thomas/stuff.conf"));
   
IO.Put_Line (Item => D.Extension (Name => ""));
   --  Malformed Name parameter
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised. Malformed Name.");

And the output:

 Starting default directory: /home/thomas/wiki_examples/aDir
 txt
  
 conf
 Name_Error raised. Malformed Name.

As you can see, if there are no extension (the foo/bar line), a null string is returned. The Name_Error exception is raised when the Name parameter is malformed.


Ada.Directories.Extension Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Extension (Name => "foo.txt"));
   IO.Put_Line (Item => D.Extension (Name => "foo/bar"));
   IO.Put_Line (Item => D.Extension (Name => "/home/thomas/stuff.conf"));
 
   IO.Put_Line (Item => D.Extension (Name => ""));
      --  Malformed Name parameter
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end aDir;

Base_Name

[edit | edit source]

The specification for Base_Name looks like this:

function Base_Name (Name : String) return String;

I'm quite sure you've already guessed what Base_Name does, but indulge me in some example-code:

IO.Put_Line (Item => D.Base_Name (Name => "foo.txt"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret.conf"));
IO.Put_Line (Item => D.Base_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Base_Name (Name => "/home/thomas/stuff.conf"));
   
IO.Put_Line (Item => D.Base_Name (Name => ""));
   --  Malformed Name parameter
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised. Malformed Name.");

The output:

 Starting default directory: /home/thomas/wiki_examples/aDir
 foo
 
 .secret
 bar
 stuff
 Name_Error raised. Malformed Name.

Notice the "odd" behavior in regards to the .secret file. Instead of returning the full .secret string, which is in fact the base name of the file, all we get is a null string. This might be considered somewhat weird, but it is in line with what the programmers of Base_Name intended. Here's a comment from the actual Base_Name function:

--  Look for the last dot in the file name and return the part of the
--  file name preceding this last dot. If the first dot is the first
--  character of the file name, the base name is the empty string.

So there we have it, straight from the horse's mouth: If Base_Name returns a null string, you are probably dealing with a dot file, and you will have to parse it manually. So no, you cannot expect Base_Name to return the same results as for example the standard GNU tool basename. Base_Name must work reliably across many platforms, thus the authors had to decide on a common, predictable, method. Returning a null string for dot files is just that, predictable.


Ada.Directories.Base_Name Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Base_Name (Name => "foo.txt"));
   IO.Put_Line (Item => D.Base_Name (Name => ".secret"));
   IO.Put_Line (Item => D.Base_Name (Name => ".secret.conf"));
   IO.Put_Line (Item => D.Base_Name (Name => "foo/bar"));
   IO.Put_Line (Item => D.Base_Name (Name => "/home/thomas/stuff.conf"));   
 
   IO.Put_Line (Item => D.Base_Name (Name => ""));
      --  Malformed Name parameter
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end aDir;

Compose

[edit | edit source]

The specification for Compose looks like this:

function Compose
     (Containing_Directory : String := "";
      Name                 : String;
      Extension            : String := "") return String;

Compose allows us to construct strings from directory paths, simple names and extensions. Some basic checks are made to ensure that the resulting string is syntactically sound. Compose does not bother with the actual existence of the resulting path, it only cares about it's validity as a full name to a file.

Lets see how it works:

IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
                                Name                 => "bar",
                                Extension            => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
                                Name                 => "bar",
                                Extension            => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                Name                 => "bar",
                                Extension            => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                Name                 => "bar.conf",
                                Extension            => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                Name                 => "",
                                Extension            => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
                                Name                 => "",
                                Extension            => "conf"));
 
IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
                                Name                 => "",
                                Extension            => ""));
--  Force Name_Error by omitting Name and Extension
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised.");

The output from the above is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 foo/bar.conf
 bar.conf
 /foo/bar
 /foo/bar.conf
 /foo/.conf
 .conf
 Name_Error raised. Malformed Name.

There's no surprise here.

The Name_Error exception is raised when:

The exception Name_Error is propagated if the string given as Containing_Directory is not null and does not allow the identification of a directory, or if the string given as Extension is not null and is not a possible extension, or if the string given as Name is not a possible simple name (if Extension is null) or base name (if Extension is non-null).

Compose is quite handy when building string paths to a file. It's a far stretch better than just concatenating the strings yourself.


Ada.Directories.Compose Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
 
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
                                   Name                 => "bar",
                                   Extension            => "conf"));
   IO.Put_Line (Item => D.Compose (Containing_Directory => "",
                                   Name                 => "bar",
                                   Extension            => "conf"));
   IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                   Name                 => "bar",
                                   Extension            => ""));
   IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                   Name                 => "bar.conf",
                                   Extension            => ""));
   IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
                                   Name                 => "",
                                   Extension            => "conf"));
   IO.Put_Line (Item => D.Compose (Containing_Directory => "",
                                   Name                 => "",
                                   Extension            => "conf"));
 
   IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
                                   Name                 => "",
                                   Extension            => ""));
   --  Force Name_Error by omitting Name and Extension
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised.");
end aDir;

File and directory queries

[edit | edit source]

The purpose of the following functions is obvious. With names such as Exists, Kind, Size and Modification_Time it shouldn't be too hard to guess what these functions do, so without further ado, lets dive in.

Exists

[edit | edit source]

The specification for Exists looks like this:

function Exists (Name : String) return Boolean;

If you want to know whether a file or a directory exists, you should use Exists. We've already seen Exists in action in some of the previous examples, but let's see a snippet of code where Exists is the star of the show:

if D.Exists (Name => "some_file") then
   IO.Put_Line (Item => "some_file exists");
end if;
   
if D.Exists (Name => "/home/thomas/wiki_examples/aDir/some_dir") then
   IO.Put_Line (Item => "/home/thomas/wiki_examples/aDir/some_dir exists");
end if;
   
if not D.Exists (Name => "nonexistant_file") then
   IO.Put_Line (Item => "nonexistant_file does not exist");
end if;
   
if D.Exists (Name => "") then
   IO.Put_Line (Item => "This is impossible!");
end if;
   
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised");

And the output:

 Starting default directory: /home/thomas/wiki_examples/aDir
 some_file exists
 /home/thomas/wiki_examples/aDir/some_dir exists
 nonexistant_file does not exist
 Name_Error raised

As can be seen, the current default directory is taken into account if a relative path is given and Name_Error is raised when the given Name is invalid as either a file or a directory, as is the case with the empty string in the above example. Other than that, there's not much to say about this function.

Ada.Directories.Exists Example Source

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   if D.Exists (Name => "some_file") then
      IO.Put_Line (Item => "some_file exists");
   end if;
 
   if D.Exists (Name => "/home/thomas/wiki_examples/aDir/some_dir") then
      IO.Put_Line (Item => "/home/thomas/wiki_examples/aDir/some_dir exists");
   end if;
 
   if not D.Exists (Name => "nonexistant_file") then
      IO.Put_Line (Item => "nonexistant_file does not exist");
   end if;
 
   if D.Exists (Name => "") then
      IO.Put_Line (Item => "This is impossible!");
   end if;
 
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised");
end aDir;

The specification for Kind looks like this:

function Kind (Name : String) return File_Kind;

In Ada.Directories files/directories are classified as one of three possible "file kinds": DIRECTORY, SPECIAL_FILE or ORDINARY_FILE. A DIRECTORY is a file that is a container for other files. A SPECIAL_FILE is a file that cannot be read or created by a predefined Ada input-output package. A File that is not either a DIRECTORY or a SPECIAL_FILE is an ORDINARY_FILE.

Lets see how it works. First we must add a line of code to the declarative part of the aDir program:

package IOE is new Ada.Text_IO.Enumeration_IO (D.File_Kind);

And then the body:

IOE.Put (Item => D.Kind (Name => "some_file"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "some_dir"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "/dev/sda"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => ""));
   
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised");

And the output:

 Starting default directory: /home/thomas/wiki_examples/aDir
 ORDINARY_FILE
 DIRECTORY
 SPECIAL_FILE
 Name_Error raised

Kind honors the current default directory, just as all the other functions and procedures of Ada.Directories. Name_Error is raised if the given Name parameter is invalid.

{{collapsible box|title=Ada.Directories.Kind Example Source|collapsed=yes|content=

with Ada.Text_IO; 
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package IOE is new Ada.Text_IO.Enumeration_IO (D.File_Kind);
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IOE.Put (Item => D.Kind (Name => "some_file"));
   IO.New_Line;
   IOE.Put (Item => D.Kind (Name => "some_dir"));
   IO.New_Line;
   IOE.Put (Item => D.Kind (Name => "/dev/sda"));
   IO.New_Line;
   IOE.Put (Item => D.Kind (Name => ""));
 
exception
   when D.Name_Error =>
      IO.Put_Line (Item => "Name_Error raised");
end aDir;


The specification for Size looks like this:

function Size (Name : String) return File_Size;

The File_Size type returned by Size is an integer with the range 0 .. Long_Long_Integer'Last. That is, on most systems, a very large number. With that noted, Size does exactly what is expected of it:

IO.Put_Line (Item => D.Size (Name => "some_file")'Img);
IO.Put_Line (Item => D.Size (Name => "adir")'Img);
IO.Put_Line 
  (Item => "Max File_Size on this system: " & Long_Long_Integer'Last'Img);

And the result is:

 Starting default directory: /home/thomas/wiki_examples/aDir
  7
  578580
 Max File_Size on this system:  9223372036854775807

The size returned is the number of stream elements contained in the file, in this case 7 and 578580 bytes. The 9223372036854775807 number is there to show you how very large a number the File_Size type can handle. I think we'd be hard pressed to find any kind of OS or file system that can handle files that large. Note that Long_Long_Integer is implementation dependent: It might be totally different on your system.

The usual Name_Error exception applies to Size and you will also find a Constraint_Error if the file size is not of type File_Size, ie. it is either lower than 0 or higher than Long_Long_Integer'Last.

{{collapsible box|title=Ada.Directories.Size Example Source|collapsed=yes|content=

with Ada.Text_IO;
with Ada.Directories;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => D.Size (Name => "some_file")'Img);
   IO.Put_Line (Item => D.Size (Name => "adir")'Img);
   IO.Put_Line 
     (Item => "Max File_Size on this system: " & Long_Long_Integer'Last'Img);
end aDir;


Modification_Time

[edit | edit source]

The specification for Modification_Time looks like this:

function Modification_Time (Name : String) return Ada.Calendar.Time;

If you need to know what time a file was last modified, this is the function for it. As you can see, it returns an Ada.Calendar.Time object, so to actually see the output, we need to add a with clause and an extra line to the declarative part of aDir. First the with clause:

with Ada.Calendar.Formatting;

And then the declaration:

package ACF renames Ada.Calendar.Formatting;

And finally the code that goes into the body:

IO.Put_Line (Item => ACF.Image (D.Modification_Time (Name => "some_file")));

The output from this little snippet is:

 Starting default directory: /home/thomas/wiki_examples/aDir
 2009-10-06 14:10:04

The usual Name_Error is raised if the file is invalid and a Use_Error is raised if the environment doesn't support the notion of modification time on a file.

{{collapsible box|title=Ada.Directories.Modification_Time Example Source|collapsed=yes|content=

with Ada.Text_IO;
with Ada.Directories;
with Ada.Calendar.Formatting;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package ACF renames Ada.Calendar.Formatting;
begin
   IO.Put ("Starting default directory: ");
   IO.Put_Line (Item => D.Current_Directory);
 
   IO.Put_Line (Item => ACF.Image (D.Modification_Time (Name => "some_file")));
end aDir;


Directory Searching

[edit | edit source]

Searching over a directory structure is done using the following types and subprograms. In the previous sections we haven't really bothered with the types, as they didn't really play an active role in using the functions and procedures, but with searching things are a bit different: Here we actively use the types to keep track of the state of a search and the result of a search.

Searching can be done either using an active or a passive iterator. The active iterator approach is merely a simple loop and the passive iterator is done using access to a subprogram that defines what to do with each result of the search. We will show how both methods work.

Directory_Entry_Type

[edit | edit source]

The specification for Directory_Entry_Type looks like this:

type Directory_Entry_Type is limited private;

private
  type Directory_Entry_Type is record
     Is_Valid : Boolean := False;
     Simple   : Ada.Strings.Unbounded.Unbounded_String;
     Full     : Ada.Strings.Unbounded.Unbounded_String;
     Kind     : File_Kind := Ordinary_File;
  end record;

The Directory_Entry_Type represents a single item in a directory. The only way to create a Directory_Entry_Type is by using the Get_Next_Entry procedure. One such Directory_Entry_Type object can then be used by appropriate subprograms, such as Simple_Name, Kind and so on. It should be obvious from the Directory_Entry_Type record what kind of information we can extract from it.

Filter_Type

[edit | edit source]

The specification for Filter_Type looks like this:

type Filter_Type is array (File_Kind) of Boolean;

There are three different kinds of files in Ada.Directories Directory, Ordinary_File and Special_File. With Filter_Type you define which of these you wish to search for:

Filter : Filter_Type := (Ordinary_File => True,
                         Special_File => False,
                         Directory => True);

Here we setup a filter that ignores Special_File and allows Ordinary_File and Directory.

Search_Type

[edit | edit source]

The specification for Search_Type looks like this:

type Search_Type is limited private;'

type Search_Data;
type Search_Ptr is access Search_Data;

private
   type Search_Type is new Ada.Finalization.Controlled with record
      Value : Search_Ptr;
   end record;

Search_Type contains the state of the search. You initialize the Search_Type object with Start_Search, you check it with More_Entries, you read it with Get_Next_Entry and you clean it up with End_Search.

[edit | edit source]

The specification for Start_Search looks like this:

procedure Start_Search
  (Search    : in out Search_Type;
   Directory : String;
   Pattern   : String;
   Filter    : Filter_Type := (others => True));

All the searching related types and subprograms depend heavily on each other. Because of that, I believe it will make the most sense to present a complete program that encompass them all. This means that we will deviate from the usual aDir program, and instead go with this:

with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;

procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package IOI is new IO.Integer_IO (D.File_Size);
   package ACF renames Ada.Calendar.Formatting;

   A_Search : D.Search_Type;
   Search_Item : D.Directory_Entry_Type;
   Filter : constant D.Filter_Type := (D.Ordinary_File => True,
                                       D.Special_File => False,
                                       D.Directory => True);
begin
   D.Start_Search (Search    => A_Search,
                   Directory => D.Current_Directory,
                   Pattern   => "",
                   Filter    => Filter);
   while D.More_Entries (Search => A_Search) loop
      D.Get_Next_Entry (Search          => A_Search,
                        Directory_Entry => Search_Item);
      IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
      IO.Set_Col (To => 25);
      if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
         IOI.Put (Item  => D.Size (Directory_Entry => Search_Item),
                  Width => 1);
         IO.Put (Item => " bytes");
      else
         IO.Put (Item => "dir");
      end if;
      IO.Set_Col (To => 45);
      IO.Put (Item => ACF.Image
              (D.Modification_Time (Directory_Entry => Search_Item)));
      IO.Set_Col (To => 70);
      IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
      IO.New_Line;
   end loop;
   D.End_Search (Search => A_Search);
end aDir;

The output from this program, when run on my system, is:

 /home/thomas/wiki_examples/aDir/adir
 .                       dir                 2009-10-23 20:17:22      /home/thomas/wiki_examples/aDir/.
 ..                      dir                 2009-09-24 20:07:57      /home/thomas/wiki_examples/aDir/..
 objects                 dir                 2009-10-27 20:56:32      /home/thomas/wiki_examples/aDir/objects
 Proj.gpr                502 bytes           2009-09-24 20:09:20      /home/thomas/wiki_examples/aDir/Proj.gpr
 adir                    594193 bytes        2009-10-23 20:17:22      /home/thomas/wiki_examples/aDir/adir
 some_dir                dir                 2009-10-06 14:10:25      /home/thomas/wiki_examples/aDir/some_dir
 adir.adb                1781 bytes          2009-10-23 20:17:21      /home/thomas/wiki_examples/aDir/adir.adb
 some_file               7 bytes             2009-10-06 14:10:04      /home/thomas/wiki_examples/aDir/some_file
 some_new_file           7 bytes             2009-10-06 14:37:15      /home/thomas/wiki_examples/aDir/some_new_file

Start_Search sets the stage for a search. It takes four parameters: Search which contains the state of the search, Directory which is the directory to search, Pattern which is a, well, pattern for matching filenames and Filter which defines the kinds of files that are returned by the search.

The interpretation of the Pattern value is implementation defined, except that an empty string equals "match everything". It is highly probable that simple and well-known patterns like *.txt will yield the expected result: All files with the .txt extension are matched.

In this case we search for everything (empty Pattern string) that isn't considered a Special_File (Filter is assigned D.Special_File => False) in the current directory.

The above program uses an active iterator: The while loop. As you can see, this loop goes on as long as D.More_Entries (Search => A_Search) is True. When this is no longer the case, the loop ends and we call End_Search to clean up the A_Search object. This basically resets it, so it no longer contains any entries.

We can avoid having to manage such basic house-keeping if we instead use a passive iterator. Here's what the program looks like when using a passive iterator:

with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;

procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package IOI is new IO.Integer_IO (D.File_Size);
   package ACF renames Ada.Calendar.Formatting;
   
   procedure Write_Search_Item (Search_Item : in D.Directory_Entry_Type) is
   begin
      IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
      IO.Set_Col (To => 25);
      if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
         IOI.Put (Item  => D.Size (Directory_Entry => Search_Item),
                  Width => 1);
         IO.Put (Item => " bytes");
      else
         IO.Put (Item => "dir");
      end if;
      IO.Set_Col (To => 45);
      IO.Put (Item => ACF.Image
              (D.Modification_Time (Directory_Entry => Search_Item)));
      IO.Set_Col (To => 70);
      IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
      IO.New_Line; 
   end Write_Search_Item;

   Filter : constant D.Filter_Type := (D.Ordinary_File => True,
                                       D.Special_File => False,
                                       D.Directory => True);
begin
   D.Search (Directory => D.Current_Directory, 
             Pattern => "", 
             Filter => Filter, 
             Process => Write_Search_Item'Access);
end aDir;

The output is:

 /home/thomas/wiki_examples/aDir/adir
 .                       dir                 2009-10-23 20:17:22      /home/thomas/wiki_examples/aDir/.
 ..                      dir                 2009-09-24 20:07:57      /home/thomas/wiki_examples/aDir/..
 objects                 dir                 2009-10-27 20:56:32      /home/thomas/wiki_examples/aDir/objects
 Proj.gpr                502 bytes           2009-09-24 20:09:20      /home/thomas/wiki_examples/aDir/Proj.gpr
 adir                    594193 bytes        2009-10-23 20:17:22      /home/thomas/wiki_examples/aDir/adir
 some_dir                dir                 2009-10-06 14:10:25      /home/thomas/wiki_examples/aDir/some_dir
 adir.adb                1781 bytes          2009-10-23 20:17:21      /home/thomas/wiki_examples/aDir/adir.adb
 some_file               7 bytes             2009-10-06 14:10:04      /home/thomas/wiki_examples/aDir/some_file
 some_new_file           7 bytes             2009-10-06 14:37:15      /home/thomas/wiki_examples/aDir/some_new_file

With the passive iterator, you get rid of the Search_Type and the Start_Search -> More_Entries -> Get_Next_Entry -> End_Search chain. It is much cleaner and less error-prone, because you don't have to worry about forgetting the final End_Search call. Both methods have their advantages though, so use whatever is the best fit for the situation.

The Search procedure accepts almost the same parameters as Start_Search, except that we have Process instead of Search. Process is of course access to the subprogram that is to handle each item returned by the search. This subprogram must accept one parameter: Item : in Directory_Entry_Type.

{{collapsible box|title=Ada.Directories.Start_Search Example Source|collapsed=yes|content=

with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package IOI is new IO.Integer_IO (D.File_Size);
   package ACF renames Ada.Calendar.Formatting;
 
   A_Search : D.Search_Type;
   Search_Item : D.Directory_Entry_Type;
   Filter : constant D.Filter_Type := (D.Ordinary_File => True,
                                       D.Special_File => False,
                                       D.Directory => True);
begin
   D.Start_Search (Search    => A_Search,
                   Directory => D.Current_Directory,
                   Pattern   => "",
                   Filter    => Filter);
   while D.More_Entries (Search => A_Search) loop
      D.Get_Next_Entry (Search          => A_Search,
                        Directory_Entry => Search_Item);
      IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
      IO.Set_Col (To => 25);
      if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
         IOI.Put (Item  => D.Size (Directory_Entry => Search_Item),
                  Width => 1);
         IO.Put (Item => " bytes");
      else
         IO.Put (Item => "dir");
      end if;
      IO.Set_Col (To => 45);
      IO.Put (Item => ACF.Image
              (D.Modification_Time (Directory_Entry => Search_Item)));
      IO.Set_Col (To => 70);
      IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
      IO.New_Line;
   end loop;
   D.End_Search (Search => A_Search);
end Adir;
with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
 
procedure aDir is
   package IO renames Ada.Text_IO;
   package D renames Ada.Directories;
   package IOI is new IO.Integer_IO (D.File_Size);
   package ACF renames Ada.Calendar.Formatting;
 
   procedure Write_Search_Item (Search_Item : in D.Directory_Entry_Type) is
   begin
      IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
      IO.Set_Col (To => 25);
      if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
         IOI.Put (Item  => D.Size (Directory_Entry => Search_Item),
                  Width => 1);
         IO.Put (Item => " bytes");
      else
         IO.Put (Item => "dir");
      end if;
      IO.Set_Col (To => 45);
      IO.Put (Item => ACF.Image
              (D.Modification_Time (Directory_Entry => Search_Item)));
      IO.Set_Col (To => 70);
      IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
      IO.New_Line; 
   end Write_Search_Item;
 
   Filter : constant D.Filter_Type := (D.Ordinary_File => True,
                                       D.Special_File => False,
                                       D.Directory => True);
begin
   D.Search (Directory => D.Current_Directory, 
             Pattern => "", 
             Filter => Filter, 
             Process => Write_Search_Item'Access);
end aDir;


[edit | edit source]

The specification for End_Search looks like this:

procedure End_Search (Search : in out Search_Type);

End_Search is a clean-up procedure. It is only necessary to use End_Search if you've called Start_Search earlier. It is used to reset the Search_Type, effectively clearing out all search entries.

Typical usage would look something like this:

Start_Search(Search => A_Search, ...);
while More_Entries (Search => A_Search) loop
   Get_Next_Entry (Search => A_Search, ...);
   --  Do stuff
end loop;
End_Search (Search => A_Search);

An actual usage example can be found here.

More_Entries

[edit | edit source]

The specification for More_Entries looks like this:

function More_Entries (Search : Search_Type) return Boolean;

More_Entries is only relevant if a prior call to Start_Search has been made. A call to More_Entries return boolean True if more entries are available on a call to Get_Next_Entry. Otherwise boolean False is returned. See Start_Search for an actual usage example.

Get_Next_Entry

[edit | edit source]

The specification for Get_Next_Entry looks like this:

procedure Get_Next_Entry
  (Search          : in out Search_Type;
   Directory_Entry : out Directory_Entry_Type);

I'm just going to quote the Ada Reference Manual on Get_Next_Entry

Returns the next Directory_Entry for the search described by Search that matches the pattern and filter. If no further matches are available, Status_Error is raised. It is implementation-defined as to whether the results returned by this routine are altered if the contents of the directory are altered while the Search object is valid (for example, by another program). The exception Use_Error is propagated if the external environment does not support continued searching of the directory represented by Search.

How this works, can be seen in the Start_Search section.

Simple_Name (Directory Entries)

[edit | edit source]

The specification for Simple_Name looks like this:

function Simple_Name (Directory_Entry : Directory_Entry_Type) return String;

The functionality of this Simple_Name function is exactly the same as the "regular" Simple_Name function, except it accepts a Directory_Entry_Type instead of a String for the Directory_Entry parameter. See Start_Search for an example.

Full_Name (Directory Entries)

[edit | edit source]

The specification for Full_Name looks like this:

function Full_Name (Directory_Entry : Directory_Entry_Type) return String;

The functionality of this Full_Name function is exactly the same as the "regular" Full_Name function, except it accepts a Directory_Entry_Type instead of a String for the Directory_Entry parameter. See Start_Search for an example.

Kind (Directory Entries)

[edit | edit source]

The specification for Kind looks like this:

function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind;

The functionality of this Kind function is exactly the same as the "regular" Kind function, except it accepts a Directory_Entry_Type instead of a String for the Directory_Entry parameter. See Start_Search for an example.

Size (Directory Entries)

[edit | edit source]

The specification for Size looks like this:

function Size (Directory_Entry : Directory_Entry_Type) return File_Size;

The functionality of this Size function is exactly the same as the "regular" Size function, except it accepts a Directory_Entry_Type instead of a String for the Directory_Entry parameter. See Start_Search for an example.

Modification_Time (Directory Entries)

[edit | edit source]

The specification for Modification_Time looks like this:

function Modification_Time
     (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time;

The functionality of this Modification_Time function is exactly the same as the "regular" Modification_Time function, except it accepts a Directory_Entry_Type instead of a String for the Directory_Entry parameter. See Start_Search for an example.

Specification

[edit | edit source]
--                     Standard Ada library specification
--   Copyright (c) 2003-2018 Maxim Reznik <reznikmm@gmail.com>
--   Copyright (c) 2004-2016 AXE Consultants
--   Copyright (c) 2004, 2005, 2006 Ada-Europe
--   Copyright (c) 2000 The MITRE Corporation, Inc.
--   Copyright (c) 1992, 1993, 1994, 1995 Intermetrics, Inc.
--   SPDX-License-Identifier: BSD-3-Clause and LicenseRef-AdaReferenceManual
-- -------------------------------------------------------------------------

with Ada.Calendar;
with Ada.IO_Exceptions;

package Ada.Directories is

   --   Directory and file operations:

   function Current_Directory return String;

   procedure Set_Directory (Directory : in String);

   procedure Create_Directory (New_Directory : in String;
                               Form          : in String := "");

   procedure Delete_Directory (Directory : in String);

   procedure Create_Path (New_Directory : in String;
                          Form          : in String := "");

   procedure Delete_Tree (Directory : in String);

   procedure Delete_File (Name : in String);

   procedure Rename (Old_Name : in String;
                     New_Name : in String);

   procedure Copy_File (Source_Name : in String;
                        Target_Name : in String;
                        Form        : in String := "");

   --   File and directory name operations:

   function Full_Name (Name : in String) return String;

   function Simple_Name (Name : in String) return String;

   function Containing_Directory (Name : in String) return String;

   function Extension (Name : in String) return String;

   function Base_Name (Name : in String) return String;

   function Compose (Containing_Directory : in String := "";
                     Name                 : in String;
                     Extension            : in String := "")
     return String;

   --   File and directory queries:

   type File_Kind is (Directory, Ordinary_File, Special_File);

   type File_Size is range 0 .. implementation_defined;

   function Exists (Name : in String) return Boolean;

   function Kind (Name : in String) return File_Kind;

   function Size (Name : in String) return File_Size;

   function Modification_Time (Name : in String) return Ada.Calendar.Time;

   --   Directory searching:

   type Directory_Entry_Type is limited private;

   type Filter_Type is array (File_Kind) of Boolean;

   type Search_Type is limited private;

   procedure Start_Search
    (Search     : in out Search_Type;
     Directory  : in     String;
     Pattern    : in     String;
     Filter     : in     Filter_Type := (others => True));

   procedure End_Search (Search : in out Search_Type);

   function More_Entries (Search : in Search_Type) return Boolean;

   procedure Get_Next_Entry (Search          : in out Search_Type;
                             Directory_Entry :    out Directory_Entry_Type);

   procedure Search
    (Directory : in String;
     Pattern   : in String;
     Filter    : in Filter_Type := (others => True);
     Process   : not null access procedure
                  (Directory_Entry : in Directory_Entry_Type));

   --   Operations on Directory Entries:

   function Simple_Name (Directory_Entry : in Directory_Entry_Type)
     return String;

   function Full_Name (Directory_Entry : in Directory_Entry_Type)
     return String;

   function Kind (Directory_Entry : in Directory_Entry_Type)
     return File_Kind;

   function Size (Directory_Entry : in Directory_Entry_Type)
     return File_Size;

   function Modification_Time (Directory_Entry : in Directory_Entry_Type)
     return Ada.Calendar.Time;

   Status_Error : exception renames Ada.IO_Exceptions.Status_Error;
   Name_Error   : exception renames Ada.IO_Exceptions.Name_Error;
   Use_Error    : exception renames Ada.IO_Exceptions.Use_Error;
   Device_Error : exception renames Ada.IO_Exceptions.Device_Error;

private

   pragma Import (Ada, Directory_Entry_Type);
   pragma Import (Ada, Search_Type);

end Ada.Directories;

See also

[edit | edit source]

Wikibook

[edit | edit source]

External examples

[edit source]

Ada Reference Manual

[edit | edit source]

Ada 2005

[edit | edit source]

Ada 2012

[edit | edit source]

Open-Source Implementations

[edit | edit source]

FSF GNAT

drake