Source file : unzipada.adb
1 ------------------------------------------------------------------------------
2 -- File: UnZipAda.adb
3 -- Description: A minimal standalone command-line unzipping tool
4 -- using the Zip-Ada library.
5 -- Author: Gautier de Montmollin
6 ------------------------------------------------------------------------------
7
8 with Ada.Characters.Handling,
9 Ada.Command_Line,
10 Ada.Calendar,
11 Ada.Directories,
12 Ada.Text_IO,
13 Ada.Float_Text_IO;
14
15 with Interfaces;
16
17 with Zip, UnZip;
18
19 -- Pure Ada Text_IO-fashion feedback; should work on every
20 -- computer having a screen [and some text console too] :
21
22 with Zip_Console_IO;
23 with Show_License;
24
25 procedure UnZipAda is
26
27 procedure Set_Modification_Time_B (Name : in String;
28 To : in Ada.Calendar.Time) is
29 begin
30 null; -- If you want the time stamps, uncomment the following and the "with" above.
31 -- Set_Modification_Time_GNAT (Name, To);
32 exception
33 when others =>
34 null; -- !! utf-8 or ascii names with characters > pos 127 fail
35 end Set_Modification_Time_B;
36 pragma Unreferenced (Set_Modification_Time_B);
37
38 Set_Time_Stamp : UnZip.Set_Time_Stamp_Proc :=
39 -- If you want the time stamps, uncomment the following
40 -- and look into Set_Modification_Time_B above.
41 --
42 -- Set_Modification_Time_B'Unrestricted_Access;
43 null;
44
45 z_options : UnZip.Option_Set := UnZip.no_option;
46 quiet : Boolean := False;
47 lower_case_match : Boolean := False;
48 comment : Boolean := False;
49
50 use UnZip;
51
52 fda : Zip.Feedback_Proc := Zip_Console_IO.My_feedback'Access;
53 rca : Resolve_Conflict_Proc := Zip_Console_IO.My_resolve_conflict'Access;
54 tda : Tell_Data_Proc := Zip_Console_IO.My_tell_data'Access;
55 gpw : constant Get_Password_Proc := Zip_Console_IO.My_get_password'Access;
56
57 last_option : Natural := 0;
58
59 password, exdir : String (1 .. 1024);
60 pass_len, exdir_len : Natural := 0;
61
62 Directory_Separator : constant Character := '/';
63 -- '/' is also accepted by Windows
64
65 function Add_extract_directory (File_Name : String) return String is
66 -- OK for UNIX & Windows, but VMS has "[x.y.z]filename.ext"
67 begin
68 if exdir_len = 0 then
69 return File_Name;
70 elsif exdir (exdir_len) = '\' or exdir (exdir_len) = '/' then
71 return exdir (1 .. exdir_len) & File_Name;
72 else
73 return exdir (1 .. exdir_len) & Directory_Separator & File_Name;
74 end if;
75 end Add_extract_directory;
76
77 function Compose_File_Name (
78 File_Name : String;
79 Name_encoding : Zip.Zip_Name_Encoding
80 )
81 return String
82 is
83 pragma Unreferenced (Name_encoding);
84 fn1 : String := File_Name;
85 begin
86 if lower_case_match then
87 fn1 := Ada.Characters.Handling.To_Lower (fn1);
88 end if;
89 return Add_extract_directory (fn1);
90 end Compose_File_Name;
91
92 My_FS_routines : constant FS_Routines_Type :=
93 (Create_Path => Ada.Directories.Create_Path'Access, -- Ada 2005
94 Set_Time_Stamp => Set_Time_Stamp,
95 Compose_File_Name => Compose_File_Name'Unrestricted_Access,
96 others => null
97 );
98
99 use Ada.Calendar, Ada.Text_IO, Ada.Float_Text_IO;
100
101 T0, T1 : Time;
102 seconds_elapsed : Duration;
103
104 package IIO is new Integer_IO (Integer);
105 package MIO is new Modular_IO (Zip.Zip_64_Data_Size_Type);
106
107 procedure Blurb is
108 begin
109 Put_Line ("UnZipAda * minimal standalone unzipping tool");
110 Put_Line ("Demo for the Zip-Ada library, by G. de Montmollin");
111 Put_Line ("Library version " & Zip.version & " dated " & Zip.reference);
112 Put_Line ("URL: " & Zip.web);
113 Show_License (Current_Output, "zip.ads");
114 end Blurb;
115
116 procedure Help is
117 begin
118 Blurb;
119 Put_Line ("Usage: unzipada [options] zipfile[.zip] [files...]");
120 New_Line;
121 Put_Line ("options: -t : test .zip file integrity, no write");
122 Put_Line (" -j : junk archived directory structure");
123 Put_Line (" -d dir : extract to ""dir"" instead of current");
124 Put_Line (" -c : case sensitive name matching");
125 Put_Line (" -l : force lower case on stored names");
126 Put_Line (" -a : output as text file, with native line endings");
127 Put_Line (" -z : display .zip archive comment only");
128 Put_Line (" -p Pwd : define a password for decryption (e.g. ""Pwd"")");
129 Put_Line (" -q : quiet mode");
130 New_Line;
131 Put ("Press Return");
132 Skip_Line;
133 end Help;
134
135 zi : Zip.Zip_Info;
136 use Zip_Console_IO;
137 use Ada.Command_Line;
138 use Interfaces;
139
140 begin
141 if Argument_Count = 0 then
142 Help;
143 return;
144 end if;
145 Set_Time_Stamp := null;
146 for i in 1 .. Argument_Count loop
147 if Argument (i)(1) = '-' or else Argument (i)(1) = '/' then
148 if last_option = i then
149 null; -- was in fact an argument for previous option (e.g. "-s")
150 else
151 last_option := i;
152 if Argument (i)'Length = 1 then
153 Help;
154 return;
155 end if;
156 case Ada.Characters.Handling.To_Lower (Argument (i)(2)) is
157 when 't' =>
158 z_options (test_only) := True;
159 when 'j' =>
160 z_options (junk_directories) := True;
161 when 'd' =>
162 if i = Argument_Count then
163 Help;
164 return; -- "-d" without the directory or anything ?!
165 end if;
166 declare
167 arg_exdir : constant String := Argument (i + 1);
168 begin
169 exdir (1 .. arg_exdir'Length) := arg_exdir;
170 exdir_len := arg_exdir'Length;
171 end;
172 last_option := i + 1;
173 when 'c' =>
174 z_options (case_sensitive_match) := True;
175 when 'l' =>
176 lower_case_match := True;
177 when 'a' =>
178 z_options (extract_as_text) := True;
179 when 'p' | 's' => -- The "-s" variant is kept for compatibility.
180 if i = Argument_Count then
181 Help;
182 return; -- "-s" without the password or anything ?!
183 end if;
184 declare
185 arg_pass : constant String := Argument (i + 1);
186 begin
187 password (1 .. arg_pass'Length) := arg_pass;
188 pass_len := arg_pass'Length;
189 end;
190 last_option := i + 1;
191 when 'q' =>
192 quiet := True;
193 when 'z' =>
194 comment := True;
195 when others =>
196 Help;
197 return;
198 end case;
199 end if;
200 end if;
201 end loop;
202
203 current_user_attitude := yes;
204
205 if quiet then
206 fda := null;
207 rca := null;
208 tda := null;
209 end if;
210
211 Zip_Console_IO.Summary.Reset;
212
213 if Argument_Count = last_option then -- options only ?!
214 Help;
215 return;
216 end if;
217 declare
218 archive_given : constant String := Argument (last_option + 1);
219 zip_ext : Boolean := False;
220 extract_all : Boolean;
221 --
222 function Archive return String is
223 begin
224 if zip_ext then
225 return archive_given & ".zip";
226 else
227 return archive_given;
228 end if;
229 end Archive;
230 --
231 begin
232 if not Zip.Exists (Archive) then
233 zip_ext := True;
234 if not Zip.Exists (Archive) then
235 Put_Line ("Archive file '" & archive_given &
236 "' or '" & Archive & "' not found");
237 return;
238 end if;
239 end if;
240 extract_all := Argument_Count = last_option + 1;
241 -- options and zipfile only
242
243 if not quiet then
244 Blurb;
245 end if;
246 if not (quiet or comment) then
247 if z_options (test_only) then
248 Put ("Testing");
249 else
250 if Set_Time_Stamp = null then
251 Put_Line (" Warning: time stamps and attributes of files" &
252 " in archive are not reproduced !");
253 New_Line;
254 end if;
255 Put ("Extracting");
256 end if;
257 if not extract_all then
258 Put (" some file(s) from");
259 end if;
260 Put_Line (" archive " & Archive);
261 end if;
262
263 T0 := Clock;
264 if comment then -- Option: -z , display comment only
265 Zip.Load (zi, Archive);
266 Zip.Put_Multi_Line (Standard_Output, Zip.Zip_Comment (zi));
267 elsif extract_all then
268 Extract (
269 Archive,
270 fda, rca, tda, gpw,
271 z_options,
272 password (1 .. pass_len),
273 My_FS_routines
274 );
275 else
276 Zip.Load (zi, Archive);
277 for i in last_option + 2 .. Argument_Count loop
278 Extract (zi, Argument (i),
279 fda, rca, tda, gpw,
280 z_options,
281 password (1 .. pass_len),
282 My_FS_routines
283 );
284 end loop;
285 end if;
286 T1 := Clock;
287 end;
288
289 seconds_elapsed := T1 - T0;
290
291 if not (quiet or comment) then
292 New_Line (2);
293 IIO.Put (Summary.total_entries, 7);
294 Put (" entries ------ Total ------ ");
295 MIO.Put (Summary.total_compressed, 10);
296 if Summary.total_uncompressed = 0 then
297 Put (" : ");
298 else
299 Put (" :");
300 IIO.Put (
301 Natural (
302 (100.0 * Long_Float (Summary.total_compressed)) /
303 Long_Float (Summary.total_uncompressed)
304 ), 4);
305 Put ("% of ");
306 end if;
307 MIO.Put (Summary.total_uncompressed, 10);
308 New_Line (2);
309
310 if z_options (test_only) then
311 Put_Line ("Test: no error found");
312 New_Line;
313 Put_Line ("Statistics per Zip sub-format (""method""):");
314 for m in Summary.files_per_method'Range loop
315 if Summary.files_per_method (m) > 0 then
316 Put (" " & Summary.Nice_image (m) & "... ");
317 IIO.Put (Summary.files_per_method (m), 5);
318 Put (" files");
319 if Summary.uncompressed_per_method (m) > 0 then
320 Put (",");
321 IIO.Put (
322 Natural (
323 (100.0 * Long_Float (Summary.uncompressed_per_method (m))) /
324 Long_Float (Summary.total_uncompressed)
325 ), 4
326 );
327 Put ("% of all data; compr.-to-decompr. ratio: ");
328 IIO.Put (
329 Natural (
330 (100.0 * Long_Float (Summary.compressed_per_method (m))) /
331 Long_Float (Summary.uncompressed_per_method (m))
332 ), 4
333 );
334 Put ('%');
335 end if;
336 New_Line;
337 end if;
338 end loop;
339 New_Line;
340 end if;
341
342 Put ("Time elapsed : ");
343 Put (Float (seconds_elapsed), 4, 2, 0);
344 Put_Line (" sec");
345
346 Put_Line ("Archive successfully processed (or empty archive, or no archive!)");
347 end if;
348
349 end UnZipAda;
Web view of Ada source code generated by GNATHTML, project: ALI_Parse version 1.0.
Zip-Ada: Ada library for zip archive files (.zip).
Ada programming.
Some news about Zip-Ada and other Ada projects
on Gautier's blog.