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 + -
显示快捷键?