Rename modify to ziptool and install it.
diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt
index d52b4ec..82fbb6a 100644
--- a/regress/CMakeLists.txt
+++ b/regress/CMakeLists.txt
@@ -14,7 +14,6 @@
 )
 
 SET(HELPER_TEST_PROGRAMS
-#  modify
 #  tryopen
 )
 
@@ -23,11 +22,6 @@
   tryopen
 )
 
-SET(HOLE_USERS
-  hole
-  modify
-)
-
 SET(EXTRA_TESTS
 	add_dir.test
 	add_from_buffer.test
@@ -133,10 +127,6 @@
   ADD_EXECUTABLE(${PROGRAM} ${PROGRAM}.c ${SRC_EXTRA_FILES})
   TARGET_LINK_LIBRARIES(${PROGRAM} zip)
 ENDFOREACH(PROGRAM ${GETOPT_USERS})
-FOREACH(PROGRAM ${HOLE_USERS})
-  ADD_EXECUTABLE(${PROGRAM} ${PROGRAM}.c ${SRC_EXTRA_FILES} source_hole.c)
-  TARGET_LINK_LIBRARIES(${PROGRAM} zip)
-ENDFOREACH(PROGRAM ${HOLE_USERS})
 
 ADD_TEST(fread ${CMAKE_CURRENT_SOURCE_DIR}/runtest ${CMAKE_CURRENT_SOURCE_DIR}/fread)
 
diff --git a/regress/Makefile.am b/regress/Makefile.am
index 9362855..7a68b5c 100644
--- a/regress/Makefile.am
+++ b/regress/Makefile.am
@@ -6,18 +6,8 @@
 	add_from_filep \
 	fopen_unchanged \
 	fread \
-	hole \
-	modify \
 	tryopen
 
-modify_SOURCES= \
-	modify.c \
-	source_hole.c
-
-hole_SOURCES= \
-	hole.c \
-	source_hole.c
-
 pkglib_LTLIBRARIES=	malloc.la
 malloc_la_SOURCES=	malloc.c
 malloc_la_LDFLAGS=	-module -avoid-version
@@ -232,10 +222,10 @@
 check-am: bigzero.zip manyfiles.zip runtest
 
 bigzero.zip: bigzero-zip.zip
-	${builddir}/modify ${srcdir}/bigzero-zip.zip cat 0 > ${builddir}/bigzero.zip
+	${top_builddir}/src/ziptool ${srcdir}/bigzero-zip.zip cat 0 > ${builddir}/bigzero.zip
 
 manyfiles.zip: manyfiles-zip.zip
-	${builddir}/modify ${srcdir}/manyfiles-zip.zip cat 0 > ${builddir}/manyfiles.zip
+	${top_builddir}/src/ziptool ${srcdir}/manyfiles-zip.zip cat 0 > ${builddir}/manyfiles.zip
 
 runtest: runtest.in
 	sed -e 's!@[s]rcdir@!${srcdir}!g' -e 's!@[a]bs_srcdir@!${abs_srcdir}!g' ${srcdir}/runtest.in > runtest
diff --git a/regress/add_dir.test b/regress/add_dir.test
index 9b75559..b6a4d37 100644
--- a/regress/add_dir.test
+++ b/regress/add_dir.test
@@ -1,5 +1,4 @@
 # add directories to zip
-program modify
 return 0
 args testdir.zip   add_dir testdir/   add_dir testdir-noslash
 file-new testdir.zip testdir.zip
diff --git a/regress/add_from_buffer.test b/regress/add_from_buffer.test
index d0a1ebb..6963c58 100644
--- a/regress/add_from_buffer.test
+++ b/regress/add_from_buffer.test
@@ -1,5 +1,4 @@
 # add buffer contents as file to zip
-program modify
 return 0
 args testbuffer.zip add teststring.txt "This is a test, and it seems to have been successful.\n"
 file-new testbuffer.zip testbuffer.zip
diff --git a/regress/add_from_file.test b/regress/add_from_file.test
index 8619167..4f97d27 100644
--- a/regress/add_from_file.test
+++ b/regress/add_from_file.test
@@ -1,5 +1,4 @@
 # add file to zip
-program modify
 return 0
 args -- testfile.zip   add_file testfile.txt testfile.txt 0 -1
 file testfile.txt testfile.txt testfile.txt
diff --git a/regress/add_from_file_duplicate.test b/regress/add_from_file_duplicate.test
index 7aca619..018b260 100644
--- a/regress/add_from_file_duplicate.test
+++ b/regress/add_from_file_duplicate.test
@@ -1,5 +1,4 @@
 # add already existing file to zip, making duplicate names
-program modify
 return 1
 args -- testfile.zip   add_file testfile.txt testfile.txt 0 -1
 file testfile.txt testfile.txt testfile.txt
diff --git a/regress/add_from_file_twice_duplicate.test b/regress/add_from_file_twice_duplicate.test
index e5a0f99..511c90b 100644
--- a/regress/add_from_file_twice_duplicate.test
+++ b/regress/add_from_file_twice_duplicate.test
@@ -1,5 +1,4 @@
 # add file to zip twice, making duplicate names
-program modify
 return 1
 args -- testfile.zip   add_file testfile.txt testfile.txt 0 -1   add_file testfile.txt testfile.txt 0 -1
 file testfile.txt testfile.txt testfile.txt
diff --git a/regress/add_from_stdin.test b/regress/add_from_stdin.test
index cf8a937..6736438 100644
--- a/regress/add_from_stdin.test
+++ b/regress/add_from_stdin.test
@@ -1,6 +1,5 @@
 # add stdin to zip
 pipein echo This is a test, and it seems to have been successful.
-program modify
 args -- teststdin.zip add_file teststring.txt /dev/stdin 0 -1
 return 0
 file-new teststdin.zip teststdin.zip
diff --git a/regress/add_from_zip_closed.test b/regress/add_from_zip_closed.test
index c90306f..4c52e56 100644
--- a/regress/add_from_zip_closed.test
+++ b/regress/add_from_zip_closed.test
@@ -1,5 +1,4 @@
 # add deflated file from zip to zip, but close the source before it can be read
-program modify
 return 1
 args -- testfile.zip   add_from_zip abac-repeat.txt testdeflated.zzip 0 0 -1  zin_close 0
 file testdeflated.zzip testdeflated.zip testdeflated.zip
diff --git a/regress/add_from_zip_deflated.test b/regress/add_from_zip_deflated.test
index c88582e..ecff27d 100644
--- a/regress/add_from_zip_deflated.test
+++ b/regress/add_from_zip_deflated.test
@@ -1,5 +1,4 @@
 # add deflated file from zip to zip
-program modify
 return 0
 args -- testfile.zip   add_from_zip abac-repeat.txt testdeflated.zzip 0 0 -1
 file testdeflated.zzip testdeflated.zip testdeflated.zip
diff --git a/regress/add_from_zip_deflated2.test b/regress/add_from_zip_deflated2.test
index 03a6c1b..ad7b4b6 100644
--- a/regress/add_from_zip_deflated2.test
+++ b/regress/add_from_zip_deflated2.test
@@ -1,5 +1,4 @@
 # add deflated files from zip to zip
-program modify
 return 0
 args -- testfile.zip   add_from_zip abac-repeat.txt testdeflated.zzip 0 0 -1  add_from_zip abac-repeat2.txt testdeflated.zzip 0 0 -1
 file testdeflated.zzip testdeflated.zip testdeflated.zip
diff --git a/regress/add_from_zip_partial_deflated.test b/regress/add_from_zip_partial_deflated.test
index f848a48..69e95c6 100644
--- a/regress/add_from_zip_partial_deflated.test
+++ b/regress/add_from_zip_partial_deflated.test
@@ -1,5 +1,4 @@
 # add parts of a file from zip to zip
-program modify
 return 0
 args -- testfile.zip   add_from_zip first firstsecond.zzip 0 0 9   add_from_zip second firstsecond.zzip 0 9 -1
 file firstsecond.zzip firstsecond.zip firstsecond.zip
diff --git a/regress/add_from_zip_partial_stored.test b/regress/add_from_zip_partial_stored.test
index fd1be84..41ebf21 100644
--- a/regress/add_from_zip_partial_stored.test
+++ b/regress/add_from_zip_partial_stored.test
@@ -1,5 +1,4 @@
 # add parts of a file from zip to zip
-program modify
 return 0
 args -- testfile.zip   add_from_zip first firstsecond.zzip 1 0 9   add_from_zip second firstsecond.zzip 1 9 -1
 file firstsecond.zzip firstsecond.zip firstsecond.zip
diff --git a/regress/add_from_zip_stored.test b/regress/add_from_zip_stored.test
index 02ee33e..2fcbe80 100644
--- a/regress/add_from_zip_stored.test
+++ b/regress/add_from_zip_stored.test
@@ -1,5 +1,4 @@
 # add stored file from zip to zip
-program modify
 return 0
 args -- testfile.zip   add_from_zip abac-repeat.txt teststored.zzip 0 0 -1
 file teststored.zzip teststored.zip teststored.zip
diff --git a/regress/add_stored.test b/regress/add_stored.test
index 628387e..ea96f25 100644
--- a/regress/add_stored.test
+++ b/regress/add_stored.test
@@ -1,5 +1,4 @@
 # add file, set compression method to ZIP_CM_STORE
-program modify
 return 0
 args -n test.zip add foo foo  set_file_compression 0 store 0
 file-new test.zip foo-stored.zip
diff --git a/regress/add_stored_in_memory.test b/regress/add_stored_in_memory.test
index 9b13849..50467c6 100644
--- a/regress/add_stored_in_memory.test
+++ b/regress/add_stored_in_memory.test
@@ -1,5 +1,4 @@
 # add file, set compression method to ZIP_CM_STORE
-program modify
 return 0
 args -mn test.zip add foo foo  set_file_compression 0 store 0
 file-new test.zip foo-stored.zip
diff --git a/regress/cm-default.test b/regress/cm-default.test
index 9241770..03e7200 100644
--- a/regress/cm-default.test
+++ b/regress/cm-default.test
@@ -1,5 +1,4 @@
 # test default compression stores if smaller
-program modify
 return 0
 args -n test.zip  add compressable aaaaaaaaaaaaaa  add uncompressable uncompressable  add_nul large-compressable 8200  add_file large-uncompressable large-uncompressable 0 -1
 file-new test.zip cm-default.zip
diff --git a/regress/count_entries.test b/regress/count_entries.test
index 0849a03..564acdc 100644
--- a/regress/count_entries.test
+++ b/regress/count_entries.test
@@ -1,5 +1,4 @@
 # zip_open: count entries for archive with >65k entries
-program modify
 args manyfiles.zip get_num_entries 0
 return 0
 file manyfiles.zip manyfiles.zip manyfiles.zip
diff --git a/regress/decrypt-aes-correct-password.test b/regress/decrypt-aes-correct-password.test
index 2091932..7c052e7 100644
--- a/regress/decrypt-aes-correct-password.test
+++ b/regress/decrypt-aes-correct-password.test
@@ -1,5 +1,4 @@
 # test AES decryption support, extract file using correct password
-program modify
 return 1
 args encrypt.zzip  set_password foofoofoo  cat 0
 file encrypt.zzip encrypt-aes.zip encrypt-aes.zip
diff --git a/regress/decrypt-aes-wrong-password.test b/regress/decrypt-aes-wrong-password.test
index 4d68f0e..bd5eb1b 100644
--- a/regress/decrypt-aes-wrong-password.test
+++ b/regress/decrypt-aes-wrong-password.test
@@ -1,5 +1,4 @@
 # test AES decryption support, extract file using wrong password
-program modify
 return 1
 args encrypt.zzip  set_password notfoonotfoo  cat 0
 file encrypt.zzip encrypt-aes.zip encrypt-aes.zip
diff --git a/regress/decrypt-correct-password.test b/regress/decrypt-correct-password.test
index 57a66b8..c126394 100644
--- a/regress/decrypt-correct-password.test
+++ b/regress/decrypt-correct-password.test
@@ -1,5 +1,4 @@
 # test decryption support, extract file using correct password
-program modify
 return 0
 args encrypt.zzip  set_password foo  cat 0
 file encrypt.zzip encrypt.zip encrypt.zip
diff --git a/regress/decrypt-wrong-password.test b/regress/decrypt-wrong-password.test
index b4e4f49..0274955 100644
--- a/regress/decrypt-wrong-password.test
+++ b/regress/decrypt-wrong-password.test
@@ -1,5 +1,4 @@
 # test decryption support, extract file using wrong password
-program modify
 return 1
 args encrypt.zzip  set_password notfoo  cat 0
 file encrypt.zzip encrypt.zip encrypt.zip
diff --git a/regress/delete_add_same.test b/regress/delete_add_same.test
index 385caac..54c0e5a 100644
--- a/regress/delete_add_same.test
+++ b/regress/delete_add_same.test
@@ -1,6 +1,5 @@
 # delete entry in zip archive then add file of same name
 # time is now(), so use zipcmp
-program modify
 return 0
 args testfile.zip delete 0 add testfile.txt test
 file testfile.zip testfile.zip test2.zip
diff --git a/regress/delete_invalid.test b/regress/delete_invalid.test
index fe317ec..9a06306 100644
--- a/regress/delete_invalid.test
+++ b/regress/delete_invalid.test
@@ -1,5 +1,4 @@
 # delete last entry in zip archive
-program modify
 return 1
 args testfile.zzip delete 5
 file testfile.zzip testfile.zip testfile.zip
diff --git a/regress/delete_last.test b/regress/delete_last.test
index a7fdb8e..3b415b5 100644
--- a/regress/delete_last.test
+++ b/regress/delete_last.test
@@ -1,5 +1,4 @@
 # delete last entry in zip archive
-program modify
 return 0
 args testfile.zzip delete 0
 file-del testfile.zzip testfile.zip
diff --git a/regress/delete_multiple_last.test b/regress/delete_multiple_last.test
index bd830a7..71ac983 100644
--- a/regress/delete_multiple_last.test
+++ b/regress/delete_multiple_last.test
@@ -1,5 +1,4 @@
 # delete multiple entries in zip archive, emptying it
-program modify
 return 0
 args testfile.zzip delete 0 delete 1 delete 2 delete 3
 file-del testfile.zzip testcomment.zip
diff --git a/regress/delete_multiple_partial.test b/regress/delete_multiple_partial.test
index 9e6dcc5..272fab3 100644
--- a/regress/delete_multiple_partial.test
+++ b/regress/delete_multiple_partial.test
@@ -1,5 +1,4 @@
 # delete some entries in zip archive
-program modify
 return 0
 args testfile.zip delete 1 delete 3
 file testfile.zip testcomment.zip testcomment13.zip
diff --git a/regress/delete_renamed_rename.test b/regress/delete_renamed_rename.test
index 4e3e414..e81996c 100644
--- a/regress/delete_renamed_rename.test
+++ b/regress/delete_renamed_rename.test
@@ -1,6 +1,5 @@
 # delete renamed entry in zip archive then rename file to same name
 # file date is now(), so use zipcmp
-program modify
 return 0
 args testfile.zip rename 0 something  add test test  delete 0  rename 1 testfile.txt
 file testfile.zip testfile.zip test2.zip
diff --git a/regress/encrypt.test b/regress/encrypt.test
index c571863..f9aad43 100644
--- a/regress/encrypt.test
+++ b/regress/encrypt.test
@@ -1,6 +1,5 @@
 # test encryption support
 # TODO: only checks recognition of encrypted entries for now.
-program modify
 return 0
 args encrypt.zzip stat 0 stat 1
 file encrypt.zzip encrypt.zip encrypt.zip
diff --git a/regress/extra_add.test b/regress/extra_add.test
index 1e3d45c..c2020f1 100644
--- a/regress/extra_add.test
+++ b/regress/extra_add.test
@@ -1,5 +1,4 @@
 # add extra field
-program modify
 #args encrypt.zip set_extra 0 2345 65535 cl extrafieldcontent
 args encrypt.zip set_extra 0 2345 65535 cl extrafieldcontent get_extra_by_id 0 2345 0 c get_extra_by_id 0 2345 0 l
 return 0
diff --git a/regress/extra_add_multiple.test b/regress/extra_add_multiple.test
index c174e4d..2623755 100644
--- a/regress/extra_add_multiple.test
+++ b/regress/extra_add_multiple.test
@@ -1,5 +1,4 @@
 # add extra fields
-program modify
 args testfile.zip  set_extra 0 2345 65535 cl extra1  set_extra 0 2345 65535 cl extra2  set_extra 0 2345 0 c extra1c  set_extra 0 2345 1 l extra2l  get_extra_by_id 0 2345 0 c  get_extra_by_id 0 2345 1 c  get_extra_by_id 0 2345 0 l  get_extra_by_id 0 2345 1 l
 return 0
 file testfile.zip testfile.zip testfile-plus-extra.zip
diff --git a/regress/extra_count.test b/regress/extra_count.test
index 2ac26e6..7d80e02 100644
--- a/regress/extra_count.test
+++ b/regress/extra_count.test
@@ -1,5 +1,4 @@
 # count extra fields for index
-program modify
 args encrypt.zip count_extra 0 l count_extra 0 c count_extra 0 lc count_extra 1 l count_extra 1 c count_extra 1 lc
 return 0
 file encrypt.zip encrypt.zip encrypt.zip
diff --git a/regress/extra_count_by_id.test b/regress/extra_count_by_id.test
index dc11f34..5029935 100644
--- a/regress/extra_count_by_id.test
+++ b/regress/extra_count_by_id.test
@@ -1,5 +1,4 @@
 # count extra fields for index
-program modify
 args encrypt.zip count_extra_by_id 0 0 l count_extra_by_id 0 1 l count_extra_by_id 0 21589 l count_extra_by_id 0 30805 l count_extra_by_id 0 21844 l count_extra_by_id 0 12345 l count_extra_by_id 0 0 c count_extra_by_id 0 1 c count_extra_by_id 0 21589 c count_extra_by_id 0 30805 c count_extra_by_id 0 21844 c count_extra_by_id 0 12345 c count_extra_by_id 0 0 cl count_extra_by_id 0 1 cl count_extra_by_id 0 21589 cl count_extra_by_id 0 30805 cl count_extra_by_id 0 21844 cl count_extra_by_id 0 12345 cl
 return 0
 file encrypt.zip encrypt.zip encrypt.zip
diff --git a/regress/extra_count_ignore_zip64.test b/regress/extra_count_ignore_zip64.test
index 2599b33..1766997 100644
--- a/regress/extra_count_ignore_zip64.test
+++ b/regress/extra_count_ignore_zip64.test
@@ -1,5 +1,4 @@
 # count extra fields for index
-program modify
 args bigzero.zip count_extra 0 l count_extra 0 c count_extra 0 lc
 return 0
 file bigzero.zip bigzero.zip bigzero.zip
diff --git a/regress/extra_delete.test b/regress/extra_delete.test
index 4ca5d49..9bccc90 100644
--- a/regress/extra_delete.test
+++ b/regress/extra_delete.test
@@ -1,5 +1,4 @@
 # delete extra field by index
-program modify
 args encrypt.zip get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l delete_extra 0 2 c delete_extra 0 0 l get_extra 0 0 c get_extra 0 1 c get_extra 0 0 l get_extra 0 1 l
 file encrypt.zip encrypt_plus_extra.zip encrypt.zip
 return 0
diff --git a/regress/extra_delete_by_id.test b/regress/extra_delete_by_id.test
index 8525739..4c8fa00 100644
--- a/regress/extra_delete_by_id.test
+++ b/regress/extra_delete_by_id.test
@@ -1,5 +1,4 @@
 # delete extra field by id and index
-program modify
 #args encrypt.zip set_extra 0 2345 65535 cl extrafieldcontent
 args encrypt.zip delete_extra_by_id 0 2345 0 c delete_extra_by_id 0 2345 0 l get_extra_by_id 0 2345 0 c
 return 1
diff --git a/regress/extra_get.test b/regress/extra_get.test
index 1455978..4f45887 100644
--- a/regress/extra_get.test
+++ b/regress/extra_get.test
@@ -1,5 +1,4 @@
 # get extra fields for index
-program modify
 args encrypt.zip get_extra 0 0 l get_extra 0 0 c get_extra 0 1 l get_extra 0 1 c get_extra 0 2 l
 return 1
 file encrypt.zip encrypt.zip encrypt.zip
diff --git a/regress/extra_get_by_id.test b/regress/extra_get_by_id.test
index b3b76f3..67f4967 100644
--- a/regress/extra_get_by_id.test
+++ b/regress/extra_get_by_id.test
@@ -1,5 +1,4 @@
 # get extra_by_id fields for index
-program modify
 args encrypt.zip get_extra_by_id 0 21589 0 l get_extra_by_id 0 30805 0 l get_extra_by_id 0 21589 0 c get_extra_by_id 0 30805 0 c get_extra_by_id 0 21544 0 c
 return 1
 file encrypt.zip encrypt.zip encrypt.zip
diff --git a/regress/extra_set.test b/regress/extra_set.test
index 32b6f88..ccdbdc9 100644
--- a/regress/extra_set.test
+++ b/regress/extra_set.test
@@ -1,5 +1,4 @@
 # set extra field
-program modify
 args -- encrypt get_extra 0 0 c get_extra 0 1 c get_extra 0 0 l get_extra 0 1 l set_extra 0 2345 -1 l extrafieldcontent set_extra 0 2345 -1 c extrafieldcontent get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l
 file encrypt encrypt.zip encrypt_plus_extra.zip
 return 0
diff --git a/regress/extra_set_modify_c.test b/regress/extra_set_modify_c.test
index 9f84ced..dca49f4 100644
--- a/regress/extra_set_modify_c.test
+++ b/regress/extra_set_modify_c.test
@@ -1,5 +1,4 @@
 # change existing central extra field
-program modify
 args encrypt.zip get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l set_extra 0 2345 0 c Extrafieldcontent get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l
 file encrypt.zip encrypt_plus_extra.zip encrypt_plus_extra_modified_c.zip
 return 0
diff --git a/regress/extra_set_modify_l.test b/regress/extra_set_modify_l.test
index 0a838b6..4dda9b7 100644
--- a/regress/extra_set_modify_l.test
+++ b/regress/extra_set_modify_l.test
@@ -1,5 +1,4 @@
 # change existing local extra field
-program modify
 args encrypt.zip get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l set_extra 0 2345 0 l Extrafieldcontent get_extra 0 0 c get_extra 0 1 c get_extra 0 2 c get_extra 0 0 l get_extra 0 1 l get_extra 0 2 l
 file encrypt.zip encrypt_plus_extra.zip encrypt_plus_extra_modified_l.zip
 return 0
diff --git a/regress/file_comment_encmismatch.test b/regress/file_comment_encmismatch.test
index 8d6dc98..a2b8209 100644
--- a/regress/file_comment_encmismatch.test
+++ b/regress/file_comment_encmismatch.test
@@ -1,5 +1,4 @@
 # set file comment to UTF-8 for CP437 encoded filename (adds InfoZIP extra field)
-program modify
 return 0
 args testfile.zip   set_file_comment 0 ÄÖÜßäöü
 file testfile.zip test-cp437.zip test-cp437-comment-utf-8.zip
diff --git a/regress/get_comment.test b/regress/get_comment.test
index 735cc97..5f2df0a 100644
--- a/regress/get_comment.test
+++ b/regress/get_comment.test
@@ -1,5 +1,4 @@
 # show comments of a zip archive
-program modify
 return 0
 args testcomment.zip   get_archive_comment   get_file_comment 0   get_file_comment 1   get_file_comment 2   get_file_comment 3
 file testcomment.zip testcomment.zip testcomment.zip
diff --git a/regress/name_locate.test b/regress/name_locate.test
index 5d7fc26..1f79231 100644
--- a/regress/name_locate.test
+++ b/regress/name_locate.test
@@ -1,5 +1,4 @@
 # various tests for zip_name_locate
-program modify
 args test.zip  name_locate nosuchfile 0  name_locate test 0  name_locate "" 0  name_locate TeSt 0  name_locate TeSt C  name_locate testdir/test2 0  name_locate tesTdir/tESt2 C  name_locate testdir/test2 d  name_locate tesTdir/tESt2 dC  name_locate test2 0  name_locate test2 d  name_locate TeST2 dC  delete 0  name_locate test 0  name_locate test u  add new teststring  name_locate new 0  name_locate new u  add "" teststring  name_locate "" 0  unchange_all  name_locate test 0  name_locate new 0
 stderr can't find entry with name 'nosuchfile' using flags '0'
 stdout name 'test' using flags '0' found at index 0
diff --git a/regress/rename_ascii.test b/regress/rename_ascii.test
index bab7301..0010ca4 100644
--- a/regress/rename_ascii.test
+++ b/regress/rename_ascii.test
@@ -1,5 +1,4 @@
 # rename file to ASCII name in zip archive
-program modify
 return 0
 args testfile rename 0 testfile.txt
 file testfile testfile-UTF8.zip testfile.zip
diff --git a/regress/rename_cp437.test b/regress/rename_cp437.test
index 5445608..5a01250 100644
--- a/regress/rename_cp437.test
+++ b/regress/rename_cp437.test
@@ -1,5 +1,4 @@
 # rename file to CP437 name in zip archive (fails)
-program modify
 return 0
 args testfile.zip rename 0 ‚ƒ„…†‡ˆ‰Š‹ŒŽ
 file testfile.zip testfile.zip testfile-cp437.zip
diff --git a/regress/rename_deleted.test b/regress/rename_deleted.test
index c323e16..e4670c6 100644
--- a/regress/rename_deleted.test
+++ b/regress/rename_deleted.test
@@ -1,5 +1,4 @@
 # rename deleted entry in zip archive (fails)
-program modify
 return 1
 args testfile.zip delete 1 delete 3 rename 1 othername
 file testfile.zip testcomment.zip testcomment13.zip
diff --git a/regress/rename_fail.test b/regress/rename_fail.test
index ccff307..c10fcbe 100644
--- a/regress/rename_fail.test
+++ b/regress/rename_fail.test
@@ -1,5 +1,4 @@
 # rename file inside zip archive, but file name already exists
-program modify
 return 1
 args rename.zip   rename 0 file4
 file rename.zip testcomment.zip testcomment.zip
diff --git a/regress/rename_ok.test b/regress/rename_ok.test
index 9d9871b..b76df9c 100644
--- a/regress/rename_ok.test
+++ b/regress/rename_ok.test
@@ -1,5 +1,4 @@
 # rename file inside zip archive
-program modify
 return 0
 args rename.zip  rename 1 notfile2
 file rename.zip testcomment.zip rename_ok.zip
diff --git a/regress/rename_utf8.test b/regress/rename_utf8.test
index 25b92f0..391ebeb 100644
--- a/regress/rename_utf8.test
+++ b/regress/rename_utf8.test
@@ -1,5 +1,4 @@
 # rename file to UTF-8 name in zip archive
-program modify
 return 0
 args testfile rename 0 ÄÖÜßäöü
 file testfile testfile.zip testfile-UTF8.zip
diff --git a/regress/rename_utf8_encmismatch.test b/regress/rename_utf8_encmismatch.test
index 12cc537..075bca1 100644
--- a/regress/rename_utf8_encmismatch.test
+++ b/regress/rename_utf8_encmismatch.test
@@ -1,5 +1,4 @@
 # rename file to UTF-8 name in zip archive with CP437 comment (sets InfoZIP UTF-8 Name Extension)
-program modify
 return 0
 args testfile rename 0 ÄÖÜßäöü
 file testfile test-cp437-fc.zip test-cp437-fc-utf-8-filename.zip
diff --git a/regress/runtest.in b/regress/runtest.in
old mode 100755
new mode 100644
index 1e1fde1..c845388
--- a/regress/runtest.in
+++ b/regress/runtest.in
@@ -38,6 +38,6 @@
 
 use NiHTest;
 
-my $test = NiHTest::new({ default_program => 'modify', srcdir => '@srcdir@', zipcmp => '../../src/zipcmp', zipcmp_flags => '-p' });
+my $test = NiHTest::new({ default_program => '../src/ziptool', srcdir => '@srcdir@', zipcmp => '../../src/zipcmp', zipcmp_flags => '-p' });
 
 $test->run(@ARGV);
diff --git a/regress/set_comment_all.test b/regress/set_comment_all.test
index 38c5e3f..5902f98 100644
--- a/regress/set_comment_all.test
+++ b/regress/set_comment_all.test
@@ -1,5 +1,4 @@
 # change local and global comments in a zip archive
-program modify
 return 0
 args testcomment.zip  set_archive_comment "This is the new,\r\nmultiline archive comment.\r\nAin't it nice?"  set_file_comment 0 "File comment no 0"  set_file_comment 1 "File comment no 1"  set_file_comment 2 "File comment no 2"  set_file_comment 3 "File comment no 3"
 file testcomment.zip testcomment.zip testchanged.zip
diff --git a/regress/set_comment_localonly.test b/regress/set_comment_localonly.test
index 6d7f20b..08f7b8e 100644
--- a/regress/set_comment_localonly.test
+++ b/regress/set_comment_localonly.test
@@ -1,5 +1,4 @@
 # change file comments in a zip archive
-program modify
 return 0
 args testcomment.zip  set_file_comment 0 "File comment no 0"  set_file_comment 1 "File comment no 1"  set_file_comment 3 "File comment no 3"  set_file_comment 2 ""  
 file testcomment.zip testcomment.zip testchangedlocal.zip
diff --git a/regress/set_comment_removeglobal.test b/regress/set_comment_removeglobal.test
index f00cf8a..b469b37 100644
--- a/regress/set_comment_removeglobal.test
+++ b/regress/set_comment_removeglobal.test
@@ -1,5 +1,4 @@
 # remove archive comment
-program modify
 return 0
 args testcomment.zip  set_archive_comment ""
 file testcomment.zip testcomment.zip testcommentremoved.zip
diff --git a/regress/set_comment_revert.test b/regress/set_comment_revert.test
index 10baf5a..da61c37 100644
--- a/regress/set_comment_revert.test
+++ b/regress/set_comment_revert.test
@@ -1,5 +1,4 @@
 # start changing local and global comments, but revert before closing
-program modify
 return 0
 args testcomment.zip  set_archive_comment "some long string, a bit longer than this at least"  set_file_comment 0 "File comment no 0"  set_file_comment 1 "File comment no 1"  set_file_comment 3 "File comment no 3"  set_file_comment 2 ""   unchange_all
 file testcomment.zip testcomment.zip testcomment.zip
diff --git a/regress/set_compression_deflate_to_deflate.test b/regress/set_compression_deflate_to_deflate.test
index 1025cb6..e0b3121 100644
--- a/regress/set_compression_deflate_to_deflate.test
+++ b/regress/set_compression_deflate_to_deflate.test
@@ -1,5 +1,4 @@
 # change method from deflated to deflated (no change)
-program modify
 return 0
 args test.zip  set_file_compression 0 deflate 0
 file test.zip testdeflated.zip testdeflated.zip
diff --git a/regress/set_compression_deflate_to_store.test b/regress/set_compression_deflate_to_store.test
index e94bcce..1678b9c 100644
--- a/regress/set_compression_deflate_to_store.test
+++ b/regress/set_compression_deflate_to_store.test
@@ -1,5 +1,4 @@
 # change method from deflated to stored
-program modify
 return 0
 args test.zip  set_file_compression 0 store 0
 file test.zip testdeflated.zip teststored.zip
diff --git a/regress/set_compression_store_to_deflate.test b/regress/set_compression_store_to_deflate.test
index 17ba9ee..1f83492 100644
--- a/regress/set_compression_store_to_deflate.test
+++ b/regress/set_compression_store_to_deflate.test
@@ -1,5 +1,4 @@
 # change method from stored to deflated
-program modify
 return 0
 args test.zip  set_file_compression 0 deflate 0
 file test.zip teststored.zip testdeflated.zip
diff --git a/regress/set_compression_store_to_store.test b/regress/set_compression_store_to_store.test
index 250d21a..66cf3dd 100644
--- a/regress/set_compression_store_to_store.test
+++ b/regress/set_compression_store_to_store.test
@@ -1,5 +1,4 @@
 # change method from stored to stored (no change)
-program modify
 return 0
 args test.zip  set_file_compression 0 store 0
 file test.zip teststored.zip teststored.zip
diff --git a/regress/set_compression_unknown.test b/regress/set_compression_unknown.test
index 5848075..2e50912 100644
--- a/regress/set_compression_unknown.test
+++ b/regress/set_compression_unknown.test
@@ -1,5 +1,4 @@
 # change method to unknown
-program modify
 return 1
 args test.zip  set_file_compression 0 unknown 0
 file test.zip teststored.zip teststored.zip
diff --git a/regress/set_mtime.test b/regress/set_mtime.test
index d88195a..c80fe4b 100644
--- a/regress/set_mtime.test
+++ b/regress/set_mtime.test
@@ -1,5 +1,4 @@
 # change mtime in a zip archive
-program modify
 return 0
 args testfile set_mtime 0 1407272201
 file testfile testfile.zip testfile2014.zip
diff --git a/regress/stat_index_cp437_guess.test b/regress/stat_index_cp437_guess.test
index 34c4d0f..f1abd1a 100644
--- a/regress/stat_index_cp437_guess.test
+++ b/regress/stat_index_cp437_guess.test
@@ -1,5 +1,4 @@
 # guess CP437 file names and autoconvert them
-program modify
 args test-cp437.zip stat 0 stat 1 stat 2 stat 3 stat 4 stat 5 stat 6 stat 7 stat 8 stat 9 stat 10 stat 11 stat 12 stat 13 stat 14 stat 15
 return 0
 file test-cp437.zip test-cp437.zip test-cp437.zip
diff --git a/regress/stat_index_cp437_raw.test b/regress/stat_index_cp437_raw.test
index 8239b45..79104f0 100644
--- a/regress/stat_index_cp437_raw.test
+++ b/regress/stat_index_cp437_raw.test
Binary files differ
diff --git a/regress/stat_index_cp437_strict.test b/regress/stat_index_cp437_strict.test
index ab5da3d..a34a0e6 100644
--- a/regress/stat_index_cp437_strict.test
+++ b/regress/stat_index_cp437_strict.test
@@ -1,5 +1,4 @@
 # strictly follow ZIP spec and expect CP437 file names, and autoconvert them
-program modify
 args -s test-cp437.zip stat 0 stat 1 stat 2 stat 3 stat 4 stat 5 stat 6 stat 7 stat 8 stat 9 stat 10 stat 11 stat 12 stat 13 stat 14 stat 15
 return 0
 file test-cp437.zip test-cp437.zip test-cp437.zip
diff --git a/regress/stat_index_fileorder.test b/regress/stat_index_fileorder.test
index e9f065c..24b02b4 100644
--- a/regress/stat_index_fileorder.test
+++ b/regress/stat_index_fileorder.test
@@ -1,5 +1,4 @@
 # zip_open: entries ordered by central directory order
-program modify
 args fileorder.zzip stat 0 stat 1
 return 0
 file fileorder.zzip fileorder.zip fileorder.zip
diff --git a/regress/stat_index_streamed.test b/regress/stat_index_streamed.test
index bda69dc..a2b3048 100644
--- a/regress/stat_index_streamed.test
+++ b/regress/stat_index_streamed.test
@@ -1,5 +1,4 @@
 # stat file in streamed zip file
-program modify
 args streamed stat 0
 file streamed streamed.zip streamed.zip
 return 0
diff --git a/regress/stat_index_streamed_zip64.test b/regress/stat_index_streamed_zip64.test
index c9dca38..18a8694 100644
--- a/regress/stat_index_streamed_zip64.test
+++ b/regress/stat_index_streamed_zip64.test
@@ -1,5 +1,4 @@
 # stat file in streamed zip file
-program modify
 args streamed stat 0
 file streamed streamed-zip64.zip streamed-zip64.zip
 return 0
diff --git a/regress/stat_index_utf8_guess.test b/regress/stat_index_utf8_guess.test
index baabbce..898db2d 100644
--- a/regress/stat_index_utf8_guess.test
+++ b/regress/stat_index_utf8_guess.test
@@ -1,5 +1,4 @@
 # guess UTF-8 file names
-program modify
 args test-utf8.zip stat 0
 return 0
 file test-utf8.zip test-utf8.zip test-utf8.zip
diff --git a/regress/stat_index_utf8_raw.test b/regress/stat_index_utf8_raw.test
index 95fd85e..881a18b 100644
--- a/regress/stat_index_utf8_raw.test
+++ b/regress/stat_index_utf8_raw.test
@@ -1,5 +1,4 @@
 # print UTF-8 file names
-program modify
 args -r test-utf8.zip stat 0
 return 0
 file test-utf8.zip test-utf8.zip test-utf8.zip
diff --git a/regress/stat_index_utf8_strict.test b/regress/stat_index_utf8_strict.test
index f1c5558..af1bb81 100644
--- a/regress/stat_index_utf8_strict.test
+++ b/regress/stat_index_utf8_strict.test
@@ -1,6 +1,5 @@
 # follow strict rules and convert UTF-8 as if it was CP437, but not
 # if the files are marked as having UTF-8 names
-program modify
 args -s test-utf8.zip stat 0
 return 0
 file test-utf8.zip test-utf8.zip test-utf8.zip
diff --git a/regress/stat_index_utf8_unmarked_strict.test b/regress/stat_index_utf8_unmarked_strict.test
index 341e64e..07954dd 100644
--- a/regress/stat_index_utf8_unmarked_strict.test
+++ b/regress/stat_index_utf8_unmarked_strict.test
@@ -1,6 +1,5 @@
 # follow strict rules and convert UTF-8 as if it was CP437,
 # if not marked otherwise (in this case: not marked)
-program modify
 args -s test-utf8-unmarked.zip stat 0
 return 0
 file test-utf8-unmarked.zip test-utf8-unmarked.zip test-utf8-unmarked.zip
diff --git a/regress/stat_index_zip64.test b/regress/stat_index_zip64.test
index 14ff74b..c3f420c 100644
--- a/regress/stat_index_zip64.test
+++ b/regress/stat_index_zip64.test
@@ -1,5 +1,4 @@
 # stat file in zip64 zip file
-program modify
 args bigzero stat 0
 file bigzero bigzero.zip bigzero.zip
 return 0
diff --git a/regress/utf-8-standardization.test b/regress/utf-8-standardization.test
index 3f89dbb..6e259a4 100644
--- a/regress/utf-8-standardization.test
+++ b/regress/utf-8-standardization.test
@@ -1,5 +1,4 @@
 # replace file contents and make UTF-8 name
-program modify
 return 0
 args testfile replace_file_contents 0 "Some new content for the file." set_file_mtime 0 1406885162
 file testfile utf-8-standardization-input.zip utf-8-standardization-output.zip
diff --git a/regress/zip-in-archive-comment.test b/regress/zip-in-archive-comment.test
index 7addf7d..4838a8f 100644
--- a/regress/zip-in-archive-comment.test
+++ b/regress/zip-in-archive-comment.test
@@ -1,5 +1,4 @@
 # stat file in zip that contains archive comment to find out if it detected the right one of the two
-program modify
 args zip-in-archive-comment.zip stat 0
 file zip-in-archive-comment.zip zip-in-archive-comment.zip zip-in-archive-comment.zip
 return 0
diff --git a/regress/zip64_creation.test b/regress/zip64_creation.test
index dfc90b6..f216088 100644
--- a/regress/zip64_creation.test
+++ b/regress/zip64_creation.test
@@ -1,5 +1,4 @@
 # create big zip64 zip file from scratch
-program modify
 args bigzero.zip  add_nul bigzero 4294967296
 file-new bigzero.zip bigzero.zip
 return 0
diff --git a/regress/zip64_stored_creation.test b/regress/zip64_stored_creation.test
index d554635..92ee02c 100644
--- a/regress/zip64_stored_creation.test
+++ b/regress/zip64_stored_creation.test
@@ -1,5 +1,4 @@
 # create big zip64 zip file from scratch
-program modify
 args -H bigstored.zh  add_nul bigzero 4294967296  set_file_compression 0 0 0  set_file_mtime 0 0  add_nul smallzero 16384  set_file_compression 1 0 0  set_file_mtime 1 0
 file-new bigstored.zh bigstored.zh
 return 0
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8906e81..48ddf3f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,3 +13,11 @@
 ADD_EXECUTABLE(zipmerge zipmerge.c ${SRC_EXTRA_FILES})
 TARGET_LINK_LIBRARIES(zipmerge zip)
 INSTALL(TARGETS zipmerge RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(ziptool ziptool.c ${SRC_EXTRA_FILES} source_hole.c)
+TARGET_LINK_LIBRARIES(ziptool zip)
+INSTALL(TARGETS ziptool RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(hole hole.c ${SRC_EXTRA_FILES} source_hole.c)
+TARGET_LINK_LIBRARIES(hole zip)
+# do not install hole, just a test helper
diff --git a/src/Makefile.am b/src/Makefile.am
index 16ade70..cd6f5e7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,10 @@
-bin_PROGRAMS=zipcmp zipmerge
+bin_PROGRAMS=zipcmp zipmerge ziptool
+noinst_PROGRAMS=hole
 
-zipcmp_SOURCES=zipcmp.c
+hole_SOURCES= hole.c source_hole.c
+ziptool_SOURCES= source_hole.c ziptool.c
 
-zipcmp_CPPFLAGS=-I${top_srcdir}/lib -I../lib
-zipcmp_LDADD=${top_builddir}/lib/libzip.la
-zipmerge_CPPFLAGS=-I${top_srcdir}/lib -I../lib
-zipmerge_LDADD=${top_builddir}/lib/libzip.la
+AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
+LDADD=${top_builddir}/lib/libzip.la
 
 EXTRA_DIST=	CMakeLists.txt getopt.c getopt.h compat.h
diff --git a/regress/hole.c b/src/hole.c
similarity index 100%
rename from regress/hole.c
rename to src/hole.c
diff --git a/regress/source_hole.c b/src/source_hole.c
similarity index 95%
rename from regress/source_hole.c
rename to src/source_hole.c
index 31da226..d2f1a16 100644
--- a/regress/source_hole.c
+++ b/src/source_hole.c
@@ -1,10 +1,10 @@
 /*
- modify_hole.c -- source for handling huge files that are mostly NULs
+ source_hole.c -- source for handling huge files that are mostly NULs
  Copyright (C) 2014-2016 Dieter Baron and Thomas Klausner
- 
+
  This file is part of libzip, a library to manipulate ZIP archives.
  The authors can be contacted at <libzip@nih.at>
- 
+
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:
@@ -17,7 +17,7 @@
  3. The names of the authors may not be used to endorse or promote
  products derived from this software without specific prior
  written permission.
- 
+
  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -94,7 +94,7 @@
 zip_source_t *source_hole_create(const char *fname, int flags, zip_error_t *error)
 {
     hole_t *ud = hole_new(fname, flags, error);
-    
+
     if (ud == NULL) {
         return NULL;
     }
@@ -106,11 +106,11 @@
 buffer_free(buffer_t *buffer)
 {
     zip_uint64_t i;
-    
+
     if (buffer == NULL) {
         return;
     }
-    
+
     if (buffer->fragment) {
         for (i=0; i<buffer->nfragments; i++) {
             free(buffer->fragment[i]);
@@ -126,13 +126,13 @@
 {
     buffer_t *buffer;
     FILE *f;
-    
+
     if ((buffer = buffer_new()) == NULL) {
         zip_error_set(error, ZIP_ER_MEMORY, 0);
         return NULL;
-        
+
     }
-    
+
     if ((flags & ZIP_TRUNCATE) == 0) {
         if ((f = fopen(fname, "rb")) == NULL) {
             if (!(errno == ENOENT && (flags & ZIP_CREATE))) {
@@ -149,7 +149,7 @@
 	    fclose(f);
         }
     }
-    
+
     return buffer;
 }
 
@@ -158,17 +158,17 @@
 buffer_new(void)
 {
     buffer_t *buffer;
-    
+
     if ((buffer = (buffer_t *)malloc(sizeof(*buffer))) == NULL) {
         return NULL;
     }
-    
+
     buffer->fragment = NULL;
     buffer->nfragments = 0;
     buffer->fragment_size = FRAGMENT_SIZE;
     buffer->size = 0;
     buffer->offset = 0;
-    
+
     return buffer;
 }
 
@@ -177,34 +177,34 @@
 buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error)
 {
     zip_uint64_t n, i, fragment_offset;
-    
+
     length = MY_MIN(length, buffer->size - buffer->offset);
-    
+
     if (length == 0) {
         return 0;
     }
     if (length > ZIP_INT64_MAX) {
         return -1;
     }
-    
+
     i = buffer->offset / buffer->fragment_size;
     fragment_offset = buffer->offset % buffer->fragment_size;
     n = 0;
     while (n < length) {
         zip_uint64_t left = MY_MIN(length - n, buffer->fragment_size - fragment_offset);
-        
+
         if (buffer->fragment[i]) {
             memcpy(data + n, buffer->fragment[i] + fragment_offset, left);
         }
         else {
             memset(data + n, 0, left);
         }
-        
+
         n += left;
         i++;
         fragment_offset = 0;
     }
-    
+
     buffer->offset += n;
     return (zip_int64_t)n;
 }
@@ -215,7 +215,7 @@
 {
     zip_uint8_t b[20];
     zip_uint64_t i;
-    
+
     if (fread(b, 20, 1, f) != 1) {
         zip_error_set(error, ZIP_ER_READ, errno);
         return -1;
@@ -225,7 +225,7 @@
         zip_error_set(error, ZIP_ER_READ, EFTYPE);
         return -1;
     }
-    
+
     buffer->fragment_size = get_u64(b+4);
     buffer->size = get_u64(b+12);
 
@@ -239,18 +239,18 @@
         zip_error_set(error, ZIP_ER_MEMORY, 0);
         return -1;
     }
-    
+
     for (i = 0; i < buffer->nfragments; i++) {
         buffer->fragment[i] = NULL;
     }
-    
+
     i = 0;
     while (i < buffer->nfragments) {
         if (fread(b, 4, 1, f) != 1) {
             zip_error_set(error, ZIP_ER_READ, errno);
             return -1;
         }
-        
+
         if (memcmp(b, MARK_DATA, 4) == 0) {
             if (buffer->fragment_size > SIZE_MAX) {
                 zip_error_set(error, ZIP_ER_MEMORY, 0);
@@ -278,7 +278,7 @@
             return -1;
         }
     }
-    
+
     return 0;
 }
 
@@ -290,7 +290,7 @@
     if (new_offset < 0) {
         return -1;
     }
-    
+
     buffer->offset = (zip_uint64_t)new_offset;
     return 0;
 }
@@ -302,16 +302,16 @@
     FILE *f = fopen(fname, "wb");
     zip_uint64_t i;
     zip_uint64_t nul_run;
-    
+
     if (f == NULL) {
         zip_error_set(error, ZIP_ER_OPEN, errno);
         return -1;
     }
-    
+
     fwrite(MARK_BEGIN, 4, 1, f);
     write_u64(buffer->fragment_size, f);
     write_u64(buffer->size, f);
-    
+
     nul_run = 0;
     for (i=0; i * buffer->fragment_size <buffer->size; i++) {
         if (buffer->fragment[i] == NULL || only_nul(buffer->fragment[i], buffer->fragment_size)) {
@@ -323,15 +323,15 @@
                 nul_run = 0;
             }
             fwrite(MARK_DATA, 4, 1, f);
-            
+
             fwrite(buffer->fragment[i], 1, buffer->fragment_size, f);
         }
     }
-    
+
     if (nul_run > 0) {
         write_nuls(nul_run, f);
     }
-    
+
     if (fclose(f) != 0) {
         zip_error_set(error, ZIP_ER_WRITE, errno);
         return -1;
@@ -349,39 +349,39 @@
         zip_uint64_t needed_fragments = (buffer->offset + length + buffer->fragment_size - 1) / buffer->fragment_size;
         zip_uint64_t new_capacity = buffer->nfragments;
         zip_uint64_t i;
-        
+
         if (new_capacity == 0) {
             new_capacity = 4;
         }
         while (new_capacity < needed_fragments) {
             new_capacity *= 2;
         }
-        
+
         fragment = realloc(buffer->fragment, new_capacity * sizeof(*fragment));
-        
+
         if (fragment == NULL) {
             zip_error_set(error, ZIP_ER_MEMORY, 0);
             return -1;
         }
-        
+
         for (i = buffer->nfragments; i < new_capacity; i++) {
             fragment[i] = NULL;
         }
-        
+
         buffer->fragment = fragment;
         buffer->nfragments = new_capacity;
     }
 
     if (!only_nul(data, length)) {
         zip_uint64_t idx, n, fragment_offset;
-        
+
         idx = buffer->offset / buffer->fragment_size;
         fragment_offset = buffer->offset % buffer->fragment_size;
         n = 0;
-        
+
         while (n < length) {
             zip_uint64_t left = MY_MIN(length - n, buffer->fragment_size - fragment_offset);
-            
+
             if (buffer->fragment[idx] == NULL) {
                 if ((buffer->fragment[idx] = (zip_uint8_t *)malloc(buffer->fragment_size)) == NULL) {
                     zip_error_set(error, ZIP_ER_MEMORY, 0);
@@ -390,18 +390,18 @@
                 memset(buffer->fragment[idx], 0, buffer->fragment_size);
             }
             memcpy(buffer->fragment[idx] + fragment_offset, data + n, left);
-            
+
             n += left;
             idx++;
             fragment_offset = 0;
         }
     }
-    
+
     buffer->offset += length;
     if (buffer->offset > buffer->size) {
         buffer->size = buffer->offset;
     }
-    
+
     return (zip_int64_t)length;
 }
 
@@ -410,9 +410,9 @@
 get_u64(const zip_uint8_t *b)
 {
     zip_uint64_t i;
-    
+
     i = (zip_uint64_t)b[0] << 56 | (zip_uint64_t)b[1] << 48 | (zip_uint64_t)b[2] << 40 | (zip_uint64_t)b[3] << 32 | (zip_uint64_t)b[4] << 24 | (zip_uint64_t)b[5] << 16 | (zip_uint64_t)b[6] << 8 | (zip_uint64_t)b[7];
-    
+
     return i;
 }
 
@@ -421,13 +421,13 @@
 only_nul(const zip_uint8_t *data, zip_uint64_t length)
 {
     zip_uint64_t i;
-    
+
     for (i=0; i< length; i++) {
         if (data[i] != '\0') {
             return 0;
         }
     }
-    
+
     return 1;
 }
 
@@ -446,7 +446,7 @@
 write_u64(zip_uint64_t u64, FILE *f)
 {
     zip_uint8_t b[8];
-    
+
     b[0] = (zip_uint8_t)((u64 >> 56) & 0xff);
     b[1] = (zip_uint8_t)((u64 >> 48) & 0xff);
     b[2] = (zip_uint8_t)((u64 >> 40) & 0xff);
@@ -455,7 +455,7 @@
     b[5] = (zip_uint8_t)((u64 >> 16) & 0xff);
     b[6] = (zip_uint8_t)((u64 >> 8) & 0xff);
     b[7] = (zip_uint8_t)(u64 & 0xff);
-    
+
     return fwrite(b, 8, 1, f) == 1 ? 0 : -1;
 }
 
@@ -477,7 +477,7 @@
 hole_new(const char *fname, int flags, zip_error_t *error)
 {
     hole_t *ctx = (hole_t *)malloc(sizeof(*ctx));
-    
+
     if (ctx == NULL) {
         zip_error_set(error, ZIP_ER_MEMORY, 0);
         return NULL;
@@ -488,15 +488,15 @@
         zip_error_set(error, ZIP_ER_MEMORY, 0);
         return NULL;
     }
-    
+
     if ((ctx->in = buffer_from_file(fname, flags, error)) == NULL) {
         free(ctx);
         return NULL;
     }
-    
+
     zip_error_init(&ctx->error);
     ctx->out = NULL;
-    
+
     return ctx;
 }
 
@@ -505,15 +505,15 @@
 source_hole_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command)
 {
     hole_t *ctx = (hole_t *)ud;
-    
+
     switch (command) {
         case ZIP_SOURCE_BEGIN_WRITE:
             ctx->out = buffer_new();
             return 0;
-            
+
         case ZIP_SOURCE_CLOSE:
             return 0;
-            
+
         case ZIP_SOURCE_COMMIT_WRITE:
             if (buffer_to_file(ctx->out, ctx->fname, &ctx->error) < 0) {
                 return -1;
@@ -522,18 +522,18 @@
             ctx->in = ctx->out;
             ctx->out = NULL;
             return 0;
-            
+
         case ZIP_SOURCE_ERROR:
             return zip_error_to_data(&ctx->error, data, length);
-            
+
         case ZIP_SOURCE_FREE:
             hole_free(ctx);
             return 0;
-            
+
         case ZIP_SOURCE_OPEN:
             ctx->in->offset = 0;
             return 0;
-           
+
         case ZIP_SOURCE_READ:
             return buffer_read(ctx->in, data, length, &ctx->error);
 
@@ -544,44 +544,44 @@
             ctx->out = NULL;
             (void)remove(ctx->fname);
             return 0;
-            
+
         case ZIP_SOURCE_ROLLBACK_WRITE:
             buffer_free(ctx->out);
             ctx->out = NULL;
             return 0;
-            
+
         case ZIP_SOURCE_SEEK:
             return buffer_seek(ctx->in, data, length, &ctx->error);
-            
+
         case ZIP_SOURCE_SEEK_WRITE:
             return buffer_seek(ctx->out, data, length, &ctx->error);
-        
+
         case ZIP_SOURCE_STAT: {
             zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
-            
+
             if (st == NULL) {
                 return -1;
             }
-            
+
             /* TODO: return ENOENT if fname doesn't exist */
-            
+
             st->valid |= ZIP_STAT_SIZE;
             st->size = ctx->in->size;
             return 0;
         }
-            
+
         case ZIP_SOURCE_TELL:
             return (zip_int64_t)ctx->in->offset;
 
         case ZIP_SOURCE_TELL_WRITE:
             return (zip_int64_t)ctx->out->offset;
-            
+
         case ZIP_SOURCE_WRITE:
             return buffer_write(ctx->out, data, length, &ctx->error);
-            
+
         case ZIP_SOURCE_SUPPORTS:
             return zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_STAT, ZIP_SOURCE_TELL, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
-            
+
         default:
             zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
             return -1;
diff --git a/regress/modify.c b/src/ziptool.c
similarity index 98%
rename from regress/modify.c
rename to src/ziptool.c
index 9c4c9b2..f647adc 100644
--- a/regress/modify.c
+++ b/src/ziptool.c
@@ -1,5 +1,5 @@
 /*
-  modify.c -- test tool for modifying zip archive in multiple ways
+  ziptool.c -- tool for modifying zip archive in multiple ways
   Copyright (C) 2012-2016 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -17,7 +17,7 @@
   3. The names of the authors may not be used to endorse or promote
      products derived from this software without specific prior
      written permission.
- 
+
   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -167,12 +167,12 @@
 add_nul(int argc, char *argv[]) {
     zip_source_t *zs;
     zip_uint64_t length = strtoull(argv[1], NULL, 10);
-    
+
     if ((zs=source_nul(za, length)) == NULL) {
         fprintf(stderr, "can't create zip_source for length: %s\n", zip_strerror(za));
         return -1;
     }
-    
+
     if (zip_add(za, argv[0], zs) == -1) {
         zip_source_free(zs);
         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
@@ -213,7 +213,7 @@
     }
     if ((err = zip_fclose(zf)) != 0) {
 	zip_error_t error;
-	
+
 	zip_error_init_with_code(&error, err);
 	fprintf(stderr, "can't close file at index '%" PRIu64 "': %s\n", idx, zip_error_strerror(&error));
 	return -1;
@@ -394,7 +394,7 @@
 	fprintf(stderr, "can't find entry with name '%s' using flags '%s'\n", argv[0], argv[1]);
     } else {
 	printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
-    }	
+    }
 
     return 0;
 }
@@ -611,11 +611,11 @@
 	return;
 
     printf("0x");
-	
+
     for (i=0; i<len; i++)
 	printf("%02x", data[i]);
 
-    return;    
+    return;
 }
 
 
@@ -625,16 +625,16 @@
     zip_error_t error;
     zip_source_t *src = NULL;
     zip_t *zs = NULL;
-    
+
     zip_error_init(&error);
-    
+
     if ((src = source_hole_create(archive, flags, &error)) == NULL
         || (zs = zip_open_from_source(src, flags, &error)) == NULL) {
         zip_source_free(src);
         *err = zip_error_code_zip(&error);
         errno = zip_error_code_system(&error);
     }
-    
+
     return zs;
 }
 
@@ -708,22 +708,22 @@
 source_nul_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command)
 {
     source_nul_t *ctx = (source_nul_t *)ud;
-    
+
     switch (command) {
         case ZIP_SOURCE_CLOSE:
             return 0;
 
         case ZIP_SOURCE_ERROR:
             return zip_error_to_data(&ctx->error, data, length);
-            
+
         case ZIP_SOURCE_FREE:
             free(ctx);
             return 0;
-            
+
         case ZIP_SOURCE_OPEN:
             ctx->offset = 0;
             return 0;
-            
+
         case ZIP_SOURCE_READ:
 	    if (length > ZIP_INT64_MAX) {
 		zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
@@ -733,24 +733,24 @@
             if (length > ctx->length - ctx->offset) {
                 length =ctx->length - ctx->offset;
             }
-            
+
             memset(data, 0, length);
             ctx->offset += length;
             return (zip_int64_t)length;
-            
+
         case ZIP_SOURCE_STAT: {
             zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
-            
+
             if (st == NULL) {
                 return -1;
             }
-            
+
             st->valid |= ZIP_STAT_SIZE;
             st->size = ctx->length;
-            
+
             return 0;
         }
-            
+
         case ZIP_SOURCE_SUPPORTS:
             return zip_source_make_command_bitmap(ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_STAT, -1);
 
@@ -765,21 +765,21 @@
 {
     source_nul_t *ctx;
     zip_source_t *src;
-    
+
     if ((ctx = (source_nul_t *)malloc(sizeof(*ctx))) == NULL) {
         zip_error_set(zip_get_error(zs), ZIP_ER_MEMORY, 0);
         return NULL;
     }
-    
+
     zip_error_init(&ctx->error);
     ctx->length = length;
     ctx->offset = 0;
-    
+
     if ((src = zip_source_function(zs, source_nul_cb, ctx)) == NULL) {
         free(ctx);
         return NULL;
     }
-    
+
     return src;
 }
 
@@ -971,7 +971,7 @@
 	    usage(prg);
 	}
     }
-    
+
     arg = optind;
 
     archive = argv[arg++];
@@ -983,11 +983,11 @@
         case SOURCE_TYPE_NONE:
             za = zip_open(archive, flags, &err);
             break;
-            
+
         case SOURCE_TYPE_IN_MEMORY:
             za = read_to_memory(archive, flags, &err, &memory_src);
             break;
-            
+
         case SOURCE_TYPE_HOLE: {
             za = read_hole(archive, flags, &err);
             break;