zip.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,455 行 · 第 1/3 页

ERL
1,455
字号
-module(zip).%% Basic api-export([unzip/1, unzip/2, extract/1, extract/2,	 zip/2, zip/3, create/2, create/3,	 list_dir/1, list_dir/2, table/1, table/2,	 t/1, tt/1]).%% unzipping peicemeal-export([openzip_open/1, openzip_open/2,	 openzip_get/1, openzip_get/2,	 openzip_t/1, openzip_tt/1,	 openzip_list_dir/1, openzip_list_dir/2,	 openzip_close/1]).%% 	 openzip_add/2]).%% zip server-export([zip_open/1, zip_open/2,	 zip_get/1, zip_get/2,	 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]).%% includes-include("file.hrl").		 % #file_info-include("zip.hrl").	         % #zip_file, #zip_comment%% max bytes fed to zlib-define(WRITE_BLOCK_SIZE, 8*1024).%% for debugging, to turn off catch-define(CATCH, catch).%% option sets-record(unzip_opts, {	  output,      % output object (fun)	  input,       % input object (fun)	  file_filter, % file filter (boolean fun)	  open_opts,   % options passed to file:open	  feedback,    % feeback (fun)	  cwd          % directory to relate paths to	 }).-record(zip_opts, {	  output,      % output object (fun)	  input,       % input object (fun)	  comment,     % zip-file comment	  open_opts,   % options passed to file:open	  feedback,    % feeback (fun)	  cwd          % directory to relate paths to	 }).-record(list_dir_opts, {	  input,       % input object (fun)	  raw_iterator, % applied to each dir entry	  open_opts    % options passed to file:open	 }).-record(openzip_opts, {	  output,      % output object (fun)	  open_opts,   % file:open options	  cwd	       % directory to relate paths to	  	 }).% openzip record, state for an open zip-file-record(openzip, {	  zip_comment, % zip archive comment	  files,       % filenames, infos, comments and offsets	  in,          % archive handle	  input,       % archive io object (fun)	  output,      % output io object (fun)	  zlib,	       % handle to open zlib	  cwd	       % directory to relate paths to	  	 }).%% max bytes read from files and archives (and fed to zlib)-define(READ_BLOCK_SIZE, 16*1024).%% -record(primzip_file, {%% 	  name,%% 	  offset,%% 	  chunk_size%% 	 }).%% -record(primzip, {%% 	  zlib,		% handle to the zlib port from zlib:open%% 	  input,        % fun/2 for file/memory input%% 	  in,		% input (file handle or binary)%% 	  files		% [#primzip_file]%% 	 }).%% ZIP-file format records and defines%% compression methods-define(STORED, 0).-define(UNCOMPRESSED, 0).-define(SHRUNK, 1).-define(REDUCED_1, 2).-define(REDUCED_2, 3).-define(REDUCED_3, 4).-define(REDUCED_4, 5).-define(IMPLODED, 6).-define(TOKENIZED, 7).-define(DEFLATED, 8).-define(DEFLATED_64, 9).-define(PKWARE_IMPLODED, 10).-define(PKWARE_RESERVED, 11).-define(BZIP2_COMPRESSED, 12).%% zip-file records-define(LOCAL_FILE_MAGIC,16#04034b50).-define(LOCAL_FILE_HEADER_SZ,(4+2+2+2+2+2+4+4+4+2+2)).-define(LOCAL_FILE_HEADER_CRC32_OFFSET, 4+2+2+2+2+2).-record(local_file_header, {version_needed,			    gp_flag,			    comp_method,			    last_mod_time,			    last_mod_date,			    crc32,			    comp_size,			    uncomp_size,			    file_name_length,			    extra_field_length}).-define(CENTRAL_FILE_HEADER_SZ,(4+2+2+2+2+2+2+4+4+4+2+2+2+2+2+4+4)).-define(CENTRAL_DIR_MAGIC, 16#06054b50).-define(CENTRAL_DIR_SZ, (4+2+2+2+2+4+4+2)).-define(CENTRAL_DIR_DIGITAL_SIG_MAGIC, 16#05054b50).-define(CENTRAL_DIR_DIGITAL_SIG_SZ, (4+2)).-define(CENTRAL_FILE_MAGIC, 16#02014b50).-record(cd_file_header, {version_made_by,			 version_needed,			 gp_flag,			 comp_method,			 last_mod_time,			 last_mod_date,			 crc32,			 comp_size,			 uncomp_size,			 file_name_length,			 extra_field_length,			 file_comment_length,			 disk_num_start,			 internal_attr,			 external_attr,			 local_header_offset}).%% Unix extra fields (not yet supported)-define(UNIX_EXTRA_FIELD_TAG, 16#000d).-record(unix_extra_field, {atime,			   mtime,			   uid,			   gid}).%% extended timestamps (not yet supported)-define(EXTENDED_TIMESTAMP_TAG, 16#5455).%% -record(extended_timestamp, {mtime,%% 			     atime,%% 			     ctime}).-define(END_OF_CENTRAL_DIR_MAGIC, 16#06054b50).-define(END_OF_CENTRAL_DIR_SZ, (4+2+2+2+2+4+4+2)).-record(eocd, {disk_num,	       start_disk_num,	       entries_on_disk,	       entries,	       size,	       offset,	       zip_comment_length}).%% Open a zip archive with options%%openzip_open(F) ->    openzip_open(F, []).openzip_open(F, Options) ->    case ?CATCH do_openzip_open(F, Options) of	{ok, OpenZip} ->	    {ok, OpenZip};	Error ->	    {error, Error}    end.do_openzip_open(F, Options) ->    Opts = get_openzip_options(Options),    #openzip_opts{output = Output, open_opts = OpO, cwd = CWD} = Opts,    Input = get_zip_input(F),    In0 = Input({open, F, OpO -- [write]}, []),    {[#zip_comment{comment = C} | Files], In1} =	get_central_dir(In0, fun raw_file_info_etc/5, Input),    Z = zlib:open(),    {ok, #openzip{zip_comment = C,		  files = Files,		  in = In1,		  input = Input,		  output = Output,		  zlib = Z,		  cwd = CWD}}.%% retrieve all files from an open archiveopenzip_get(OpenZip) ->    case ?CATCH do_openzip_get(OpenZip) of	{ok, Result} -> {ok, Result};	Error -> {error, Error}    end.do_openzip_get(#openzip{files = Files, in = In0, input = Input,			output = Output, zlib = Z, cwd = CWD}) ->    ZipOpts = #unzip_opts{output = Output, input = Input,			  file_filter = fun all/1, open_opts = [],			  feedback = fun silent/1, cwd = CWD},    R = get_z_files(Files, Z, In0, ZipOpts, []),    {ok, R};do_openzip_get(_) ->    throw(einval).%% retrieve a file from an open archiveopenzip_get(FileName, OpenZip) ->    case ?CATCH do_openzip_get(FileName, OpenZip) of	{ok, Result} -> {ok, Result};	Error -> {error, Error}    end.do_openzip_get(F, #openzip{files = Files, in = In0, input = Input,			   output = Output, zlib = Z, cwd = CWD}) ->    case lists:keysearch(F, #zip_file.name, Files) of	{value, #zip_file{offset = Offset}} ->	    In1 = Input({seek, bof, Offset}, In0),	    {R, _In2} = get_z_file(In1, Z, Input, Output, [], fun silent/1, CWD),	    {ok, R};	_ -> throw(file_not_found)    end;do_openzip_get(_, _) ->    throw(einval).%% %% add a file to an open archive%% openzip_add(File, OpenZip) ->%%     case ?CATCH do_openzip_add(File, OpenZip) of%% 	{ok, Result} -> {ok, Result};%% 	Error -> {error, Error}%%     end.%% do_openzip_add(File, #open_zip{files = Files, in = In0,%% 			       opts = Opts} = OpenZip0) ->%%     throw(nyi),%%     Z = zlib:open(),%%     R = get_z_files(Files, In0, Z, Opts, []),%%     zlib:close(Z),%%     {ok, R};%% do_openzip_add(_, _) ->%%     throw(einval).%% get file list from open archiveopenzip_list_dir(#openzip{zip_comment = Comment,			  files = Files}) ->    {ok, [#zip_comment{comment = Comment} | Files]};openzip_list_dir(_) ->    {error, einval}.openzip_list_dir(#openzip{files = Files}, [names_only]) ->    Names = [Name || #zip_file{name=Name} <- Files],    {ok, Names};openzip_list_dir(_, _) ->    {error, einval}.%% close an open archiveopenzip_close(#openzip{in = In0, input = Input, zlib = Z}) ->    Input(close, In0),    zlib:close(Z);openzip_close(_) ->    {error, einval}.%% Extract from a zip archive with options%%%% Accepted options:%% verbose, cooked, file_list, keep_old_files, file_filter, memoryunzip(F) -> unzip(F, []).unzip(F, Options) ->    case ?CATCH do_unzip(F, Options) of	{ok, R} -> {ok, R};	Error -> {error, Error}    end.do_unzip(F, Options) ->    Opts = get_unzip_options(F, Options),    #unzip_opts{input = Input, open_opts = OpO} = Opts,    In0 = Input({open, F, OpO -- [write]}, []),    RawIterator = fun raw_file_info_etc/5,    {Info, In1} = get_central_dir(In0, RawIterator, Input),    %% get rid of zip-comment    Z = zlib:open(),    Files = get_z_files(Info, Z, In1, Opts, []),    zlib:close(Z),    Input(close, In1),    {ok, Files}.%% Create zip archive name F from Files or binaries%%%% Accepted options:%% verbose, cooked, memory, commentzip(F, Files) -> zip(F, Files, []).zip(F, Files, Options) ->    case ?CATCH do_zip(F, Files, Options) of	{ok, R} -> {ok, R};	Error -> {error, Error}    end.do_zip(F, Files, Options) ->    Opts = get_zip_options(Files, Options),    #zip_opts{output = Output, open_opts = OpO} = Opts,    Out0 = Output({open, F, OpO}, []),    Z = zlib:open(),    {Out1, LHS, Pos} = put_z_files(Files, Z, Out0, 0, Opts, []),    zlib:close(Z),    Out2 = put_central_dir(LHS, Pos, Out1, Opts),    Out3 = Output({close, F}, Out2),    {ok, Out3}.%% List zip directory contents%%%% Accepted options:%% cooked, file_filter, file_output (latter 2 undocumented)list_dir(F) -> list_dir(F, []).list_dir(F, Options) ->    case ?CATCH do_list_dir(F, Options) of	{ok, R} -> {ok, R};	Error -> {error, Error}    end.do_list_dir(F, Options) ->    Opts = get_list_dir_options(F, Options),    #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),    Input(close, In1),    {ok, Info}.%% Print zip directory in short formt(F) when is_pid(F) -> zip_t(F);t(F) when is_record(F, openzip) -> openzip_t(F);t(F) -> t(F, fun raw_short_print_info_etc/5).t(F, RawPrint) ->    case ?CATCH do_t(F, RawPrint) of	ok -> ok;	Error -> {error, Error}    end.do_t(F, RawPrint) ->    Input = get_input(F),    OpO = [raw],    In0 = Input({open, F, OpO}, []),    {_Info, In1} = get_central_dir(In0, RawPrint, Input),    Input(close, In1),    ok.%% Print zip directory in long form (like ls -l)tt(F) when is_pid(F) -> zip_tt(F);tt(F) when is_record(F, openzip) -> openzip_tt(F);tt(F) -> t(F, fun raw_long_print_info_etc/5).%% option utils get_unzip_opt([], Opts) ->    Opts;get_unzip_opt([verbose | Rest], Opts) ->    get_unzip_opt(Rest, Opts#unzip_opts{feedback = fun verbose_unzip/1});get_unzip_opt([cooked | Rest], #unzip_opts{open_opts = OpO} = Opts) ->    get_unzip_opt(Rest, Opts#unzip_opts{open_opts = OpO -- [raw]});get_unzip_opt([memory | Rest], Opts) ->    get_unzip_opt(Rest, Opts#unzip_opts{output = fun binary_io/2});get_unzip_opt([{cwd, CWD} | Rest], Opts) ->    get_unzip_opt(Rest, Opts#unzip_opts{cwd = CWD});get_unzip_opt([{file_filter, F} | Rest], Opts) ->    Filter = fun_and_1(F, Opts#unzip_opts.file_filter),    get_unzip_opt(Rest, Opts#unzip_opts{file_filter = Filter});get_unzip_opt([{file_list, L} | Rest], Opts) ->    FileInList = fun(F) -> file_in_list(F, L) end,    Filter = fun_and_1(FileInList, Opts#unzip_opts.file_filter),    get_unzip_opt(Rest, Opts#unzip_opts{file_filter = Filter});get_unzip_opt([keep_old_files | Rest], Opts) ->    Keep = fun keep_old_file/1,    Filter = fun_and_1(Keep, Opts#unzip_opts.file_filter),    get_unzip_opt(Rest, Opts#unzip_opts{file_filter = Filter});get_unzip_opt([Unknown | _Rest], _Opts) ->    throw({bad_option, Unknown}).get_list_dir_opt([], Opts) ->    Opts;get_list_dir_opt([cooked | Rest], #list_dir_opts{open_opts = OpO} = Opts) ->    get_list_dir_opt(Rest, Opts#list_dir_opts{open_opts = OpO -- [raw]});get_list_dir_opt([names_only | Rest], Opts) ->    get_list_dir_opt(Rest, Opts#list_dir_opts{			     raw_iterator = fun(A, B, C, D, E) -> raw_name_only(A, B, C, D, E) end});%% get_list_dir_opt([{file_output, F} | Rest], Opts) ->%%     get_list_dir_opt(Rest, Opts#list_dir_opts{file_output = F});%% get_list_dir_opt([{file_filter, F} | Rest], Opts) ->%%     get_list_dir_opt(Rest, Opts#list_dir_opts{file_filter = F});get_list_dir_opt([Unknown | _Rest], _Opts) ->    throw({bad_option, Unknown}).get_zip_opt([], Opts) ->    Opts;get_zip_opt([verbose | Rest], Opts) ->    get_zip_opt(Rest, Opts#zip_opts{feedback = fun verbose_zip/1});get_zip_opt([cooked | Rest], #zip_opts{open_opts = OpO} = Opts) ->    get_zip_opt(Rest, Opts#zip_opts{open_opts = OpO -- [raw]});get_zip_opt([memory | Rest], Opts) ->    get_zip_opt(Rest, Opts#zip_opts{output = fun binary_io/2});get_zip_opt([{cwd, CWD} | Rest], Opts) ->    get_zip_opt(Rest, Opts#zip_opts{cwd = CWD});get_zip_opt([{comment, C} | Rest], Opts) ->    get_zip_opt(Rest, Opts#zip_opts{comment = C});get_zip_opt([Unknown | _Rest], _Opts) ->    throw({bad_option, Unknown}).%% feedback funssilent(_) -> ok.verbose_unzip(FN) -> io:format("extracting: ~p\n", [FN]).verbose_zip(FN) -> io:format("adding: ~p\n", [FN]).%% file filter funsall(_) -> true.file_in_list(#zip_file{name = FileName}, List) ->    lists:member(FileName, List);file_in_list(_, _) ->    false.keep_old_file(#zip_file{name = FileName}) ->    not (filelib:is_file(FileName) orelse filelib:is_dir(FileName));keep_old_file(_) ->    false.%% fun combinerfun_and_1(Fun1, Fun2) ->    fun(A) -> Fun1(A) andalso Fun2(A) end.%% getting optionsget_zip_options(Files, Options) ->    Opts = #zip_opts{output = fun file_io/2,		     input = get_zip_input(hd(Files)),		     open_opts = [raw, write],		     comment = "",		     feedback = fun silent/1,		     cwd = ""		    },    get_zip_opt(Options, Opts).get_unzip_options(F, Options) ->    Opts = #unzip_opts{file_filter = fun all/1,		       output = fun file_io/2,		       input = get_input(F),		       open_opts = [raw],		       feedback = fun silent/1,		       cwd = ""		      },    get_unzip_opt(Options, Opts).get_openzip_options(Options) ->    Opts = #openzip_opts{open_opts = [raw, read],			 output = fun file_io/2,			 cwd = ""},    get_openzip_opt(Options, Opts).

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?