summaryrefslogtreecommitdiffstats
path: root/tests/posix
diff options
context:
space:
mode:
Diffstat (limited to 'tests/posix')
-rw-r--r--tests/posix/H.out1
-rw-r--r--tests/posix/H.sh1
-rw-r--r--tests/posix/H_broken.out1
-rw-r--r--tests/posix/H_broken.sh1
-rw-r--r--tests/posix/H_loops.out4
-rw-r--r--tests/posix/H_loops.sh1
-rw-r--r--tests/posix/H_notdir.out1
-rw-r--r--tests/posix/H_notdir.sh1
-rw-r--r--tests/posix/H_slash.out1
-rw-r--r--tests/posix/H_slash.sh1
-rw-r--r--tests/posix/H_type_l.out2
-rw-r--r--tests/posix/H_type_l.sh1
-rw-r--r--tests/posix/L.out17
-rw-r--r--tests/posix/L.sh1
-rw-r--r--tests/posix/L_broken.out1
-rw-r--r--tests/posix/L_broken.sh1
-rw-r--r--tests/posix/L_depth.out17
-rw-r--r--tests/posix/L_depth.sh1
-rw-r--r--tests/posix/L_loops.sh4
-rw-r--r--tests/posix/L_notdir.out1
-rw-r--r--tests/posix/L_notdir.sh1
-rw-r--r--tests/posix/L_type_l.out1
-rw-r--r--tests/posix/L_type_l.sh1
-rw-r--r--tests/posix/L_xdev.out5
-rw-r--r--tests/posix/L_xdev.sh13
-rw-r--r--tests/posix/a.out2
-rw-r--r--tests/posix/a.sh1
-rw-r--r--tests/posix/bang.out16
-rw-r--r--tests/posix/bang.sh1
-rw-r--r--tests/posix/basic.out19
-rw-r--r--tests/posix/basic.sh1
-rw-r--r--tests/posix/data_flow_and_swap.out12
-rw-r--r--tests/posix/data_flow_and_swap.sh1
-rw-r--r--tests/posix/data_flow_group.out19
-rw-r--r--tests/posix/data_flow_group.sh1
-rw-r--r--tests/posix/data_flow_or_swap.out12
-rw-r--r--tests/posix/data_flow_or_swap.sh1
-rw-r--r--tests/posix/data_flow_type.out0
-rw-r--r--tests/posix/data_flow_type.sh1
-rw-r--r--tests/posix/data_flow_user.out19
-rw-r--r--tests/posix/data_flow_user.sh1
-rw-r--r--tests/posix/de_morgan_and.out10
-rw-r--r--tests/posix/de_morgan_and.sh1
-rw-r--r--tests/posix/de_morgan_not.out6
-rw-r--r--tests/posix/de_morgan_not.sh1
-rw-r--r--tests/posix/de_morgan_or.out18
-rw-r--r--tests/posix/de_morgan_or.sh1
-rw-r--r--tests/posix/deep.out16
-rw-r--r--tests/posix/deep.sh2
-rw-r--r--tests/posix/depth.out19
-rw-r--r--tests/posix/depth.sh1
-rw-r--r--tests/posix/depth_error.out2
-rw-r--r--tests/posix/depth_error.sh7
-rw-r--r--tests/posix/depth_slash.out19
-rw-r--r--tests/posix/depth_slash.sh1
-rw-r--r--tests/posix/double_negation.out3
-rw-r--r--tests/posix/double_negation.sh1
-rw-r--r--tests/posix/exec.out19
-rw-r--r--tests/posix/exec.sh1
-rw-r--r--tests/posix/exec_nonexistent.out19
-rw-r--r--tests/posix/exec_nonexistent.sh4
-rw-r--r--tests/posix/exec_nopath.out19
-rw-r--r--tests/posix/exec_nopath.sh7
-rw-r--r--tests/posix/exec_plus.out1
-rw-r--r--tests/posix/exec_plus.sh1
-rw-r--r--tests/posix/exec_plus_nonexistent.out19
-rw-r--r--tests/posix/exec_plus_nonexistent.sh2
-rw-r--r--tests/posix/exec_plus_nothing.sh2
-rw-r--r--tests/posix/exec_plus_semicolon.out19
-rw-r--r--tests/posix/exec_plus_semicolon.sh5
-rw-r--r--tests/posix/exec_plus_status.out19
-rw-r--r--tests/posix/exec_plus_status.sh3
-rw-r--r--tests/posix/exec_ulimit.out16
-rw-r--r--tests/posix/exec_ulimit.sh2
-rw-r--r--tests/posix/extra_paren.sh1
-rw-r--r--tests/posix/flag_comma.out2
-rw-r--r--tests/posix/flag_comma.sh3
-rw-r--r--tests/posix/flag_weird_names.out28
-rw-r--r--tests/posix/flag_weird_names.sh2
-rw-r--r--tests/posix/group_id.out19
-rw-r--r--tests/posix/group_id.sh1
-rw-r--r--tests/posix/group_name.out19
-rw-r--r--tests/posix/group_name.sh1
-rw-r--r--tests/posix/group_nogroup.out19
-rw-r--r--tests/posix/group_nogroup.sh2
-rw-r--r--tests/posix/implicit_and.out2
-rw-r--r--tests/posix/implicit_and.sh1
-rw-r--r--tests/posix/incomplete.sh1
-rw-r--r--tests/posix/links.out2
-rw-r--r--tests/posix/links.sh1
-rw-r--r--tests/posix/links_minus.out1
-rw-r--r--tests/posix/links_minus.sh1
-rw-r--r--tests/posix/links_plus.out2
-rw-r--r--tests/posix/links_plus.sh1
-rw-r--r--tests/posix/missing_paren.sh1
-rw-r--r--tests/posix/name.out4
-rw-r--r--tests/posix/name.sh1
-rw-r--r--tests/posix/name_backslash.out0
-rw-r--r--tests/posix/name_backslash.sh2
-rw-r--r--tests/posix/name_bracket.out1
-rw-r--r--tests/posix/name_bracket.sh9
-rw-r--r--tests/posix/name_character_class.out3
-rw-r--r--tests/posix/name_character_class.sh1
-rw-r--r--tests/posix/name_double_backslash.out1
-rw-r--r--tests/posix/name_double_backslash.sh2
-rw-r--r--tests/posix/name_root.out1
-rw-r--r--tests/posix/name_root.sh1
-rw-r--r--tests/posix/name_root_depth.out1
-rw-r--r--tests/posix/name_root_depth.sh1
-rw-r--r--tests/posix/name_star_star.out4
-rw-r--r--tests/posix/name_star_star.sh1
-rw-r--r--tests/posix/name_trailing_slash.out1
-rw-r--r--tests/posix/name_trailing_slash.sh1
-rw-r--r--tests/posix/newer.out4
-rw-r--r--tests/posix/newer.sh1
-rw-r--r--tests/posix/newer_broken.out1
-rw-r--r--tests/posix/newer_broken.sh4
-rw-r--r--tests/posix/newer_nonexistent.sh1
-rw-r--r--tests/posix/nogroup.out0
-rw-r--r--tests/posix/nogroup.sh1
-rw-r--r--tests/posix/nogroup_ulimit.out0
-rw-r--r--tests/posix/nogroup_ulimit.sh2
-rw-r--r--tests/posix/not_prune.out13
-rw-r--r--tests/posix/not_prune.sh1
-rw-r--r--tests/posix/nouser.out0
-rw-r--r--tests/posix/nouser.sh1
-rw-r--r--tests/posix/nouser_ulimit.out0
-rw-r--r--tests/posix/nouser_ulimit.sh2
-rw-r--r--tests/posix/o.out13
-rw-r--r--tests/posix/o.sh1
-rw-r--r--tests/posix/ok_plus_nothing.sh2
-rw-r--r--tests/posix/ok_stdin.out19
-rw-r--r--tests/posix/ok_stdin.sh3
-rw-r--r--tests/posix/or_purity.out0
-rw-r--r--tests/posix/or_purity.sh2
-rw-r--r--tests/posix/overlayfs.out5
-rw-r--r--tests/posix/overlayfs.sh11
-rw-r--r--tests/posix/parens.out4
-rw-r--r--tests/posix/parens.sh1
-rw-r--r--tests/posix/path.out7
-rw-r--r--tests/posix/path.sh1
-rw-r--r--tests/posix/perm_000.out1
-rw-r--r--tests/posix/perm_000.sh1
-rw-r--r--tests/posix/perm_000_minus.out8
-rw-r--r--tests/posix/perm_000_minus.sh1
-rw-r--r--tests/posix/perm_222.out1
-rw-r--r--tests/posix/perm_222.sh1
-rw-r--r--tests/posix/perm_222_minus.out1
-rw-r--r--tests/posix/perm_222_minus.sh1
-rw-r--r--tests/posix/perm_644.out1
-rw-r--r--tests/posix/perm_644.sh1
-rw-r--r--tests/posix/perm_644_minus.out3
-rw-r--r--tests/posix/perm_644_minus.sh1
-rw-r--r--tests/posix/perm_leading_plus_symbolic_minus.out0
-rw-r--r--tests/posix/perm_leading_plus_symbolic_minus.sh1
-rw-r--r--tests/posix/perm_setid.out3
-rw-r--r--tests/posix/perm_setid.sh1
-rw-r--r--tests/posix/perm_sticky.out2
-rw-r--r--tests/posix/perm_sticky.sh1
-rw-r--r--tests/posix/perm_symbolic.out0
-rw-r--r--tests/posix/perm_symbolic.sh1
-rw-r--r--tests/posix/perm_symbolic_minus.out3
-rw-r--r--tests/posix/perm_symbolic_minus.sh1
-rw-r--r--tests/posix/permcopy.out1
-rw-r--r--tests/posix/permcopy.sh1
-rw-r--r--tests/posix/prune.out3
-rw-r--r--tests/posix/prune.sh1
-rw-r--r--tests/posix/prune_file.out10
-rw-r--r--tests/posix/prune_file.sh1
-rw-r--r--tests/posix/prune_or_print.out13
-rw-r--r--tests/posix/prune_or_print.sh1
-rw-r--r--tests/posix/readdir_error.sh37
-rw-r--r--tests/posix/size.out6
-rw-r--r--tests/posix/size.sh1
-rw-r--r--tests/posix/size_bytes.out1
-rw-r--r--tests/posix/size_bytes.sh1
-rw-r--r--tests/posix/size_plus.out1
-rw-r--r--tests/posix/size_plus.sh1
-rw-r--r--tests/posix/type_bind_mount.out1
-rw-r--r--tests/posix/type_bind_mount.sh9
-rw-r--r--tests/posix/type_d.out12
-rw-r--r--tests/posix/type_d.sh1
-rw-r--r--tests/posix/type_f.out7
-rw-r--r--tests/posix/type_f.sh1
-rw-r--r--tests/posix/type_l.out1
-rw-r--r--tests/posix/type_l.sh1
-rw-r--r--tests/posix/unionfs.out10
-rw-r--r--tests/posix/unionfs.sh9
-rw-r--r--tests/posix/user_id.out19
-rw-r--r--tests/posix/user_id.sh1
-rw-r--r--tests/posix/user_name.out19
-rw-r--r--tests/posix/user_name.sh1
-rw-r--r--tests/posix/user_nouser.out19
-rw-r--r--tests/posix/user_nouser.sh2
-rw-r--r--tests/posix/weird_names.out28
-rw-r--r--tests/posix/weird_names.sh2
-rw-r--r--tests/posix/xdev.out4
-rw-r--r--tests/posix/xdev.sh11
198 files changed, 969 insertions, 0 deletions
diff --git a/tests/posix/H.out b/tests/posix/H.out
new file mode 100644
index 0000000..ff635ff
--- /dev/null
+++ b/tests/posix/H.out
@@ -0,0 +1 @@
+links/deeply/nested/dir
diff --git a/tests/posix/H.sh b/tests/posix/H.sh
new file mode 100644
index 0000000..5bae1be
--- /dev/null
+++ b/tests/posix/H.sh
@@ -0,0 +1 @@
+bfs_diff -H links/deeply/nested/dir
diff --git a/tests/posix/H_broken.out b/tests/posix/H_broken.out
new file mode 100644
index 0000000..21d6316
--- /dev/null
+++ b/tests/posix/H_broken.out
@@ -0,0 +1 @@
+links/broken
diff --git a/tests/posix/H_broken.sh b/tests/posix/H_broken.sh
new file mode 100644
index 0000000..9ff761c
--- /dev/null
+++ b/tests/posix/H_broken.sh
@@ -0,0 +1 @@
+bfs_diff -H links/broken
diff --git a/tests/posix/H_loops.out b/tests/posix/H_loops.out
new file mode 100644
index 0000000..1fc8f8f
--- /dev/null
+++ b/tests/posix/H_loops.out
@@ -0,0 +1,4 @@
+loops/deeply/nested/loop
+loops/deeply/nested/loop/nested
+loops/deeply/nested/loop/nested/dir
+loops/deeply/nested/loop/nested/loop
diff --git a/tests/posix/H_loops.sh b/tests/posix/H_loops.sh
new file mode 100644
index 0000000..90383b8
--- /dev/null
+++ b/tests/posix/H_loops.sh
@@ -0,0 +1 @@
+bfs_diff -H loops/deeply/nested/loop
diff --git a/tests/posix/H_notdir.out b/tests/posix/H_notdir.out
new file mode 100644
index 0000000..6e6658d
--- /dev/null
+++ b/tests/posix/H_notdir.out
@@ -0,0 +1 @@
+links/notdir
diff --git a/tests/posix/H_notdir.sh b/tests/posix/H_notdir.sh
new file mode 100644
index 0000000..68d7be7
--- /dev/null
+++ b/tests/posix/H_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -H links/notdir
diff --git a/tests/posix/H_slash.out b/tests/posix/H_slash.out
new file mode 100644
index 0000000..df7701b
--- /dev/null
+++ b/tests/posix/H_slash.out
@@ -0,0 +1 @@
+links/deeply/nested/dir/
diff --git a/tests/posix/H_slash.sh b/tests/posix/H_slash.sh
new file mode 100644
index 0000000..b44d756
--- /dev/null
+++ b/tests/posix/H_slash.sh
@@ -0,0 +1 @@
+bfs_diff -H links/deeply/nested/dir/
diff --git a/tests/posix/H_type_l.out b/tests/posix/H_type_l.out
new file mode 100644
index 0000000..e67f10b
--- /dev/null
+++ b/tests/posix/H_type_l.out
@@ -0,0 +1,2 @@
+links/skip/broken
+links/skip/link
diff --git a/tests/posix/H_type_l.sh b/tests/posix/H_type_l.sh
new file mode 100644
index 0000000..416a53e
--- /dev/null
+++ b/tests/posix/H_type_l.sh
@@ -0,0 +1 @@
+bfs_diff -H links/skip -type l
diff --git a/tests/posix/L.out b/tests/posix/L.out
new file mode 100644
index 0000000..ec9e861
--- /dev/null
+++ b/tests/posix/L.out
@@ -0,0 +1,17 @@
+links
+links/broken
+links/deeply
+links/deeply/nested
+links/deeply/nested/broken
+links/deeply/nested/dir
+links/deeply/nested/file
+links/deeply/nested/link
+links/file
+links/hardlink
+links/notdir
+links/skip
+links/skip/broken
+links/skip/dir
+links/skip/file
+links/skip/link
+links/symlink
diff --git a/tests/posix/L.sh b/tests/posix/L.sh
new file mode 100644
index 0000000..d8aebe6
--- /dev/null
+++ b/tests/posix/L.sh
@@ -0,0 +1 @@
+bfs_diff -L links
diff --git a/tests/posix/L_broken.out b/tests/posix/L_broken.out
new file mode 100644
index 0000000..21d6316
--- /dev/null
+++ b/tests/posix/L_broken.out
@@ -0,0 +1 @@
+links/broken
diff --git a/tests/posix/L_broken.sh b/tests/posix/L_broken.sh
new file mode 100644
index 0000000..9ff761c
--- /dev/null
+++ b/tests/posix/L_broken.sh
@@ -0,0 +1 @@
+bfs_diff -H links/broken
diff --git a/tests/posix/L_depth.out b/tests/posix/L_depth.out
new file mode 100644
index 0000000..ec9e861
--- /dev/null
+++ b/tests/posix/L_depth.out
@@ -0,0 +1,17 @@
+links
+links/broken
+links/deeply
+links/deeply/nested
+links/deeply/nested/broken
+links/deeply/nested/dir
+links/deeply/nested/file
+links/deeply/nested/link
+links/file
+links/hardlink
+links/notdir
+links/skip
+links/skip/broken
+links/skip/dir
+links/skip/file
+links/skip/link
+links/symlink
diff --git a/tests/posix/L_depth.sh b/tests/posix/L_depth.sh
new file mode 100644
index 0000000..59d7ee9
--- /dev/null
+++ b/tests/posix/L_depth.sh
@@ -0,0 +1 @@
+bfs_diff -L links -depth
diff --git a/tests/posix/L_loops.sh b/tests/posix/L_loops.sh
new file mode 100644
index 0000000..01b7efc
--- /dev/null
+++ b/tests/posix/L_loops.sh
@@ -0,0 +1,4 @@
+# POSIX says it's okay to either stop or keep going on seeing a filesystem
+# loop, as long as a diagnostic is printed
+invoke_bfs -L loops >/dev/null 2>"$OUT" && fail
+test -s "$OUT"
diff --git a/tests/posix/L_notdir.out b/tests/posix/L_notdir.out
new file mode 100644
index 0000000..6e6658d
--- /dev/null
+++ b/tests/posix/L_notdir.out
@@ -0,0 +1 @@
+links/notdir
diff --git a/tests/posix/L_notdir.sh b/tests/posix/L_notdir.sh
new file mode 100644
index 0000000..68d7be7
--- /dev/null
+++ b/tests/posix/L_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -H links/notdir
diff --git a/tests/posix/L_type_l.out b/tests/posix/L_type_l.out
new file mode 100644
index 0000000..725d398
--- /dev/null
+++ b/tests/posix/L_type_l.out
@@ -0,0 +1 @@
+links/skip/broken
diff --git a/tests/posix/L_type_l.sh b/tests/posix/L_type_l.sh
new file mode 100644
index 0000000..ee9e563
--- /dev/null
+++ b/tests/posix/L_type_l.sh
@@ -0,0 +1 @@
+bfs_diff -L links/skip -type l
diff --git a/tests/posix/L_xdev.out b/tests/posix/L_xdev.out
new file mode 100644
index 0000000..788579d
--- /dev/null
+++ b/tests/posix/L_xdev.out
@@ -0,0 +1,5 @@
+.
+./foo
+./foo/bar
+./foo/qux
+./mnt
diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh
new file mode 100644
index 0000000..82d8605
--- /dev/null
+++ b/tests/posix/L_xdev.sh
@@ -0,0 +1,13 @@
+test "$UNAME" = "Darwin" && skip
+
+cd "$TEST"
+mkdir foo mnt
+
+bfs_sudo mount -t tmpfs tmpfs mnt || skip
+defer bfs_sudo umount mnt
+
+ln -s ../mnt foo/bar
+"$XTOUCH" mnt/baz
+ln -s ../mnt/baz foo/qux
+
+bfs_diff -L . -xdev
diff --git a/tests/posix/a.out b/tests/posix/a.out
new file mode 100644
index 0000000..722962c
--- /dev/null
+++ b/tests/posix/a.out
@@ -0,0 +1,2 @@
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/a.sh b/tests/posix/a.sh
new file mode 100644
index 0000000..7d82d88
--- /dev/null
+++ b/tests/posix/a.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -a -type d
diff --git a/tests/posix/bang.out b/tests/posix/bang.out
new file mode 100644
index 0000000..b286454
--- /dev/null
+++ b/tests/posix/bang.out
@@ -0,0 +1,16 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/k/foo/bar
+basic/l
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/bang.sh b/tests/posix/bang.sh
new file mode 100644
index 0000000..27840cd
--- /dev/null
+++ b/tests/posix/bang.sh
@@ -0,0 +1 @@
+bfs_diff basic \! -name foo
diff --git a/tests/posix/basic.out b/tests/posix/basic.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/basic.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/basic.sh b/tests/posix/basic.sh
new file mode 100644
index 0000000..3d43529
--- /dev/null
+++ b/tests/posix/basic.sh
@@ -0,0 +1 @@
+bfs_diff basic
diff --git a/tests/posix/data_flow_and_swap.out b/tests/posix/data_flow_and_swap.out
new file mode 100644
index 0000000..e604709
--- /dev/null
+++ b/tests/posix/data_flow_and_swap.out
@@ -0,0 +1,12 @@
+basic
+basic/c
+basic/e
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/k/foo
+basic/l
+basic/l/foo
+basic/l/foo/bar
diff --git a/tests/posix/data_flow_and_swap.sh b/tests/posix/data_flow_and_swap.sh
new file mode 100644
index 0000000..9a141af
--- /dev/null
+++ b/tests/posix/data_flow_and_swap.sh
@@ -0,0 +1 @@
+bfs_diff basic \! -type f -a -type d
diff --git a/tests/posix/data_flow_group.out b/tests/posix/data_flow_group.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/data_flow_group.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/data_flow_group.sh b/tests/posix/data_flow_group.sh
new file mode 100644
index 0000000..453dc3e
--- /dev/null
+++ b/tests/posix/data_flow_group.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -group "$(id -g)" -nogroup \) -o \( -group "$(id -g)" -o -nogroup \)
diff --git a/tests/posix/data_flow_or_swap.out b/tests/posix/data_flow_or_swap.out
new file mode 100644
index 0000000..e604709
--- /dev/null
+++ b/tests/posix/data_flow_or_swap.out
@@ -0,0 +1,12 @@
+basic
+basic/c
+basic/e
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/k/foo
+basic/l
+basic/l/foo
+basic/l/foo/bar
diff --git a/tests/posix/data_flow_or_swap.sh b/tests/posix/data_flow_or_swap.sh
new file mode 100644
index 0000000..e8f504b
--- /dev/null
+++ b/tests/posix/data_flow_or_swap.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -type f -o \! -type d \)
diff --git a/tests/posix/data_flow_type.out b/tests/posix/data_flow_type.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/data_flow_type.out
diff --git a/tests/posix/data_flow_type.sh b/tests/posix/data_flow_type.sh
new file mode 100644
index 0000000..33339df
--- /dev/null
+++ b/tests/posix/data_flow_type.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -type f -o \! -type f \)
diff --git a/tests/posix/data_flow_user.out b/tests/posix/data_flow_user.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/data_flow_user.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/data_flow_user.sh b/tests/posix/data_flow_user.sh
new file mode 100644
index 0000000..44b6e1f
--- /dev/null
+++ b/tests/posix/data_flow_user.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -user "$(id -u)" -nouser \) -o \( -user "$(id -u)" -o -nouser \)
diff --git a/tests/posix/de_morgan_and.out b/tests/posix/de_morgan_and.out
new file mode 100644
index 0000000..7b7afd2
--- /dev/null
+++ b/tests/posix/de_morgan_and.out
@@ -0,0 +1,10 @@
+basic
+basic/c
+basic/e
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/l
+basic/l/foo/bar
diff --git a/tests/posix/de_morgan_and.sh b/tests/posix/de_morgan_and.sh
new file mode 100644
index 0000000..d52975e
--- /dev/null
+++ b/tests/posix/de_morgan_and.sh
@@ -0,0 +1 @@
+bfs_diff basic \( \! -name 'foo' -a \! -type f \)
diff --git a/tests/posix/de_morgan_not.out b/tests/posix/de_morgan_not.out
new file mode 100644
index 0000000..5916da3
--- /dev/null
+++ b/tests/posix/de_morgan_not.out
@@ -0,0 +1,6 @@
+basic/a
+basic/b
+basic/c/d
+basic/e/f
+basic/k/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/de_morgan_not.sh b/tests/posix/de_morgan_not.sh
new file mode 100644
index 0000000..7393ce0
--- /dev/null
+++ b/tests/posix/de_morgan_not.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -name 'foo' -o \! -type f \)
diff --git a/tests/posix/de_morgan_or.out b/tests/posix/de_morgan_or.out
new file mode 100644
index 0000000..2a57066
--- /dev/null
+++ b/tests/posix/de_morgan_or.out
@@ -0,0 +1,18 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/de_morgan_or.sh b/tests/posix/de_morgan_or.sh
new file mode 100644
index 0000000..378aab2
--- /dev/null
+++ b/tests/posix/de_morgan_or.sh
@@ -0,0 +1 @@
+bfs_diff basic \( \! -name 'foo' -o \! -type f \)
diff --git a/tests/posix/deep.out b/tests/posix/deep.out
new file mode 100644
index 0000000..c385fce
--- /dev/null
+++ b/tests/posix/deep.out
@@ -0,0 +1,16 @@
+deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
+deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
diff --git a/tests/posix/deep.sh b/tests/posix/deep.sh
new file mode 100644
index 0000000..36a88c0
--- /dev/null
+++ b/tests/posix/deep.sh
@@ -0,0 +1,2 @@
+ulimit -n $((NOPENFD + 13))
+bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \;
diff --git a/tests/posix/depth.out b/tests/posix/depth.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/depth.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/depth.sh b/tests/posix/depth.sh
new file mode 100644
index 0000000..444eba5
--- /dev/null
+++ b/tests/posix/depth.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth
diff --git a/tests/posix/depth_error.out b/tests/posix/depth_error.out
new file mode 100644
index 0000000..7ed5f0d
--- /dev/null
+++ b/tests/posix/depth_error.out
@@ -0,0 +1,2 @@
+.
+./foo
diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh
new file mode 100644
index 0000000..db414ba
--- /dev/null
+++ b/tests/posix/depth_error.sh
@@ -0,0 +1,7 @@
+cd "$TEST"
+"$XTOUCH" -p foo/bar
+
+chmod a-r foo
+defer chmod +r foo
+
+! bfs_diff . -depth
diff --git a/tests/posix/depth_slash.out b/tests/posix/depth_slash.out
new file mode 100644
index 0000000..77526d5
--- /dev/null
+++ b/tests/posix/depth_slash.out
@@ -0,0 +1,19 @@
+basic/
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/depth_slash.sh b/tests/posix/depth_slash.sh
new file mode 100644
index 0000000..f73e9f1
--- /dev/null
+++ b/tests/posix/depth_slash.sh
@@ -0,0 +1 @@
+bfs_diff basic/ -depth
diff --git a/tests/posix/double_negation.out b/tests/posix/double_negation.out
new file mode 100644
index 0000000..e9d47b1
--- /dev/null
+++ b/tests/posix/double_negation.out
@@ -0,0 +1,3 @@
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/double_negation.sh b/tests/posix/double_negation.sh
new file mode 100644
index 0000000..eefe464
--- /dev/null
+++ b/tests/posix/double_negation.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \! -name 'foo'
diff --git a/tests/posix/exec.out b/tests/posix/exec.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/exec.sh b/tests/posix/exec.sh
new file mode 100644
index 0000000..96c897b
--- /dev/null
+++ b/tests/posix/exec.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec echo {} \;
diff --git a/tests/posix/exec_nonexistent.out b/tests/posix/exec_nonexistent.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec_nonexistent.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh
new file mode 100644
index 0000000..a9ff052
--- /dev/null
+++ b/tests/posix/exec_nonexistent.sh
@@ -0,0 +1,4 @@
+# Failure to execute the command should lead to an error message and
+# non-zero exit status. See https://unix.stackexchange.com/q/704522/56202
+bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print 2>"$TEST/err" && fail
+test -s "$TEST/err"
diff --git a/tests/posix/exec_nopath.out b/tests/posix/exec_nopath.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec_nopath.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/exec_nopath.sh b/tests/posix/exec_nopath.sh
new file mode 100644
index 0000000..6e05d2e
--- /dev/null
+++ b/tests/posix/exec_nopath.sh
@@ -0,0 +1,7 @@
+(
+ unset PATH
+ invoke_bfs basic -exec echo {} \; >"$OUT"
+)
+
+sort_output
+diff_output
diff --git a/tests/posix/exec_plus.out b/tests/posix/exec_plus.out
new file mode 100644
index 0000000..f6b423b
--- /dev/null
+++ b/tests/posix/exec_plus.out
@@ -0,0 +1 @@
+basic basic/a basic/b basic/c basic/c/d basic/e basic/e/f basic/g basic/g/h basic/i basic/j basic/j/foo basic/k basic/k/foo basic/k/foo/bar basic/l basic/l/foo basic/l/foo/bar basic/l/foo/bar/baz
diff --git a/tests/posix/exec_plus.sh b/tests/posix/exec_plus.sh
new file mode 100644
index 0000000..56a93f1
--- /dev/null
+++ b/tests/posix/exec_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec "$TESTS/sort-args.sh" {} +
diff --git a/tests/posix/exec_plus_nonexistent.out b/tests/posix/exec_plus_nonexistent.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec_plus_nonexistent.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh
new file mode 100644
index 0000000..24582a3
--- /dev/null
+++ b/tests/posix/exec_plus_nonexistent.sh
@@ -0,0 +1,2 @@
+bfs_diff basic -exec "$TESTS/nonexistent" {} + -print 2>"$TEST/err" && fail
+test -s "$TEST/err"
diff --git a/tests/posix/exec_plus_nothing.sh b/tests/posix/exec_plus_nothing.sh
new file mode 100644
index 0000000..347722d
--- /dev/null
+++ b/tests/posix/exec_plus_nothing.sh
@@ -0,0 +1,2 @@
+# Regression test: don't look OOB for {} +
+! invoke_bfs basic -exec +
diff --git a/tests/posix/exec_plus_semicolon.out b/tests/posix/exec_plus_semicolon.out
new file mode 100644
index 0000000..f33c48f
--- /dev/null
+++ b/tests/posix/exec_plus_semicolon.out
@@ -0,0 +1,19 @@
+foo basic bar + baz
+foo basic/a bar + baz
+foo basic/b bar + baz
+foo basic/c bar + baz
+foo basic/c/d bar + baz
+foo basic/e bar + baz
+foo basic/e/f bar + baz
+foo basic/g bar + baz
+foo basic/g/h bar + baz
+foo basic/i bar + baz
+foo basic/j bar + baz
+foo basic/j/foo bar + baz
+foo basic/k bar + baz
+foo basic/k/foo bar + baz
+foo basic/k/foo/bar bar + baz
+foo basic/l bar + baz
+foo basic/l/foo bar + baz
+foo basic/l/foo/bar bar + baz
+foo basic/l/foo/bar/baz bar + baz
diff --git a/tests/posix/exec_plus_semicolon.sh b/tests/posix/exec_plus_semicolon.sh
new file mode 100644
index 0000000..449a3f9
--- /dev/null
+++ b/tests/posix/exec_plus_semicolon.sh
@@ -0,0 +1,5 @@
+# POSIX says:
+# Only a <plus-sign> that immediately follows an argument containing only the two characters "{}"
+# shall punctuate the end of the primary expression. Other uses of the <plus-sign> shall not be
+# treated as special.
+bfs_diff basic -exec echo foo {} bar + baz \;
diff --git a/tests/posix/exec_plus_status.out b/tests/posix/exec_plus_status.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec_plus_status.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/exec_plus_status.sh b/tests/posix/exec_plus_status.sh
new file mode 100644
index 0000000..a814c4e
--- /dev/null
+++ b/tests/posix/exec_plus_status.sh
@@ -0,0 +1,3 @@
+# -exec ... {} + should always return true, but if the command fails, bfs
+# should exit with a non-zero status
+! bfs_diff basic -exec false {} + -print
diff --git a/tests/posix/exec_ulimit.out b/tests/posix/exec_ulimit.out
new file mode 100644
index 0000000..144169e
--- /dev/null
+++ b/tests/posix/exec_ulimit.out
@@ -0,0 +1,16 @@
+64 deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
+64 deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE
diff --git a/tests/posix/exec_ulimit.sh b/tests/posix/exec_ulimit.sh
new file mode 100644
index 0000000..655fbec
--- /dev/null
+++ b/tests/posix/exec_ulimit.sh
@@ -0,0 +1,2 @@
+ulimit -Sn 64
+bfs_diff deep -type f -exec bash -c 'printf "%d %s\n" $(ulimit -Sn) "${1:0:6}/.../${1##*/}"' bash {} \;
diff --git a/tests/posix/extra_paren.sh b/tests/posix/extra_paren.sh
new file mode 100644
index 0000000..d15022f
--- /dev/null
+++ b/tests/posix/extra_paren.sh
@@ -0,0 +1 @@
+! invoke_bfs basic -print \)
diff --git a/tests/posix/flag_comma.out b/tests/posix/flag_comma.out
new file mode 100644
index 0000000..3574388
--- /dev/null
+++ b/tests/posix/flag_comma.out
@@ -0,0 +1,2 @@
+,
+,/f
diff --git a/tests/posix/flag_comma.sh b/tests/posix/flag_comma.sh
new file mode 100644
index 0000000..cec87e7
--- /dev/null
+++ b/tests/posix/flag_comma.sh
@@ -0,0 +1,3 @@
+# , is a filename until a non-flag is seen
+cd weirdnames
+bfs_diff -L ',' -print
diff --git a/tests/posix/flag_weird_names.out b/tests/posix/flag_weird_names.out
new file mode 100644
index 0000000..c395659
--- /dev/null
+++ b/tests/posix/flag_weird_names.out
@@ -0,0 +1,28 @@
+!-
+!-
+!-/e
+!-/e
+(-
+(-
+(-/c
+(-/c
+)
+)
+)/g
+)/g
+,
+,
+,/f
+,/f
+-
+-
+-/a
+-/a
+./!
+./!
+./!/d
+./!/d
+./(
+./(
+./(/b
+./(/b
diff --git a/tests/posix/flag_weird_names.sh b/tests/posix/flag_weird_names.sh
new file mode 100644
index 0000000..f6596e9
--- /dev/null
+++ b/tests/posix/flag_weird_names.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -L '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
diff --git a/tests/posix/group_id.out b/tests/posix/group_id.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/group_id.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/group_id.sh b/tests/posix/group_id.sh
new file mode 100644
index 0000000..2ff7bb3
--- /dev/null
+++ b/tests/posix/group_id.sh
@@ -0,0 +1 @@
+bfs_diff basic -group "$(id -g)"
diff --git a/tests/posix/group_name.out b/tests/posix/group_name.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/group_name.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/group_name.sh b/tests/posix/group_name.sh
new file mode 100644
index 0000000..36799d9
--- /dev/null
+++ b/tests/posix/group_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -group "$(id -gn)"
diff --git a/tests/posix/group_nogroup.out b/tests/posix/group_nogroup.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/group_nogroup.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/group_nogroup.sh b/tests/posix/group_nogroup.sh
new file mode 100644
index 0000000..cbd1ffc
--- /dev/null
+++ b/tests/posix/group_nogroup.sh
@@ -0,0 +1,2 @@
+# Regression test: this was wrongly optimized to -false
+bfs_diff basic -group "$(id -g)" \! -nogroup
diff --git a/tests/posix/implicit_and.out b/tests/posix/implicit_and.out
new file mode 100644
index 0000000..722962c
--- /dev/null
+++ b/tests/posix/implicit_and.out
@@ -0,0 +1,2 @@
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/implicit_and.sh b/tests/posix/implicit_and.sh
new file mode 100644
index 0000000..161ab0b
--- /dev/null
+++ b/tests/posix/implicit_and.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -type d
diff --git a/tests/posix/incomplete.sh b/tests/posix/incomplete.sh
new file mode 100644
index 0000000..bca5a13
--- /dev/null
+++ b/tests/posix/incomplete.sh
@@ -0,0 +1 @@
+! invoke_bfs basic \(
diff --git a/tests/posix/links.out b/tests/posix/links.out
new file mode 100644
index 0000000..996ffc8
--- /dev/null
+++ b/tests/posix/links.out
@@ -0,0 +1,2 @@
+links/file
+links/hardlink
diff --git a/tests/posix/links.sh b/tests/posix/links.sh
new file mode 100644
index 0000000..3d8ad80
--- /dev/null
+++ b/tests/posix/links.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links 2
diff --git a/tests/posix/links_minus.out b/tests/posix/links_minus.out
new file mode 100644
index 0000000..eda26f1
--- /dev/null
+++ b/tests/posix/links_minus.out
@@ -0,0 +1 @@
+links/deeply/nested/file
diff --git a/tests/posix/links_minus.sh b/tests/posix/links_minus.sh
new file mode 100644
index 0000000..3ee0803
--- /dev/null
+++ b/tests/posix/links_minus.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links -2
diff --git a/tests/posix/links_plus.out b/tests/posix/links_plus.out
new file mode 100644
index 0000000..996ffc8
--- /dev/null
+++ b/tests/posix/links_plus.out
@@ -0,0 +1,2 @@
+links/file
+links/hardlink
diff --git a/tests/posix/links_plus.sh b/tests/posix/links_plus.sh
new file mode 100644
index 0000000..375834b
--- /dev/null
+++ b/tests/posix/links_plus.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links +1
diff --git a/tests/posix/missing_paren.sh b/tests/posix/missing_paren.sh
new file mode 100644
index 0000000..d906fbe
--- /dev/null
+++ b/tests/posix/missing_paren.sh
@@ -0,0 +1 @@
+! invoke_bfs basic \( -print
diff --git a/tests/posix/name.out b/tests/posix/name.out
new file mode 100644
index 0000000..a9e5d42
--- /dev/null
+++ b/tests/posix/name.out
@@ -0,0 +1,4 @@
+basic/e/f
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/name.sh b/tests/posix/name.sh
new file mode 100644
index 0000000..a673ad0
--- /dev/null
+++ b/tests/posix/name.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '*f*'
diff --git a/tests/posix/name_backslash.out b/tests/posix/name_backslash.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/name_backslash.out
diff --git a/tests/posix/name_backslash.sh b/tests/posix/name_backslash.sh
new file mode 100644
index 0000000..ff9b539
--- /dev/null
+++ b/tests/posix/name_backslash.sh
@@ -0,0 +1,2 @@
+# An unescaped \ doesn't match
+bfs_diff weirdnames -name '\'
diff --git a/tests/posix/name_bracket.out b/tests/posix/name_bracket.out
new file mode 100644
index 0000000..5ff3c0c
--- /dev/null
+++ b/tests/posix/name_bracket.out
@@ -0,0 +1 @@
+weirdnames/[
diff --git a/tests/posix/name_bracket.sh b/tests/posix/name_bracket.sh
new file mode 100644
index 0000000..e2f943d
--- /dev/null
+++ b/tests/posix/name_bracket.sh
@@ -0,0 +1,9 @@
+# fnmatch() is broken on some platforms
+case "$UNAME" in
+ Darwin|NetBSD)
+ skip
+ ;;
+esac
+
+# An unclosed [ should be matched literally
+bfs_diff weirdnames -name '['
diff --git a/tests/posix/name_character_class.out b/tests/posix/name_character_class.out
new file mode 100644
index 0000000..e9d47b1
--- /dev/null
+++ b/tests/posix/name_character_class.out
@@ -0,0 +1,3 @@
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/name_character_class.sh b/tests/posix/name_character_class.sh
new file mode 100644
index 0000000..ecda190
--- /dev/null
+++ b/tests/posix/name_character_class.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '[e-g][!a-n][!p-z]'
diff --git a/tests/posix/name_double_backslash.out b/tests/posix/name_double_backslash.out
new file mode 100644
index 0000000..45ceda0
--- /dev/null
+++ b/tests/posix/name_double_backslash.out
@@ -0,0 +1 @@
+weirdnames/\
diff --git a/tests/posix/name_double_backslash.sh b/tests/posix/name_double_backslash.sh
new file mode 100644
index 0000000..009553a
--- /dev/null
+++ b/tests/posix/name_double_backslash.sh
@@ -0,0 +1,2 @@
+# An escaped \\ matches
+bfs_diff weirdnames -name '\\'
diff --git a/tests/posix/name_root.out b/tests/posix/name_root.out
new file mode 100644
index 0000000..511198f
--- /dev/null
+++ b/tests/posix/name_root.out
@@ -0,0 +1 @@
+basic/a
diff --git a/tests/posix/name_root.sh b/tests/posix/name_root.sh
new file mode 100644
index 0000000..785861e
--- /dev/null
+++ b/tests/posix/name_root.sh
@@ -0,0 +1 @@
+bfs_diff basic/a -name a
diff --git a/tests/posix/name_root_depth.out b/tests/posix/name_root_depth.out
new file mode 100644
index 0000000..cf4d5a9
--- /dev/null
+++ b/tests/posix/name_root_depth.out
@@ -0,0 +1 @@
+basic/g
diff --git a/tests/posix/name_root_depth.sh b/tests/posix/name_root_depth.sh
new file mode 100644
index 0000000..dc3b8bb
--- /dev/null
+++ b/tests/posix/name_root_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -depth -name g
diff --git a/tests/posix/name_star_star.out b/tests/posix/name_star_star.out
new file mode 100644
index 0000000..a9e5d42
--- /dev/null
+++ b/tests/posix/name_star_star.out
@@ -0,0 +1,4 @@
+basic/e/f
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/name_star_star.sh b/tests/posix/name_star_star.sh
new file mode 100644
index 0000000..035f635
--- /dev/null
+++ b/tests/posix/name_star_star.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '**f**'
diff --git a/tests/posix/name_trailing_slash.out b/tests/posix/name_trailing_slash.out
new file mode 100644
index 0000000..daff2f5
--- /dev/null
+++ b/tests/posix/name_trailing_slash.out
@@ -0,0 +1 @@
+basic/g/
diff --git a/tests/posix/name_trailing_slash.sh b/tests/posix/name_trailing_slash.sh
new file mode 100644
index 0000000..ab058d1
--- /dev/null
+++ b/tests/posix/name_trailing_slash.sh
@@ -0,0 +1 @@
+bfs_diff basic/g/ -name g
diff --git a/tests/posix/newer.out b/tests/posix/newer.out
new file mode 100644
index 0000000..7f6c0dd
--- /dev/null
+++ b/tests/posix/newer.out
@@ -0,0 +1,4 @@
+times
+times/b
+times/c
+times/l
diff --git a/tests/posix/newer.sh b/tests/posix/newer.sh
new file mode 100644
index 0000000..860623a
--- /dev/null
+++ b/tests/posix/newer.sh
@@ -0,0 +1 @@
+bfs_diff times -newer times/a
diff --git a/tests/posix/newer_broken.out b/tests/posix/newer_broken.out
new file mode 100644
index 0000000..d2dcdd1
--- /dev/null
+++ b/tests/posix/newer_broken.out
@@ -0,0 +1 @@
+times
diff --git a/tests/posix/newer_broken.sh b/tests/posix/newer_broken.sh
new file mode 100644
index 0000000..dccaa73
--- /dev/null
+++ b/tests/posix/newer_broken.sh
@@ -0,0 +1,4 @@
+ln -s nowhere "$TEST/broken"
+"$XTOUCH" -h -t "1991-12-14 00:03" "$TEST/broken"
+
+bfs_diff times -newer "$TEST/broken"
diff --git a/tests/posix/newer_nonexistent.sh b/tests/posix/newer_nonexistent.sh
new file mode 100644
index 0000000..5f2da4b
--- /dev/null
+++ b/tests/posix/newer_nonexistent.sh
@@ -0,0 +1 @@
+! invoke_bfs times -newer times/nonexistent
diff --git a/tests/posix/nogroup.out b/tests/posix/nogroup.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/nogroup.out
diff --git a/tests/posix/nogroup.sh b/tests/posix/nogroup.sh
new file mode 100644
index 0000000..60ffd68
--- /dev/null
+++ b/tests/posix/nogroup.sh
@@ -0,0 +1 @@
+bfs_diff basic -nogroup
diff --git a/tests/posix/nogroup_ulimit.out b/tests/posix/nogroup_ulimit.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/nogroup_ulimit.out
diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh
new file mode 100644
index 0000000..a39dd1f
--- /dev/null
+++ b/tests/posix/nogroup_ulimit.sh
@@ -0,0 +1,2 @@
+ulimit -n $((NOPENFD + 13))
+bfs_diff deep -type f -nogroup
diff --git a/tests/posix/not_prune.out b/tests/posix/not_prune.out
new file mode 100644
index 0000000..59e3c42
--- /dev/null
+++ b/tests/posix/not_prune.out
@@ -0,0 +1,13 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/l
diff --git a/tests/posix/not_prune.sh b/tests/posix/not_prune.sh
new file mode 100644
index 0000000..6d7b092
--- /dev/null
+++ b/tests/posix/not_prune.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -name foo -prune \)
diff --git a/tests/posix/nouser.out b/tests/posix/nouser.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/nouser.out
diff --git a/tests/posix/nouser.sh b/tests/posix/nouser.sh
new file mode 100644
index 0000000..e7c48c0
--- /dev/null
+++ b/tests/posix/nouser.sh
@@ -0,0 +1 @@
+bfs_diff basic -nouser
diff --git a/tests/posix/nouser_ulimit.out b/tests/posix/nouser_ulimit.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/nouser_ulimit.out
diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh
new file mode 100644
index 0000000..a94b8c5
--- /dev/null
+++ b/tests/posix/nouser_ulimit.sh
@@ -0,0 +1,2 @@
+ulimit -n $((NOPENFD + 13))
+bfs_diff deep -type f -nouser
diff --git a/tests/posix/o.out b/tests/posix/o.out
new file mode 100644
index 0000000..1650c4d
--- /dev/null
+++ b/tests/posix/o.out
@@ -0,0 +1,13 @@
+basic
+basic/c
+basic/e
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/l
+basic/l/foo
+basic/l/foo/bar
diff --git a/tests/posix/o.sh b/tests/posix/o.sh
new file mode 100644
index 0000000..6dcd442
--- /dev/null
+++ b/tests/posix/o.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -o -type d
diff --git a/tests/posix/ok_plus_nothing.sh b/tests/posix/ok_plus_nothing.sh
new file mode 100644
index 0000000..77c7644
--- /dev/null
+++ b/tests/posix/ok_plus_nothing.sh
@@ -0,0 +1,2 @@
+# Regression test: don't look OOB for {} +
+! invoke_bfs basic -ok +
diff --git a/tests/posix/ok_stdin.out b/tests/posix/ok_stdin.out
new file mode 100644
index 0000000..7acf711
--- /dev/null
+++ b/tests/posix/ok_stdin.out
@@ -0,0 +1,19 @@
+basic/a? y
+basic/b? y
+basic/c/d? y
+basic/c? y
+basic/e/f? y
+basic/e? y
+basic/g/h? y
+basic/g? y
+basic/i? y
+basic/j/foo? y
+basic/j? y
+basic/k/foo/bar? y
+basic/k/foo? y
+basic/k? y
+basic/l/foo/bar/baz? y
+basic/l/foo/bar? y
+basic/l/foo? y
+basic/l? y
+basic? y
diff --git a/tests/posix/ok_stdin.sh b/tests/posix/ok_stdin.sh
new file mode 100644
index 0000000..a190d81
--- /dev/null
+++ b/tests/posix/ok_stdin.sh
@@ -0,0 +1,3 @@
+# -ok should *not* close stdin
+# See https://savannah.gnu.org/bugs/?24561
+yes | bfs_diff basic -ok bash -c 'printf "%s? " "$1" && head -n1' bash {} \;
diff --git a/tests/posix/or_purity.out b/tests/posix/or_purity.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/or_purity.out
diff --git a/tests/posix/or_purity.sh b/tests/posix/or_purity.sh
new file mode 100644
index 0000000..277b18b
--- /dev/null
+++ b/tests/posix/or_purity.sh
@@ -0,0 +1,2 @@
+# Regression test: (-o lhs(pure) rhs(always_true)) <==> rhs is only valid if rhs is pure
+bfs_diff basic -name '*' -o -print
diff --git a/tests/posix/overlayfs.out b/tests/posix/overlayfs.out
new file mode 100644
index 0000000..b472b56
--- /dev/null
+++ b/tests/posix/overlayfs.out
@@ -0,0 +1,5 @@
+merged
+merged/bar
+merged/baz
+merged/baz/qux
+merged/foo
diff --git a/tests/posix/overlayfs.sh b/tests/posix/overlayfs.sh
new file mode 100644
index 0000000..21ef22f
--- /dev/null
+++ b/tests/posix/overlayfs.sh
@@ -0,0 +1,11 @@
+test "$UNAME" = "Linux" || skip
+
+cd "$TEST"
+"$XTOUCH" -p lower/{foo,bar,baz} upper/{bar,baz/qux}
+
+mkdir -p work merged
+bfs_sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged || skip
+defer bfs_sudo rm -rf work
+defer bfs_sudo umount merged
+
+bfs_diff merged
diff --git a/tests/posix/parens.out b/tests/posix/parens.out
new file mode 100644
index 0000000..a9e5d42
--- /dev/null
+++ b/tests/posix/parens.out
@@ -0,0 +1,4 @@
+basic/e/f
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/parens.sh b/tests/posix/parens.sh
new file mode 100644
index 0000000..abbb20f
--- /dev/null
+++ b/tests/posix/parens.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -name '*f*' \)
diff --git a/tests/posix/path.out b/tests/posix/path.out
new file mode 100644
index 0000000..ae1ae21
--- /dev/null
+++ b/tests/posix/path.out
@@ -0,0 +1,7 @@
+basic/e/f
+basic/j/foo
+basic/k/foo
+basic/k/foo/bar
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/path.sh b/tests/posix/path.sh
new file mode 100644
index 0000000..04606eb
--- /dev/null
+++ b/tests/posix/path.sh
@@ -0,0 +1 @@
+bfs_diff basic -path 'basic/*f*'
diff --git a/tests/posix/perm_000.out b/tests/posix/perm_000.out
new file mode 100644
index 0000000..5fd30bc
--- /dev/null
+++ b/tests/posix/perm_000.out
@@ -0,0 +1 @@
+perms/0
diff --git a/tests/posix/perm_000.sh b/tests/posix/perm_000.sh
new file mode 100644
index 0000000..ee25f23
--- /dev/null
+++ b/tests/posix/perm_000.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 000
diff --git a/tests/posix/perm_000_minus.out b/tests/posix/perm_000_minus.out
new file mode 100644
index 0000000..d7494b8
--- /dev/null
+++ b/tests/posix/perm_000_minus.out
@@ -0,0 +1,8 @@
+perms
+perms/0
+perms/r
+perms/rw
+perms/rwx
+perms/rx
+perms/w
+perms/wx
diff --git a/tests/posix/perm_000_minus.sh b/tests/posix/perm_000_minus.sh
new file mode 100644
index 0000000..5027b91
--- /dev/null
+++ b/tests/posix/perm_000_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -000
diff --git a/tests/posix/perm_222.out b/tests/posix/perm_222.out
new file mode 100644
index 0000000..1690e43
--- /dev/null
+++ b/tests/posix/perm_222.out
@@ -0,0 +1 @@
+perms/w
diff --git a/tests/posix/perm_222.sh b/tests/posix/perm_222.sh
new file mode 100644
index 0000000..40f5804
--- /dev/null
+++ b/tests/posix/perm_222.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 222
diff --git a/tests/posix/perm_222_minus.out b/tests/posix/perm_222_minus.out
new file mode 100644
index 0000000..1690e43
--- /dev/null
+++ b/tests/posix/perm_222_minus.out
@@ -0,0 +1 @@
+perms/w
diff --git a/tests/posix/perm_222_minus.sh b/tests/posix/perm_222_minus.sh
new file mode 100644
index 0000000..4e7ad5a
--- /dev/null
+++ b/tests/posix/perm_222_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -222
diff --git a/tests/posix/perm_644.out b/tests/posix/perm_644.out
new file mode 100644
index 0000000..4e64e49
--- /dev/null
+++ b/tests/posix/perm_644.out
@@ -0,0 +1 @@
+perms/rw
diff --git a/tests/posix/perm_644.sh b/tests/posix/perm_644.sh
new file mode 100644
index 0000000..9a4f41d
--- /dev/null
+++ b/tests/posix/perm_644.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 644
diff --git a/tests/posix/perm_644_minus.out b/tests/posix/perm_644_minus.out
new file mode 100644
index 0000000..2e2576b
--- /dev/null
+++ b/tests/posix/perm_644_minus.out
@@ -0,0 +1,3 @@
+perms
+perms/rw
+perms/rwx
diff --git a/tests/posix/perm_644_minus.sh b/tests/posix/perm_644_minus.sh
new file mode 100644
index 0000000..6464f84
--- /dev/null
+++ b/tests/posix/perm_644_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -644
diff --git a/tests/posix/perm_leading_plus_symbolic_minus.out b/tests/posix/perm_leading_plus_symbolic_minus.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/perm_leading_plus_symbolic_minus.out
diff --git a/tests/posix/perm_leading_plus_symbolic_minus.sh b/tests/posix/perm_leading_plus_symbolic_minus.sh
new file mode 100644
index 0000000..60389c0
--- /dev/null
+++ b/tests/posix/perm_leading_plus_symbolic_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -+rwx
diff --git a/tests/posix/perm_setid.out b/tests/posix/perm_setid.out
new file mode 100644
index 0000000..865a74e
--- /dev/null
+++ b/tests/posix/perm_setid.out
@@ -0,0 +1,3 @@
+rainbow/sgid
+rainbow/sugid
+rainbow/suid
diff --git a/tests/posix/perm_setid.sh b/tests/posix/perm_setid.sh
new file mode 100644
index 0000000..3b98647
--- /dev/null
+++ b/tests/posix/perm_setid.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -perm -u+s -o -perm -g+s
diff --git a/tests/posix/perm_sticky.out b/tests/posix/perm_sticky.out
new file mode 100644
index 0000000..c07eb61
--- /dev/null
+++ b/tests/posix/perm_sticky.out
@@ -0,0 +1,2 @@
+rainbow/sticky
+rainbow/sticky_ow
diff --git a/tests/posix/perm_sticky.sh b/tests/posix/perm_sticky.sh
new file mode 100644
index 0000000..6bdf8e9
--- /dev/null
+++ b/tests/posix/perm_sticky.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -perm -a+t
diff --git a/tests/posix/perm_symbolic.out b/tests/posix/perm_symbolic.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/posix/perm_symbolic.out
diff --git a/tests/posix/perm_symbolic.sh b/tests/posix/perm_symbolic.sh
new file mode 100644
index 0000000..5cfddb6
--- /dev/null
+++ b/tests/posix/perm_symbolic.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm a+r,u=wX,g+wX-w
diff --git a/tests/posix/perm_symbolic_minus.out b/tests/posix/perm_symbolic_minus.out
new file mode 100644
index 0000000..2e2576b
--- /dev/null
+++ b/tests/posix/perm_symbolic_minus.out
@@ -0,0 +1,3 @@
+perms
+perms/rw
+perms/rwx
diff --git a/tests/posix/perm_symbolic_minus.sh b/tests/posix/perm_symbolic_minus.sh
new file mode 100644
index 0000000..b6ba3a5
--- /dev/null
+++ b/tests/posix/perm_symbolic_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -a+r,u=wX,g+wX-w
diff --git a/tests/posix/permcopy.out b/tests/posix/permcopy.out
new file mode 100644
index 0000000..4e64e49
--- /dev/null
+++ b/tests/posix/permcopy.out
@@ -0,0 +1 @@
+perms/rw
diff --git a/tests/posix/permcopy.sh b/tests/posix/permcopy.sh
new file mode 100644
index 0000000..3c85cce
--- /dev/null
+++ b/tests/posix/permcopy.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm u+rw,g+u-w,o=g
diff --git a/tests/posix/prune.out b/tests/posix/prune.out
new file mode 100644
index 0000000..e9d47b1
--- /dev/null
+++ b/tests/posix/prune.out
@@ -0,0 +1,3 @@
+basic/j/foo
+basic/k/foo
+basic/l/foo
diff --git a/tests/posix/prune.sh b/tests/posix/prune.sh
new file mode 100644
index 0000000..b48ab48
--- /dev/null
+++ b/tests/posix/prune.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -prune
diff --git a/tests/posix/prune_file.out b/tests/posix/prune_file.out
new file mode 100644
index 0000000..7575ae4
--- /dev/null
+++ b/tests/posix/prune_file.out
@@ -0,0 +1,10 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/e
+basic/g
+basic/i
+basic/j
+basic/k
+basic/l
diff --git a/tests/posix/prune_file.sh b/tests/posix/prune_file.sh
new file mode 100644
index 0000000..29a3a33
--- /dev/null
+++ b/tests/posix/prune_file.sh
@@ -0,0 +1 @@
+bfs_diff basic -print -name '?' -prune
diff --git a/tests/posix/prune_or_print.out b/tests/posix/prune_or_print.out
new file mode 100644
index 0000000..59e3c42
--- /dev/null
+++ b/tests/posix/prune_or_print.out
@@ -0,0 +1,13 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/l
diff --git a/tests/posix/prune_or_print.sh b/tests/posix/prune_or_print.sh
new file mode 100644
index 0000000..85b97fd
--- /dev/null
+++ b/tests/posix/prune_or_print.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -prune -o -print
diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh
new file mode 100644
index 0000000..82fcd17
--- /dev/null
+++ b/tests/posix/readdir_error.sh
@@ -0,0 +1,37 @@
+test "$UNAME" = "Linux" || skip
+
+cd "$TEST"
+mkfifo hang pid wait running
+
+(
+ # Create a zombie process
+ cat hang >/dev/null &
+ # Write the PID to pid
+ echo $! >pid
+ # Don't wait on the zombie process
+ exec cat wait hang >running
+) &
+
+# Kill the parent cat on exit
+defer kill -9 %1
+
+# Read the child PID
+read -r pid <pid
+
+# Make sure the parent cat is running before we kill the child, because bash
+# will wait() on its children
+echo >wait &
+read -r _ <running
+
+# Turn the child into a zombie
+kill -9 "$pid"
+
+# Wait until it's really a zombie
+state=R
+while [ "$state" != "Z" ]; do
+ read -r _ _ state _ <"/proc/$pid/stat"
+done
+
+# On Linux, open(/proc/$pid/net) will succeed but readdir() will fail
+test -r "/proc/$pid/net" || skip
+! invoke_bfs "/proc/$pid/net" >/dev/null
diff --git a/tests/posix/size.out b/tests/posix/size.out
new file mode 100644
index 0000000..eeabbd7
--- /dev/null
+++ b/tests/posix/size.out
@@ -0,0 +1,6 @@
+basic/a
+basic/b
+basic/c/d
+basic/e/f
+basic/j/foo
+basic/k/foo/bar
diff --git a/tests/posix/size.sh b/tests/posix/size.sh
new file mode 100644
index 0000000..1e7528a
--- /dev/null
+++ b/tests/posix/size.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size 0
diff --git a/tests/posix/size_bytes.out b/tests/posix/size_bytes.out
new file mode 100644
index 0000000..279f3f1
--- /dev/null
+++ b/tests/posix/size_bytes.out
@@ -0,0 +1 @@
+basic/l/foo/bar/baz
diff --git a/tests/posix/size_bytes.sh b/tests/posix/size_bytes.sh
new file mode 100644
index 0000000..6a68321
--- /dev/null
+++ b/tests/posix/size_bytes.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size +0c
diff --git a/tests/posix/size_plus.out b/tests/posix/size_plus.out
new file mode 100644
index 0000000..279f3f1
--- /dev/null
+++ b/tests/posix/size_plus.out
@@ -0,0 +1 @@
+basic/l/foo/bar/baz
diff --git a/tests/posix/size_plus.sh b/tests/posix/size_plus.sh
new file mode 100644
index 0000000..01853d5
--- /dev/null
+++ b/tests/posix/size_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size +0
diff --git a/tests/posix/type_bind_mount.out b/tests/posix/type_bind_mount.out
new file mode 100644
index 0000000..2f06c47
--- /dev/null
+++ b/tests/posix/type_bind_mount.out
@@ -0,0 +1 @@
+./null
diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh
new file mode 100644
index 0000000..97b7305
--- /dev/null
+++ b/tests/posix/type_bind_mount.sh
@@ -0,0 +1,9 @@
+test "$UNAME" = "Linux" || skip
+
+cd "$TEST"
+"$XTOUCH" file null
+
+bfs_sudo mount --bind /dev/null null || skip
+defer bfs_sudo umount null
+
+bfs_diff . -type c
diff --git a/tests/posix/type_d.out b/tests/posix/type_d.out
new file mode 100644
index 0000000..e604709
--- /dev/null
+++ b/tests/posix/type_d.out
@@ -0,0 +1,12 @@
+basic
+basic/c
+basic/e
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/k
+basic/k/foo
+basic/l
+basic/l/foo
+basic/l/foo/bar
diff --git a/tests/posix/type_d.sh b/tests/posix/type_d.sh
new file mode 100644
index 0000000..8d06b73
--- /dev/null
+++ b/tests/posix/type_d.sh
@@ -0,0 +1 @@
+bfs_diff basic -type d
diff --git a/tests/posix/type_f.out b/tests/posix/type_f.out
new file mode 100644
index 0000000..6218a0c
--- /dev/null
+++ b/tests/posix/type_f.out
@@ -0,0 +1,7 @@
+basic/a
+basic/b
+basic/c/d
+basic/e/f
+basic/j/foo
+basic/k/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/type_f.sh b/tests/posix/type_f.sh
new file mode 100644
index 0000000..1fd0c8c
--- /dev/null
+++ b/tests/posix/type_f.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f
diff --git a/tests/posix/type_l.out b/tests/posix/type_l.out
new file mode 100644
index 0000000..f2c8b19
--- /dev/null
+++ b/tests/posix/type_l.out
@@ -0,0 +1 @@
+links/skip
diff --git a/tests/posix/type_l.sh b/tests/posix/type_l.sh
new file mode 100644
index 0000000..457f74d
--- /dev/null
+++ b/tests/posix/type_l.sh
@@ -0,0 +1 @@
+bfs_diff links/skip -type l
diff --git a/tests/posix/unionfs.out b/tests/posix/unionfs.out
new file mode 100644
index 0000000..28c4ec1
--- /dev/null
+++ b/tests/posix/unionfs.out
@@ -0,0 +1,10 @@
+.
+./lower
+./lower/bar
+./lower/baz
+./lower/foo
+./upper
+./upper/bar
+./upper/baz
+./upper/baz/qux
+./upper/foo
diff --git a/tests/posix/unionfs.sh b/tests/posix/unionfs.sh
new file mode 100644
index 0000000..94d3929
--- /dev/null
+++ b/tests/posix/unionfs.sh
@@ -0,0 +1,9 @@
+[[ "$UNAME" == *BSD* ]] || skip
+
+cd "$TEST"
+"$XTOUCH" -p lower/{foo,bar,baz} upper/{bar,baz/qux}
+
+bfs_sudo mount -t unionfs -o below lower upper || skip
+defer bfs_sudo umount upper
+
+bfs_diff .
diff --git a/tests/posix/user_id.out b/tests/posix/user_id.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/user_id.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/user_id.sh b/tests/posix/user_id.sh
new file mode 100644
index 0000000..c3e4b31
--- /dev/null
+++ b/tests/posix/user_id.sh
@@ -0,0 +1 @@
+bfs_diff basic -user "$(id -u)"
diff --git a/tests/posix/user_name.out b/tests/posix/user_name.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/user_name.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/user_name.sh b/tests/posix/user_name.sh
new file mode 100644
index 0000000..8599249
--- /dev/null
+++ b/tests/posix/user_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -user "$(id -un)"
diff --git a/tests/posix/user_nouser.out b/tests/posix/user_nouser.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/user_nouser.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/c/d
+basic/e
+basic/e/f
+basic/g
+basic/g/h
+basic/i
+basic/j
+basic/j/foo
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar/baz
diff --git a/tests/posix/user_nouser.sh b/tests/posix/user_nouser.sh
new file mode 100644
index 0000000..e72bd45
--- /dev/null
+++ b/tests/posix/user_nouser.sh
@@ -0,0 +1,2 @@
+# Regression test: this was wrongly optimized to -false
+bfs_diff basic -user "$(id -u)" \! -nouser
diff --git a/tests/posix/weird_names.out b/tests/posix/weird_names.out
new file mode 100644
index 0000000..c395659
--- /dev/null
+++ b/tests/posix/weird_names.out
@@ -0,0 +1,28 @@
+!-
+!-
+!-/e
+!-/e
+(-
+(-
+(-/c
+(-/c
+)
+)
+)/g
+)/g
+,
+,
+,/f
+,/f
+-
+-
+-/a
+-/a
+./!
+./!
+./!/d
+./!/d
+./(
+./(
+./(/b
+./(/b
diff --git a/tests/posix/weird_names.sh b/tests/posix/weird_names.sh
new file mode 100644
index 0000000..8a9a8cd
--- /dev/null
+++ b/tests/posix/weird_names.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
diff --git a/tests/posix/xdev.out b/tests/posix/xdev.out
new file mode 100644
index 0000000..6253434
--- /dev/null
+++ b/tests/posix/xdev.out
@@ -0,0 +1,4 @@
+.
+./foo
+./foo/bar
+./mnt
diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh
new file mode 100644
index 0000000..c59c5c8
--- /dev/null
+++ b/tests/posix/xdev.sh
@@ -0,0 +1,11 @@
+test "$UNAME" = "Darwin" && skip
+
+cd "$TEST"
+mkdir foo mnt
+
+bfs_sudo mount -t tmpfs tmpfs mnt || skip
+defer bfs_sudo umount mnt
+
+"$XTOUCH" foo/bar mnt/baz
+
+bfs_diff . -xdev