# Copyright (C) 2010-2012 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.

# Symlink related tests

. $srcdir/test-lib.sh

require cat
use_local_patch
use_tmpdir

# ==============================================================

cat > create.diff <<EOF
--- /dev/null
+++ l
@@ -0,0 +1 @@
+one
EOF

rm -f f l
echo one > f
ncheck 'ln -s f l'

check 'patch < create.diff || echo "Status: $?"' <<EOF
File l is not a regular file -- refusing to patch
1 out of 1 hunk ignored -- saving rejects to file l.rej
Status: 1
EOF

check 'cat f' <<EOF
one
EOF

# --------------------------------------------------------------

rm -f f l
echo one > f
ncheck 'ln -s f l'

cat > modify.diff <<EOF
--- l
+++ l
@@ -1 +1 @@
-one
+two
EOF

check 'patch < modify.diff || echo "Status: $?"' <<EOF
File l is not a regular file -- refusing to patch
1 out of 1 hunk ignored -- saving rejects to file l.rej
Status: 1
EOF

check 'patch --follow-symlinks < modify.diff || echo "Status: $?"' <<EOF
patching file l
EOF

check 'cat f' <<EOF
one
EOF

check 'cat l' <<EOF
two
EOF

# --------------------------------------------------------------

rm -f f l
echo one > f
ncheck 'ln -s f l'

cat > delete.diff <<EOF
--- l
+++ /dev/null
@@ -1 +0,0 @@
-one
EOF

check 'patch < delete.diff || echo "Status: $?"' <<EOF
File l is not a regular file -- refusing to patch
1 out of 1 hunk ignored -- saving rejects to file l.rej
Status: 1
EOF

# --------------------------------------------------------------

cat > create-symlink.diff <<EOF
diff --git a/symlink b/symlink
new file mode 120000
index 0000000..12a8d8a
--- /dev/null
+++ b/symlink
@@ -0,0 +1 @@
+target1
\ No newline at end of file
EOF

check 'patch -p1 < create-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

check 'echo a > target1 && cat symlink' <<EOF
a
EOF
check 'echo b > target1 && cat symlink' <<EOF
b
EOF
rm -f target1

cat > modify-symlink.diff <<EOF
diff --git a/symlink b/symlink
index 12a8d8a..3b7781e 120000
--- a/symlink
+++ b/symlink
@@ -1 +1 @@
-target1
\ No newline at end of file
+target2
\ No newline at end of file
EOF

check 'patch -p1 < modify-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

check 'echo a > target2 && cat symlink' <<EOF
a
EOF
check 'echo b > target2 && cat symlink' <<EOF
b
EOF
rm -f target2

cat > delete-symlink.diff <<EOF
diff --git a/symlink b/symlink
deleted file mode 120000
index 3b7781e..0000000
--- a/symlink
+++ /dev/null
@@ -1 +0,0 @@
-target2
\ No newline at end of file
EOF

check 'patch -p1 < delete-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

ncheck 'test ! -L symlink'

# --------------------------------------------------------------

# Recursive symlinks

ln -s l1 l2
ln -s l2 l1

cat > f.diff <<EOF
--- l1/f
+++ l1/f
@@ -0,0 +1 @@
+new
EOF

check 'patch -p0 < f.diff || echo "Status: $?"' <<EOF
Invalid file name l1/f -- skipping patch
Status: 1
EOF

rm -f l1 l2

# --------------------------------------------------------------

cat > retraverse.diff <<EOF
--- abc/def/ghi/jkl
+++ abc/def/ghi/jkl
@@ -0,0 +1 @@
+Parent directory traversal
EOF

ncheck 'mkdir abc'
ncheck 'mkdir abc/def'
ln -sf ../../abc/def abc/def/ghi
check 'patch -p0 < retraverse.diff || echo "Status: $?"' << EOF
patching file abc/def/ghi/jkl
EOF

# --------------------------------------------------------------

# Patch should not create symlinks which point outside the working directory.

mkdir d
echo one > d/f
ln -s d ld

cat > ld.diff <<EOF
--- ld/f
+++ ld/f
@@ -1 +1 @@
-one
+two
EOF

check 'patch -p0 < ld.diff' <<EOF
patching file ld/f
EOF

mkdir e
ln -s ../d e/ld

cat > eld.diff <<EOF
--- e/ld/f
+++ e/ld/f
@@ -1 +1 @@
-two
+three
EOF

check 'patch -p0 < eld.diff' <<EOF
patching file e/ld/f
EOF

rm -f e/ld
ln -sf ../ld e/ld

check 'patch -p0 -R < eld.diff' <<EOF
patching file e/ld/f
EOF

