##
# Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Universal Permissive License v 1.0 as shown at
# http://oss.oracle.com/licenses/upl.
#
# Update this (and the VERSION_HISTORY in version.h) when the version changes.
VERSION = 1.58

#SAN = -fsanitize=thread
#SAN = -fsanitize=address
#GCOV_FLAGS = -fprofile-arcs -ftest-coverage
CFLAGS=-Wall -Wextra -Wshadow -Wconversion -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -Wswitch-enum -Werror -g -O1 -pthread -std=gnu99 $(SAN) $(GCOV_FLAGS)
CPPFLAGS=-DVERSION=\"$(VERSION)\"
#CPPFLAGS+=-DSPEW_ENABLED

# It turns out that we need -lrt (probably because of clock_gettime()
# or else we'll end up with a dependency on glibc 2.17, which often is
# too advanced (e.g., on Oracle Linux 6.9)
LDLIBS=-lrt
LDFLAGS=-pthread $(SAN) $(GCOV_FLAGS)

VALGRIND = valgrind --leak-check=full --error-exitcode=3 -q

PARTAR = partar

default: $(PARTAR) parcp parrm $(PARTAR).1.gz parcp.1.gz parrm.1.gz

# Don't delete intermediate files (specify .SECONDARY with no dependencies)
.SECONDARY:

rpms: ../fss-parallel-tools-$(VERSION).tar.gz fss-parallel-tools.spec
	mkdir -p ~/rpmbuild/SOURCES
	mkdir -p ~/rpmbuild/SPECS
	cp ../fss-parallel-tools-$(VERSION).tar.gz ~/rpmbuild/SOURCES
	cp fss-parallel-tools.spec ~/rpmbuild/SPECS
	cd ~/rpmbuild/SPECS;rpmbuild -ba fss-parallel-tools.spec
