128 lines
4.2 KiB
Ada
128 lines
4.2 KiB
Ada
with Ada.Command_Line;
|
|
with Ada.Containers;
|
|
with Ada.Containers.Ordered_Maps;
|
|
with Ada.Containers.Indefinite_Vectors;
|
|
with Ada.Directories;
|
|
with Ada.Streams.Stream_IO;
|
|
with Ada.Text_IO;
|
|
|
|
with GNAT.MD5;
|
|
|
|
procedure Find_Duplicates is
|
|
|
|
Root_Dir : constant String := Ada.Command_Line.Argument (1);
|
|
Size_Limit : constant Ada.Directories.File_Size :=
|
|
Ada.Directories.File_Size'Value (Ada.Command_Line.Argument (2));
|
|
Pattern : constant String := "*";
|
|
Filter : constant Ada.Directories.Filter_Type :=
|
|
(
|
|
Ada.Directories.Directory => True,
|
|
Ada.Directories.Ordinary_File => True,
|
|
others => False
|
|
);
|
|
|
|
type File_Data is
|
|
record
|
|
Hash : GNAT.MD5.Message_Digest;
|
|
Size : Ada.Directories.File_Size;
|
|
end record;
|
|
|
|
type File_Name is new String;
|
|
|
|
function File_Order (Left, Right : File_Data) return Boolean is
|
|
(Ada.Directories."<" (Right.Size, Left.Size));
|
|
|
|
package Dir_Vectors is new Ada.Containers.Indefinite_Vectors
|
|
(Index_Type => Positive, Element_Type => File_Name);
|
|
package Dir_Maps is new Ada.Containers.Ordered_Maps
|
|
(Key_Type => File_Data, Element_Type => Dir_Vectors.Vector,
|
|
"<" => File_Order, "=" => Dir_Vectors."=");
|
|
|
|
function File_Hash (Name : String) return GNAT.MD5.Message_Digest is
|
|
File : Ada.Streams.Stream_IO.File_Type;
|
|
Context : GNAT.MD5.Context := GNAT.MD5.Initial_Context;
|
|
Items : Ada.Streams.Stream_Element_Array
|
|
(1 .. 4096);
|
|
Last : Ada.Streams.Stream_Element_Offset := 0;
|
|
begin
|
|
Ada.Streams.Stream_IO.Open (File, Ada.Streams.Stream_IO.In_File, Name);
|
|
|
|
while not Ada.Streams.Stream_IO.End_Of_File (File) loop
|
|
Ada.Streams.Stream_IO.Read (File, Items, Last);
|
|
|
|
if Ada.Streams."<" (Last, Items'Last) then
|
|
GNAT.MD5.Update (Context, Items (Items'First .. Last));
|
|
else
|
|
GNAT.MD5.Update (Context, Items);
|
|
end if;
|
|
end loop;
|
|
|
|
return GNAT.MD5.Digest (Context);
|
|
end;
|
|
|
|
Dir_Map : Dir_Maps.Map := Dir_Maps.Empty_Map;
|
|
|
|
procedure Insert (Name : String) is
|
|
Hash : constant GNAT.MD5.Message_Digest := File_Hash (Name);
|
|
Size : constant Ada.Directories.File_Size := Ada.Directories.Size (Name);
|
|
Data : constant File_Data := (Hash, Size);
|
|
Pos : Dir_Maps.Cursor;
|
|
Ins : Boolean;
|
|
procedure Append_Name (K : File_Data; V : in out Dir_Vectors.Vector) is
|
|
begin
|
|
V.Append (File_Name (Name));
|
|
end Append_Name;
|
|
begin
|
|
Dir_Map.Insert (Data, Dir_Vectors.Empty_Vector, Pos, Ins);
|
|
-- We can update whether we inserted a new element or not.
|
|
Dir_Map.Update_Element (Pos, Append_Name'Access);
|
|
end Insert;
|
|
|
|
procedure Walk (Dir_Ent : Ada.Directories.Directory_Entry_Type) is
|
|
Name : constant String := Ada.Directories.Full_Name (Dir_Ent);
|
|
SName : constant String := Ada.Directories.Simple_Name (Dir_Ent);
|
|
Size : Ada.Directories.File_Size;
|
|
begin
|
|
if SName = "." or else SName = ".." then
|
|
return;
|
|
end if;
|
|
|
|
case Ada.Directories.Kind (Dir_Ent) is
|
|
when Ada.Directories.Ordinary_File =>
|
|
Size := Ada.Directories.Size (Name);
|
|
if Ada.Directories.">=" (Size, Size_Limit) then
|
|
Insert (Name);
|
|
end if;
|
|
when Ada.Directories.Directory =>
|
|
Ada.Directories.Search
|
|
(Name, Pattern, Filter, Walk'Access);
|
|
when others => raise Constraint_Error;
|
|
end case;
|
|
end Walk;
|
|
|
|
procedure Show_Duplicates is
|
|
Count : Ada.Containers.Count_Type;
|
|
K : File_Data;
|
|
Dv : Dir_Vectors.Vector;
|
|
begin
|
|
for Pos in Dir_Map.Iterate loop
|
|
Dv := Dir_Map.Constant_Reference (Pos);
|
|
Count := Dv.Length;
|
|
|
|
if Ada.Containers.">"(Count, 1) then
|
|
K := Dir_Maps.Key (Pos);
|
|
Ada.Text_IO.Put_Line
|
|
("Found " & Count'Image & " files matching Size => "
|
|
& K.Size'Image & ", MD5 => " & K.Hash);
|
|
|
|
for E of Dv loop
|
|
Ada.Text_IO.Put_Line (String (E));
|
|
end loop;
|
|
end if;
|
|
end loop;
|
|
end Show_Duplicates;
|
|
begin
|
|
Ada.Directories.Search
|
|
(Root_Dir, Pattern, Filter, Walk'Access);
|
|
Show_Duplicates;
|
|
end Find_Duplicates;
|