diff --git a/vlib/os/os.v b/vlib/os/os.v index ffa6dd505e..3faaaf3c55 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -563,7 +563,7 @@ pub fn walk(path string, f fn (string)) { pub type FnWalkContextCB = fn (voidptr, string) // walk_with_context traverses the given directory `path`. -// For each encountred file, it will call your `fcb` callback, +// For each encountred file and directory, it will call your `fcb` callback, // passing it the arbitrary `context` in its first parameter, // and the path to the file in its second parameter. pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) { @@ -580,10 +580,9 @@ pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) { } for file in files { p := path + local_path_separator + file + fcb(context, p) if is_dir(p) && !is_link(p) { walk_with_context(p, context, fcb) - } else { - fcb(context, p) } } return diff --git a/vlib/szip/szip.v b/vlib/szip/szip.v index b7738d6d10..b65e45289c 100644 --- a/vlib/szip/szip.v +++ b/vlib/szip/szip.v @@ -5,6 +5,11 @@ import os #flag -I @VEXEROOT/thirdparty/zip #include "zip.c" +[params] +pub struct ZipFolderOptions { + omit_empty_folders bool +} + struct C.zip_t { } @@ -257,36 +262,48 @@ pub fn zip_files(path_to_file []string, path_to_export_zip string) ? { } } -/* -TODO add -// zip all files in directory to zip file -pub fn zip_folder(path_to_dir string, path_to_export_zip string) { - - // get list files from directory - files := os.ls(path_to_dir) or { panic(err) } +// zip_folder zips all entries in `folder` *recursively* to the zip file at `zip_file`. +// Empty folders will be included, unless specified otherwise in `opt`. +pub fn zip_folder(folder string, zip_file string, opt ZipFolderOptions) ? { + // get list of files from directory + path := folder.trim_right(os.path_separator) + mut files := []string{} + os.walk_with_context(path, &files, fn (mut files []string, file string) { + files << file + }) // open or create new zip - mut zip := szip.open(path_to_export_zip, .no_compression, .write) or { panic(err) } + mut zip := open(zip_file, .no_compression, .write) ? + // close zip + defer { + zip.close() + } // add all files from the directory to the archive for file in files { - eprintln('Zipping $file to ${path_to_export_zip}...') - println(path_to_dir + file) - - // add file to zip - zip.open_entry(file) or { panic(err) } - file_as_byte := os.read_bytes(path_to_dir + '/'+ file) or { panic(err) } - zip.write_entry(file_as_byte) or { panic(err) } - - zip.close_entry() + is_dir := os.is_dir(file) + if opt.omit_empty_folders && is_dir { + continue + } + // strip each zip entry for the path prefix - this way + // all files in the archive can be made relative. + mut zip_file_entry := file.trim_string_left(path + os.path_separator) + // Normalize path on Windows \ -> / + $if windows { + zip_file_entry = zip_file_entry.replace(os.path_separator, '/') + } + if is_dir { + zip_file_entry += '/' // Tells the implementation that the entry is a directory + } + // add file or directory (ends with "/") to zip + zip.open_entry(zip_file_entry) ? + if !is_dir { + file_as_byte := os.read_bytes(file) ? + zip.write_entry(file_as_byte) ? + } + zip.close_entry() } - - // close zip - zip.close() - - eprintln('Successfully') } -*/ // total returns the number of all entries (files and directories) in the zip archive. pub fn (mut zentry Zip) total() ?int { diff --git a/vlib/szip/szip_test.v b/vlib/szip/szip_test.v index a362464787..138ad16bb9 100644 --- a/vlib/szip/szip_test.v +++ b/vlib/szip/szip_test.v @@ -3,21 +3,33 @@ import os const ( test_out_zip = 'v_test_zip.zip' + test_dir_zip = 'v_test_dir_zip.zip' test_path = 'zip files' test_path2 = '.zip folder' + test_path3 = 'test zip folder' + test_path3_1 = os.join_path(test_path3, '1', '1') + test_path3_2 = os.join_path(test_path3, '2', '1') + test_path3_3 = os.join_path(test_path3, '3', '1') + test_path3_4 = os.join_path(test_path3, '4', '1') fname1 = 'file_1.txt' fpath1 = os.join_path(test_path, fname1) fname2 = 'file_2.txt' fpath2 = os.join_path(test_path, fname2) fname3 = '.New Text Document.txt' fpath3 = os.join_path(test_path2, fname3) + fname4 = 'file.txt' + fpath4 = os.join_path(test_path3_1, fname4) + fpath5 = os.join_path(test_path3_2, fname4) + fpath6 = os.join_path(test_path3_4, fname4) ) fn cleanup() { os.chdir(os.temp_dir()) or {} os.rmdir_all(test_path) or {} os.rmdir_all(test_path2) or {} + os.rmdir_all(test_path3) or {} os.rm(test_out_zip) or {} + os.rm(test_dir_zip) or {} } fn testsuite_begin() ? { @@ -110,3 +122,53 @@ fn test_reading_zipping_files() ? { } zp.close() } + +fn test_zip_folder() ? { + cleanup() + os.mkdir_all(test_path3_1) ? + os.mkdir_all(test_path3_2) ? + os.mkdir_all(test_path3_3) ? + os.mkdir_all(test_path3_4) ? + os.write_file(fpath4, '4') ? + os.write_file(fpath5, '5') ? + os.write_file(fpath6, '6') ? + + szip.zip_folder(test_path3, test_dir_zip) ? + assert os.exists(test_dir_zip) + + os.rmdir_all(test_path3) ? + os.mkdir_all(test_path3) ? + szip.extract_zip_to_dir(test_dir_zip, test_path3) ? + assert os.exists(test_path3_1) + assert os.exists(test_path3_2) + assert os.exists(test_path3_3) // This is the empty dir + assert os.exists(test_path3_4) + assert (os.read_file(fpath4) ?) == '4' + assert (os.read_file(fpath5) ?) == '5' + assert (os.read_file(fpath6) ?) == '6' +} + +fn test_zip_folder_omit_empty_directories() ? { + cleanup() + os.mkdir_all(test_path3_1) ? + os.mkdir_all(test_path3_2) ? + os.mkdir_all(test_path3_3) ? + os.mkdir_all(test_path3_4) ? + os.write_file(fpath4, '4') ? + os.write_file(fpath5, '5') ? + os.write_file(fpath6, '6') ? + + szip.zip_folder(test_path3, test_dir_zip, omit_empty_folders: true) ? + assert os.exists(test_dir_zip) + + os.rmdir_all(test_path3) ? + os.mkdir_all(test_path3) ? + szip.extract_zip_to_dir(test_dir_zip, test_path3) ? + assert os.exists(test_path3_1) + assert os.exists(test_path3_2) + assert !os.exists(test_path3_3) // This is the empty dir, should be omitted with `omit_empty_folders` + assert os.exists(test_path3_4) + assert (os.read_file(fpath4) ?) == '4' + assert (os.read_file(fpath5) ?) == '5' + assert (os.read_file(fpath6) ?) == '6' +}