fss-parallel-tools.spec: fss-parallel-tools.spec.in Makefile
	sed s/VERSION_DEFINED_IN_MAKEFILE/$(VERSION)/ < $< > $@
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
SOURCES = gitrev.c $(wildcard *.c *.h common/*.c common/*.h) Makefile LICENSE

../fss-parallel-tools-$(VERSION).tar.gz: $(SOURCES)
	tar czf $@ --transform 's,^,fss-parallel-tools/,' $(SOURCES)
tarball: ../fss-parallel-tools-$(VERSION).tar.gz

todo.o: todo.h 
dircache.o: dircache.h malloc.h
htable.o: htable.h malloc.h
fwg.o: malloc.h fwg.h hashfun.h
stringset.o: stringset.h hashfun.h malloc.h

####################
# COMMON
####################
# sources
common/assert.o: common/assert.h common/backtrace.h common/spew.h version.h
COMMON_O += common/assert.o 
COMMON_O += common/backtrace.o
common/backtrace.o: common/spew.h common/simplest-defs.h common/sparse.h common/backtrace.h
COMMON_O += common/debug-tag.o
common/debug-tag.o: common/debug-tag.h common/simplest-defs.h common/sparse.h
COMMON_O += common/error.o
common/error.o: common/error.h common/simplest-defs.h common/sparse.h
COMMON_O += common/spew.o
common/spew.o: common/spew.h common/backtrace.h common/simplest-defs.h common/tsan.h version.h common/debug-tag.h common/sparse.h common/error.h 

fdleak.o: fdleak.h
putils.o: putils.h common/spew.h
parcp.o:  putils.h malloc.h version.h sched.h vector-of-strings.h common/spew.h
$(PARTAR).o: putils.h malloc.h version.h sched.h vector-of-strings.h inodemap.h stringset.h vec.h fdleak.h fwg.h common/spew.h
parrm.o:  putils.h malloc.h version.h sched.h vector-of-strings.h common/spew.h
vector-of-strings.o: vector-of-strings.h malloc.h
$(PARTAR): $(PARTAR).o putils.o sched.o vector-of-strings.o gitrev.o fwg.o stringset.o vec.o fdleak.o inodemap.o $(COMMON_O)
inodemap.o: inodemap.h malloc.h
parcp:  parcp.o  putils.o sched.o vector-of-strings.o gitrev.o inodemap.o todo.o $(COMMON_O)
parrm: parrm.o putils.o gitrev.o sched.o vector-of-strings.o $(COMMON_O)

.DELETE_ON_ERROR:

.SUFFIX: .1.gz
%.1.gz: %
	PATH=$$PATH:. help2man -N --name="parallel implementation of $(subst par,,$<)" $< | gzip - > $@

gitrev.o: gitrev.c
# Don't create a new gitrev.c file if nothing changed.  We don't want
# to rerun all the tests just because I modified the Makefile.
gitrev.c: $(filter-out gitrev.c,$(wildcard *.c *.h common/*.c common/*.h Makefile))
	echo \#include \"version.h\" > gitrev_new.c
	echo const char gitrev[] = \"$$(git diff-index --quiet HEAD -- || echo "++")$$(git rev-parse HEAD)\"\; >> gitrev_new.c
	if ! diff gitrev_new.c gitrev.c >> /dev/null ; then mv gitrev_new.c gitrev.c; fi
setup: $(PARTAR)
	rm -rf tar-1.30

# Tests

.PHONY: check
TESTS =  fifo tar-1.30 linux-4.15 longfilename3
TESTS_T = $(patsubst %, %.test, $(TESTS))
check: parrm_eaccess_test.test
check: parrm_eaccess_test2.test
check: parrm_eaccess_test3.test
check: parrm_eaccess_test4.test 
check: excld.test
check: excld-equal.test
check: error.test
check: linklink.test
check: permissions.test
check: $(TESTS_T) test_fib.run
check: decompression-errors.test
check: parcp0.test parcp1.test parcp2.test parcp3.test parcp4.test parcp5.test parcp6.test parcp7.test
.SUFFIX: .test
%.test: %.partar_test %.parcp_test %.parrm_test %.tar_test
	touch $@

.SUFFIX: .partar_test
%.partar_test: %.partar1_diff %.partar2_diff %.partar3_diff %.partar4_diff partar1.test
	touch $@

test_fib.run: test_fib
	./test_fib
	touch $@
	
$(info VAR="$(COMMON_O)")
	
test_fib: test_fib.o sched.o $(COMMON_O) gitrev.o
test_fib.o: malloc.h sched.h common/spew.h
sched.o: malloc.h sched.h common/spew.h

# 

# These tests run partar in various ways and compare the output to what happened with tar.
# partar_diff1_test runs "partar pxf %.tar"
# partar_diff2_test runs "partar pxf - < %.tar"
# partar_diff3_test runs "partar pxzf %.tar.gz"
# partar_diff4_test runs "partar pxzf - < %.tar.gz"

# partar_test has things that are fifos in it, and we want to ignore
# diff error messages about those.  That's what the grep is about in
# the diff line.

output-with-tar output-with-partar1 output-with-partar2 output-with-partar3 output-with-partar4 uncompressed-tars gzipped-tars:
	mkdir -p $@

uncompressed-tars/%.tar: test-tars/%.tar.xz | uncompressed-tars
	xzcat $< > $@

gzipped-tars/%.tar.gz: uncompressed-tars/%.tar | gzipped-tars
	gzip < $< > $@

%.tar_done:                        uncompressed-tars/%.tar | output-with-tar
	rm -rf          $(patsubst uncompressed-tars/%.tar,output-with-tar/%, $<)
	tar xf $< -C output-with-tar
	touch $@

# partar1 tests makes sure that partar works with a named file
%.partar1_done:                    uncompressed-tars/%.tar $(PARTAR) | output-with-partar1 
	rm -rf          $(patsubst uncompressed-tars/%.tar,output-with-partar1/%, $<)
	$(VALGRIND) ./$(PARTAR) xf     $< -C output-with-partar1
	touch $@

%.partar1_diff: %.tar_done %.partar1_done
	diff --no-dereference -r $(patsubst %.tar_done,output-with-tar/%,$<) $(patsubst %.tar_done,output-with-partar1/%,$<) | (! grep -v "is a fifo.*is a fifo")
	touch $@

# partar2 tests make sure that partar works from stdin
%.partar2_done:                             uncompressed-tars/%.tar  $(PARTAR) | output-with-partar2
	rm -rf                   $(patsubst uncompressed-tars/%.tar,output-with-partar2/%, $<)
	$(VALGRIND) ./$(PARTAR) xf - -C output-with-partar2 < $<
	touch $@

%.partar2_diff: %.tar_done %.partar2_done
	diff --no-dereference -r $(patsubst %.tar_done,output-with-tar/%,$<) $(patsubst %.tar_done,output-with-partar2/%,$<) | (! grep -v "is a fifo.*is a fifo")
	touch $@

# partar3 tests make sure that partar works with a gzipped named file.
.SUFFIX: .partar3_done
%.partar3_done:                         gzipped-tars/%.tar.gz $(PARTAR) | output-with-partar3
	rm -rf               $(patsubst gzipped-tars/%.tar.gz,output-with-partar3/%, $<)
	$(VALGRIND) ./$(PARTAR) xzf $< -C output-with-partar3
	touch $@

.SUFFIX: .partar3_diff
%.partar3_diff: %.tar_done %.partar3_done
	diff --no-dereference -r $(patsubst %.tar_done,output-with-tar/%,$<) $(patsubst %.tar_done,output-with-partar3/%,$<) | (! grep -v "is a fifo.*is a fifo")
	touch $@

# partar4 tests make sure that partar works with a gzipped stdin
%.partar4_done:                             gzipped-tars/%.tar.gz $(PARTAR) | output-with-partar4
	rm -rf                   $(patsubst gzipped-tars/%.tar.gz,output-with-partar4/%, $<)
	$(VALGRIND) ./$(PARTAR) xzf - -C output-with-partar4 < $<
	touch $@

%.partar4_diff: %.tar_done %.partar4_done
	diff --no-dereference -r $(patsubst %.tar_done,output-with-tar/%,$<) $(patsubst %.tar_done,output-with-partar4/%,$<) | (! grep -v "is a fifo.*is a fifo")
	touch $@

# Test untarring onto a read-only file
partar1.test: $(PARTAR) | partar1.test.dir
	rm -rf $@.dir/src $@.dir/dst
	mkdir -p $@.dir/src $@.dir/dst
	touch $@.dir/dst/readonly
	chmod guo-w $@.dir/dst/readonly
	echo contents > $@.dir/src/readonly
	chmod guo-w $@.dir/src/readonly
	set -e; set -o pipefail; $(VALGRIND) ./$(PARTAR) c -C $@.dir/src readonly|$(VALGRIND) ./$(PARTAR) x -C $@.dir/dst
	diff -r $@.dir/{src,dst}
	touch $@

output-with-parcp output-to-rm output-of-partarred:
	mkdir -p $@

.SUFFIX: .parcp_test
# Turn off valgrind for the big linux parcp
linux-4.15.parcp_test: VALGRIND=
%.parcp_test: parcp %.tar_done | output-with-parcp
	rm -rf $(patsubst %.parcp_test,output-with-parcp/%, $@)
	$(VALGRIND) ./parcp -P 3 $(patsubst %.parcp_test,output-with-tar/%, $@) $(patsubst %.parcp_test,output-with-parcp/%, $@)
	diff --no-dereference -r $(patsubst %.parcp_test,output-with-tar/%, $@) $(patsubst %.parcp_test,output-with-parcp/%, $@)  | (! grep -v "is a fifo.*is a fifo")
	touch $@

.SUFFIX: .parrm_test
%.parrm_test: uncompressed-tars/%.tar parrm $(PARTAR) | output-to-rm
	$(VALGRIND) ./$(PARTAR) xf - -C output-to-rm < $<
	test -d $(patsubst %.parrm_test,output-to-rm/%,$@)
	$(VALGRIND) ./parrm -P 3 $(patsubst %.parrm_test,output-to-rm/%,$@)
	test ! -d $(patsubst %.parrm_test,output-to-rm/%,$@)
	touch $@

# This test only works on a few cases, since the order of the files isn't unique
.SUFFIX: .tardiff_test
%.tardiff_test: %.tar_out %.partar_out
	diff $^
	touch $@

.SUFFIX: .tar_test
%.tar_test: %.partar_out %.tar_done | output-of-partarred
	rm -rf $(patsubst %.partar_out,output-of-partarred/%,$<)
	$(VALGRIND) ./$(PARTAR) xf $< -C output-of-partarred
	diff --no-dereference -r $(patsubst %.tar_test,output-with-tar/%,$@) $(patsubst %.tar_test,output-of-partarred/%,$@) | (! grep -v "is a fifo.*is a fifo")
	$(EXTRA_TEST)

# These two tests check to see what happens if parrm runs into a
# directory that it hasn't got permission to access.  The whole
# directory, or a subdirectory, respectively.

ED = parrm_eaccess_test_dir
parrm_eaccess_test.test: parrm
	-chmod u+rwx $(ED) > /dev/null 2>&1 ;true
	rm -rf $(ED)
	mkdir $(ED)
	touch $(ED)/foo
	chmod ugo-rwx $(ED)
	rm -rf $(ED) > parrm_eaccess_test.out 2>&1 ; test $$? = 1
	fgrep Permission parrm_eaccess_test.out > /dev/null
	./parrm $(ED) > parrm_eaccess_test.out 2>&1 ; test $$? = 1
	fgrep Permission parrm_eaccess_test.out > /dev/null
	touch $@

ED2 = parrm_eaccess_test2_dir
parrm_eaccess_test2.test: parrm
	chmod -R u+rwx $(ED2)  > /dev/null 2>&1 ;true
	rm -rf $(ED2)
	mkdir -p $(ED2)/$(ED2)
	touch $(ED2)/$(ED2)/foo
	touch $(ED2)/foo
	chmod ugo-rwx $(ED2)/$(ED2)
	./parrm $(ED2) > $(ED2).out 2>&1 ; test $$? = 1
	chmod -R u+rwx $(ED2)  > /dev/null 2>&1 ;true
	test ! -f $(ED2)/foo
	test -f $(ED2)/$(ED2)/foo
	touch $@

# And these are the same tests where we can access the dir but we cannot unlink it.
ED3 = parrm_eaccess_test3_dir
parrm_eaccess_test3.test: parrm
	-chmod u+rwx $(ED3) > /dev/null 2>&1 ;true
	rm -rf $(ED3)
	mkdir $(ED3)
	touch $(ED3)/foo
	chmod ugo-w $(ED3)
	ls -la $(ED3)
	rm -rf $(ED3) > $(ED3).rm.out 2>&1 ; test $$? = 1
	fgrep Permission $(ED3).rm.out > /dev/null
	./parrm $(ED3) > $(ED3).parrm.out 2>&1 ; test $$? = 1
	fgrep Permission $(ED3).parrm.out > /dev/null
	touch $@

ED4 = parrm_eaccess_test4_dir
parrm_eaccess_test4.test: parrm
	chmod -R u+rwx $(ED4)  > /dev/null 2>&1 ;true
	rm -rf $(ED4)
	mkdir -p $(ED4)/$(ED4)
	touch $(ED4)/$(ED4)/foo
	touch $(ED4)/foo
	chmod ugo-rx $(ED4)/$(ED4)
	./parrm $(ED4) > $(ED4).out 2>&1 ; test $$? = 1
	chmod -R u+rwx $(ED4)  > /dev/null 2>&1 ;true
	test ! -f $(ED4)/foo
	test -f $(ED4)/$(ED4)/foo
	touch $@

# Some tarballs have extra requirements that are hard to capture with diff.  We'd really like to verify that the topology of two directories is the same, but I don't know how to get diff to really do it.  So we'll have an ad hoc test that these two files really are the same. 
longfilename3.tar_test: EXTRA_TEST = test output-of-partarred/longfilename3/aaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddeeeeeeeeeeeeeeffffffffffffffg -ef output-of-partarred/longfilename3/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 

.SUFFIX: .tar_out .partar_out
%.tar_out: %.tar_done
	tar cf $@ $(patsubst %.tar_done,output-with-tar/%,$<)
%.partar_out: %.tar_done $(PARTAR)
	(cd output-with-tar;../$(PARTAR) cf - $(patsubst %.tar_done,%,$<)) > $@

error.test: $(PARTAR)
	! ./$(PARTAR) xf - < /dev/null 2> error.test.out
	fgrep -q ": This does not look like a tar archive" error.test.out
	fgrep -q ": Exiting with failure status due to previous errors" error.test.out
	! ./$(PARTAR) xzf - < /dev/null 2> error.test.out
	fgrep -q "gzip: stdin: unexpected end of file" error.test.out
	fgrep -q ": Child returned status 1" error.test.out
	fgrep -q ": Exiting with failure status due to previous errors" error.test.out
	! ./$(PARTAR) cf error.tar output-with-tar/fifo no-such-file output-with-tar/fifo 2> error.test.out
	fgrep -q ": no-such-file: Cannot stat: No such file or directory" error.test.out
	fgrep -q ": Exiting with failure status due to previous errors" error.test.out
	touch $@

# Test excluding files both in "c" mode and in "x" mode.
excld.test: tar-1.30.tar_done $(PARTAR)
	rm -rf excld.dir.tar excld.dir.partar excld.odir.tar excld.odir.partar
	mkdir excld.dir.tar excld.dir.partar excld.odir.tar excld.odir.partar
	tar      cf - -C output-with-tar/ --exclude d\?c tar-1.30 | tar xf - -C excld.dir.tar
	$(VALGRIND) ./$(PARTAR) cf - -C output-with-tar/ --exclude d\?c tar-1.30 | tar xf - -C excld.dir.partar
	diff -r excld.dir.tar excld.dir.partar
	tar      c    -C output-with-tar/                tar-1.30 | tar x    -C excld.odir.tar --exclude d\?c
	diff -r excld.dir.tar excld.odir.tar
	tar      c    -C output-with-tar/                tar-1.30 | $(VALGRIND) ./$(PARTAR) x    -C excld.odir.partar --exclude d\?c
	diff -r excld.dir.tar excld.odir.partar
	touch $@

# Test excluding files both in "c" mode and in "x" mode same as above but uses --exclude=PATTERN.
excld-equal.test: tar-1.30.tar_done $(PARTAR)
	rm -rf excld-equal.dir.tar excld-equal.dir.partar excld-equal.odir.tar excld-equal.odir.partar
	mkdir excld-equal.dir.tar excld-equal.dir.partar excld-equal.odir.tar excld-equal.odir.partar
	tar      cf - -C output-with-tar/ --exclude=d\?c tar-1.30 | tar xf - -C excld-equal.dir.tar
	$(VALGRIND) ./$(PARTAR) cf - -C output-with-tar/ --exclude=d\?c tar-1.30 | tar xf - -C excld-equal.dir.partar
	diff -r excld-equal.dir.tar excld-equal.dir.partar
	tar      c    -C output-with-tar/                tar-1.30 | tar x    -C excld-equal.odir.tar --exclude d\?c
	diff -r excld-equal.dir.tar excld-equal.odir.tar
	tar      c    -C output-with-tar/                tar-1.30 | $(VALGRIND) ./$(PARTAR) x    -C excld-equal.odir.partar --exclude=d\?c
	diff -r excld-equal.dir.tar excld-equal.odir.partar
	touch $@
	
# Test the same file appearing twice on the command line.   tar makes a hardlink from the file to itself.
linklink.test: $(PARTAR)
	echo a > linklink.txt
	tar      cf linklink.tar linklink.txt linklink.txt
	$(VALGRIND) ./$(PARTAR) cf linklink.partar linklink.txt linklink.txt
	# Don't run the diff unless we have a recent version of tar
	if (tar --version|head -1|fgrep 1.3 > /dev/null); then diff linklink.tar linklink.partar; fi
	mkdir -p linklink.tdir
	tar xf linklink.tar -C linklink.tdir
	mkdir -p linklink.pdir
	tar xf linklink.partar -C linklink.pdir
	diff -r linklink.tdir linklink.pdir
	touch $@

# This isn't part of the standard tests, since it requires sudo to run properly.
permissions.sudo.test: $(PARTAR)
	sudo rm -rf permissions.sudopartar.testdir permissions.sudountar.testdir
	mkdir -p permissions.sudopartar.testdir
	mkdir -p permissions.sudountar.testdir
	sudo tar      pxf test-tars/permissions.tar -C permissions.sudountar.testdir
	sudo ./$(PARTAR) pxf test-tars/permissions.tar -C permissions.sudopartar.testdir
	# for some reason tar doesn't set the mtime on partar/src/bin in those directories, so let's ignore bin.
	(cd permissions.sudopartar.testdir; ls -lR)|sort | egrep -v "bin$$" > permissions.sudopartar.out
	(cd permissions.sudountar.testdir;  ls -lR)|sort | egrep -v "bin$$" > permissions.sudountar.out
	diff permissions.sudopartar.out permissions.sudountar.out

# permissions test for not sudo
permissions.test: $(PARTAR)
	rm -rf permissions.in.dir permissions.tar.out.dir permissions.partar.out.dir permissions.tar.ls permissions.partar.ls
	mkdir -p permissions.in.dir/s
	mkdir -p permissions.in.dir/s/d1
	mkdir -p permissions.in.dir/s/d2
	ls -l permissions.in.dir/s
	chmod 770 permissions.in.dir/s/d1
	chmod 750 permissions.in.dir/s/d2
	ls -l permissions.in.dir/s
	mkdir -p permissions.tar.out.dir
	tar cf - -C permissions.in.dir s | tar xf - -C permissions.tar.out.dir
	ls -l permissions.tar.out.dir/s	| sort > permissions.tar.ls
	mkdir -p permissions.partar.out.dir
	tar cf - -C permissions.in.dir s | $(VALGRIND) ./$(PARTAR) xf - -C permissions.partar.out.dir
	ls -l permissions.partar.out.dir/s | sort > permissions.partar.ls
	diff permissions.tar.ls permissions.partar.ls
	touch $@

# Test for detecting .gz and .xz inputs.
decompression-errors.test: ./partar
	! ./partar xf test-tars/fifo.tar.xz 2> decompression-errors.out
	fgrep "Input does not appear to be a tar file." decompression-errors.out > /dev/null
	fgrep "Input appears to be xz compressed" decompression-errors.out > /dev/null
	!(xzcat test-tars/fifo.tar.xz |gzip|./partar xf - 2> decompression-errors.out)
	fgrep "Input does not appear to be a tar file." decompression-errors.out > /dev/null
	fgrep "appears to be gzipped" decompression-errors.out > /dev/null
	touch $@

%.dir:
	mkdir $@

# Some specific tests for parcp
parcp0.test: parcp tar-1.30.tar_done | parcp0.test.dir
	rm -rf $@.dir/*
	./parcp --restore -- output-with-tar/tar-1.30 $@.dir
	touch $@

parcp1.test: parcp
	./parcp --version |fgrep $(VERSION) > /dev/null
	./parcp --help |fgrep OPTION > /dev/null
	touch $@

# test that if we copy into an existing file that is longer, that it's properly trunated
parcp2.test: parcp | parcp2.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst/src
	echo "hello" > $@.dir/src/hello
	echo "hello there" > $@.dir/dst/src/hello
	ls -i $@.dir/dst/src/hello > $@.dir/original.inode
	./parcp -P 3 $@.dir/src $@.dir/dst
	ls -i $@.dir/dst/src/hello > $@.dir/new.inode
	diff $@.dir/src/hello $@.dir/dst/src/hello
	diff $@.dir/new.inode $@.dir/original.inode
	touch $@

parcp3.test:  parcp | parcp3.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	# source and dest are the same symlink, so expect no change
	ln -s unchanged $@.dir/src/uc
	ln -s unchanged $@.dir/dst/uc
	# source and dest are slightly different symlinks, the same length.
	ln -s changed0  $@.dir/src/ch
	ln -s changed1  $@.dir/dst/ch
	# source and dest are different symlinks of different length.
	ln -s shortch   $@.dir/src/chlen
	ln -s longerch  $@.dir/dst/chlen
	# source is symlink, dest is not there
	ln -s notthere  $@.dir/src/nt
	# source is a symlink, dest is a dir
	ln -s dest-was-dir $@.dir/src/wasdir
	mkdir $@.dir/dst/wasdir
	# source is a symlink, dest is a regular file.
	ln -s dest-was-reg $@.dir/src/wasreg
	touch $@.dir/dst/wasreg
	#ls -l $@.dir/dst	
	ls -i parcp3.test.dir/dst/uc > $@.dir/original.uc.symlink
	# source and dest are fifos.  Make sure the inode is reused
	mknod $@.dir/src/src_fifo_dst_fifo p
	mknod $@.dir/dst/src_fifo_dst_fifo p
	ls -i $@.dir/dst/src_fifo_dst_fifo > $@.dir/src_fifo_dst_fifo.original
	# source is fifo and dest is regular.  Make sure dest becomes fifo
	mknod $@.dir/src/src_fifo_dst_reg p
	touch $@.dir/dst/src_fifo_dst_reg
	# source is regular and dest is fifo.  Make sure dest becomes reg
	touch $@.dir/src/src_reg_dst_fifo
	mknod $@.dir/dst/src_reg_dst_fifo p
	# source is file, dest is dir
	touch $@.dir/src/src_reg_dst_dir
	mkdir $@.dir/dst/src_reg_dst_dir
	# source is dir, dest is file
	mkdir $@.dir/src/src_dir_dst_reg
	touch $@.dir/dst/src_dir_dst_reg
	# two files that are the same inode in src, nothing in dst
	touch  $@.dir/src/src_two_inodes_dst_null.1
	ln $@.dir/src/src_two_inodes_dst_null.1 $@.dir/src/src_two_inodes_dst_null.2
	# two files the same inode in src, a file in dst
	touch  $@.dir/src/src_two_inodes_dst_reg.1
	ln $@.dir/src/src_two_inodes_dst_reg.1 $@.dir/src/src_two_inodes_dst_reg.2
	touch $@.dir/dst/src_two_inodes_dst_reg.1
	# two files the same inode in src, two dirs in dst
	touch  $@.dir/src/src_two_inodes_dst_two_dirs.1
	ln $@.dir/src/src_two_inodes_dst_two_dirs.1 $@.dir/src/src_two_inodes_dst_two_dirs.2
	mkdir $@.dir/dst/src_two_inodes_dst_two_dirs.1
	mkdir $@.dir/dst/src_two_inodes_dst_two_dirs.2
	# three files the same inode in src, three files in dst, two of which are the same inode.
	touch  $@.dir/src/src_3_inodes_dst_3_files.1
	ln $@.dir/src/src_3_inodes_dst_3_files.1 $@.dir/src/src_3_inodes_dst_3_files.2
	ln $@.dir/src/src_3_inodes_dst_3_files.1 $@.dir/src/src_3_inodes_dst_3_files.3
	touch  $@.dir/dst/src_3_inodes_dst_3_files.1
	ln $@.dir/dst/src_3_inodes_dst_3_files.1 $@.dir/dst/src_3_inodes_dst_3_files.2
	touch $@.dir/dst/src_3_inodes_dst_3_files.3
	# do the copy
	$(VALGRIND) ./parcp $@.dir/src $@.dir/dst
	ls -l $@.dir/dst	
	test -h $@.dir/dst/ch
	ls -l $@.dir/dst/ch | fgrep "ch -> changed0"
	ls -l $@.dir/dst/nt | fgrep "nt -> notthere"
	ls -l $@.dir/dst/wasdir | fgrep "wasdir -> dest-was-dir"
	ls -l $@.dir/dst/chlen | fgrep "chlen -> shortch"
	test -h $@.dir/dst/wasreg
	ls -l $@.dir/dst/wasreg| fgrep "wasreg -> dest-was-reg"
	# test follows symlinks so we must do htis more carefully
	ls -i parcp3.test.dir/dst/uc > $@.dir/new.uc.symlink
	diff $@.dir/original.uc.symlink $@.dir/new.uc.symlink
	ls -i $@.dir/dst/src_fifo_dst_fifo > $@.dir/src_fifo_dst_fifo.new
	diff $@.dir/src_fifo_dst_fifo.new $@.dir/src_fifo_dst_fifo.original
	test -p $@.dir/dst/src_fifo_dst_reg
	test -f $@.dir/dst/src_reg_dst_fifo
	test -f $@.dir/dst/src_reg_dst_dir
	test -d $@.dir/dst/src_dir_dst_reg
	test  parcp3.test.dir/dst/src_two_inodes_dst_null.1 -ef parcp3.test.dir/dst/src_two_inodes_dst_null.2
	test  parcp3.test.dir/dst/src_two_inodes_dst_reg.1 -ef parcp3.test.dir/dst/src_two_inodes_dst_reg.2
	test  parcp3.test.dir/dst/src_3_inodes_dst_3_files.1 -ef parcp3.test.dir/dst/src_3_inodes_dst_3_files.2
	test  parcp3.test.dir/dst/src_3_inodes_dst_3_files.1 -ef parcp3.test.dir/dst/src_3_inodes_dst_3_files.3
	touch $@

# Check --delete
parcp4.test: parcp | parcp4.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	mkdir -p $@.dir/dst/a/b/c/d/e
	touch $@.dir/dst/a/b/c/d/e/f
	mkdir -p $@.dir/dst/a/b/c/d/e2
	touch $@.dir/dst/a/b/c/d/e2/f
	mkdir -p $@.dir/src/x/y/z
	mkdir -p $@.dir/src/a/w
	touch  $@.dir/src/a/w/foo
	$(VALGRIND) ./parcp --delete $@.dir/src $@.dir/dst
	diff -r $@.dir/src $@.dir/dst
	touch $@

# Check --exclude-from
parcp5.test: parcp | parcp5.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	mkdir $@.dir/src/wanted
	mkdir $@.dir/src/unwanted
	touch $@.dir/src/wanted/file1.keep
	touch $@.dir/src/wanted/file2.nokeep
	touch $@.dir/src/wanted/file3.nokeep
	echo unwanted > $@.dir/excludelist
	echo wanted/file2.nokeep >> $@.dir/excludelist
	echo file3.nokeep >> $@.dir/excludelist
	$(VALGRIND) ./parcp --exclude-from=$@.dir/excludelist $@.dir/src $@.dir/dst
	test -d $@.dir/dst/wanted
	test -f $@.dir/dst/wanted/file1.keep
	ls $@.dir/dst/wanted|fgrep -v file2.nokeep > /dev/null
	ls $@.dir/dst/wanted|fgrep -v file3.nokeep > /dev/null
	test ! -d $@.dir/dst/unwanted
	touch $@

# Check --exclude-from with --delete
parcp6.test: parcp Makefile | parcp6.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	mkdir -p $@.dir/src/src_wanted
	mkdir -p $@.dir/dst/dst_wanted
	mkdir -p $@.dir/dst/dst_extra
	mkdir -p $@.dir/dst/dst_extra2
	mkdir -p $@.dir/dst/dst_kill
	touch $@.dir/dst/dst_wanted/keepme
	touch $@.dir/dst/dst_extra/dst_wanted_file
	touch $@.dir/dst/dst_extra2/keep2
	touch $@.dir/dst/dst_extra2/killme
	touch $@.dir/dst/dst_kill/killme2
	echo dst_wanted > $@.dir/excludelist
	echo dst_wanted_file >> $@.dir/excludelist
	echo dst_extra2/keep2 >> $@.dir/excludelist
	$(VALGRIND) ./parcp --exclude-from=$@.dir/excludelist --delete $@.dir/src $@.dir/dst
	test -f $@.dir/dst/dst_wanted/keepme
	test -f $@.dir/dst/dst_extra/dst_wanted_file
	test -f $@.dir/dst/dst_extra2/keep2
	test ! -f $@.dir/dst/dst_extra2/killme
	test ! -f $@.dir/dst/dst_kill/killme2
	test ! -d $@.dir/dst/dst_kill
	touch $@

# Check target contains a read-only file
#  Three cases: 1) No such file in src
#               2) Exactly the same file in src
#               3) A different file in src
parcp7.test: parcp | parcp7.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	touch $@.dir/dst/no-such-file-in-src
	chmod ugo-w $@.dir/dst/no-such-file-in-src
	echo same in src >  $@.dir/src/same-file-in-src
	chmod ugo-w $@.dir/src/same-file-in-src
	cp -a $@.dir/src/same-file-in-src $@.dir/dst/same-file-in-src
	echo diffrent in src > $@.dir/src/diff-file-in-src
	chmod ugo-w $@.dir/src/diff-file-in-src
	echo diffrent in dst dst > $@.dir/dst/diff-file-in-src
	chmod ugo-w $@.dir/dst/diff-file-in-src
	ls -l $@.dir/{src,dst}
	$(VALGRIND) ./parcp --delete $@.dir/src $@.dir/dst
	ls -l $@.dir/{src,dst}
	test ! -f $@.dir/dst/no-such-file-in-src
	test -f $@.dir/dst/diff-file-in-src
	test -f $@.dir/dst/same-file-in-src
	diff -r $@.dir/{src,dst}
	touch $@

parcp8.test: parcp | parcp7.test.dir
	rm -rf $@.dir/*
	mkdir -p $@.dir/src $@.dir/dst
	touch $@.dir/src/rws-file
	chmod +sx $@.dir/src/rws-file
	./parcp $@.dir/src $@.dir/dst
	test -x parcp8.test.dir/dst/rws-file
	test -u parcp8.test.dir/dst/rws-file
	echo touch $@

.PHONY: clean
clean:
	rm -rf parrm parcp $(PARTAR) parrm.1.gz parcp.1.gz $(PARTAR).1.gz *.o output-with-tar output-to-rm output-with-parcp *.tar_done *.parrm_test *.parcp_test *.partar_test
	rm -rf gitrev.c
	rm -rf output-with-partar{1,2,3,4} output-of-partarred
	rm -rf uncompressed-tars gzipped-tars
	rm -rf  *.partar{1,2,3,4}_diff
	rm -rf  *.partar{1,2,3,4}_done
	rm -rf excld.dir.*tar excld.test
	rm -rf excld-equal.dir.*tar excld-equal.test
	if test -n "$$(find . -maxdepth 1 -name 'parrm_eaccess_test*_dir' -print -quit)"; then chmod -R u+rwx parrm_eaccess_test*_dir; fi
	rm -rf parrm_eaccess_test*{.out,_dir}
	rm -rf error.test.out error.tar linklink.tar linklink.partar linklink.tdir linklink.pdir linklink.txt  excld.odir.partar excld.odir.tar
	rm -rf permissions.in.dir permissions.tar.out.dir permissions.partar.out.dir permissions.tar.ls permissions.partar.ls
	rm -rf *.ls *.test *.tar_test
	rm -rf *.partar_out *.tar_out test1.out *.out
	rm -rf test_fin
	rm -f *.gcda *.gcno common/*.gcda common/*.gcno
	rm -rf *.dir/
	rm -rf *.1.gz
	rm -f $(COMMON_O)
	rm -rf partar_log
		
.PHONY: realclean
realclean: clean
