summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2022-04-19 11:30:47 -0400
committerTavian Barnes <tavianator@tavianator.com>2022-04-21 20:03:16 -0400
commitaa6797db463b5143c5ca05bb5e90b05d2f72122a (patch)
tree4ad640bd5d851b7c594fa2f5efaa872ceece6941 /docs
parent7f37e4131c24c08e0575db06ccf7ea986c4cc190 (diff)
docs: Move some documentation into a subfolder
Diffstat (limited to 'docs')
-rw-r--r--docs/CHANGELOG.md655
-rw-r--r--docs/CONTRIBUTING.md167
-rw-r--r--docs/bfs.1811
3 files changed, 1633 insertions, 0 deletions
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
new file mode 100644
index 0000000..aafc497
--- /dev/null
+++ b/docs/CHANGELOG.md
@@ -0,0 +1,655 @@
+2.*
+===
+
+2.5
+---
+
+**March 27, 2022**
+
+- Added compiler-style context for errors and warnings.
+ Errors look like this:
+
+ $ bfs -nam needle
+ bfs: error: bfs -nam needle
+ bfs: error: ~~~~
+ bfs: error: Unknown argument; did you mean -name?
+
+ and warnings look like this:
+
+ $ bfs -print -name 'needle'
+ bfs: warning: bfs -print -name needle
+ bfs: warning: ~~~~~~~~~~~~
+ bfs: warning: The result of this expression is ignored.
+
+- Updated from C99 to C11
+
+- Fixed the tests when built against musl
+
+- Fixed a build error reported on Manjaro
+
+
+2.4.1
+-----
+
+**February 24, 2022**
+
+- Fixed the build when Oniguruma is not installed in the default search paths ([#82])
+
+- Fixed string encoding bugs with Oniguruma enabled
+
+- Fixed regex error reporting bugs
+
+[#82]: https://github.com/tavianator/bfs/issues/82
+
+
+2.4
+---
+
+**February 22, 2022**
+
+- Added the Oniguruma regular expression library as an (optional, but enabled by default) dependency ([#81]).
+ Oniguruma supports more regular expression syntax types than the POSIX regex API, and often performs better.
+ To build `bfs` without this new dependency, do `make WITH_ONIGURUMA=` to disable it.
+ Thanks @data-man!
+
+- Added support for the `ed`, `emacs`, `grep`, and `sed` regular expression types ([#21])
+
+- Before executing a process with `-exec[dir]`/`-ok[dir]`, `bfs` now ensures all output streams are flushed.
+ Previously, I/O from subprocesses could be interleaved unpredictably with buffered I/O from `bfs` itself.
+
+[#81]: https://github.com/tavianator/bfs/pull/81
+[#21]: https://github.com/tavianator/bfs/issues/21
+
+
+2.3.1
+-----
+
+**January 21, 2022**
+
+- Fixed the build on Debian kFreeBSD
+
+- Fixed a crash on GNU Hurd when piping bfs's output
+
+- Fixed a double-`close()` on non-Linux platforms if `fdopendir()` fails
+
+- Reduced memory allocations on startup
+
+
+2.3
+---
+
+**November 25, 2021**
+
+- More tweaks to `PAGER` and `LESS` handling for `bfs -help` ([#76])
+
+- Use 512-byte blocks for `-ls` when `POSIXLY_CORRECT` is set ([#77])
+
+- Implemented `-files0-from FILE` to take a list of `'\0'`-separated starting paths.
+ GNU find will implement the same feature in an upcoming release.
+
+- Added colors to `-printf` output ([#62])
+
+- Faster recovery from `E2BIG` during `-exec`
+
+[#76]: https://github.com/tavianator/bfs/issues/76
+[#77]: https://github.com/tavianator/bfs/issues/77
+[#62]: https://github.com/tavianator/bfs/issues/62
+
+
+2.2.1
+-----
+
+**June 2, 2021**
+
+- Fixed some incorrect coloring of broken links when links are being followed (`-L`)
+
+- Made the tests work when run as root by dropping privileges.
+ This may be helpful for certain packaging or CI environments, but is not recommended.
+
+- Treat empty `PAGER` and `LESS` environment variables like they're unset, for `bfs -help` ([#71]).
+ Thanks @markus-oberhumer!
+
+- The soft `RLIMIT_NOFILE` is now raised automatically to a fairly large value when possible.
+ This provides a minor performance benefit for large directory trees.
+
+- Implemented time units for `-mtime` as found in FreeBSD find ([#75])
+
+[#71]: https://github.com/tavianator/bfs/issues/71
+[#75]: https://github.com/tavianator/bfs/issues/75
+
+
+2.2
+---
+
+**March 6, 2021**
+
+- Fixed `-hidden` on hidden start paths
+
+- Added a Bash completion script.
+ Thanks @bmundt6!
+
+- Fixed rounding in `-used`.
+ Corresponding fixes were made to GNU find in version 4.8.0.
+
+- Optimized the open directory representation.
+ On Linux, much libc overhead is bypassed by issuing syscalls directly.
+ On all platforms, a few fewer syscalls and open file descriptors will be used.
+
+- Implemented `-flags` from BSD find
+
+
+2.1
+---
+
+**November 11, 2020**
+
+- Added a new `-status` option that displays the search progress in a bar at the bottom of the terminal
+
+- Fixed an optimizer bug introduced in version 2.0 that affected some combinations of `-user`/`-group` and `-nouser`/`-nogroup`
+
+
+2.0
+---
+
+**October 14, 2020**
+
+- [#8]: New `-exclude <expression>` syntax to more easily and reliably filter out paths.
+ For example:
+
+ bfs -name config -exclude -name .git
+
+ will find all files named `config`, without searching any directories (or files) named `.git`.
+ In this case, the same effect could have been achieved (more awkwardly) with `-prune`:
+
+ bfs ! \( -name .git -prune \) -name config
+
+ But `-exclude` will work in more cases:
+
+ # -exclude works with -depth, while -prune doesn't:
+ bfs -depth -name config -exclude -name .git
+
+ # -exclude applies even to paths below the minimum depth:
+ bfs -mindepth 3 -name config -exclude -name .git
+
+- [#30]: `-nohidden` is now equivalent to `-exclude -hidden`.
+ This changes the behavior of command lines like
+
+ bfs -type f -nohidden
+
+ to do what was intended.
+
+- Optimized the iterative deepening (`-S ids`) implementation
+
+- Added a new search strategy: exponential deepening search (`-S eds`).
+ This strategy provides many of the benefits of iterative deepening, but much faster due to fewer re-traversals.
+
+- Fixed an optimizer bug that could skip `-empty`/`-xtype` if they didn't always lead to an action
+
+- Implemented `-xattrname` to find files with a particular extended attribute (from macOS find)
+
+- Made `-printf %l` still respect the width specifier (e.g. `%10l`) for non-links, to match GNU find
+
+- Made `bfs` fail if `-color` is given explicitly and `LS_COLORS` can't be parsed, rather than falling back to non-colored output
+
+[#8]: https://github.com/tavianator/bfs/issues/8
+[#30]: https://github.com/tavianator/bfs/issues/30
+
+
+1.*
+===
+
+1.7
+---
+
+**April 22, 2020**
+
+- Fixed `-ls` printing numeric IDs instead of user/group names in large directory trees
+- Cached the user and group tables for a performance boost
+- Fixed interpretation of "default" ACLs
+- Implemented `-s` flag to sort results
+
+
+1.6
+---
+
+**February 25, 2020**
+
+- Implemented `-newerXt` (explicit reference times), `-since`, `-asince`, etc.
+- Fixed `-empty` to skip special files (pipes, devices, sockets, etc.)
+
+
+1.5.2
+-----
+
+**January 9, 2020**
+
+- Fixed the build on NetBSD
+- Added support for NFSv4 ACLs on FreeBSD
+- Added a `+` after the file mode for files with ACLs in `-ls`
+- Supported more file types (whiteouts, doors) in symbolic modes for `-ls`/`-printf %M`
+- Implemented `-xattr` on FreeBSD
+
+
+1.5.1
+-----
+
+**September 14, 2019**
+
+- Added a warning to `-mount`, since it will change behaviour in the next POSIX revision
+- Added a workaround for environments that block `statx()` with `seccomp()`, like older Docker
+- Fixed coloring of nonexistent leading directories
+- Avoided calling `stat()` on all mount points at startup
+
+
+1.5
+---
+
+**June 27, 2019**
+
+- New `-xattr` predicate to find files with extended attributes
+- Fixed the `-acl` implementation on macOS
+- Implemented depth-first (`-S dfs`) and iterative deepening search (`-S ids`)
+- Piped `-help` output into `$PAGER` by default
+- Fixed crashes on some invalid `LS_COLORS` values
+
+
+1.4.1
+-----
+
+**April 5, 2019**
+
+- Added a nicer error message when the tests are run as root
+- Fixed detection of comparison expressions with signs, to match GNU find for things like `-uid ++10`
+- Added support for https://no-color.org/
+- Decreased the number of `stat()` calls necessary in some cases
+
+
+1.4
+---
+
+**April 15, 2019**
+
+- New `-unique` option that filters out duplicate files ([#48])
+- Optimized the file coloring implementation
+- Fixed the coloring implementation to match GNU ls more closely in many corner cases
+ - Implemented escape sequence parsing for `LS_COLORS`
+ - Implemented `ln=target` for coloring links like their targets
+ - Fixed the order of fallbacks used when some color keys are unset
+- Add a workaround for incorrect file types for bind-mounted files on Linux ([#37])
+
+[#48]: https://github.com/tavianator/bfs/issues/48
+[#37]: https://github.com/tavianator/bfs/issues/37
+
+
+1.3.3
+-----
+
+**February 10, 2019**
+
+- Fixed unpredictable behaviour for empty responses to `-ok`/`-okdir` caused by an uninitialized string
+- Writing to standard output now causes `bfs` to fail if the descriptor was closed
+- Fixed incomplete file coloring in error messages
+- Added some data flow optimizations
+- Fixed `-nogroup`/`-nouser` in big directory trees
+- Added `-type w` for whiteouts, as supported by FreeBSD `find`
+- Re-wrote the `-help` message and manual page
+
+
+1.3.2
+-----
+
+**January 11, 2019**
+
+- Fixed an out-of-bounds read if LS_COLORS doesn't end with a `:`
+- Allowed multiple debug flags to be specified like `-D opt,tree`
+
+
+1.3.1
+-----
+
+**January 3, 2019**
+
+- Fixed some portability problems affecting FreeBSD
+
+
+1.3
+---
+
+**January 2, 2019**
+
+New features:
+
+- `-acl` finds files with non-trivial Access Control Lists (from FreeBSD)
+- `-capable` finds files with capabilities set
+- `-D all` turns on all debugging flags at once
+
+Fixes:
+
+- `LS_COLORS` handling has been improved:
+ - Extension colors are now case-insensitive like GNU `ls`
+ - `or` (orphan) and `mi` (missing) files are now treated differently
+ - Default colors can be unset with `di=00` or similar
+ - Specific colors fall back to more general colors when unspecified in more places
+ - `LS_COLORS` no longer needs a trailing colon
+- `-ls`/`-fls` now prints the major/minor numbers for device nodes
+- `-exec ;` is rejected rather than segfaulting
+- `bfs` now builds on old Linux versions that require `-lrt` for POSIX timers
+- For files whose access/change/modification times can't be read, `bfs` no longer fails unless those times are needed for tests
+- The testsuite is now more correct and portable
+
+
+1.2.4
+-----
+
+**September 24, 2018**
+
+- GNU find compatibility fixes for `-printf`:
+ - `%Y` now prints `?` if an error occurs resolving the link
+ - `%B` is now supported for birth/creation time (as well as `%W`/`%w`)
+ - All standard `strftime()` formats are supported, not just the ones from the GNU find manual
+- Optimizations are now re-run if any expressions are reordered
+- `-exec` and friends no longer leave zombie processes around when `exec()` fails
+
+
+1.2.3
+-----
+
+**July 15, 2018**
+
+- Fixed `test_depth_error` on filesystems that don't fill in `d_type`
+- Fixed the build on Linux architectures that don't have the `statx()` syscall (ia64, sh4)
+- Fixed use of AT_EMPTY_PATH for fstatat on systems that don't support it (Hurd)
+- Fixed `ARG_MAX` accounting on architectures with large pages (ppc64le)
+- Fixed the build against the upcoming glibc 2.28 release that includes its own `statx()` wrapper
+
+
+1.2.2
+-----
+
+**June 23, 2018**
+
+- Minor bug fixes:
+ - Fixed `-exec ... '{}' +` argument size tracking after recovering from `E2BIG`
+ - Fixed `-fstype` if `/proc` is available but `/etc/mtab` is not
+ - Fixed an uninitialized variable when given `-perm +rw...`
+ - Fixed some potential "error: 'path': Success" messages
+- Reduced reliance on GNU coreutils in the testsuite
+- Refactored and simplified the internals of `bftw()`
+
+
+1.2.1
+-----
+
+**February 8, 2018**
+
+- Performance optimizations
+
+
+1.2
+---
+
+**January 20, 2018**
+
+- Added support for the `-perm +7777` syntax deprecated by GNU find (equivalent to `-perm /7777`), for compatibility with BSD finds
+- Added support for file birth/creation times on platforms that report it
+ - `-Bmin`/`-Btime`/`-Bnewer`
+ - `B` flag for `-newerXY`
+ - `%w` and `%Wk` directives for `-printf`
+ - Uses the `statx(2)` system call on new enough Linux kernels
+- More robustness to `E2BIG` added to the `-exec` implementation
+
+
+1.1.4
+-----
+
+**October 27, 2017**
+
+- Added a man page
+- Fixed cases where multiple actions write to the same file
+- Report errors that occur when closing files/flushing streams
+- Fixed "argument list too long" errors with `-exec ... '{}' +`
+
+
+1.1.3
+-----
+
+**October 4, 2017**
+
+- Refactored the optimizer
+- Implemented data flow optimizations
+
+
+1.1.2
+-----
+
+**September 10, 2017**
+
+- Fixed `-samefile` and similar predicates when passed broken symbolic links
+- Implemented `-fstype` on Solaris
+- Fixed `-fstype` under musl
+- Implemented `-D search`
+- Implemented a cost-based optimizer
+
+
+1.1.1
+-----
+
+**August 10, 2017**
+
+- Re-licensed under the BSD Zero Clause License
+- Fixed some corner cases with `-exec` and `-ok` parsing
+
+
+1.1
+---
+
+**July 22, 2017**
+
+- Implemented some primaries from NetBSD `find`:
+ - `-exit [STATUS]` (like `-quit`, but with an optional explicit exit status)
+ - `-printx` (escape special characters for `xargs`)
+ - `-rm` (alias for `-delete`)
+- Warn if `-prune` will have no effect due to `-depth`
+- Handle y/n prompts according to the user's locale
+- Prompt the user to correct typos without having to re-run `bfs`
+- Fixed handling of paths longer than `PATH_MAX`
+- Fixed spurious "Inappropriate ioctl for device" errors when redirecting `-exec ... +` output
+- Fixed the handling of paths that treat a file as a directory (e.g. `a/b/c` where `a/b` is a regular file)
+- Fixed an expression optimizer bug that broke command lines like `bfs -name '*' -o -print`
+
+
+1.0.2
+-----
+
+**June 15, 2017**
+
+Bugfix release.
+
+- Fixed handling of \0 inside -printf format strings
+- Fixed `-perm` interpretation of permcopy actions (e.g. `u=rw,g=r`)
+
+
+1.0.1
+-----
+
+**May 17, 2017**
+
+Bugfix release.
+
+- Portability fixes that mostly affect GNU Hurd
+- Implemented `-D exec`
+- Made `-quit` not disable the implicit `-print`
+
+
+1.0
+---
+
+**April 24, 2017**
+
+This is the first release of bfs with support for all of GNU find's primitives.
+
+Changes since 0.96:
+
+- Implemented `-fstype`
+- Implemented `-exec/-execdir ... +`
+- Implemented BSD's `-X`
+- Fixed the tests under Bash 3 (mostly for macOS)
+- Some minor optimizations and fixes
+
+
+0.*
+===
+
+
+0.96
+----
+
+**March 11, 2017**
+
+73/76 GNU find features supported.
+
+- Implemented -nouser and -nogroup
+- Implemented -printf and -fprintf
+- Implemented -ls and -fls
+- Implemented -type with multiple types at once (e.g. -type f,d,l)
+- Fixed 32-bit builds
+- Fixed -lname on "symlinks" in Linux /proc
+- Fixed -quit to take effect as soon as it's reached
+- Stopped redirecting standard input from /dev/null for -ok and -okdir, as that violates POSIX
+- Many test suite improvements
+
+
+0.88
+----
+
+**December 20, 2016**
+
+67/76 GNU find features supported.
+
+- Fixed the build on macOS, and some other UNIXes
+- Implemented `-regex`, `-iregex`, `-regextype`, and BSD's `-E`
+- Implemented `-x` (same as `-mount`/`-xdev`) from BSD
+- Implemented `-mnewer` (same as `-newer`) from BSD
+- Implemented `-depth N` from BSD
+- Implemented `-sparse` from FreeBSD
+- Implemented the `T` and `P` suffices for `-size`, for BSD compatibility
+- Added support for `-gid NAME` and `-uid NAME` as in BSD
+
+
+0.84.1
+------
+
+**November 24, 2016**
+
+Bugfix release.
+
+- Fixed [#7] again
+- Like GNU find, don't print warnings by default if standard input is not a terminal
+- Redirect standard input from /dev/null for -ok and -okdir
+- Skip . when -delete'ing
+- Fixed -execdir when the root path has no slashes
+- Fixed -execdir in /
+- Support -perm +MODE for symbolic modes
+- Fixed the build on FreeBSD
+
+[#7]: https://github.com/tavianator/bfs/issues/7
+
+
+0.84
+----
+
+**October 29, 2016**
+
+64/76 GNU find features supported.
+
+- Spelling suggestion improvements
+- Handle `--`
+- (Untested) support for exotic file types like doors, ports, and whiteouts
+- Improved robustness in the face of closed std{in,out,err}
+- Fixed the build on macOS
+- Implement `-ignore_readdir_race`, `-noignore_readdir_race`
+- Implement `-perm`
+
+
+0.82
+----
+
+**September 4, 2016**
+
+62/76 GNU find features supported.
+
+- Rework optimization levels
+ - `-O1`
+ - Simple boolean simplification
+ - `-O2`
+ - Purity-based optimizations, allowing side-effect-free tests like `-name` or `-type` to be moved or removed
+ - `-O3` (**default**):
+ - Re-order tests to reduce the expected cost (TODO)
+ - `-O4`
+ - Aggressive optimizations that may have surprising effects on warning/error messages and runtime, but should not otherwise affect the results
+ - `-Ofast`:
+ - Always the highest level, currently the same as `-O4`
+- Color files with multiple hard links correctly
+- Treat `-`, `)`, and `,` as paths when required to by POSIX
+ - `)` and `,` are only supported before the expression begins
+- Implement `-D opt`
+- Implement `-D rates`
+- Implement `-fprint`
+- Implement `-fprint0`
+- Implement BSD's `-f`
+- Suggest fixes for typo'd arguments
+
+0.79
+----
+
+**May 27, 2016**
+
+60/76 GNU find features supported.
+
+- Remove an errant debug `printf()` from `-used`
+- Implement the `{} ;` variants of `-exec`, `-execdir`, `-ok`, and `-okdir`
+
+
+0.74
+----
+
+**March 12, 2016**
+
+56/76 GNU find features supported.
+
+- Color broken symlinks correctly
+- Fix [#7]
+- Fix `-daystart`'s rounding of midnight
+- Implement (most of) `-newerXY`
+- Implement `-used`
+- Implement `-size`
+
+[#7]: https://github.com/tavianator/bfs/issues/7
+
+
+0.70
+----
+
+**February 23, 2016**
+
+53/76 GNU find features supported.
+
+- New `make install` and `make uninstall` targets
+- Squelch non-positional warnings for `-follow`
+- Reduce memory footprint by as much as 64% by closing `DIR*`s earlier
+- Speed up `bfs` by ~5% by using a better FD cache eviction policy
+- Fix infinite recursion when evaluating `! expr`
+- Optimize unused pure expressions (e.g. `-empty -a -false`)
+- Optimize double-negation (e.g. `! ! -name foo`)
+- Implement `-D stat` and `-D tree`
+- Implement `-O`
+
+
+0.67
+----
+
+**February 14, 2016**
+
+Initial release.
+
+51/76 GNU find features supported.
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 0000000..77e8e2c
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,167 @@
+Contributing
+============
+
+License
+-------
+
+`bfs` is licensed under the [Zero-Clause BSD License](https://opensource.org/licenses/0BSD), a maximally permissive license.
+Contributions must use the same license.
+
+
+Building
+--------
+
+`bfs` uses [GNU Make](https://www.gnu.org/software/make/) as its build system.
+A simple invocation of
+
+ $ make
+
+should build `bfs` successfully, with no additional steps necessary.
+As usual with `make`, you can run a [parallel build](https://www.gnu.org/software/make/manual/html_node/Parallel.html) with `-j`.
+For example, to use all your cores, run `make -j$(nproc)`.
+
+### Targets
+
+| Command | Description |
+|------------------|---------------------------------------------------------------|
+| `make` | Builds just the `bfs` binary |
+| `make all` | Builds everything, including the tests (but doesn't run them) |
+| `make check` | Builds everything, and runs the tests |
+| `make install` | Installs `bfs` (with man page, shell completions, etc.) |
+| `make uninstall` | Uninstalls `bfs` |
+
+### Flag-like targets
+
+The build system provides a few shorthand targets for handy configurations:
+
+| Command | Description |
+|----------------|-------------------------------------------------------------|
+| `make release` | Build `bfs` with optimizations, LTO, and without assertions |
+| `make asan` | Enable [AddressSanitizer] |
+| `make lsan` | Enable [LeakSanitizer] |
+| `make msan` | Enable [MemorySanitizer] |
+| `make tsan` | Enable [ThreadSanitizer] |
+| `make ubsan` | Enable [UndefinedBehaviorSanitizer] |
+| `make gcov` | Enable [code coverage] |
+
+[AddressSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizer
+[LeakSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode
+[MemorySanitizer]: https://github.com/google/sanitizers/wiki/MemorySanitizer
+[ThreadSanitizer]: https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
+[UndefinedBehaviorSanitizer]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
+[code coverage]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html
+
+You can combine multiple flags and other targets (e.g. `make asan ubsan check`), but not all of them will work together.
+
+### Flags
+
+Other flags are controlled with `make` variables and/or environment variables.
+Here are some of the common ones; check the [`Makefile`](/Makefile) for more.
+
+| Flag | Description |
+|----------------------------------|---------------------------------------------|
+| `CC` | The C compiler to use, e.g. `make CC=clang` |
+| `CFLAGS`<br>`EXTRA_CFLAGS` | Override/add to the default compiler flags |
+| `LDFLAGS`<br>`EXTRA_LDFLAGS` | Override/add to the linker flags |
+| `WITH_ACL`<br>`WITH_ATTR`<br>... | Enable/disable optional dependencies |
+| `TEST_FLAGS` | `tests.sh` flags for `make check` |
+| `DESTDIR` | The root directory for `make install` |
+| `PREFIX` | The installation prefix (default: `/usr`) |
+| `MANDIR` | The man page installation directory |
+
+### Dependency tracking
+
+The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the `Makefile`).
+So if you edit a header file, `make` will rebuild the necessary object files ensuring they don't go out of sync.
+
+We go one step further than most build systems by tracking the flags that were used for the previous compilation.
+That means you can change configurations without having to `make clean`.
+For example,
+
+ $ make
+ $ make release
+
+will build the project in debug mode and then rebuild it in release mode.
+
+A side effect of this may be surprising: `make check` by itself will rebuild the project in the default configuration.
+To test a different configuration, you'll have to repeat it (e.g. `make release check`).
+
+
+Testing
+-------
+
+`bfs` comes with an extensive testsuite which can be run with
+
+ $ make check
+
+Most of the testsuite is implemented in the file [`tests.sh`](/tests.sh).
+This script contains hundreds of separate test cases.
+Most of them are *snapshot tests* which compare `bfs`'s output to a known-good copy saved under [`tests`](/tests).
+
+You can pass the name of a particular test case (or a few) to run just those tests.
+For example:
+
+ $ ./tests.sh test_basic
+
+If you need to update the reference snapshot, pass `--update`.
+It can be handy to generate the snapshot with a different `find` implementation to ensure the output is correct, for example:
+
+ $ ./tests.sh test_basic --bfs=find --update
+
+But keep in mind, other `find` implementations may not be correct.
+To my knowledge, no other implementation passes even the POSIX-compatible subset of the tests:
+
+ $ ./tests.sh --bfs=find --posix
+ ...
+ tests passed: 89
+ tests failed: 5
+
+Run
+
+ $ ./tests.sh --help
+
+for more details.
+
+### Validation
+
+A more thorough testsuite is run by the [CI](https://github.com/tavianator/bfs/actions) and to validate releases.
+It builds `bfs` in multiple configurations to test for latent bugs, memory leaks, 32-bit compatibility, etc.
+You can run it yourself with
+
+ $ make distcheck
+
+Some of these tests require `sudo`, and will prompt for your password if necessary.
+
+
+Hacking
+-------
+
+`bfs` is written in [C](https://en.wikipedia.org/wiki/C_(programming_language)), specifically [C11](https://en.wikipedia.org/wiki/C11_(C_standard_revision)).
+You can get a feel for the coding style by skimming the source code.
+[`main.c`](/src/main.c) contains an overview of the rest of source files.
+A quick summary:
+
+- Tabs for indentation, spaces for alignment.
+- Most types and functions should be namespaced with `bfs_`.
+ Exceptions are made for things that could be generally useful outside of `bfs`.
+- Error handling follows the C standard library conventions: return a nonzero `int` or a `NULL` pointer, with the error code in `errno`.
+ All failure cases should be handled, including `malloc()` failures.
+- `goto` is not harmful for cleaning up in error paths.
+
+### Adding tests
+
+Both new features and bug fixes should have associated tests.
+To add a test, create a new function in `tests.sh` called `test_<something>`.
+Snapshot tests use the `bfs_diff` function to automatically compare the generated and expected outputs.
+For example,
+
+```bash
+function test_something() {
+ bfs_diff basic -name something
+}
+```
+
+`basic` is one of the directory trees generated for test cases; others include `links`, `loops`, `deep`, and `rainbow`.
+
+Run `./tests.sh test_something --update` to generate the reference snapshot (and don't forget to `git add` it).
+Finally, add the test case to one of the arrays `posix_tests`, `bsd_tests`, `gnu_tests`, or `bfs_tests` depending on which `find` implementations it should be compatible with.
diff --git a/docs/bfs.1 b/docs/bfs.1
new file mode 100644
index 0000000..53a9831
--- /dev/null
+++ b/docs/bfs.1
@@ -0,0 +1,811 @@
+.TH BFS 1
+.SH NAME
+bfs \- breadth-first search for your files
+.SH SYNOPSIS
+.B bfs
+.RB [ flags ...]
+.RI [ paths ...]
+.RB [ expression ...]
+.PP
+flags
+.RB ( \-H / \-L / \-P
+etc.),
+.IR paths ,
+and
+.B expressions
+may be freely mixed in any order.
+.SH DESCRIPTION
+.B bfs
+is a breadth-first version of the UNIX
+.BR find (1)
+command.
+.PP
+.B bfs
+supports almost every feature from every major
+.BR find (1)
+implementation, so your existing command lines should work as-is.
+It also adds some features of its own, such as a more forgiving command line parser and some additional options.
+.PP
+Each
+.I path
+specified on the command line is treated as a starting path to search through.
+If no paths are specified, the current directory
+.RI ( . )
+is searched by default.
+.PP
+Like
+.BR find (1),
+.B bfs
+interprets its arguments as a short-circuiting Boolean expression.
+For example,
+.PP
+.nf
+.RS
+.B bfs \\\( \-name '*.txt' \-or \-lname '*.txt' \\\\) \-and \-print
+.RE
+.fi
+.PP
+will print the all the paths that are either .txt files or symbolic links to .txt files.
+.B \-and
+is implied between two consecutive expressions, so this is equivalent:
+.PP
+.nf
+.RS
+.B bfs \\\( \-name '*.txt' \-or \-lname '*.txt' \\\\) \-print
+.RE
+.fi
+.PP
+Finally,
+.B \-print
+is implied if no actions are specified, so this too is equivalent:
+.PP
+.nf
+.RS
+.B bfs \-name '*.txt' \-or \-lname '*.txt'
+.RE
+.fi
+.PP
+Most options that take a numeric argument
+.I N
+will also accept
+.I \-N
+or
+.IR +N .
+.IR \-N
+means "less than
+.IR N ,"
+and
+.I +N
+means "greater than
+.IR N ."
+.SH FLAGS
+.TP
+.B \-H
+Follow symbolic links on the command line, but not while searching.
+.TP
+.B \-L
+Follow all symbolic links.
+.TP
+.B \-P
+Never follow symbolic links (the default).
+.TP
+.B \-E
+Use extended regular expressions (same as \fB\-regextype posix-extended\fR).
+.TP
+.B \-X
+Filter out files with
+.RB non- xargs (1)-safe
+names.
+.TP
+.B \-d
+Search in post-order (same as
+.BR \-depth ).
+.TP
+.B \-s
+Visit directory entries in sorted order.
+The sorting takes place within each directory separately, which makes it different from
+.B bfs ... |
+.BR sort ,
+but still provides a deterministic ordering.
+.TP
+.B \-x
+Don't descend into other mount points (same as \fB\-xdev\fR).
+.TP
+\fB\-f \fIPATH\fR
+Treat
+.I PATH
+as a path to search (useful if it begins with a dash).
+.PP
+.TP
+\fB\-D \fIFLAG\fR
+Turn on a debugging flag (see
+.B \-D
+.IR help ).
+.PP
+\fB\-O\fIN\fR
+.RS
+Enable optimization level
+.I N
+(default:
+.IR 3 ).
+.TP
+.BI \-O 0
+Disable all optimizations.
+.TP
+.BI \-O 1
+Basic logical simplifications.
+.TP
+.BI \-O 2
+All
+.BI \-O 1
+optimizations, plus dead code elimination and data flow analysis.
+.TP
+.BI \-O 3
+All
+.BI \-O 2
+optimizations, plus re-order expressions to reduce expected cost.
+.TP
+\fB\-O\fI4\fR/\fB\-O\fIfast\fR
+All optimizations, including aggressive optimizations that may alter the observed behavior in corner cases.
+.RE
+.PP
+\fB\-S \fIbfs\fR|\fIdfs\fR|\fIids\fR|\fIeds\fR
+.RS
+Choose the search strategy.
+.TP
+.I bfs
+Breadth-first search (the default).
+.TP
+.I dfs
+Depth-first search.
+Uses less memory than breadth-first search, but is typically slower to return relevant results.
+.TP
+.I ids
+Iterative deepening search.
+Performs repeated depth-first searches with increasing depth limits.
+This gives results in the same order as breadth-first search, but with the reduced memory consumption of depth-first search.
+Tends to be very slow in practice, so use it only if you absolutely need breadth-first ordering, but
+.B \-S
+.I bfs
+consumes too much memory.
+.TP
+.I eds
+Exponential deepening search.
+A compromise between breadth- and depth-first search, which searches exponentially increasing depth ranges (e.g 0-1, 1-2, 2-4, 4-8, etc.).
+Provides many of the benefits of breadth-first search with depth-first's reduced memory consumption.
+Typically far faster than
+.B \-S
+.IR ids .
+.RE
+.SH OPERATORS
+.TP
+\fB( \fIexpression \fB)\fR
+Parentheses are used for grouping expressions together.
+You'll probably have to write
+.B \\\\(
+.I expression
+.B \\\\)
+to avoid the parentheses being interpreted by the shell.
+.PP
+\fB! \fIexpression\fR
+.br
+\fB\-not \fIexpression\fR
+.RS
+The "not" operator: returns the negation of the truth value of the
+.IR expression .
+You may have to write \fB\\! \fIexpression\fR to avoid \fB!\fR being interpreted by the shell.
+.RE
+.PP
+\fIexpression\fR \fIexpression\fR
+.br
+\fIexpression \fB\-a \fIexpression\fR
+.br
+\fIexpression \fB\-and \fIexpression\fR
+.RS
+Short-circuiting "and" operator: if the left-hand
+.I expression
+is
+.BR true ,
+returns the right-hand
+.IR expression ;
+otherwise, returns
+.BR false .
+.RE
+.PP
+\fIexpression \fB\-o \fIexpression\fR
+.br
+\fIexpression \fB\-or \fIexpression\fR
+.RS
+Short-circuiting "or" operator: if the left-hand
+.I expression
+is
+.BR false ,
+returns the right-hand
+.IR expression ;
+otherwise, returns
+.BR true .
+.RE
+.TP
+\fIexpression \fB, \fIexpression\fR
+The "comma" operator: evaluates the left-hand
+.I expression
+but discards the result, returning the right-hand
+.IR expression .
+.SH SPECIAL FORMS
+.TP
+\fB\-exclude \fIexpression\fR
+Exclude all paths matching the
+.I expression
+from the search.
+This is more powerful than
+.BR \-prune ,
+because it applies even when the expression wouldn't otherwise be evaluated, due to
+.B \-depth
+or
+.B \-mindepth
+for example.
+Exclusions are always applied before other expressions, so it may be least confusing to put them first on the command line.
+.SH OPTIONS
+.PP
+.B \-color
+.br
+.B \-nocolor
+.RS
+Turn colors on or off (default:
+.B \-color
+if outputting to a terminal,
+.B \-nocolor
+otherwise).
+.RE
+.TP
+.B \-daystart
+Measure time relative to the start of today.
+.TP
+.B \-depth
+Search in post-order (descendents first).
+.TP
+.B \-follow
+Follow all symbolic links (same as
+.BR \-L ).
+.TP
+\fB\-files0\-from \fIFILE\fR
+Treat the NUL ('\\0')-separated paths in
+.I FILE
+as starting points for the search.
+Pass
+.B \-files0\-from
+.I \-
+to read the paths from standard input.
+.PP
+\fB\-ignore_readdir_race\fR
+.br
+\fB\-noignore_readdir_race\fR
+.RS
+Whether to report an error if
+.B bfs
+detects that the file tree is modified during the search (default:
+.BR \-noignore_readdir_race ).
+.RE
+.PP
+\fB\-maxdepth \fIN\fR
+.br
+\fB\-mindepth \fIN\fR
+.RS
+Ignore files deeper/shallower than
+.IR N .
+.RE
+.TP
+.B \-mount
+Don't descend into other mount points (same as
+.B \-xdev
+for now, but will skip mount points entirely in the future).
+.TP
+.B \-nohidden
+Exclude hidden files and directories.
+.TP
+.B \-noleaf
+Ignored; for compatibility with GNU find.
+.TP
+\fB\-regextype \fITYPE\fR
+Use
+.IR TYPE -flavored
+regexes (default:
+.IR posix-basic ;
+see
+.B \-regextype
+.IR help ).
+.TP
+.B \-status
+Display a status bar while searching.
+.TP
+.B \-unique
+Skip any files that have already been seen.
+Particularly useful along with