1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2006-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: -module(zip_SUITE). 20: 21: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 22: init_per_group/2,end_per_group/2, borderline/1, atomic/1, 23: bad_zip/1, unzip_from_binary/1, unzip_to_binary/1, 24: zip_to_binary/1, 25: unzip_options/1, zip_options/1, list_dir_options/1, aliases/1, 26: openzip_api/1, zip_api/1, unzip_jar/1, 27: compress_control/1, 28: foldl/1]). 29: 30: -include_lib("test_server/include/test_server.hrl"). 31: -include("test_server_line.hrl"). 32: -include_lib("kernel/include/file.hrl"). 33: -include_lib("stdlib/include/zip.hrl"). 34: 35: suite() -> [{ct_hooks,[ts_install_cth]}]. 36: 37: all() -> 38: [borderline, atomic, bad_zip, unzip_from_binary, 39: unzip_to_binary, zip_to_binary, unzip_options, 40: zip_options, list_dir_options, aliases, openzip_api, 41: zip_api, unzip_jar, compress_control, foldl]. 42: 43: groups() -> 44: []. 45: 46: init_per_suite(Config) -> 47: Config. 48: 49: end_per_suite(_Config) -> 50: ok. 51: 52: init_per_group(_GroupName, Config) -> 53: Config. 54: 55: end_per_group(_GroupName, Config) -> 56: Config. 57: 58: 59: borderline(doc) -> 60: ["Test creating, listing and extracting one file from an archive " 61: "multiple times with different file sizes. Also check that the " 62: "modification date of the extracted file has survived."]; 63: borderline(Config) when is_list(Config) -> 64: RootDir = ?config(priv_dir, Config), 65: TempDir = filename:join(RootDir, "borderline"), 66: ok = file:make_dir(TempDir), 67: 68: Record = 512, 69: Block = 20 * Record, 70: 71: lists:foreach(fun(Size) -> borderline_test(Size, TempDir) end, 72: [0, 1, 10, 13, 127, 333, Record-1, Record, Record+1, 73: Block-Record-1, Block-Record, Block-Record+1, 74: Block-1, Block, Block+1, 75: Block+Record-1, Block+Record, Block+Record+1]), 76: 77: %% Clean up. 78: delete_files([TempDir]), 79: ok. 80: 81: borderline_test(Size, TempDir) -> 82: Archive = filename:join(TempDir, "ar_"++integer_to_list(Size)++".zip"), 83: Name = filename:join(TempDir, "file_"++integer_to_list(Size)), 84: io:format("Testing size ~p", [Size]), 85: 86: %% Create a file and archive it. 87: {_, _, X0} = erlang:now(), 88: file:write_file(Name, random_byte_list(X0, Size)), 89: {ok, Archive} = zip:zip(Archive, [Name]), 90: ok = file:delete(Name), 91: 92: %% Verify listing and extracting. 93: {ok, [#zip_comment{comment = []}, 94: #zip_file{name = Name, 95: info = Info, 96: offset = 0, 97: comp_size = _}]} = zip:list_dir(Archive), 98: Size = Info#file_info.size, 99: {ok, [Name]} = zip:extract(Archive, [verbose]), 100: 101: %% Verify contents of extracted file. 102: {ok, Bin} = file:read_file(Name), 103: true = match_byte_list(X0, binary_to_list(Bin)), 104: 105: 106: %% Verify that Unix zip can read it. (if we have a unix zip that is!) 107: unzip_list(Archive, Name), 108: 109: ok. 110: 111: unzip_list(Archive, Name) -> 112: case unix_unzip_exists() of 113: true -> 114: unzip_list1(Archive, Name); 115: _ -> 116: ok 117: end. 118: 119: %% Used to do os:find_executable() to check if unzip exists, but on 120: %% some hosts that would give an unzip program which did not take the 121: %% "-Z" option. 122: %% Here we check that "unzip -Z" (which should display usage) and 123: %% check that it exists with status 0. 124: unix_unzip_exists() -> 125: case os:type() of 126: {unix,_} -> 127: Port = open_port({spawn,"unzip -Z > /dev/null"}, [exit_status]), 128: receive 129: {Port,{exit_status,0}} -> 130: true; 131: {Port,{exit_status,_Fail}} -> 132: false 133: end; 134: _ -> 135: false 136: end. 137: 138: unzip_list1(Archive, Name) -> 139: Expect = Name ++ "\n", 140: cmd_expect("unzip -Z -1 " ++ Archive, Expect). 141: 142: cmd_expect(Cmd, Expect) -> 143: Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, eof]), 144: get_data(Port, Expect). 145: 146: get_data(Port, Expect) -> 147: receive 148: {Port, {data, Bytes}} -> 149: get_data(Port, match_output(Bytes, Expect, Port)); 150: {Port, eof} -> 151: Port ! {self(), close}, 152: receive 153: {Port, closed} -> 154: true 155: end, 156: receive 157: {'EXIT', Port, _} -> 158: ok 159: after 1 -> % force context switch 160: ok 161: end, 162: match_output(eof, Expect, Port) 163: end. 164: 165: match_output([C|Output], [C|Expect], Port) -> 166: match_output(Output, Expect, Port); 167: match_output([_|_], [_|_], Port) -> 168: kill_port_and_fail(Port, badmatch); 169: match_output([X|Output], [], Port) -> 170: kill_port_and_fail(Port, {too_much_data, [X|Output]}); 171: match_output([], Expect, _Port) -> 172: Expect; 173: match_output(eof, [], _Port) -> 174: []; 175: match_output(eof, Expect, Port) -> 176: kill_port_and_fail(Port, {unexpected_end_of_input, Expect}). 177: 178: kill_port_and_fail(Port, Reason) -> 179: unlink(Port), 180: exit(Port, die), 181: test_server:fail(Reason). 182: 183: make_cmd(Cmd) -> 184: Cmd. 185: %% case os:type() of 186: %% {win32, _} -> lists:concat(["cmd /c", Cmd]); 187: %% {unix, _} -> lists:concat(["sh -c '", Cmd, "'"]) 188: %% end. 189: 190: %% Verifies a random byte list. 191: 192: match_byte_list(X0, [Byte|Rest]) -> 193: X = next_random(X0), 194: case (X bsr 26) band 16#ff of 195: Byte -> match_byte_list(X, Rest); 196: _ -> false 197: end; 198: match_byte_list(_, []) -> 199: true. 200: 201: %% Generates a random byte list. 202: 203: random_byte_list(X0, Count) -> 204: random_byte_list(X0, Count, []). 205: 206: random_byte_list(X0, Count, Result) when Count > 0-> 207: X = next_random(X0), 208: random_byte_list(X, Count-1, [(X bsr 26) band 16#ff|Result]); 209: random_byte_list(_X, 0, Result) -> 210: lists:reverse(Result). 211: 212: %% This RNG is from line 21 on page 102 in Knuth: The Art of Computer Programming, 213: %% Volume II, Seminumerical Algorithms. 214: 215: next_random(X) -> 216: (X*17059465+1) band 16#fffffffff. 217: 218: atomic(doc) -> 219: ["Test the 'atomic' operations: zip/unzip/list_dir, on archives." 220: "Also test the 'cooked' option."]; 221: atomic(suite) -> []; 222: atomic(Config) when is_list(Config) -> 223: ok = file:set_cwd(?config(priv_dir, Config)), 224: DataFiles = data_files(), 225: Names = [Name || {Name,_,_} <- DataFiles], 226: io:format("Names: ~p", [Names]), 227: 228: %% Create a zip archive. 229: 230: Zip2 = "zip.zip", 231: {ok, Zip2} = zip:zip(Zip2, Names, []), 232: Names = names_from_list_dir(zip:list_dir(Zip2)), 233: 234: %% Same test again, but this time created with 'cooked' 235: 236: Zip3 = "cooked.zip", 237: {ok, Zip3} = zip:zip(Zip3, Names, [cooked]), 238: Names = names_from_list_dir(zip:list_dir(Zip3)), 239: Names = names_from_list_dir(zip:list_dir(Zip3, [cooked])), 240: 241: %% Clean up. 242: delete_files([Zip2,Zip3|Names]), 243: 244: ok. 245: 246: openzip_api(doc) -> 247: ["Test the openzip_open/2, openzip_get/1, openzip_get/2, openzip_close/1 " 248: "and openzip_list_dir/1 functions."]; 249: openzip_api(suite) -> []; 250: openzip_api(Config) when is_list(Config) -> 251: ok = file:set_cwd(?config(priv_dir, Config)), 252: DataFiles = data_files(), 253: Names = [Name || {Name, _, _} <- DataFiles], 254: io:format("Names: ~p", [Names]), 255: 256: %% Create a zip archive 257: 258: Zip = "zip.zip", 259: {ok, Zip} = zip:zip(Zip, Names, []), 260: 261: %% Open archive 262: {ok, OpenZip} = zip:openzip_open(Zip, [memory]), 263: 264: %% List dir 265: Names = names_from_list_dir(zip:openzip_list_dir(OpenZip)), 266: 267: %% Get a file 268: Name1 = hd(Names), 269: {ok, Data1} = file:read_file(Name1), 270: {ok, {Name1, Data1}} = zip:openzip_get(Name1, OpenZip), 271: 272: %% Get all files 273: FilesDatas = lists:map(fun(Name) -> {ok, B} = file:read_file(Name), 274: {Name, B} end, Names), 275: {ok, FilesDatas} = zip:openzip_get(OpenZip), 276: 277: %% Close 278: ok = zip:openzip_close(OpenZip), 279: 280: %% Clean up. 281: delete_files([Names]), 282: 283: ok. 284: 285: zip_api(doc) -> 286: ["Test the zip_open/2, zip_get/1, zip_get/2, zip_close/1 " 287: "and zip_list_dir/1 functions."]; 288: zip_api(suite) -> []; 289: zip_api(Config) when is_list(Config) -> 290: ok = file:set_cwd(?config(priv_dir, Config)), 291: DataFiles = data_files(), 292: Names = [Name || {Name, _, _} <- DataFiles], 293: io:format("Names: ~p", [Names]), 294: 295: %% Create a zip archive 296: Zip = "zip.zip", 297: {ok, Zip} = zip:zip(Zip, Names, []), 298: 299: %% Open archive 300: {ok, ZipSrv} = zip:zip_open(Zip, [memory]), 301: 302: %% List dir 303: Names = names_from_list_dir(zip:zip_list_dir(ZipSrv)), 304: 305: %% Get a file 306: Name1 = hd(Names), 307: {ok, Data1} = file:read_file(Name1), 308: {ok, {Name1, Data1}} = zip:zip_get(Name1, ZipSrv), 309: 310: %% Get all files 311: FilesDatas = lists:map(fun(Name) -> {ok, B} = file:read_file(Name), 312: {Name, B} end, Names), 313: {ok, FilesDatas} = zip:zip_get(ZipSrv), 314: 315: %% Close 316: ok = zip:zip_close(ZipSrv), 317: 318: %% Clean up. 319: delete_files([Names]), 320: 321: ok. 322: 323: unzip_options(doc) -> 324: ["Test options for unzip, only cwd and file_list currently"]; 325: unzip_options(suite) -> 326: []; 327: unzip_options(Config) when is_list(Config) -> 328: DataDir = ?config(data_dir, Config), 329: PrivDir = ?config(priv_dir, Config), 330: Long = filename:join(DataDir, "abc.zip"), 331: 332: %% create a temp directory 333: Subdir = filename:join(PrivDir, "t"), 334: ok = file:make_dir(Subdir), 335: 336: FList = ["quotes/rain.txt","wikipedia.txt"], 337: 338: %% Unzip a zip file in Subdir 339: ?line {ok, RetList} = zip:unzip(Long, [{cwd, Subdir}, 340: {file_list, FList}]), 341: 342: %% Verify. 343: ?line true = (length(FList) =:= length(RetList)), 344: ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)), 345: {ok,B} = file:read_file(filename:join(Subdir, F)) end, 346: FList), 347: ?line lists:foreach(fun(F)-> ok = file:delete(F) end, 348: RetList), 349: 350: %% Clean up and verify no more files. 351: ?line 0 = delete_files([Subdir]), 352: ok. 353: 354: unzip_jar(doc) -> 355: ["Test unzip a jar file (OTP-7382)"]; 356: unzip_jar(suite) -> 357: []; 358: unzip_jar(Config) when is_list(Config) -> 359: DataDir = ?config(data_dir, Config), 360: PrivDir = ?config(priv_dir, Config), 361: JarFile = filename:join(DataDir, "test.jar"), 362: 363: %% create a temp directory 364: Subdir = filename:join(PrivDir, "jartest"), 365: ok = file:make_dir(Subdir), 366: ok = file:set_cwd(Subdir), 367: 368: FList = ["META-INF/MANIFEST.MF","test.txt"], 369: 370: {ok, RetList} = zip:unzip(JarFile), 371: 372: %% Verify. 373: ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)), 374: {ok,B} = file:read_file(filename:join(Subdir, F)) end, 375: FList), 376: ?line lists:foreach(fun(F)-> ok = file:delete(F) end, 377: RetList), 378: 379: %% Clean up and verify no more files. 380: ?line 0 = delete_files([Subdir]), 381: ok. 382: 383: zip_options(doc) -> 384: ["Test the options for unzip, only cwd currently"]; 385: zip_options(suite) -> 386: []; 387: zip_options(Config) when is_list(Config) -> 388: PrivDir = ?config(priv_dir, Config), 389: ok = file:set_cwd(PrivDir), 390: DataFiles = data_files(), 391: Names = [Name || {Name, _, _} <- DataFiles], 392: 393: %% Make sure cwd is not where we get the files 394: ok = file:set_cwd(?config(data_dir, Config)), 395: 396: %% Create a zip archive 397: {ok, {_,Zip}} = 398: zip:zip("filename_not_used.zip", Names, [memory, {cwd, PrivDir}]), 399: 400: %% Open archive 401: {ok, ZipSrv} = zip:zip_open(Zip, [memory]), 402: 403: %% List dir 404: Names = names_from_list_dir(zip:zip_list_dir(ZipSrv)), 405: 406: %% Get a file 407: Name1 = hd(Names), 408: {ok, Data1} = file:read_file(filename:join(PrivDir, Name1)), 409: {ok, {Name1, Data1}} = zip:zip_get(Name1, ZipSrv), 410: 411: %% Get all files 412: FilesDatas = lists:map(fun(Name) -> {ok, B} = file:read_file(filename:join(PrivDir, Name)), 413: {Name, B} end, Names), 414: {ok, FilesDatas} = zip:zip_get(ZipSrv), 415: 416: %% Close 417: ok = zip:zip_close(ZipSrv), 418: 419: %% Clean up. 420: delete_files([Names]), 421: 422: ok. 423: 424: list_dir_options(doc) -> 425: ["Test the options for list_dir... one day"]; 426: list_dir_options(suite) -> 427: []; 428: list_dir_options(Config) when is_list(Config) -> 429: ok. 430: 431: 432: 433: 434: %% convert zip_info as returned from list_dir to a list of names 435: names_from_list_dir({ok, Info}) -> 436: names_from_list_dir(Info); 437: names_from_list_dir(Info) -> 438: tl(lists:map(fun(#zip_file{name = Name}) -> Name; 439: (_) -> ok end, Info)). 440: 441: %% Returns a sequence of characters. 442: char_seq(N, First) -> 443: char_seq(N, First, []). 444: 445: char_seq(0, _, Result) -> 446: Result; 447: char_seq(N, C, Result) when C < 127 -> 448: char_seq(N-1, C+1, [C|Result]); 449: char_seq(N, _, Result) -> 450: char_seq(N, $!, Result). 451: 452: data_files() -> 453: Files = [{"first_file", 1555, $a}, 454: {"small_file", 7, $d}, 455: {"big_file", 23875, $e}, 456: {"last_file", 7500, $g}], 457: create_files(Files), 458: Files. 459: 460: create_files([{Name, dir, _First}|Rest]) -> 461: ok = file:make_dir(Name), 462: create_files(Rest); 463: create_files([{Name, Size, First}|Rest]) when is_integer(Size) -> 464: ok = file:write_file(Name, char_seq(Size, First)), 465: create_files(Rest); 466: create_files([]) -> 467: ok. 468: 469: %% make_dirs([Dir|Rest], []) -> 470: %% ok = file:make_dir(Dir), 471: %% make_dirs(Rest, Dir); 472: %% make_dirs([Dir|Rest], Parent) -> 473: %% Name = filename:join(Parent, Dir), 474: %% ok = file:make_dir(Name), 475: %% make_dirs(Rest, Name); 476: %% make_dirs([], Dir) -> 477: %% Dir. 478: 479: bad_zip(doc) -> 480: ["Try zip:unzip/1 on some corrupted zip files."]; 481: bad_zip(Config) when is_list(Config) -> 482: ok = file:set_cwd(?config(priv_dir, Config)), 483: try_bad("bad_crc", {bad_crc, "abc.txt"}, Config), 484: try_bad("bad_central_directory", bad_central_directory, Config), 485: try_bad("bad_file_header", bad_file_header, Config), 486: try_bad("bad_eocd", bad_eocd, Config), 487: try_bad("enoent", enoent, Config), 488: GetNotFound = fun(A) -> 489: {ok, O} = zip:openzip_open(A, []), 490: zip:openzip_get("not_here", O) 491: end, 492: try_bad("abc", file_not_found, GetNotFound, Config), 493: ok. 494: 495: try_bad(N, R, Config) -> 496: try_bad(N, R, fun(A) -> io:format("name : ~p\n", [A]), 497: zip:unzip(A, [verbose]) end, Config). 498: 499: try_bad(Name0, Reason, What, Config) -> 500: %% Intentionally no macros here. 501: 502: DataDir = ?config(data_dir, Config), 503: Name = Name0 ++ ".zip", 504: io:format("~nTrying ~s", [Name]), 505: Full = filename:join(DataDir, Name), 506: Expected = {error, Reason}, 507: case What(Full) of 508: Expected -> 509: io:format("Result: ~p\n", [Expected]); 510: Other -> 511: io:format("unzip/2 returned ~p (expected ~p)\n", [Other, Expected]), 512: test_server:fail({bad_return_value, Other}) 513: end. 514: 515: unzip_to_binary(doc) -> 516: ["Test extracting to binary with memory option."]; 517: unzip_to_binary(Config) when is_list(Config) -> 518: DataDir = ?config(data_dir, Config), 519: PrivDir = ?config(priv_dir, Config), 520: 521: delete_all_in(PrivDir), 522: file:set_cwd(PrivDir), 523: Long = filename:join(DataDir, "abc.zip"), 524: 525: %% Unzip a zip file into a binary 526: {ok, FBList} = zip:unzip(Long, [memory]), 527: 528: %% Verify. 529: lists:foreach(fun({F,B}) -> {ok,B}=file:read_file(filename:join(DataDir, F)) 530: end, FBList), 531: 532: %% Make sure no files created in cwd 533: {ok,[]} = file:list_dir(PrivDir), 534: 535: ok. 536: 537: zip_to_binary(doc) -> 538: ["Test compressing to binary with memory option."]; 539: zip_to_binary(Config) when is_list(Config) -> 540: DataDir = ?config(data_dir, Config), 541: PrivDir = ?config(priv_dir, Config), 542: delete_all_in(PrivDir), 543: file:set_cwd(PrivDir), 544: FileName = "abc.txt", 545: ZipName = "t.zip", 546: FilePath = filename:join(DataDir, FileName), 547: {ok, _Size} = file:copy(FilePath, FileName), 548: 549: %% Zip to a binary archive 550: {ok, {ZipName, ZipB}} = zip:zip(ZipName, [FileName], [memory]), 551: 552: %% Make sure no files created in cwd 553: {ok,[FileName]} = file:list_dir(PrivDir), 554: 555: %% Zip to a file 556: {ok, ZipName} = zip:zip(ZipName, [FileName]), 557: 558: %% Verify. 559: {ok, ZipB} = file:read_file(ZipName), 560: {ok, FData} = file:read_file(FileName), 561: {ok, [{FileName, FData}]} = zip:unzip(ZipB, [memory]), 562: 563: %% Clean up. 564: delete_files([FileName, ZipName]), 565: 566: ok. 567: 568: aliases(doc) -> 569: ["Test using the aliases, extract/2, table/2 and create/3"]; 570: aliases(Config) when is_list(Config) -> 571: {_, _, X0} = erlang:now(), 572: Size = 100, 573: B = list_to_binary(random_byte_list(X0, Size)), 574: %% create 575: {ok, {"z.zip", ZArchive}} = zip:create("z.zip", [{"b", B}], [memory]), 576: %% extract 577: {ok, [{"b", B}]} = zip:extract(ZArchive, [memory]), 578: %% table 579: {ok, [#zip_comment{comment = _}, #zip_file{name = "b", 580: info = FI, 581: comp_size = _, 582: offset = 0}]} = 583: zip:table(ZArchive), 584: Size = FI#file_info.size, 585: 586: ok. 587: 588: 589: 590: unzip_from_binary(doc) -> 591: ["Test extracting a zip archive from a binary."]; 592: unzip_from_binary(Config) when is_list(Config) -> 593: DataDir = ?config(data_dir, Config), 594: PrivDir = ?config(priv_dir, Config), 595: ExtractDir = filename:join(PrivDir, "extract_from_binary"), 596: ok = file:make_dir(ExtractDir), 597: Archive = filename:join(ExtractDir, "abc.zip"), 598: {ok, _Size} = file:copy(filename:join(DataDir, "abc.zip"), Archive), 599: FileName = "abc.txt", 600: Quote = "quotes/rain.txt", 601: Wikipedia = "wikipedia.txt", 602: EmptyFile = "emptyFile", 603: file:set_cwd(ExtractDir), 604: 605: %% Read a zip file into a binary and extract from the binary. 606: {ok, Bin} = file:read_file(Archive), 607: {ok, [FileName,Quote,Wikipedia,EmptyFile]} = zip:unzip(Bin), 608: 609: %% Verify. 610: DestFilename = filename:join(ExtractDir, "abc.txt"), 611: {ok, Data} = file:read_file(filename:join(DataDir, FileName)), 612: {ok, Data} = file:read_file(DestFilename), 613: 614: DestQuote = filename:join([ExtractDir, "quotes", "rain.txt"]), 615: {ok, QuoteData} = file:read_file(filename:join(DataDir, Quote)), 616: {ok, QuoteData} = file:read_file(DestQuote), 617: 618: %% Clean up. 619: delete_files([DestFilename, DestQuote, Archive, ExtractDir]), 620: ok. 621: 622: %% oac_files() -> 623: %% Files = [{"oac_file", 1459, $x}, 624: %% {"oac_small", 99, $w}, 625: %% {"oac_big", 33896, $A}], 626: %% create_files(Files), 627: %% Files. 628: 629: %% Delete the given list of files and directories. 630: %% Return total number of deleted files (not directories) 631: delete_files(List) -> 632: do_delete_files(List, 0). 633: do_delete_files([],Cnt) -> 634: Cnt; 635: do_delete_files([Item|Rest], Cnt) -> 636: case file:delete(Item) of 637: ok -> 638: DelCnt = 1; 639: {error,eperm} -> 640: file:change_mode(Item, 8#777), 641: DelCnt = delete_files(filelib:wildcard(filename:join(Item, "*"))), 642: file:del_dir(Item); 643: {error,eacces} -> 644: %% We'll see about that! 645: file:change_mode(Item, 8#777), 646: case file:delete(Item) of 647: ok -> 648: DelCnt = 1; 649: {error,_} -> 650: erlang:yield(), 651: file:change_mode(Item, 8#777), 652: file:delete(Item), 653: DelCnt = 1 654: end; 655: {error,_} -> 656: DelCnt = 0 657: end, 658: do_delete_files(Rest, Cnt + DelCnt). 659: 660: delete_all_in(Dir) -> 661: {ok, Files} = file:list_dir(Dir), 662: delete_files(lists:map(fun(F) -> filename:join(Dir,F) end, 663: Files)). 664: 665: compress_control(doc) -> 666: ["Test control of which files that should be compressed"]; 667: compress_control(suite) -> []; 668: compress_control(Config) when is_list(Config) -> 669: ok = file:set_cwd(?config(priv_dir, Config)), 670: Dir = "compress_control", 671: Files = [ 672: {Dir, dir, $d}, 673: {filename:join([Dir, "first_file.txt"]), 10000, $f}, 674: {filename:join([Dir, "a_dir"]), dir, $d}, 675: {filename:join([Dir, "a_dir", "zzz.zip"]), 10000, $z}, 676: {filename:join([Dir, "a_dir", "lll.lzh"]), 10000, $l}, 677: {filename:join([Dir, "a_dir", "eee.exe"]), 10000, $e}, 678: {filename:join([Dir, "a_dir", "ggg.arj"]), 10000, $g}, 679: {filename:join([Dir, "a_dir", "b_dir"]), dir, $d}, 680: {filename:join([Dir, "a_dir", "b_dir", "ggg.arj"]), 10000, $a}, 681: {filename:join([Dir, "last_file.txt"]), 10000, $l} 682: ], 683: 684: test_compress_control(Dir, 685: Files, 686: [{compress, []}], 687: []), 688: 689: test_compress_control(Dir, 690: Files, 691: [{uncompress, all}], 692: []), 693: 694: test_compress_control(Dir, 695: Files, 696: [{uncompress, []}], 697: [".txt", ".exe", ".zip", ".lzh", ".arj"]), 698: 699: test_compress_control(Dir, 700: Files, 701: [], 702: [".txt", ".exe"]), 703: 704: test_compress_control(Dir, 705: Files, 706: [{uncompress, {add, [".exe"]}}, 707: {uncompress, {del, [".zip", "arj"]}}], 708: [".txt", ".zip", "arj"]), 709: 710: test_compress_control(Dir, 711: Files, 712: [{uncompress, []}, 713: {uncompress, {add, [".exe"]}}, 714: {uncompress, {del, [".zip", "arj"]}}], 715: [".txt", ".zip", ".lzh", ".arj"]), 716: 717: ok. 718: 719: test_compress_control(Dir, Files, ZipOptions, Expected) -> 720: %% Cleanup 721: Zip = "zip.zip", 722: Names = [N || {N, _, _} <- Files], 723: delete_files([Zip]), 724: delete_files(lists:reverse(Names)), 725: 726: create_files(Files), 727: {ok, Zip} = zip:create(Zip, [Dir], ZipOptions), 728: 729: {ok, OpenZip} = zip:openzip_open(Zip, [memory]), 730: {ok,[#zip_comment{comment = ""} | ZipList]} = zip:openzip_list_dir(OpenZip), 731: io:format("compress_control: -> ~p -> ~p\n -> ~pn", [Expected, ZipOptions, ZipList]), 732: verify_compression(Files, ZipList, OpenZip, ZipOptions, Expected), 733: ok = zip:openzip_close(OpenZip), 734: 735: %% Cleanup 736: delete_files([Zip]), 737: delete_files(lists:reverse(Names)), % Remove plain files before directories 738: 739: ok. 740: 741: verify_compression([{Name, Kind, _Filler} | Files], ZipList, OpenZip, ZipOptions, Expected) -> 742: {Name2, BinSz} = 743: case Kind of 744: dir -> 745: {Name ++ "/", 0}; 746: _ -> 747: {ok, {Name, Bin}} = zip:openzip_get(Name, OpenZip), 748: {Name, size(Bin)} 749: end, 750: {Name2, {value, ZipFile}} = {Name2, lists:keysearch(Name2, #zip_file.name, ZipList)}, 751: #zip_file{info = #file_info{size = InfoSz, type = InfoType}, comp_size = InfoCompSz} = ZipFile, 752: 753: Ext = filename:extension(Name), 754: IsComp = is_compressed(Ext, Kind, ZipOptions), 755: ExpComp = lists:member(Ext, Expected), 756: case {Name, Kind, InfoType, IsComp, ExpComp, BinSz, InfoSz, InfoCompSz} of 757: {_, dir, directory, false, _, Sz, Sz, Sz} when Sz =:= BinSz -> ok; 758: {_, Sz, regular, false, false, Sz, Sz, Sz} when Sz =:= BinSz -> ok; 759: {_, Sz, regular, true, true, Sz, Sz, OtherSz} when Sz =:= BinSz, OtherSz =/= BinSz -> ok 760: end, 761: verify_compression(Files, ZipList -- [ZipFile], OpenZip, ZipOptions, Expected); 762: verify_compression([], [], _OpenZip, _ZipOptions, _Expected) -> 763: ok. 764: 765: is_compressed(_Ext, dir, _Options) -> 766: false; 767: is_compressed(Ext, _Sz, Options) -> 768: CompressOpt = 769: case [What || {compress, What} <- Options] of 770: [] -> all; 771: CompressOpts-> extensions(CompressOpts, all) 772: end, 773: DoCompress = (CompressOpt =:= all) orelse lists:member(Ext, CompressOpt), 774: Default = [".Z", ".zip", ".zoo", ".arc", ".lzh", ".arj"], 775: UncompressOpt = 776: case [What || {uncompress, What} <- Options] of 777: [] -> Default; 778: UncompressOpts-> extensions(UncompressOpts, Default) 779: end, 780: DoUncompress = (UncompressOpt =:= all) orelse lists:member(Ext, UncompressOpt), 781: DoCompress andalso not DoUncompress. 782: 783: extensions([H | T], Old) -> 784: case H of 785: all -> 786: extensions(T, H); 787: H when is_list(H) -> 788: extensions(T, H); 789: {add, New} when is_list(New), is_list(Old) -> 790: extensions(T, Old ++ New); 791: {del, New} when is_list(New), is_list(Old) -> 792: extensions(T, Old -- New); 793: _ -> 794: extensions(T, Old) 795: end; 796: extensions([], Old) -> 797: Old. 798: 799: foldl(Config) -> 800: PrivDir = ?config(priv_dir, Config), 801: File = filename:join([PrivDir, "foldl.zip"]), 802: 803: FooBin = <<"FOO">>, 804: BarBin = <<"BAR">>, 805: Files = [{"foo", FooBin}, {"bar", BarBin}], 806: ?line {ok, {File, Bin}} = zip:create(File, Files, [memory]), 807: ZipFun = fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end, 808: ?line {ok, FileSpec} = zip:foldl(ZipFun, [], {File, Bin}), 809: ?line [{"bar", BarBin, #file_info{}}, {"foo", FooBin, #file_info{}}] = FileSpec, 810: ?line {ok, {File, Bin}} = zip:create(File, lists:reverse(FileSpec), [memory]), 811: ?line {foo_bin, FooBin} = 812: try 813: zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {File, Bin}) 814: catch 815: throw:FooBin -> 816: {foo_bin, FooBin} 817: end, 818: ?line ok = file:write_file(File, Bin), 819: ?line {ok, FileSpec} = zip:foldl(ZipFun, [], File), 820: 821: ?line {error, einval} = zip:foldl(fun() -> ok end, [], File), 822: ?line {error, einval} = zip:foldl(ZipFun, [], 42), 823: ?line {error, einval} = zip:foldl(ZipFun, [], {File, 42}), 824: 825: ?line ok = file:delete(File), 826: ?line {error, enoent} = zip:foldl(ZipFun, [], File), 827: 828: ok.