Ada et le système de fichiers
Un vaste répertoire
Introduction¶
Depuis sa création, l’environnement prédéfini du langage Ada n’a cessé de s’enrichir. Même si les implémentations des compilateurs proposaient des librairies dédiées, il était par exemple peu aisé d’interagir avec le système de fichiers et ce de manière pérenne et portable. C’est pour cela qu’Ada₂₀₀₅ a introduit la hiérarchie Ada.Directories.
Au travers d’un exemple, nous allons faire un bref aperçu des possibilités offertes par cette hiérarchie.
Le paquetage Ada.Directories¶
Ce paquetage offre des primitives permettant d’interagir avec un système de fichiers, indépendamment de son type (NTFS, FAT32, ext3…) et de son système d’exploitation hôte (Windows, Linux…).
Il permet par exemple :
- De faire des opérations sur des répertoires ou des fichiers (suppression, renommage, copie, etc.).
- D’identifier un répertoire ou un fichier (nom complet, nom de base, extension, etc.).
- De connaitre les caractéristiques d’un fichier ou d’un répertoire (type, existence, date de modification, taille, etc.).
- De rechercher dans un répertoire des entrées spécifiques.
Exemple¶
En utilisant en grande partie les services du paquetage Ada.Directories, nous allons faire une version simplifiée de la commande tree. Pour rappel, cette commande permet d’afficher une structure de répertoires sous forme arborescente.
Pour cela, nous allons nous baser sur une arborescence test a sur laquelle la commande tree -s -D a produit :
a
├── [ 1 Apr 23 11:41] a.txt
├── [ 0 Apr 23 11:35] b
│ ├── [ 2 Apr 23 11:41] b.txt
│ └── [ 0 Apr 23 11:35] c
│ └── [ 3 Apr 23 11:41] c.txt
└── [ 0 Apr 23 11:35] d
├── [ 4 Apr 23 11:41] d.txt
└── [ 0 Apr 23 11:35] e
├── [ 5 Apr 23 11:41] e.txt
├── [ 0 Apr 23 11:35] f
│ └── [ 6 Apr 23 11:41] f.txt
└── [ 0 Apr 23 11:35] g
└── [ 7 Apr 23 11:41] g.txt
6 directories, 7 files
Avec comme options :
- -s : Affiche la taille des fichiers.
- -D : Affiche la date et l’heure de la dernière modification des fichiers.
Pour notre exemple, nous allons écrire un programme atree en Ada₂₀₂₂ afin que la commande atree a affiche un résultat proche du précédent.
Dans un premier temps, nous n’allons pas nous préoccuper de l’affichage, mais simplement parcourir l’arborescence de a et afficher ce que nous y trouvons. C’est ce que permet le code ci-dessous :
with Ada.Directories,
Ada.Command_Line,
Ada.Text_IO;
use Ada;
procedure Atree is
procedure Tree (Path : in String) is
procedure Walk (Path : in String);
procedure Process (Search_Item : in
Directories.Directory_Entry_Type) is
SN : constant String := Directories.Simple_Name (Search_Item);
FN : constant String := Directories.Full_Name (Search_Item);
use all type Directories.File_Kind;
begin
if SN /= "." and then SN /= ".." then
Text_IO.Put_Line (SN); -- affiche par exemple a.txt
if Directories.Kind (Search_Item) = Directory then
Walk (Path => FN);
end if;
end if;
end Process;
procedure Walk (Path : in String) is
use all type Directories.File_Kind;
begin
Directories.Search (Directory => Path,
Pattern => "", -- tous
Filter => [Directory => True,
Ordinary_File => True,
Special_File => False],
Process => Process'Access);
end Walk;
begin
Text_IO.Put_Line (Directories.Simple_Name (Path));
Walk (Path);
end Tree;
begin
Tree (Command_Line.Argument (1));
end Atree;
Et la commande atree a affiche :
a
a.txt
b
b.txt
c
c.txt
d
d.txt
e
e.txt
f
f.txt
g
g.txt
Dans ce code, la procédure locale Tree appelle la procédure locale Walk qui, elle-même, appelle la procédure Ada.Directories.Search avec :
type File_Kind is (Directory, Ordinary_File, Special_File);
type Filter_Type is array (File_Kind) of Boolean;
type Directory_Entry_Type is limited private;
procedure Search (Directory : String; -- par exemple "./"
Pattern : String; -- par exemple "*.ad[sb]"
Filter : Filter_Type := [others => True];
Process : not null access
procedure (Directory_Entry : Directory_Entry_Type));
Cette procédure Search recherche dans le répertoire Directory les entrées correspondantes à Pattern. Le sous-programme Process est alors appelé avec comme argument l’entrée trouvée.
Dans notre cas, nous demandons à parcourir tous les dossiers et fichiers de a et pour chacun d’eux, d’appeler la procédure Process.
procedure Walk (Path : in String) is
use all type Directories.File_Kind;
begin
Directories.Search (Directory => Path,
Pattern => "", -- tous
Filter => [Directory => True,
Ordinary_File => True,
Special_File => False],
Process => Process'Access);
end Walk;
Dans le cas où le nom simple de l’entrée n’est ni « . » ni « .. » la procédure Process vient :
- Afficher le nom simple de l’entrée (SN).
- Appeler à nouveau la procédure Walk sur l’entrée (FN) si celle-ci est un répertoire. Ainsi donc, nous allons parcourir l’arborescence en réappliquant le même traitement.
procedure Process (Search_Item : in Directories.Directory_Entry_Type) is
SN : constant String := Directories.Simple_Name (Search_Item);
FN : constant String := Directories.Full_Name (Search_Item);
use all type Directories.File_Kind;
begin
if SN /= "." and then SN /= ".." then
Text_IO.Put_Line (SN); -- affiche par exemple a.txt
if Directories.Kind (Search_Item) = Directory then
Walk (Path => FN);
end if;
end if;
end Process;
Nous allons maintenant nous préoccuper de l’affichage afin d’être au plus proche de ce que produit la commande tree -s -D a.
Pour cela, nous allons nous appuyer sur le code ci-dessous :
with Ada.Directories,
Ada.Command_Line,
Ada.Calendar.Formatting,
Ada.Text_IO,
GNAT.Directory_Operations; -- !! GNAT : uniquement pour Dir_Separator
use Ada,
GNAT;
procedure Atree is
procedure Tree (Path : in String) is
Cpt_Directories, Cpt_Files : Natural := 0;
procedure Walk (Path : in String);
procedure Indent (Path : in String) is
Col : Text_IO.Positive_Count := Text_IO.Positive_Count'First;
use type Text_IO.Positive_Count;
begin
for C of Path loop
if C = Directory_Operations.Dir_Separator then -- !! GNAT Windows => '\'
Col := Col + 4; -- !! Linux => '/'
end if;
end loop;
Text_IO.Set_Col (Col);
end Indent;
procedure Process (Search_Item : in Directories.Directory_Entry_Type) is
SN : constant String := Directories.Simple_Name (Search_Item);
FN : constant String := Directories.Full_Name (Search_Item);
MT : constant String := Calendar.Formatting.Local_Image
(Directories.Modification_Time (Search_Item));
Size : constant String := Directories.Size (Search_Item)'Image;
use all type Directories.File_Kind;
begin
if SN /= "." and then SN /= ".." then
Indent (FN); -- en fonction du nombre de '/' ou '\' dans FN
Text_IO.Put_Line ("[" & Size & " " & MT & "] " & SN);
if Directories.Kind (Search_Item) = Directory then
Cpt_Directories := Cpt_Directories + 1;
Walk (Path => FN);
else
Cpt_Files := Cpt_Files + 1;
end if;
end if;
end Process;
procedure Walk (Path : in String) is
use all type Directories.File_Kind;
begin
Directories.Search (Directory => Path,
Pattern => "",
Filter => [Directory => True,
Ordinary_File => True,
Special_File => False],
Process => Process'Access);
end Walk;
begin
Text_IO.Put_Line (Directories.Simple_Name (Path));
Walk (Path);
Text_IO.New_Line;
Text_IO.Put_Line (Cpt_Directories'Image & " directories," &
Cpt_Files'Image & " files");
end Tree;
begin
Tree (Command_Line.Argument (1));
end Atree;
Ici l’évolution la plus significative concerne la procédure Process.
Toujours dans le cas où le nom simple de l’entrée n’est ni « . » ni « .. » elle vient :
- Au travers de la procédure Indent, positionner le curseur d’affichage en fonction du chemin de l’entrée. Pour simplifier l’implémentation, la procédure Indent utilise la constante GNAT.Directory_Operations.Dir_Separator afin de connaitre le caractère de séparation des répertoires (‘/’ ou ’').
- Afficher la taille, la date de modification (temps local) et le nom simple de l’entrée.
- Appeler à nouveau la procédure Walk sur l’entrée si celle-ci est un répertoire.
- Compter le nombre d’entrées qui sont soit des fichiers, soit des répertoires.
procedure Process (Search_Item : in Directories.Directory_Entry_Type) is
SN : constant String := Directories.Simple_Name (Search_Item);
FN : constant String := Directories.Full_Name (Search_Item);
MT : constant String := Calendar.Formatting.Local_Image
(Directories.Modification_Time (Search_Item));
Size : constant String := Directories.Size (Search_Item)'Image;
use all type Directories.File_Kind;
begin
if SN /= "." and then SN /= ".." then
Indent (FN); -- en fonction du nombre de ‘/’ ou ‘\’ dans FN
Text_IO.Put_Line ("[" & Size & " " & MT & "] " & SN);
if Directories.Kind (Search_Item) = Directory then
Cpt_Directories := @ + 1;
Walk (Path => FN);
else
Cpt_Files := @ + 1;
end if;
end if;
end Process;
Maintenant, la commande atree a affiche quelque-chose de proche de ce que produisait la commande tree -s -D a :
a
[ 1 2025-04-23 11:41:30] a.txt
[ 0 2025-04-23 11:35:28] b
[ 2 2025-04-23 11:41:30] b.txt
[ 0 2025-04-23 11:35:33] c
[ 3 2025-04-23 11:41:30] c.txt
[ 0 2025-04-23 11:35:40] d
[ 4 2025-04-23 11:41:31] d.txt
[ 0 2025-04-23 11:35:47] e
[ 5 2025-04-23 11:41:31] e.txt
[ 0 2025-04-23 11:35:04] f
[ 6 2025-04-23 11:41:31] f.txt
[ 0 2025-04-23 11:35:55] g
[ 7 2025-04-23 11:41:30] g.txt
6 directories, 7 files
Conclusion¶
La hiérarchie Ada.Directories fournit une interface standardisée permettant d’interagir avec les systèmes de fichiers de différents systèmes d’exploitation sans avoir à se soucier des spécificités de ces derniers. Étant défini par le standard Ada₂₀₀₅, et si le profil d’exécution s’y prête, il est donc disponible dans toutes les implémentations conformes au standard. Son utilisation est donc un gage de maintenabilité et de portabilité.
Références¶
[1] : https://en.wikibooks.org/wiki/Ada_Programming/Libraries/Ada.Directories
[2] : image © 2010 by Tomasz Sienicki [user: tsca, mail: tomasz.sienicki at gmail.com], CC BY 3.0 https://creativecommons.org/licenses/by/3.0, via Wikimedia Commons