mkdir g
ln -sf ../../z g/bad-rel
ln -sf .. bad-rel-step2
ln -sf ../bad-rel-step2/z g/bad-rel-step1
ln -sf /z g/bad-abs

cat > follow-bad-symlink.diff <<EOF
--- g/bad-rel/x
+++ g/bad-rel/x
@@ -0,0 +1 @@
+relative
--- g/bad-rel-step1/x
+++ g/bad-rel-step1/x
@@ -0,0 +1 @@
+relative, 2 steps
--- g/bad-abs/x
+++ g/bad-abs/x
@@ -0,0 +1 @@
+absolute
EOF

check 'patch -p0 < follow-bad-symlink.diff || echo "Status: $?"' <<EOF
Invalid file name g/bad-rel/x -- skipping patch
Invalid file name g/bad-rel-step1/x -- skipping patch
Invalid file name g/bad-abs/x -- skipping patch
Status: 1
EOF

rm -rf ld d e g

cat > symlink-target.diff <<EOF
diff --git a/dir/foo b/dir/foo
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/foo
@@ -0,0 +1 @@
+../foo
\ No newline at end of file
EOF

check 'patch -p1 < symlink-target.diff || echo "Status: $?"' <<EOF
patching symbolic link dir/foo
EOF

rm -f dir/foo
cat > follow-symlink.diff <<EOF
diff --git a/dir/foo b/dir/foo
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/foo
@@ -0,0 +1 @@
+..
\ No newline at end of file
diff --git a/dir/foo/bar b/dir/foo/bar
new file mode 100644
index 0000000..2ab772d
--- /dev/null
+++ b/dir/foo/bar
@@ -0,0 +1 @@
+created in ..
diff --git a/dir/bad b/dir/bad
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/bad
@@ -0,0 +1 @@
+../..
\ No newline at end of file
diff --git a/dir/bad/baz b/dir/bad/baz
new file mode 100644
index 0000000..2ab772d
--- /dev/null
+++ b/dir/bad/baz
@@ -0,0 +1 @@
+created in ../..
EOF

check 'patch -f -p1 < follow-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link dir/foo
patching file dir/foo/bar
patching symbolic link dir/bad
Invalid file name dir/bad/baz -- skipping patch
Status: 1
EOF

check 'cat bar' <<EOF
created in ..
EOF

rm -f bar

cat > bad-symlink-target1.diff <<EOF
diff --git a/bar b/bar
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/bar
@@ -0,0 +1 @@
+/bar
\ No newline at end of file
EOF

check 'patch -p1 < bad-symlink-target1.diff || echo "Status: $?"' <<EOF
patching symbolic link bar
EOF

cat > bad-symlink-target2.diff <<EOF
diff --git a/baz b/baz
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/baz
@@ -0,0 +1 @@
+../baz
\ No newline at end of file
EOF

check 'patch -p1 < bad-symlink-target2.diff || echo "Status: $?"' <<EOF
patching symbolic link baz
EOF

# --------------------------------------------------------------

# Absolute symlinks that point back into the working directory

mkdir d
ln -s $PWD here
ln -s $PWD/here/d here_d

cat > good-absolute.diff <<EOF
--- /dev/null
+++ here/d/foo
@@ -0,0 +1 @@
+foo
--- /dev/null
+++ here_d/bar
@@ -0,0 +1 @@
+bar
EOF

check 'patch -p0 < good-absolute.diff' <<EOF
patching file here/d/foo
patching file here_d/bar
EOF

rm -rf d

# --------------------------------------------------------------

# The backup file of a new symlink is an empty regular file.

check 'patch -p1 --backup < create-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

ncheck 'test -f symlink.orig && test ! -s symlink.orig'

# The backup file of a modified symlink is the old symlink.

check 'patch -p1 --backup < modify-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

check 'echo a > symlink.orig && cat target1' <<EOF
a
EOF
check 'echo b > symlink.orig && cat target1' <<EOF
b
EOF
rm -f target1

# The backup file of a deleted symlink is the old symlink.

check 'patch -p1 --backup < delete-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link symlink
EOF

check 'echo a > symlink.orig && cat target2' <<EOF
a
EOF
check 'echo b > symlink.orig && cat target2' <<EOF
b
EOF
rm -f target2

# --------------------------------------------------------------
# Make sure we do follow symlinks to patch files.

ncheck 'mkdir d'
cat > d/ab.diff <<EOF
--- /dev/null
+++ b/foo
@@ -0,0 +1 @@
+foo
EOF

ncheck 'ln -sf d l'

check 'patch -p1 -i l/ab.diff' <<EOF
patching file foo
EOF