From a20eb61c2fdd027a89acd249eea4f452e4accfb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=C3=A5kan=20Mattsson?=
Date: Mon, 1 Mar 2010 19:53:48 +0100
Subject: Add function zip:foldl/3 to iterate over zip archives
This is the public interface of prim_zip:open/3,
which has been used in earlier releases by both
erl_prim_loader and escript. The new function
can be used as a replacement for the undocumented
function escript:foldl/3 that is likely to be
removed without further notice.
The error handling of prim_zip:open/3 (and
prim_zip:foldl/3) has been improved in order to
better suite a public interface. For example it
could happen that a file or a zlib port could be
left open in some errors cases.
The documentation of the FileSpec parameter to
zip:create/3 has been updated to show that
file info can be explicitly specified. A FileSpec
may contain {Filename, binary(), #file_info{}}
elements. The function zip:create/3 was already
prepared to partly support this, but now after
a few minor fixes it is fully supported.
---
erts/preloaded/ebin/erl_prim_loader.beam | Bin 50808 -> 50816 bytes
erts/preloaded/ebin/erlang.beam | Bin 23800 -> 23808 bytes
erts/preloaded/ebin/init.beam | Bin 44488 -> 44500 bytes
erts/preloaded/ebin/otp_ring0.beam | Bin 1436 -> 1448 bytes
erts/preloaded/ebin/prim_file.beam | Bin 29480 -> 29496 bytes
erts/preloaded/ebin/prim_inet.beam | Bin 57308 -> 57320 bytes
erts/preloaded/ebin/prim_zip.beam | Bin 21756 -> 22600 bytes
erts/preloaded/ebin/zlib.beam | Bin 10612 -> 10620 bytes
erts/preloaded/src/prim_zip.erl | 89 ++++++++++++++++++++----------
lib/stdlib/doc/src/zip.xml | 90 +++++++++++++++++++++++++------
lib/stdlib/src/zip.erl | 80 ++++++++++++++++++---------
lib/stdlib/test/zip_SUITE.erl | 78 +++++++++++++++++++--------
12 files changed, 246 insertions(+), 91 deletions(-)
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 304ac41dce..afd8a90b3f 100644
Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 21e5525b2f..4ec84948d8 100644
Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 8ccc553c13..c3e746f3ee 100644
Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index 8ae73ea9a7..4b2d8bb2de 100644
Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 3acda843fd..2916baaa77 100644
Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 9457b6d360..46912e2bea 100644
Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 6837cb4661..ccf8aff6f6 100644
Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 4e4f85d312..ccd597ba68 100644
Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ
diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl
index 3f5a5b9721..6a9856fdad 100644
--- a/erts/preloaded/src/prim_zip.erl
+++ b/erts/preloaded/src/prim_zip.erl
@@ -65,34 +65,55 @@ filter_fun() ->
open(F) ->
open(filter_fun(), undefined, F).
-open(FilterFun, FilterAcc, F) ->
- case ?CATCH do_open(FilterFun, FilterAcc, F) of
- {ok, PrimZip, Acc} ->
- {ok, PrimZip, Acc};
- Error ->
- {error, Error}
- end.
+open(FilterFun, FilterAcc, F) when is_function(FilterFun, 2) ->
+ try
+ do_open(FilterFun, FilterAcc, F)
+ catch
+ throw:{filter_fun_throw, Reason} ->
+ throw(Reason);
+ throw:InternalReason ->
+ {error, InternalReason};
+ Class:Reason ->
+ erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ end;
+open(_, _, _) ->
+ {error, einval}.
do_open(FilterFun, FilterAcc, F) ->
Input = get_zip_input(F),
In0 = Input({open, F, [read, binary, raw]}, []),
Z = zlib:open(),
PrimZip = #primzip{files = [], zlib = Z, in = In0, input = Input},
- {PrimZip2, FilterAcc2} = get_central_dir(PrimZip, FilterFun, FilterAcc),
- {ok, PrimZip2, FilterAcc2}.
+ try
+ {PrimZip2, FilterAcc2} = get_central_dir(PrimZip, FilterFun, FilterAcc),
+ {ok, PrimZip2, FilterAcc2}
+ catch
+ Class:Reason ->
+ close(PrimZip),
+ erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ end.
%% iterate over all files in a zip archive
-foldl(FilterFun, FilterAcc, #primzip{files = Files} = PrimZip) ->
- case ?CATCH do_foldl(FilterFun, FilterAcc, Files, [], PrimZip, PrimZip) of
- {ok, FilterAcc2, PrimZip2} -> {ok, PrimZip2, FilterAcc2};
- Error -> {error, Error}
+foldl(FilterFun, FilterAcc, #primzip{files = Files} = PrimZip)
+ when is_function(FilterFun, 2) ->
+ try
+ {ok, FilterAcc2, PrimZip2} =
+ do_foldl(FilterFun, FilterAcc, Files, [], PrimZip, PrimZip),
+ {ok, PrimZip2, FilterAcc2}
+ catch
+ throw:{filter_fun_throw, Reason} ->
+ throw(Reason);
+ throw:InternalReason ->
+ {error, InternalReason};
+ Class:Reason ->
+ erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
end;
foldl(_, _, _) ->
{error, einval}.
do_foldl(FilterFun, FilterAcc, [PF | Tail], Acc0, PrimZip, PrimZipOrig) ->
#primzip_file{name = F, get_info = GetInfo, get_bin = GetBin} = PF,
- case FilterFun({F, GetInfo, GetBin}, FilterAcc) of
+ try FilterFun({F, GetInfo, GetBin}, FilterAcc) of
{Continue, Include, FilterAcc2} ->
Acc1 = include_acc(Include, PF, Acc0),
case Continue of
@@ -103,6 +124,9 @@ do_foldl(FilterFun, FilterAcc, [PF | Tail], Acc0, PrimZip, PrimZipOrig) ->
end;
FilterRes ->
throw({illegal_filter, FilterRes})
+ catch
+ throw:Reason ->
+ throw({filter_fun_throw, Reason})
end;
do_foldl(_FilterFun, FilterAcc, [], Acc, PrimZip, _PrimZipOrig) ->
{ok, FilterAcc, PrimZip#primzip{files = reverse(Acc)}}.
@@ -121,12 +145,14 @@ include_acc(Include, PF, Acc) ->
List when is_list(List) ->
%% Add new entries
Fun = fun(I, A) -> include_acc(I, PF, A) end,
- lists_foldl(Fun, Acc, List)
+ lists_foldl(Fun, Acc, List);
+ Bad ->
+ throw({illegal_filter, Bad})
end.
lists_foldl(F, Accu, [Hd|Tail]) ->
lists_foldl(F, F(Hd, Accu), Tail);
-lists_foldl(F, Accu, []) when is_function(F, 2) ->
+lists_foldl(F, Accu, []) when is_function(F, 2) ->
Accu.
%% close a zip archive
@@ -139,7 +165,9 @@ close(_) ->
get_zip_input({F, B}) when is_binary(B), is_list(F) ->
fun binary_io/2;
get_zip_input(F) when is_list(F) ->
- fun prim_file_io/2.
+ fun prim_file_io/2;
+get_zip_input(_) ->
+ throw(einval).
%% get a file from the archive
get_z_file(F, Offset, ChunkSize, #primzip{zlib = Z, in = In0, input = Input}) ->
@@ -218,15 +246,15 @@ get_cd_loop(N, BCD, Acc0, PrimZip, FileName, Offset, CFH, EndOffset, FilterFun,
GetInfo = fun() -> cd_file_header_to_file_info(FileName, CFH, <<>>) end,
GetBin = fun() -> get_z_file(FileName, Offset, Size, PrimZip) end,
PF = #primzip_file{name = FileName, get_info = GetInfo, get_bin = GetBin},
- case FilterFun({FileName, GetInfo, GetBin}, FilterAcc) of
+ try FilterFun({FileName, GetInfo, GetBin}, FilterAcc) of
{Continue, Include, FilterAcc2} ->
Acc1 =
case Include of
- false ->
+ false ->
Acc0;
true ->
[PF | Acc0];
- {true, Nick} ->
+ {true, Nick} ->
[PF#primzip_file{name = Nick} | Acc0];
{true, Nick, GI, GB} ->
PF2 = #primzip_file{name = Nick, get_info = GI, get_bin = GB},
@@ -247,13 +275,16 @@ get_cd_loop(N, BCD, Acc0, PrimZip, FileName, Offset, CFH, EndOffset, FilterFun,
end;
FilterRes ->
throw({illegal_filter, FilterRes})
+ catch
+ throw:Reason ->
+ throw({filter_fun_throw, Reason})
end.
get_file_header(BCD) ->
BCFH =
case BCD of
<> ->
B;
_ ->
@@ -266,11 +297,11 @@ get_file_header(BCD) ->
ToGet = FileNameLen + ExtraLen + CommentLen,
{B2, BCDRest} =
case BCD of
- <<_:?CENTRAL_FILE_HEADER_SZ/binary,
+ <<_:?CENTRAL_FILE_HEADER_SZ/binary,
G:ToGet/binary,
- Rest/binary>> ->
+ Rest/binary>> ->
{G, Rest};
- _ ->
+ _ ->
throw(bad_central_directory)
end,
FileName = get_filename_from_b2(B2, FileNameLen, ExtraLen, CommentLen),
@@ -319,9 +350,9 @@ prim_file_io({file_info, F}, _) ->
end;
prim_file_io({open, FN, Opts}, _) ->
case ?CATCH prim_file:open(FN, Opts++[binary]) of
- {ok, H} ->
+ {ok, H} ->
H;
- {error, E} ->
+ {error, E} ->
throw(E)
end;
prim_file_io({read, N}, H) ->
@@ -476,7 +507,7 @@ cd_file_header_to_file_info(FileName,
%% FI; % not yet supported, and not widely used
add_extra_info(FI, _) ->
FI.
-%%
+%%
%% unix_extra_field_and_var_from_bin(<
<> = <>,
<> = <>,
{{YearFrom1980+1980, Month, Day},
- {Hour, Min, Sec}}.
+ {Hour, Min, Sec}}.
cd_file_header_from_bin(<
reverse([H|T], Y) ->
reverse(T, [H|Y]);
-reverse([], X) ->
+reverse([], X) ->
X.
last([E|Es]) -> last(E, Es).
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index af407f1925..4d98a20206 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -42,16 +42,18 @@
By convention, the name of a zip file should end in ".zip ".
To abide to the convention, you'll need to add ".zip " yourself
to the name.
- Zip archives are created with the
- zip/2 or the
+
Zip archives are created with the
+ zip/2 or the
zip/3 function. (They are
also available as create , to resemble the erl_tar
module.)
- To extract files from a zip archive, use the
- unzip/1 or the
+
To extract files from a zip archive, use the
+ unzip/1 or the
unzip/2 function. (They are
also available as extract .)
- To return a list of the files in a zip archive, use the
+
To fold a function over all files in a zip archive, use the
+ foldl_3 .
+ To return a list of the files in a zip archive, use the
list_dir/1 or the
list_dir/2 function. (They
are also available as table .)
@@ -132,7 +134,7 @@ zip_file()
Name = filename()
FileList = [FileSpec]
- FileSpec = filename() | {filename(), binary()}
+ FileSpec = filename() | {filename(), binary()} | {filename(), binary(), #file_info{}}
Options = [Option]
Option = memory | cooked | verbose | {comment, Comment} | {cwd, CWD} | {compress, What} | {uncompress, What}
What = all | [Extension] | {add, [Extension]} | {del, [Extension]}
@@ -212,16 +214,16 @@ zip_file()
all
means that all files will be compressed (as long
- as they pass the uncompress condition).
+ as they pass the uncompress condition).
[Extension]
means that only files with exactly these extensions
- will be compressed.
+ will be compressed.
{add,[Extension]}
adds these extensions to the list of compress
- extensions.
+ extensions.
{del,[Extension]}
deletes these extensions from the list of compress
- extensions.
+ extensions.
{uncompress, What}
@@ -231,16 +233,16 @@ zip_file()
The following values of What are allowed:
all
- means that no files will be compressed.
+ means that no files will be compressed.
[Extension]
means that files with these extensions will be
- uncompressed.
+ uncompressed.
{add,[Extension]}
adds these extensions to the list of uncompress
- extensions.
+ extensions.
{del,[Extension]}
deletes these extensions from the list of uncompress
- extensions.
+ extensions.
@@ -283,7 +285,7 @@ zip_file()
the unzip/2 function will only extract the files
whose names are included in FileList . The full
paths, including the names of all sub directories within
- the zip archive, must be specified.
+ the zip archive, must be specified.
cooked
-
@@ -326,6 +328,64 @@ zip_file()
+
+ foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason}
+ Fold a function over all files in a zip archive
+
+ Fun = fun(FileInArchive, GetInfo, GetBin, AccIn) -> AccOut
+ FileInArchive = filename()
+ GetInfo = fun() -> #file_info{}
+ GetBin = fun() -> binary()
+ Acc0 = Acc1 = AccIn = AccOut = term()
+ Archive = filename() | {filename(), binary()}
+
+
+ The foldl/3 function
+ calls Fun(FileInArchive, GetInfo, GetBin, AccIn) on
+ successive files in the Archive , starting with AccIn
+ == Acc0 . FileInArchive is the name that the file
+ has in the archive. GetInfo is a fun that returns info
+ about the the file. GetBin returns the contents of the
+ file. Both GetInfo and GetBin must be called
+ within the Fun . Their behavior is undefined if they are
+ called outside the context of the Fun . The Fun
+ must return a new accumulator which is passed to the next
+ call. foldl/3 returns the final value of the
+ accumulator. Acc0 is returned if the archive is
+ empty. It is not necessary to iterate over all files in the
+ archive. The iteration may be ended prematurely in a
+ controlled manner by throwing an exception.
+
+ For example:
+
+> Name = "dummy.zip".
+"dummy.zip"
+> {ok, {Name, Bin}} = zip:create(Name, [{"foo", <<"FOO">>}, {"bar", <<"BAR">>}], [memory]).
+{ok,{"dummy.zip",
+ <<80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0,
+ 0,0,3,0,0,...>>}}
+> {ok, FileSpec} = zip:foldl(fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end, [], {Name, Bin}).
+{ok,[{"bar",<<"BAR">>,
+ {file_info,3,regular,read_write,
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ 54,1,0,0,0,0,0}},
+ {"foo",<<"FOO">>,
+ {file_info,3,regular,read_write,
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ 54,1,0,0,0,0,0}}]}
+> {ok, {Name, Bin}} = zip:create(Name, lists:reverse(FileSpec), [memory]).
+{ok,{"dummy.zip",
+ <<80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0,
+ 0,0,3,0,0,...>>}}
+> catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {Name, Bin}).
+<<"FOO">>
+
+
+
list_dir(Archive) -> RetValue
list_dir(Archive, Options)
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index e76d588cb5..d41aeefa59 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -20,7 +20,7 @@
%% Basic api
-export([unzip/1, unzip/2, extract/1, extract/2,
- zip/2, zip/3, create/2, create/3,
+ zip/2, zip/3, create/2, create/3, foldl/3,
list_dir/1, list_dir/2, table/1, table/2,
t/1, tt/1]).
@@ -38,7 +38,7 @@
zip_t/1, zip_tt/1,
zip_list_dir/1, zip_list_dir/2,
zip_close/1]).
-
+
%% just for debugging zip server, not documented, not tested, not to be used
-export([zip_get_state/1]).
@@ -82,7 +82,7 @@
-record(openzip_opts, {
output, % output object (fun)
open_opts, % file:open options
- cwd % directory to relate paths to
+ cwd % directory to relate paths to
}).
% openzip record, state for an open zip-file
@@ -93,10 +93,10 @@
input, % archive io object (fun)
output, % output io object (fun)
zlib, % handle to open zlib
- cwd % directory to relate paths to
+ cwd % directory to relate paths to
}).
-% Things that I would like to add to the public record #zip_file,
+% Things that I would like to add to the public record #zip_file,
% but can't as it would make things fail at upgrade.
% Instead we use {#zip_file,#zip_file_extra} internally.
-record(zip_file_extra, {
@@ -278,7 +278,7 @@ file_name_search(Name,Files) ->
[ZFile|_] -> ZFile;
[] -> false
end.
-
+
%% %% add a file to an open archive
%% openzip_add(File, OpenZip) ->
%% case ?CATCH do_openzip_add(File, OpenZip) of
@@ -344,6 +344,25 @@ do_unzip(F, Options) ->
Input(close, In1),
{ok, Files}.
+%% Iterate over all files in a zip archive
+foldl(Fun, Acc0, Archive) when is_function(Fun, 4) ->
+ ZipFun =
+ fun({Name, GetInfo, GetBin}, A) ->
+ A2 = Fun(Name, GetInfo, GetBin, A),
+ {true, false, A2}
+ end,
+ case prim_zip:open(ZipFun, Acc0, Archive) of
+ {ok, PrimZip, Acc1} ->
+ ok = prim_zip:close(PrimZip),
+ {ok, Acc1};
+ {error, bad_eocd} ->
+ {error, "Not an archive file"};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+foldl(_,_, _) ->
+ {error, einval}.
+
%% Create zip archive name F from Files or binaries
%%
%% Accepted options:
@@ -383,7 +402,7 @@ list_dir(F, Options) ->
do_list_dir(F, Options) ->
Opts = get_list_dir_options(F, Options),
- #list_dir_opts{input = Input, open_opts = OpO,
+ #list_dir_opts{input = Input, open_opts = OpO,
raw_iterator = RawIterator} = Opts,
In0 = Input({open, F, OpO}, []),
{Info, In1} = get_central_dir(In0, RawIterator, Input),
@@ -417,7 +436,7 @@ tt(F) when is_record(F, openzip) -> openzip_tt(F);
tt(F) -> t(F, fun raw_long_print_info_etc/5).
-%% option utils
+%% option utils
get_unzip_opt([], Opts) ->
Opts;
get_unzip_opt([verbose | Rest], Opts) ->
@@ -470,7 +489,7 @@ get_zip_opt([{cwd, CWD} | Rest], Opts) ->
get_zip_opt([{comment, C} | Rest], Opts) ->
get_zip_opt(Rest, Opts#zip_opts{comment = C});
get_zip_opt([{compress, Which} = O| Rest], Opts) ->
- Which2 =
+ Which2 =
case Which of
all ->
all;
@@ -485,7 +504,7 @@ get_zip_opt([{compress, Which} = O| Rest], Opts) ->
end,
get_zip_opt(Rest, Opts#zip_opts{compress = Which2});
get_zip_opt([{uncompress, Which} = O| Rest], Opts) ->
- Which2 =
+ Which2 =
case Which of
all ->
all;
@@ -560,16 +579,24 @@ get_openzip_options(Options) ->
get_input(F) when is_binary(F) ->
fun binary_io/2;
get_input(F) when is_list(F) ->
- fun file_io/2.
+ fun file_io/2;
+get_input(_) ->
+ throw(einval).
get_zip_input({F, B}) when is_binary(B), is_list(F) ->
fun binary_io/2;
+get_zip_input({F, B, #file_info{}}) when is_binary(B), is_list(F) ->
+ fun binary_io/2;
+get_zip_input({F, #file_info{}, B}) when is_binary(B), is_list(F) ->
+ fun binary_io/2;
get_zip_input(F) when is_list(F) ->
fun file_io/2;
get_zip_input({files, []}) ->
fun binary_io/2;
get_zip_input({files, [File | _]}) ->
- get_zip_input(File).
+ get_zip_input(File);
+get_zip_input(_) ->
+ throw(einval).
get_list_dir_options(F, Options) ->
Opts = #list_dir_opts{raw_iterator = fun raw_file_info_public/5,
@@ -620,6 +647,8 @@ put_eocd(N, Pos, Sz, Comment, Output, Out0) ->
get_filename({Name, _}, Type) ->
get_filename(Name, Type);
+get_filename({Name, _, _}, Type) ->
+ get_filename(Name, Type);
get_filename(Name, regular) ->
Name;
get_filename(Name, directory) ->
@@ -895,7 +924,7 @@ local_file_header_to_bin(
CompSize:32/little,
UncompSize:32/little,
FileNameLength:16/little,
- ExtraFieldLength:16/little>>.
+ ExtraFieldLength:16/little>>.
eocd_to_bin(#eocd{disk_num = DiskNum,
start_disk_num = StartDiskNum,
@@ -912,7 +941,7 @@ eocd_to_bin(#eocd{disk_num = DiskNum,
Offset:32/little,
ZipCommentLength:16/little>>.
-%% put together a local file header
+%% put together a local file header
local_file_header_from_info_method_name(#file_info{mtime = MTime},
UncompSize,
CompMethod, Name) ->
@@ -939,7 +968,7 @@ server_loop(OpenZip) ->
server_loop(NewOpenZip);
Error ->
From ! {self(), Error}
- end;
+ end;
{From, close} ->
From ! {self(), openzip_close(OpenZip)};
{From, get} ->
@@ -1024,7 +1053,7 @@ lists_foreach(F, [Hd|Tl]) ->
F(Hd),
lists_foreach(F, Tl).
-%% option utils
+%% option utils
get_openzip_opt([], Opts) ->
Opts;
get_openzip_opt([cooked | Rest], #openzip_opts{open_opts = OO} = Opts) ->
@@ -1121,7 +1150,7 @@ raw_file_info_public(CD, FileName, FileComment, BExtraField, Acc0) ->
Other -> Other
end,
[H2|T].
-
+
%% make a file_info from a central directory header
cd_file_header_to_file_info(FileName,
@@ -1213,8 +1242,8 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
{dir, In3};
_ ->
%% FileInfo = local_file_header_to_file_info(LH)
- %%{Out, In4, CRC, UncompSize} =
- {Out, In4, CRC, _UncompSize} =
+ %%{Out, In4, CRC, UncompSize} =
+ {Out, In4, CRC, _UncompSize} =
get_z_data(CompMethod, In3, FileName1,
CompSize, Input, Output, OpO, Z),
In5 = skip_z_data_descriptor(GPFlag, Input, In4),
@@ -1280,7 +1309,7 @@ get_z_data_loop(CompSize, UncompSize, In0, Out0, Input, Output, Z) ->
Out1 = Output({write, Uncompressed}, Out0),
get_z_data_loop(CompSize-N, UncompSize + iolist_size(Uncompressed),
In1, Out1, Input, Output, Z)
- end.
+ end.
%% skip data descriptor if any
@@ -1298,7 +1327,7 @@ dos_date_time_to_datetime(DosDate, DosTime) ->
<> = <>,
<> = <>,
{{YearFrom1980+1980, Month, Day},
- {Hour, Min, Sec}}.
+ {Hour, Min, Sec}}.
dos_date_time_from_datetime({{Year, Month, Day}, {Hour, Min, Sec}}) ->
YearFrom1980 = Year-1980,
@@ -1319,7 +1348,6 @@ unix_extra_field_and_var_from_bin(<
throw(bad_unix_extra_field).
-
%% A pwrite-like function for iolists (used by memory-option)
@@ -1478,6 +1506,8 @@ local_file_header_from_bin(_) ->
%% io functions
binary_io({file_info, {_Filename, _B, #file_info{} = FI}}, _A) ->
FI;
+binary_io({file_info, {_Filename, #file_info{} = FI, _B}}, _A) ->
+ FI;
binary_io({file_info, {_Filename, B}}, A) ->
binary_io({file_info, B}, A);
binary_io({file_info, B}, _) ->
@@ -1493,9 +1523,11 @@ binary_io({file_info, B}, _) ->
links = 1, major_device = 0,
minor_device = 0, inode = 0,
uid = 0, gid = 0};
-binary_io({open, {_Filename, B, _FI}, _Opts}, _) ->
+binary_io({open, {_Filename, B, _FI}, _Opts}, _) when is_binary(B) ->
+ {0, B};
+binary_io({open, {_Filename, _FI, B}, _Opts}, _) when is_binary(B) ->
{0, B};
-binary_io({open, {_Filename, B}, _Opts}, _) ->
+binary_io({open, {_Filename, B}, _Opts}, _) when is_binary(B) ->
{0, B};
binary_io({open, B, _Opts}, _) when is_binary(B) ->
{0, B};
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 12ca655000..48b14396c1 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -18,12 +18,13 @@
%%
-module(zip_SUITE).
--export([all/1, borderline/1, atomic/1,
+-export([all/1, borderline/1, atomic/1,
bad_zip/1, unzip_from_binary/1, unzip_to_binary/1,
zip_to_binary/1,
unzip_options/1, zip_options/1, list_dir_options/1, aliases/1,
openzip_api/1, zip_api/1, unzip_jar/1,
- compress_control/1]).
+ compress_control/1,
+ foldl/1]).
-include("test_server.hrl").
-include("test_server_line.hrl").
@@ -35,7 +36,8 @@ all(suite) -> [borderline, atomic, bad_zip,
zip_to_binary,
unzip_options, zip_options, list_dir_options, aliases,
openzip_api, zip_api, unzip_jar,
- compress_control].
+ compress_control,
+ foldl].
borderline(doc) ->
["Test creating, listing and extracting one file from an archive "
@@ -110,17 +112,17 @@ get_data(Port, Expect) ->
{Port, {data, Bytes}} ->
get_data(Port, match_output(Bytes, Expect, Port));
{Port, eof} ->
- Port ! {self(), close},
+ Port ! {self(), close},
receive
{Port, closed} ->
true
- end,
+ end,
receive
- {'EXIT', Port, _} ->
+ {'EXIT', Port, _} ->
ok
after 1 -> % force context switch
ok
- end,
+ end,
match_output(eof, Expect, Port)
end.
@@ -290,7 +292,7 @@ unzip_options(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
Long = filename:join(DataDir, "abc.zip"),
-
+
%% create a temp directory
Subdir = filename:join(PrivDir, "t"),
ok = file:make_dir(Subdir),
@@ -303,7 +305,7 @@ unzip_options(Config) when is_list(Config) ->
%% Verify.
?line true = (length(FList) =:= length(RetList)),
- ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)),
+ ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)),
{ok,B} = file:read_file(filename:join(Subdir, F)) end,
FList),
?line lists:foreach(fun(F)-> ok = file:delete(F) end,
@@ -321,7 +323,7 @@ unzip_jar(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
JarFile = filename:join(DataDir, "test.jar"),
-
+
%% create a temp directory
Subdir = filename:join(PrivDir, "jartest"),
ok = file:make_dir(Subdir),
@@ -479,7 +481,7 @@ unzip_to_binary(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
- delete_all_in(PrivDir),
+ delete_all_in(PrivDir),
file:set_cwd(PrivDir),
Long = filename:join(DataDir, "abc.zip"),
@@ -595,17 +597,17 @@ do_delete_files([],Cnt) ->
Cnt;
do_delete_files([Item|Rest], Cnt) ->
case file:delete(Item) of
- ok ->
+ ok ->
DelCnt = 1;
{error,eperm} ->
file:change_mode(Item, 8#777),
DelCnt = delete_files(filelib:wildcard(filename:join(Item, "*"))),
- file:del_dir(Item);
+ file:del_dir(Item);
{error,eacces} ->
%% We'll see about that!
file:change_mode(Item, 8#777),
case file:delete(Item) of
- ok ->
+ ok ->
DelCnt = 1;
{error,_} ->
erlang:yield(),
@@ -643,22 +645,22 @@ compress_control(Config) when is_list(Config) ->
],
test_compress_control(Dir,
- Files,
+ Files,
[{compress, []}],
[]),
test_compress_control(Dir,
- Files,
+ Files,
[{uncompress, all}],
[]),
test_compress_control(Dir,
- Files,
+ Files,
[{uncompress, []}],
[".txt", ".exe", ".zip", ".lzh", ".arj"]),
test_compress_control(Dir,
- Files,
+ Files,
[],
[".txt", ".exe"]),
@@ -686,7 +688,7 @@ test_compress_control(Dir, Files, ZipOptions, Expected) ->
create_files(Files),
{ok, Zip} = zip:create(Zip, [Dir], ZipOptions),
-
+
{ok, OpenZip} = zip:openzip_open(Zip, [memory]),
{ok,[#zip_comment{comment = ""} | ZipList]} = zip:openzip_list_dir(OpenZip),
io:format("compress_control: -> ~p -> ~p\n -> ~pn", [Expected, ZipOptions, ZipList]),
@@ -698,19 +700,19 @@ test_compress_control(Dir, Files, ZipOptions, Expected) ->
delete_files(lists:reverse(Names)), % Remove plain files before directories
ok.
-
+
verify_compression([{Name, Kind, _Filler} | Files], ZipList, OpenZip, ZipOptions, Expected) ->
{Name2, BinSz} =
case Kind of
- dir ->
+ dir ->
{Name ++ "/", 0};
- _ ->
+ _ ->
{ok, {Name, Bin}} = zip:openzip_get(Name, OpenZip),
{Name, size(Bin)}
end,
{Name2, {value, ZipFile}} = {Name2, lists:keysearch(Name2, #zip_file.name, ZipList)},
#zip_file{info = #file_info{size = InfoSz, type = InfoType}, comp_size = InfoCompSz} = ZipFile,
-
+
Ext = filename:extension(Name),
IsComp = is_compressed(Ext, Kind, ZipOptions),
ExpComp = lists:member(Ext, Expected),
@@ -757,3 +759,33 @@ extensions([H | T], Old) ->
extensions([], Old) ->
Old.
+foldl(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ File = filename:join([PrivDir, "foldl.zip"]),
+
+ FooBin = <<"FOO">>,
+ BarBin = <<"BAR">>,
+ Files = [{"foo", FooBin}, {"bar", BarBin}],
+ ?line {ok, {File, Bin}} = zip:create(File, Files, [memory]),
+ ZipFun = fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end,
+ ?line {ok, FileSpec} = zip:foldl(ZipFun, [], {File, Bin}),
+ ?line [{"bar", BarBin, #file_info{}}, {"foo", FooBin, #file_info{}}] = FileSpec,
+ ?line {ok, {File, Bin}} = zip:create(File, lists:reverse(FileSpec), [memory]),
+ ?line {foo_bin, FooBin} =
+ try
+ zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {File, Bin})
+ catch
+ throw:FooBin ->
+ {foo_bin, FooBin}
+ end,
+ ?line ok = file:write_file(File, Bin),
+ ?line {ok, FileSpec} = zip:foldl(ZipFun, [], File),
+
+ ?line {error, einval} = zip:foldl(fun() -> ok end, [], File),
+ ?line {error, einval} = zip:foldl(ZipFun, [], 42),
+ ?line {error, einval} = zip:foldl(ZipFun, [], {File, 42}),
+
+ ?line ok = file:delete(File),
+ ?line {error, enoent} = zip:foldl(ZipFun, [], File),
+
+ ok.
--
cgit v1.2.3