Open-Source Wikis

/

fd

/

Systems

/

CLI parsing

sharkdp/fd

CLI parsing

Active contributors: tmccombs, sharkdp

Purpose

This subsystem turns argv into the immutable Config that the rest of the binary reads. It owns the entire flag definition, the smart-case logic, the override semantics for paired --foo/--no-foo flags, and a custom parser for -x/-X because clap's derive API cannot model "everything until ; is one positional argument".

Directory layout

src/
├── cli.rs          # Opts struct (clap derive), enums, custom Exec parser, default_num_threads
├── main.rs         # construct_config, build_pattern_regex, build_regex,
│                   # ensure_search_pattern_is_not_a_path, ensure_use_hidden_option_for_leading_dot_pattern
├── config.rs       # The Config struct, populated by construct_config
└── regex_helper.rs # pattern_has_uppercase_char, pattern_matches_strings_with_leading_dot

Key abstractions

Type File Purpose
Opts src/cli.rs clap-derived struct holding every flag. About 950 lines, including long help texts.
Exec src/cli.rs Manual clap::FromArgMatches + clap::Args impl that handles -x/-X argv terminators.
Config src/config.rs The post-parse, post-policy bag of settings consumed by walker/filters/output.
FileType src/cli.rs clap ValueEnum for -t: file/f, directory/d/dir, symlink/l, block-device/b, char-device/c, executable/x, empty/e, socket/s, pipe/p.
ColorWhen, HyperlinkWhen, StripCwdWhen src/cli.rs clap ValueEnums for --color, --hyperlink, --strip-cwd-prefix.
pattern_has_uppercase_char src/regex_helper.rs The smart-case oracle. Walks the parsed regex_syntax::Hir to check for any literal or class containing an uppercase character.

How it works

graph TD
    argv[argv] -->|clap derive| opts[Opts]
    opts -->|gen_completions?| completions[print_completions<br/>and exit]
    opts -->|opts.search_paths| paths[Vec PathBuf]
    opts -->|build_pattern_regex| pre_regex[Vec String<br/>raw regex sources]
    pre_regex -->|construct_config| config[Config]
    pre_regex -->|build_regex| regex[Vec Regex]
    config --> walk[walk::scan]
    regex --> walk
    paths --> walk
  1. Opts::parse() runs the clap-derive parser. Help and version handling is automatic.
  2. If --gen-completions <shell> was given (and the completions feature is on), print_completions writes the script via clap_complete::generate and the program exits.
  3. set_working_dir honours --base-directory by chdir-ing.
  4. opts.search_paths() collects positional --search-path and [path]... values, falling back to ["."].
  5. build_pattern_regex converts the user's pattern depending on mode: glob → globset::GlobBuilder regex, fixed-strings → regex::escape, default → as-is.
  6. construct_config derives every effective setting:
    • Smart case. True unless --ignore-case, or the user passed --case-sensitive, or any pattern contains an uppercase character.
    • Path separator. --path-separator if set; else default_path_separator() (which only kicks in for MSYS on Windows); else MAIN_SEPARATOR. On Windows, separators must be exactly one byte (check_path_separator_length).
    • Color. Always/Never or Auto — the latter combines enable_ansi_support() (Windows), NO_COLOR, and is_terminal() on stdout. If colored, LsColors::from_env() falls back to the bundled DEFAULT_LS_COLORS constant.
    • Hyperlink. Maps Auto to whatever the color decision was, so hyperlinks follow color emission by default.
    • Command. --exec/--exec-batch populate Opts::exec. --list-details synthesises an ls -lhd --color=… template via determine_ls_command, picking GNU gls on macOS/BSD when present.
    • File types. Each -t flag toggles a boolean in FileTypes. -t executable implies files; -t empty alone implies both files and directories.
    • Extensions. Compiled into a single case-insensitive RegexSet of \.<ext>$ patterns.
    • Time constraints. Both --changed-within and --changed-before flow through TimeFilter::after/::before, with descriptive errors otherwise.
  7. Two friendly diagnostics run after Config exists:
    • ensure_search_pattern_is_not_a_path rejects fd /etc/passwd (without --full-path) with a hint pointing the user at the right command.
    • ensure_use_hidden_option_for_leading_dot_pattern notices when the pattern only matches dotfiles but --hidden was not passed.
  8. build_regex turns each pattern source into a regex::bytes::Regex with case_insensitive(!case_sensitive) and dot_matches_new_line(true). Errors here recommend --fixed-strings or --glob.

The Exec parser

clap's derive API can't model a positional with a ; terminator that may include leading - arguments. So src/cli.rs defines an Exec newtype with manual FromArgMatches/Args impls. The args are declared with value_terminator(";"), allow_hyphen_values(true), and num_args(1..). The parser collects each occurrence of --exec or --exec-batch, passes the captured groups through CommandSet::new or CommandSet::new_batch, and stores the result on Opts::exec.

The user-facing rule — "everything after -x/-X belongs to the command, terminate with \; if you need more fd args" — is implemented entirely by these clap settings.

Override semantics

Many flags ship with both an enable form (--hidden) and a disable form (--no-hidden), so users can override values set elsewhere (e.g. shell aliases). The pattern in Opts is:

#[arg(long, short = 'H', help = "Search hidden files and directories", long_help)]
pub hidden: bool,

#[arg(long, overrides_with = "hidden", hide = true, action = ArgAction::SetTrue)]
no_hidden: (),

The hidden () field exists only to consume the override — clap reads it but the field itself is never used by fd code.

Smart case in detail

pattern_has_uppercase_char parses each pattern with regex_syntax::ParserBuilder::new().utf8(false).build() and recursively walks the Hir:

  • Literal bytes: any uppercase byte triggers true.
  • Unicode class ranges: any range whose start or end is uppercase triggers true.
  • Byte class ranges: same, treating each byte as a char.
  • Captures and repetitions recurse into the inner hir.
  • Concats and alternations recurse into all children.

Because the parser runs over the raw user pattern (including escaped sequences like \.), the function correctly does not flag \Acargo or \x6F as uppercase.

Entry points for modification

  • To add a new flag: add a field to Opts in src/cli.rs, document it (clap uses doc comments for --help), then read it in construct_config (src/main.rs) and store the derived value on Config. If the flag is paired with a --no-foo override, follow the existing pattern with a hidden () companion field.
  • To change smart-case behaviour: edit pattern_has_uppercase_char (src/regex_helper.rs) and add a unit test next to it.
  • To extend --list-details: edit determine_ls_command in src/main.rs. It already branches on Linux vs macOS/BSD vs Windows.
  • To support a new completion shell: depend on a newer clap_complete::Shell variant; nothing else needs to change in print_completions.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

CLI parsing – fd wiki | Factory