## fls
A nearly-POSIX-compliant and libc-less `ls` that's smaller, faster, and prettier than GNU's1.
[exa](https://github.com/ogham/exa) and [lsd](https://github.com/Peltoche/lsd) are both great `ls`-like Rust programs, but they're slower than the system `ls` and about 10x the code size. Plus you can't actually replace your `ls` with one of them, because some software relies on parsing the output of `ls`. But even as a user experience improvement, I think other projects tell the wrong story; modern software does not need to be larger or slower.
1I don't mean to rag on GNU's `ls`, but as far as I can tell it's the closest thing along the metrics I value.
## Crude benchmarks
| | --color=never -R / > /dev/null | --color=always -R / | --color=auto ~ | --color=auto -l ~ |
| ---------| ------------------------------ | -------------------- | -------------- | ----------------- |
| `fls` | 0.66 s | 2.32 s | 0.16 ms | 0.30 ms |
| GNU `ls` | 1.22 s | 4.37 s | 0.38 ms | 2.30 ms |
| `exa` | 3.61 s | 63.7 s 3 | 0.78 ms | 3.30 ms 4 |
| `lsd` | ???2 | ???2 | 36.5 ms | 36.8 ms |
These do not cover all reasonable combinations of options, but if you can find a combination of flags for which `fls` is slower than any alternatives, please open an issue.
2`lsd` doesn't detect symlink cycles and thus runs indefinitely on `-R /`.
3I have some large directories of fuzzing corpora; from running `perf top` as I was collecting this data, I see `exa` spends most of its time in `term_grid::Grid::column_widths`. I suspect its grid layout algorithm is quadratic.
4In all cases I report wall time; this is the only case where CPU time is significantly different. `exa`'s CPU time is ~2.2x this value.
## "libc-less"
`fls` does not link to anything. The (stripped) `fls` executable is smaller than GNU's (stripped) `ls` executable, even though some of the code that powers GNU's is in another file.
## smaller _and_ faster?
The biggest impact on code size is `#![no_std]`, because the standard library's runtime is relatively large. Most individual components of the standard library are a totally reasonable size, but the code for generating backtraces is huge and as far as I can tell `#![no_std]` is the only way to get rid of it. The rest of the code size was trimmed down mostly by running the excellent tool [`cargo bloat`](https://crates.io/crates/cargo-bloat) to identify places to replace generics with runtime dispatch, and just manually reviewing the code to factor out repeated code patterns.
In terms of speed, `fls` is _probably_ faster than GNU's `ls` because it doesn't use the POSIX interfaces for listing files. We directly call `getdents64` and parse the output, instead of juggling calls to `read_dir`. And since we're calling `getdents64`, we get access to the optional directory entry type information, which usually lets us omit a number of `stat` calls, which can be expensive relative to other filesystem syscalls.
I say _probably_ because `fls` has always been faster than GNU's `ls`. The original goal was just to use `getdents64` directly (see below), and as soon as I had a working prototype, it was faster than the competition.
## `--color=auto`
`fls` has the same interpretation as GNU ls for `--color=always` and `--color=never`, but under `--color=auto`, `fls` will _only_ apply colors based on file extension and the information available from `getdents64`, which is optional. Thus, the coloring of `fls --color=auto` is unpredictable, but you get _some_ coloring of output without any expensive `stat` calls. `fls` was originally developed when my dev environment was a compute node with an HPC filesystem, and `ls --color=always` on large directories could take seconds to minutes. `fls --color=auto` provides the same colors in those directories, in the blink of an eye. Thus, `--color=auto` is the assumed if no arguments are provided and stdout is a terminal.
## Sorting
In the absence of any options, `fls` sorts names using a comparsion function similar to `ls -v`, which attempts to treat runs of digits as a single number. You don't need to pad numbers in filenames to a fixed width to make them display in the intuitive order.
## POSIX features:
- [x] -A do not list implied `.` and `..`
- [x] -C list entries in columns
- [x] -F append an indicator to entries
- [x] -H follow symlinks when provided on the command line
- [x] -L always follow symlinks
- [x] -R recurse into subdirectories
- [x] -S sort by size
- [x] -a do not ignore entries whose names begin with `.`
- [x] -c sort by ctime
- [x] -d list directories themselves, not their contents
- [x] -f do not sort
- [x] -g long format but without owner
- [x] -i print each entry's inode
- [ ] -k pretend block size is 1024 bytes
- [x] -l long format
- [x] -m single row, separated by `, `
- [x] -n long format but list uid and gid instead of names
- [x] -o long format but without groups
- [x] -p append an indicator to directories
- [ ] -q replace non-printable characters with `?`
- [x] -r reverse sorting order
- [x] -s print size of each file in blocks
- [x] -t sort by modification time
- [x] -u sort by access time
- [ ] -x sort entries across rows
- [x] -1 list one entry per line