Source file : unzip.adb
1 -- Legal licensing note:
2
3 -- Copyright (c) 1999 .. 2023 Gautier de Montmollin
4 -- SWITZERLAND
5
6 -- Permission is hereby granted, free of charge, to any person obtaining a copy
7 -- of this software and associated documentation files (the "Software"), to deal
8 -- in the Software without restriction, including without limitation the rights
9 -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 -- copies of the Software, and to permit persons to whom the Software is
11 -- furnished to do so, subject to the following conditions:
12
13 -- The above copyright notice and this permission notice shall be included in
14 -- all copies or substantial portions of the Software.
15
16 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 -- THE SOFTWARE.
23
24 -- NB: this is the MIT License, as found 12-Sep-2007 on the site
25 -- http://www.opensource.org/licenses/mit-license.php
26
27 with Zip.Headers, UnZip.Decompress;
28 with Zip_Streams;
29
30 with Ada.IO_Exceptions;
31 with Interfaces;
32
33 package body UnZip is
34
35 use Ada.Strings.Unbounded, Interfaces;
36
37 boolean_to_encoding : constant array (Boolean) of Zip.Zip_Name_Encoding :=
38 (False => Zip.IBM_437, True => Zip.UTF_8);
39
40 fallback_compressed_size : constant := 16#FFFF_FFFF#;
41
42 --------------------------------------------------
43 -- *The* internal 1-file unzipping procedure. --
44 -- Input must be _open_ and won't be _closed_ ! --
45 --------------------------------------------------
46
47 procedure UnZipFile
48 (zip_file : in out Zip_Streams.Root_Zipstream_Type'Class;
49 out_name : String;
50 out_name_encoding : Zip.Zip_Name_Encoding;
51 name_from_header : Boolean;
52 header_index : in out Zip_Streams.ZS_Index_Type;
53 hint_comp_size : Zip.Zip_64_Data_Size_Type; -- Added 2007 for .ODS files
54 hint_crc_32 : Unsigned_32; -- Added 2012 for decryption
55 feedback : Zip.Feedback_Proc;
56 help_the_file_exists : Resolve_Conflict_Proc;
57 tell_data : Tell_Data_Proc;
58 get_pwd : Get_Password_Proc;
59 options : Option_Set;
60 password : in out Unbounded_String;
61 file_system_routines : FS_Routines_Type)
62 is
63 work_index : Zip_Streams.ZS_Index_Type := header_index;
64 local_header : Zip.Headers.Local_File_Header;
65 data_descriptor_after_data : Boolean;
66 method : PKZip_Method;
67
68 skip_this_file : Boolean := False;
69 bin_text_mode : constant array (Boolean) of Write_Mode_Type :=
70 (write_to_binary_file, write_to_text_file);
71 mode : constant array (Boolean) of Write_Mode_Type :=
72 (bin_text_mode (options (extract_as_text)), just_test);
73 actual_mode : Write_Mode_Type := mode (options (test_only));
74
75 true_packed_size : Zip.Zip_64_Data_Size_Type; -- encryption adds 12 to packed size
76
77 the_output_name : Unbounded_String;
78
79 -- 27-Jun-2001 : possibility of trashing directory part of a name
80 -- e.g. : zipada/uza_src/unzip.ads -> unzip.ads
81 function Maybe_trash_dir (n : String) return String is
82 idx : Integer := n'First - 1;
83 begin
84 if options (junk_directories) then
85 for i in n'Range loop
86 if n (i) in '/' | '\' then
87 idx := i;
88 end if;
89 end loop;
90 -- idx points on the index just before the interesting part
91 return n (idx + 1 .. n'Last);
92 else
93 return n;
94 end if;
95 end Maybe_trash_dir;
96
97 procedure Set_definitively_named_outfile (composed_name : String) is
98 idx : Integer := composed_name'First - 1;
99 first_in_name : Integer;
100 begin
101 for i in composed_name'Range loop
102 if composed_name (i) in '/' | '\' then
103 idx := i;
104 end if;
105 end loop;
106 -- idx points on the index just before the name part
107
108 if idx >= composed_name'First and then
109 actual_mode in Write_to_file and then
110 file_system_routines.Create_Path /= null
111 then
112 -- Not only the name, also a path.
113 -- In that case, we may need to create parts of the path.
114 declare
115 Directory_Separator : constant Character := '/';
116 -- The '/' separator is also recognized by Windows' routines,
117 -- so we can just use it as a standard. See the discussion started
118 -- in July 2010 in the Ada Comment mailing list about it
119 -- for the 2012 standard.
120 path : String := composed_name (composed_name'First .. idx - 1);
121 begin
122 -- Set the file separator recognized by the O.S.
123 for i in path'Range loop
124 if path (i) in '/' | '\' then
125 path (i) := Directory_Separator;
126 end if;
127 end loop;
128 if path = "" then
129 null;
130 elsif path (path'Last) = ':' then
131 null; -- We are on Windows and cannot create drives (like "D:")
132 else
133 file_system_routines.Create_Path (path);
134 end if;
135 end;
136 end if;
137 -- Now we can create the file itself.
138 first_in_name := composed_name'First;
139 --
140 the_output_name :=
141 To_Unbounded_String (composed_name (first_in_name .. composed_name'Last));
142 end Set_definitively_named_outfile;
143
144 function Full_Path_Name (
145 file_name_in_archive : String;
146 encoding : Zip.Zip_Name_Encoding)
147 return String
148 is
149 begin
150 if file_system_routines.Compose_File_Name = null then
151 return file_name_in_archive;
152 else
153 return file_system_routines.Compose_File_Name (file_name_in_archive, encoding);
154 end if;
155 end Full_Path_Name;
156
157 procedure Set_outfile (
158 long_not_composed_name : String;
159 encoding : Zip.Zip_Name_Encoding
160 )
161 is
162 -- Eventually trash the archived directory structure, then
163 -- eventually add/modify/... another one:
164 name : constant String :=
165 Full_Path_Name (Maybe_trash_dir (long_not_composed_name), encoding);
166 begin
167 Set_definitively_named_outfile (name);
168 end Set_outfile;
169
170 procedure Set_outfile_interactive (
171 long_not_composed_possible_name : String;
172 encoding : Zip.Zip_Name_Encoding
173 )
174 is
175 -- Eventually trash the archived directory structure, then
176 -- eventually add/modify/... another one:
177 possible_name : constant String :=
178 Full_Path_Name (Maybe_trash_dir (long_not_composed_possible_name), encoding);
179 -- possible_name may have a different encoding depending on Compose_File_Name...
180 new_name : String (1 .. 1024);
181 new_name_length : Natural;
182 begin
183 if help_the_file_exists /= null and then Zip.Exists (possible_name) then
184 loop
185 case current_user_attitude is
186 when yes | no | rename_it => -- then ask for this name too
187 help_the_file_exists (
188 long_not_composed_possible_name, encoding,
189 current_user_attitude,
190 new_name, new_name_length
191 );
192 when yes_to_all | none | abort_now =>
193 exit; -- nothing to decide: previous decision was definitive
194 end case;
195 exit when not (
196 current_user_attitude = rename_it and then -- new name exists too!
197 Zip.Exists (new_name (1 .. new_name_length))
198 );
199 end loop;
200
201 -- User has decided.
202 case current_user_attitude is
203 when yes | yes_to_all =>
204 skip_this_file := False;
205 Set_definitively_named_outfile (possible_name);
206 when no | none =>
207 skip_this_file := True;
208 when rename_it =>
209 skip_this_file := False;
210 Set_definitively_named_outfile (new_name (1 .. new_name_length));
211 when abort_now =>
212 raise User_abort;
213 end case;
214
215 else -- no name conflict or non-interactive (help_the_file_exists=null)
216
217 skip_this_file := False;
218 Set_definitively_named_outfile (possible_name);
219 end if;
220 end Set_outfile_interactive;
221
222 procedure Inform_User (
223 name : String;
224 comp, uncomp : Zip.Zip_64_Data_Size_Type
225 )
226 is
227 begin
228 if tell_data /= null then
229 tell_data (name, comp, uncomp, method);
230 end if;
231 end Inform_User;
232
233 the_name : String (1 .. 65_535); -- Seems overkill, but Zip entry names can be that long!
234 the_name_len : Natural;
235 use Zip_Streams;
236 use type Zip.PKZip_method;
237 use type Zip.Feedback_Proc;
238
239 actual_feedback : Zip.Feedback_Proc;
240
241 dummy_memory : p_Stream_Element_Array;
242 dummy_stream : constant p_Stream := null;
243 encrypted, dummy_bool : Boolean;
244
245 begin
246 begin
247 Set_Index (zip_file, work_index);
248 Zip.Headers.Read_and_Check (zip_file, local_header);
249 exception
250 when Zip.Headers.bad_local_header =>
251 raise; -- Processed later, on Extract
252 when others =>
253 raise Zip.Archive_corrupted;
254 end;
255
256 method := Zip.Method_from_Code (local_header.zip_type);
257 if method = Zip.unknown then
258 raise UnZip.Unsupported_method with
259 "Format (method) #" & Unsigned_16'Image (local_header.zip_type) &
260 " is unknown";
261 end if;
262
263 -- calculate offset of data
264
265 work_index :=
266 work_index +
267 ZS_Size_Type (
268 local_header.filename_length +
269 local_header.extra_field_length +
270 Zip.Headers.local_header_length
271 );
272
273 --
274 -- Zip64 extension.
275 --
276 if local_header.extra_field_length >= 4 then
277 declare
278 mem : constant Zip_Streams.ZS_Index_Type := Index (zip_file);
279 local_header_extension : Zip.Headers.Local_File_Header_Extension;
280 dummy_offset : Unsigned_64 := 0; -- Initialized for avoiding random value = 16#FFFF_FFFF#
281 begin
282 Set_Index (zip_file, mem + Zip_Streams.ZS_Index_Type (local_header.filename_length));
283 Zip.Headers.Read_and_Check (zip_file, local_header_extension);
284 Set_Index (zip_file, mem);
285 Zip.Headers.Interpret
286 (local_header_extension,
287 local_header.dd.uncompressed_size,
288 local_header.dd.compressed_size,
289 dummy_offset);
290 end;
291 end if;
292
293 data_descriptor_after_data := (local_header.bit_flag and 8) /= 0;
294
295 if data_descriptor_after_data then
296 -- Sizes and CRC are stored after the data
297 -- We set size to avoid getting a sudden Zip_EOF !
298 if local_header.zip_type = 0
299 and then hint_comp_size = fallback_compressed_size
300 then
301 -- For Stored (Method 0) data we need a correct "compressed" size.
302 -- If the hint is the bogus fallback value, it is better to trust
303 -- the local header, since this size is known in advance. Case found
304 -- in Microsoft's OneDrive cloud storage (in 2018). Zip files,
305 -- created by the server for downloading more than one file, are
306 -- using the "Store" format and a postfixed Data Descriptor for
307 -- writing the CRC value.
308 --
309 null; -- Do not overwrite the compressed size in that case.
310 else
311 local_header.dd.compressed_size := hint_comp_size;
312 end if;
313 local_header.dd.crc_32 := hint_crc_32;
314 local_header.dd.uncompressed_size := fallback_compressed_size;
315 actual_feedback := null; -- no feedback possible: unknown sizes
316 else
317 -- Sizes and CRC are stored before the data, inside the local header
318 actual_feedback := feedback; -- use the given feedback procedure
319 end if;
320
321 encrypted := (local_header.bit_flag and Zip.Headers.Encryption_Flag_Bit) /= 0;
322
323 -- 13-Dec-2002
324 true_packed_size := local_header.dd.compressed_size;
325 if encrypted then
326 true_packed_size := true_packed_size - 12;
327 end if;
328
329 if name_from_header then -- Name from local header is used as output name
330 the_name_len := Natural (local_header.filename_length);
331 if the_name_len > 0 then
332 String'Read (zip_file'Access, the_name (1 .. the_name_len));
333 end if;
334 if not data_descriptor_after_data then
335 Inform_User (
336 the_name (1 .. the_name_len),
337 true_packed_size,
338 local_header.dd.uncompressed_size
339 );
340 end if;
341 if the_name_len = 0 or else the_name (the_name_len) in '/' | '\' then
342 -- This is a directory name (12-feb-2000)
343 skip_this_file := True;
344 elsif actual_mode in Write_to_file then
345 Set_outfile_interactive (
346 the_name (1 .. the_name_len),
347 boolean_to_encoding ((local_header.bit_flag and
348 Zip.Headers.Language_Encoding_Flag_Bit) /= 0)
349 );
350 else -- only informational, no need for interaction
351 Set_outfile (the_name (1 .. the_name_len),
352 boolean_to_encoding ((local_header.bit_flag and
353 Zip.Headers.Language_Encoding_Flag_Bit) /= 0)
354 );
355 end if;
356 else -- Output name is given: out_name
357 if not data_descriptor_after_data then
358 Inform_User (
359 out_name,
360 true_packed_size,
361 local_header.dd.uncompressed_size
362 );
363 end if;
364 if out_name'Length = 0 or else out_name (out_name'Last) in '/' | '\' then
365 -- This is a directory name, so do not write anything (30-Jan-2012).
366 skip_this_file := True;
367 elsif actual_mode in Write_to_file then
368 Set_outfile_interactive (out_name, out_name_encoding);
369 else -- only informational, no need for interaction
370 Set_outfile (out_name, out_name_encoding);
371 end if;
372 end if;
373
374 if skip_this_file then
375 actual_mode := just_test;
376 end if;
377
378 if skip_this_file and not data_descriptor_after_data then
379 -- We can skip actually since sizes are known.
380 if feedback /= null then
381 feedback (
382 percents_done => 0,
383 entry_skipped => True,
384 user_abort => dummy_bool
385 );
386 end if;
387 else
388 begin
389 Set_Index (zip_file, work_index); -- eventually skips the file name
390 exception
391 when others =>
392 raise Zip.Archive_corrupted with
393 "End of stream reached (location: between local header and archived data)";
394 end;
395 UnZip.Decompress.Decompress_Data (
396 zip_file => zip_file,
397 format => method,
398 write_mode => actual_mode,
399 output_file_name => To_String (the_output_name),
400 output_memory_access => dummy_memory,
401 output_stream_access => dummy_stream,
402 feedback => actual_feedback,
403 explode_literal_tree => (local_header.bit_flag and 4) /= 0,
404 explode_slide_8KB_LZMA_EOS => (local_header.bit_flag and Zip.Headers.LZMA_EOS_Flag_Bit) /= 0,
405 data_descriptor_after_data => data_descriptor_after_data,
406 is_encrypted => encrypted,
407 password => password,
408 get_new_password => get_pwd,
409 hint => local_header
410 );
411
412 if actual_mode /= just_test then
413 begin
414 if file_system_routines.Set_Time_Stamp /= null then
415 file_system_routines.Set_Time_Stamp (
416 To_String (the_output_name),
417 Zip.Convert (local_header.file_timedate)
418 );
419 elsif file_system_routines.Set_ZTime_Stamp /= null then
420 file_system_routines.Set_ZTime_Stamp (
421 To_String (the_output_name),
422 local_header.file_timedate
423 );
424 end if;
425 exception
426 when Zip_Streams.Calendar.Time_Error | Ada.Calendar.Time_Error =>
427 null; -- invalid time, we give up setting the time stamp
428 end;
429 end if;
430
431 if data_descriptor_after_data then -- Sizes and CRC at the end
432 -- Inform after decompression
433 Inform_User (
434 To_String (the_output_name),
435 local_header.dd.compressed_size,
436 local_header.dd.uncompressed_size
437 );
438 end if;
439
440 end if; -- not ( skip_this_file and not data_descriptor )
441
442 -- Set the offset on the next zipped file
443 header_index := header_index +
444 ZS_Size_Type (
445 local_header.filename_length +
446 local_header.extra_field_length +
447 Zip.Headers.local_header_length
448 ) +
449 ZS_Size_Type (local_header.dd.compressed_size);
450
451 if data_descriptor_after_data then
452 header_index :=
453 header_index + ZS_Size_Type (Zip.Headers.data_descriptor_length);
454 end if;
455
456 exception
457 when Ada.IO_Exceptions.End_Error =>
458 raise Zip.Archive_corrupted with "End of stream reached";
459 end UnZipFile;
460
461 ----------------------------------
462 -- Simple extraction procedures --
463 ----------------------------------
464
465 -- Extract all files from an archive (from)
466
467 procedure Extract (from : String;
468 options : Option_Set := no_option;
469 password : String := "";
470 file_system_routines : FS_Routines_Type := null_routines)
471 is
472 begin
473 Extract (from, null, null, null, null,
474 options, password, file_system_routines);
475 end Extract;
476
477 procedure Extract (from : String;
478 what : String;
479 options : Option_Set := no_option;
480 password : String := "";
481 file_system_routines : FS_Routines_Type := null_routines)
482 is
483 begin
484 Extract (from, what, null, null, null, null,
485 options, password, file_system_routines);
486 end Extract;
487
488 procedure Extract (from : String;
489 what : String;
490 rename : String;
491 options : Option_Set := no_option;
492 password : String := "";
493 file_system_routines : FS_Routines_Type := null_routines)
494 is
495 begin
496 Extract (from, what, rename, null, null, null,
497 options, password, file_system_routines);
498 end Extract;
499
500 procedure Extract (from : Zip.Zip_Info;
501 options : Option_Set := no_option;
502 password : String := "";
503 file_system_routines : FS_Routines_Type := null_routines)
504 is
505 begin
506 Extract (from, null, null, null, null,
507 options, password, file_system_routines);
508 end Extract;
509
510 procedure Extract (from : Zip.Zip_Info;
511 what : String;
512 options : Option_Set := no_option;
513 password : String := "";
514 file_system_routines : FS_Routines_Type := null_routines)
515 is
516 begin
517 Extract (from, what, null, null, null, null,
518 options, password, file_system_routines);
519 end Extract;
520
521 procedure Extract (from : Zip.Zip_Info;
522 what : String;
523 rename : String;
524 options : Option_Set := no_option;
525 password : String := "";
526 file_system_routines : FS_Routines_Type := null_routines)
527 is
528 begin
529 Extract (from, what, rename, null, null, null,
530 options, password, file_system_routines);
531 end Extract;
532
533 -- All previous extract call the following ones, with bogus UI arguments
534
535 ------------------------------------------------------------
536 -- All previous extraction procedures, for user interface --
537 ------------------------------------------------------------
538
539 -- Extract one precise file (what) from an archive (from)
540
541 procedure Extract (from : String;
542 what : String;
543 feedback : Zip.Feedback_Proc;
544 help_the_file_exists : Resolve_Conflict_Proc;
545 tell_data : Tell_Data_Proc;
546 get_pwd : Get_Password_Proc;
547 options : Option_Set := no_option;
548 password : String := "";
549 file_system_routines : FS_Routines_Type := null_routines)
550 is
551 use Zip_Streams;
552 use type Zip.Feedback_Proc;
553 zip_file : File_Zipstream;
554 header_index : ZS_Index_Type;
555 comp_size : Zip.Zip_64_Data_Size_Type;
556 uncomp_size : Zip.Zip_64_Data_Size_Type;
557 crc_32 : Unsigned_32;
558 work_password : Unbounded_String := To_Unbounded_String (password);
559 begin
560 if feedback = null then
561 current_user_attitude := yes_to_all; -- non-interactive
562 end if;
563 Set_Name (zip_file, from);
564 Open (zip_file, In_File);
565 Zip.Find_Offset
566 (file => zip_file,
567 name => what,
568 case_sensitive => options (case_sensitive_match),
569 file_index => header_index,
570 comp_size => comp_size,
571 uncomp_size => uncomp_size,
572 crc_32 => crc_32);
573 --
574 UnZipFile
575 (zip_file => zip_file,
576 out_name => what,
577 out_name_encoding => Zip.IBM_437, -- assumption...
578 name_from_header => False,
579 header_index => header_index,
580 hint_comp_size => comp_size,
581 hint_crc_32 => crc_32,
582 feedback => feedback,
583 help_the_file_exists => help_the_file_exists,
584 tell_data => tell_data,
585 get_pwd => get_pwd,
586 options => options,
587 password => work_password,
588 file_system_routines => file_system_routines);
589 --
590 Close (zip_file);
591 exception
592 when Zip.Headers.bad_local_header =>
593 raise Zip.Archive_corrupted with "Bad local header";
594 end Extract;
595
596 -- Extract one precise file (what) from an archive (from),
597 -- but save under a new name (rename)
598
599 procedure Extract (from : String;
600 what : String;
601 rename : String;
602 feedback : Zip.Feedback_Proc;
603 tell_data : Tell_Data_Proc;
604 get_pwd : Get_Password_Proc;
605 options : Option_Set := no_option;
606 password : String := "";
607 file_system_routines : FS_Routines_Type := null_routines)
608 is
609 use Zip_Streams;
610 use type Zip.Feedback_Proc;
611 zip_file : aliased File_Zipstream;
612 header_index : Zip_Streams.ZS_Index_Type;
613 comp_size : Zip.Zip_64_Data_Size_Type;
614 uncomp_size : Zip.Zip_64_Data_Size_Type;
615 crc_32 : Unsigned_32;
616 work_password : Unbounded_String := To_Unbounded_String (password);
617 begin
618 if feedback = null then
619 current_user_attitude := yes_to_all; -- non-interactive
620 end if;
621 Set_Name (zip_file, from);
622 Open (zip_file, In_File);
623 Zip.Find_Offset
624 (file => zip_file,
625 name => what,
626 case_sensitive => options (case_sensitive_match),
627 file_index => header_index,
628 comp_size => comp_size,
629 uncomp_size => uncomp_size,
630 crc_32 => crc_32);
631 --
632 UnZipFile
633 (zip_file => zip_file,
634 out_name => rename,
635 out_name_encoding => Zip.IBM_437, -- assumption...
636 name_from_header => False,
637 header_index => header_index,
638 hint_comp_size => comp_size,
639 hint_crc_32 => crc_32,
640 feedback => feedback,
641 help_the_file_exists => null,
642 tell_data => tell_data,
643 get_pwd => get_pwd,
644 options => options,
645 password => work_password,
646 file_system_routines => file_system_routines);
647 --
648 Close (zip_file);
649 exception
650 when Zip.Headers.bad_local_header =>
651 raise Zip.Archive_corrupted with "Bad local header";
652 end Extract;
653
654 -- Extract all files from an archive (from)
655
656 procedure Extract (from : String;
657 feedback : Zip.Feedback_Proc;
658 help_the_file_exists : Resolve_Conflict_Proc;
659 tell_data : Tell_Data_Proc;
660 get_pwd : Get_Password_Proc;
661 options : Option_Set := no_option;
662 password : String := "";
663 file_system_routines : FS_Routines_Type := null_routines)
664 is
665 use Zip_Streams;
666 use type Zip.Feedback_Proc;
667 zip_file : File_Zipstream;
668 header_index : Zip_Streams.ZS_Index_Type;
669 work_password : Unbounded_String := To_Unbounded_String (password);
670 begin
671 if feedback = null then
672 current_user_attitude := yes_to_all; -- non-interactive
673 end if;
674 Set_Name (zip_file, from);
675 Open (zip_file, In_File);
676 Zip.Find_first_Offset (zip_file, header_index); -- >= 13-May-2001
677 -- We simply unzip everything sequentially, until the end:
678 all_files : loop
679 UnZipFile
680 (zip_file => zip_file,
681 out_name => "",
682 out_name_encoding => Zip.IBM_437, -- ignored
683 name_from_header => True,
684 header_index => header_index,
685 hint_comp_size => fallback_compressed_size,
686 -- ^ no better hint available if comp_size is 0 in local header
687 hint_crc_32 => 0, -- 2.0 decryption can fail if data descriptor after data
688 feedback => feedback,
689 help_the_file_exists => help_the_file_exists,
690 tell_data => tell_data,
691 get_pwd => get_pwd,
692 options => options,
693 password => work_password,
694 file_system_routines => file_system_routines);
695 end loop all_files;
696 exception
697 when Zip.Headers.bad_local_header | Zip.Archive_is_empty =>
698 Close (zip_file); -- Normal case: end of archived entries (of fuzzy data) was hit
699 when Zip.Archive_open_error =>
700 raise; -- Couldn't open zip file
701 when others =>
702 Close (zip_file);
703 raise; -- Something else went wrong
704 end Extract;
705
706 -- Extract all files from an archive (from)
707 -- Needs Zip.Load(from, ...) prior to the extraction
708
709 procedure Extract (from : Zip.Zip_Info;
710 feedback : Zip.Feedback_Proc;
711 help_the_file_exists : Resolve_Conflict_Proc;
712 tell_data : Tell_Data_Proc;
713 get_pwd : Get_Password_Proc;
714 options : Option_Set := no_option;
715 password : String := "";
716 file_system_routines : FS_Routines_Type := null_routines)
717 is
718 procedure Extract_1_file (name : String) is
719 begin
720 Extract
721 (from => from,
722 what => name,
723 feedback => feedback,
724 help_the_file_exists => help_the_file_exists,
725 tell_data => tell_data,
726 get_pwd => get_pwd,
727 options => options,
728 password => password,
729 file_system_routines => file_system_routines);
730 end Extract_1_file;
731 --
732 procedure Extract_all_files is new Zip.Traverse (Extract_1_file);
733 --
734 begin
735 Extract_all_files (from);
736 end Extract;
737
738 -- Extract one precise file (what) from an archive (from)
739 -- Needs Zip.Load(from, ...) prior to the extraction
740
741 procedure Extract (from : Zip.Zip_Info;
742 what : String;
743 feedback : Zip.Feedback_Proc;
744 help_the_file_exists : Resolve_Conflict_Proc;
745 tell_data : Tell_Data_Proc;
746 get_pwd : Get_Password_Proc;
747 options : Option_Set := no_option;
748 password : String := "";
749 file_system_routines : FS_Routines_Type := null_routines
750 ) is
751
752 header_index : Zip_Streams.ZS_Index_Type;
753 comp_size : Zip.Zip_64_Data_Size_Type;
754 uncomp_size : Zip.Zip_64_Data_Size_Type;
755 crc_32 : Unsigned_32;
756 work_password : Unbounded_String := To_Unbounded_String (password);
757 use Zip_Streams;
758 use type Zip.Feedback_Proc;
759 zip_file : aliased File_Zipstream;
760 input_stream : Zipstream_Class_Access;
761 use_a_file : constant Boolean := Zip.Zip_Stream (from) = null;
762 name_encoding : Zip.Zip_Name_Encoding;
763 begin
764 if use_a_file then
765 input_stream := zip_file'Unchecked_Access;
766 Set_Name (zip_file, Zip.Zip_Name (from));
767 Open (zip_file, In_File);
768 else -- use the given stream
769 input_stream := Zip.Zip_Stream (from);
770 end if;
771 if feedback = null then
772 current_user_attitude := yes_to_all; -- non-interactive
773 end if;
774 Zip.Find_Offset
775 (info => from,
776 name => what,
777 name_encoding => name_encoding,
778 file_index => header_index,
779 comp_size => comp_size,
780 uncomp_size => uncomp_size,
781 crc_32 => crc_32);
782 --
783 UnZipFile
784 (zip_file => input_stream.all,
785 out_name => what,
786 out_name_encoding => name_encoding,
787 name_from_header => False,
788 header_index => header_index,
789 hint_comp_size => comp_size,
790 hint_crc_32 => crc_32,
791 feedback => feedback,
792 help_the_file_exists => help_the_file_exists,
793 tell_data => tell_data,
794 get_pwd => get_pwd,
795 options => options,
796 password => work_password,
797 file_system_routines => file_system_routines);
798 --
799 if use_a_file then
800 Close (zip_file);
801 end if;
802 exception
803 when Zip.Headers.bad_local_header =>
804 if use_a_file and then Is_Open (zip_file) then
805 Close (zip_file);
806 end if;
807 raise Zip.Archive_corrupted with "Bad local header";
808 when others =>
809 if use_a_file and then Is_Open (zip_file) then
810 Close (zip_file);
811 end if;
812 raise;
813 end Extract;
814
815 -- Extract one precise file (what) from an archive (from)
816 -- but save under a new name (rename)
817 -- Needs Zip.Load(from, ...) prior to the extraction
818
819 procedure Extract (from : Zip.Zip_Info;
820 what : String;
821 rename : String;
822 feedback : Zip.Feedback_Proc;
823 tell_data : Tell_Data_Proc;
824 get_pwd : Get_Password_Proc;
825 options : Option_Set := no_option;
826 password : String := "";
827 file_system_routines : FS_Routines_Type := null_routines)
828 is
829
830 header_index : Zip_Streams.ZS_Index_Type;
831 comp_size : Zip.Zip_64_Data_Size_Type;
832 uncomp_size : Zip.Zip_64_Data_Size_Type;
833 crc_32 : Unsigned_32;
834 work_password : Unbounded_String := To_Unbounded_String (password);
835 use Zip_Streams;
836 use type Zip.Feedback_Proc;
837 zip_file : aliased File_Zipstream;
838 input_stream : Zipstream_Class_Access;
839 use_a_file : constant Boolean := Zip.Zip_Stream (from) = null;
840 name_encoding : Zip.Zip_Name_Encoding;
841 begin
842 if use_a_file then
843 input_stream := zip_file'Unchecked_Access;
844 Set_Name (zip_file, Zip.Zip_Name (from));
845 Open (zip_file, In_File);
846 else -- use the given stream
847 input_stream := Zip.Zip_Stream (from);
848 end if;
849 if feedback = null then
850 current_user_attitude := yes_to_all; -- non-interactive
851 end if;
852 Zip.Find_Offset
853 (info => from,
854 name => what,
855 name_encoding => name_encoding,
856 file_index => header_index,
857 comp_size => comp_size,
858 uncomp_size => uncomp_size,
859 crc_32 => crc_32);
860 --
861 UnZipFile
862 (zip_file => input_stream.all,
863 out_name => rename,
864 out_name_encoding => name_encoding, -- assumption: encoding same as name
865 name_from_header => False,
866 header_index => header_index,
867 hint_comp_size => comp_size,
868 hint_crc_32 => crc_32,
869 feedback => feedback,
870 help_the_file_exists => null,
871 tell_data => tell_data,
872 get_pwd => get_pwd,
873 options => options,
874 password => work_password,
875 file_system_routines => file_system_routines);
876 --
877 if use_a_file then
878 Close (zip_file);
879 end if;
880 exception
881 when Zip.Headers.bad_local_header =>
882 if use_a_file and then Is_Open (zip_file) then
883 Close (zip_file);
884 end if;
885 raise Zip.Archive_corrupted with "Bad local header";
886 when others =>
887 if use_a_file and then Is_Open (zip_file) then
888 Close (zip_file);
889 end if;
890 raise;
891 end Extract;
892
893 end UnZip;
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.