Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions XFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,41 @@ xff walks each starting path and acts on the entries matching an expression, lik

xff has two flavors selected by the program name: invoked as `find` it is strict find (only the standard vocabulary); invoked as `xff` it enables the modern extensions. An explicit `--config=find|xff` overrides the program name. Items marked as xff extensions below are the additions over find.

## Configuration

xff configuration. Options resolve from layered config tiers, then the command line; later layers win. A style (find / xff / rg) sets the baseline defaults, which the tiers and the command line then adjust. Run `--explain` to print exactly what resolved.

### Layers (lowest to highest precedence)

- `system config` - machine-wide defaults (+ a root-owned [policy] that can hard-deny arming)
- `user config` - your personal defaults
- `--xffrc=FILE` - an explicitly named file (repeatable) - a NON-ARMING tier
- `command line` - flags and --config, highest

There is no project / ancestor .xffrc discovery: config comes from the system and user files plus any `--xffrc` you name. `--no-config` ignores the discovered system/user files.

### Choosing a style

`--config=NAME` selects find / xff / rg (repeatable, last wins); see `--help=styles` for the table. The invocation name (argv[0]) is the leading selector, so a symlink named `find` runs the strict find style and `rg` the rg style; any other name (e.g. a `mytool` symlink) activates a same-named config block over the xff default. An explicit `--config` still stacks on top.

### Arming dangerous directives

A dangerous directive (the exec family -exec/-execdir/-ok/-capture, or -delete) carried by an --xffrc file is inert unless `--allow-exec` is set from a TRUSTED tier (the command line or the system/user config, never an --xffrc file itself). Unarmed lines are dropped with a warning; the root system [policy] can hard-deny even `--allow-exec`.

### Config flags
- `--config=NAME` - select a config style: find (strict), xff (evolved), rg (opinionated); repeatable _(global, xff)_
A config style sets the defaults for ignore files, hidden files, sizes, sort order, and case. find is strict find compatibility; xff keeps find's grammar but sorts and prints human sizes; rg is opinionated (respect .gitignore, skip hidden, smart case). Repeatable and layered, last one wins. See --help=styles for the per-style defaults.
- `--no-config` - ignore discovered .xffrc files _(global, xff)_
- `--xffrc=FILE` - also load a specific config file (a non-arming tier; see --allow-exec) _(global, xff)_
Loads FILE as a config tier above the user config (naming it is consent to LOAD it). It is a NON-ARMING tier: safe directives apply, but a dangerous one - the exec family (-exec/-execdir/-ok, -capture) or -delete - is inert unless --allow-exec is set from a trusted tier (the CLI or the user/system config, never from an --xffrc file itself). An unarmed dangerous line is dropped with a one-line warning. Repeatable; later files win.
Affects: --allow-exec
Affected by: --allow-exec
- `--allow-exec` - arm dangerous directives loaded from an --xffrc file (exec family, -delete) _(global, xff)_
Permits the sensitive/destructive directives (the exec family -exec/-execdir/-ok and -capture, and the destructive -delete) carried by an --xffrc-loaded file to actually run. Honored only from a trusted tier - typed on the CLI, or set in the user/system config - never from an --xffrc file (so a named config cannot authorize itself). The root-owned system [policy] can hard-deny even this. Without it, such lines are inert (dropped + warned); -delete still obeys its own --safe/--dry-run guards.
Affects: --xffrc
Affected by: --xffrc
- `--explain` - print the resolved configuration and exit _(global, xff)_

## Options

### Config
Expand Down
43 changes: 0 additions & 43 deletions xff/cli/help.cc
Original file line number Diff line number Diff line change
Expand Up @@ -168,46 +168,6 @@ std::string RenderExpressions() {
return out;
}

// The `--help=config` topic: how xff resolves options (layered tiers + the command line), how a
// style is chosen (--config and the argv[0] invocation name), and how dangerous --xffrc directives
// are armed. The config flags are pulled from the globals SOT via the "config" topic tag, so the
// flag list here cannot drift; the layering / argv[0] / arming rules are prose.
std::string RenderConfig() {
std::string out =
"xff configuration. Options resolve from layered config tiers, then the command line; later\n"
"layers win. A style (find / xff / rg) sets the baseline defaults, which the tiers and the\n"
"command line then adjust. Run --explain to print exactly what resolved.\n"
"\n"
"Layers, lowest to highest precedence:\n"
" system config machine-wide defaults (+ a root-owned [policy] that can hard-deny arming)\n"
" user config your personal defaults\n"
" --xffrc=FILE an explicitly named file (repeatable) - a NON-ARMING tier\n"
" command line flags and --config, highest\n"
"\n"
"There is no project / ancestor .xffrc discovery: config comes from the system and user files\n"
"plus any --xffrc you name. --no-config ignores the discovered system/user files.\n"
"\n"
"Choosing a style:\n"
" --config=NAME selects find / xff / rg (repeatable, last wins); see --help=styles for the table.\n"
" The invocation name (argv[0]) is the leading selector, so a symlink named `find` runs the\n"
" strict find style and `rg` the rg style; any other name (e.g. a `mytool` symlink) activates a\n"
" same-named config block over the xff default. An explicit --config still stacks on top.\n"
"\n"
"Arming dangerous directives:\n"
" A dangerous directive (the exec family -exec/-execdir/-ok/-capture, or -delete) carried by an\n"
" --xffrc file is inert unless --allow-exec is set from a TRUSTED tier (the command line or the\n"
" system/user config, never an --xffrc file itself). Unarmed lines are dropped with a warning;\n"
" the root system [policy] can hard-deny even --allow-exec.\n"
"\n"
"Config flags:\n";
for (const GlobalFlag& flag : Globals()) {
if (flag.topic == "config") {
absl::StrAppend(&out, RenderGlobalFlag(flag, /*with_details=*/true), "\n");
}
}
return out;
}

} // namespace

// The `--help=cookbook` topic (aliases examples / recipes), folded into --help=full: task-oriented
Expand Down Expand Up @@ -495,9 +455,6 @@ absl::StatusOr<std::string> RenderHelp(std::string_view topic) {
if (topic == "expressions") {
return RenderExpressions(); // the annotated Tests/Actions/Operators list, sans globals
}
if (topic == "config") {
return RenderConfig(); // config tiers, style selection, and arming
}
if (topic == "cookbook" || topic == "examples" || topic == "recipes") {
return RenderCookbook(); // worked examples composing the building blocks end to end
}
Expand Down
54 changes: 54 additions & 0 deletions xff/cli/help_build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,57 @@ Section StatsSection() {
return section;
}

// CONFIGURATION: how options resolve (layered tiers + the command line), how a style is
// chosen (--config / argv[0]), and how dangerous --xffrc directives are armed. The flags
// are pulled from the globals SOT via the "config" topic tag so the list cannot drift;
// the layering / argv[0] / arming rules are prose. Standalone as `--help=config` (see
// TopicReference) and folded into the full reference.
Section ConfigSection() {
Section section{.title = "Configuration"};
section.children.push_back(ProseOf(
"xff configuration. Options resolve from layered config tiers, then the command line; later "
"layers win. A style (find / xff / rg) sets the baseline defaults, which the tiers and the "
"command line then adjust. Run `--explain` to print exactly what resolved."));

static constexpr std::array<DocPair, 4> kLayers = {{
{"system config", "machine-wide defaults (+ a root-owned [policy] that can hard-deny arming)"},
{"user config", "your personal defaults"},
{"--xffrc=FILE", "an explicitly named file (repeatable) - a NON-ARMING tier"},
{"command line", "flags and --config, highest"},
}};
Subsection layers{.title = "Layers (lowest to highest precedence)"};
layers.children.push_back(RowsOf(kLayers));
layers.children.push_back(ProseOf(
"There is no project / ancestor .xffrc discovery: config comes from the system and user files "
"plus any `--xffrc` you name. `--no-config` ignores the discovered system/user files."));
section.children.push_back(Content{.node = std::move(layers)});

Subsection style{.title = "Choosing a style"};
style.children.push_back(ProseOf(
"`--config=NAME` selects find / xff / rg (repeatable, last wins); see `--help=styles` for the "
"table. The invocation name (argv[0]) is the leading selector, so a symlink named `find` runs the "
"strict find style and `rg` the rg style; any other name (e.g. a `mytool` symlink) activates a "
"same-named config block over the xff default. An explicit `--config` still stacks on top."));
section.children.push_back(Content{.node = std::move(style)});

Subsection arming{.title = "Arming dangerous directives"};
arming.children.push_back(ProseOf(
"A dangerous directive (the exec family -exec/-execdir/-ok/-capture, or -delete) carried by an "
"--xffrc file is inert unless `--allow-exec` is set from a TRUSTED tier (the command line or the "
"system/user config, never an --xffrc file itself). Unarmed lines are dropped with a warning; the "
"root system [policy] can hard-deny even `--allow-exec`."));
section.children.push_back(Content{.node = std::move(arming)});

Subsection flags{.title = "Config flags"};
for (const GlobalFlag& flag : Globals()) {
if (flag.topic == "config") {
flags.children.push_back(FlagEntry(flag));
}
}
section.children.push_back(Content{.node = std::move(flags)});
return section;
}

// EXAMPLES: the cookbook recipes as structured nodes - each a subsection with the
// verbatim command (an Example, kept copy-pastable) and its explanation (Prose, which
// wraps). The recipe list is the SOT in help.cc, run end to end by cookbook_test.
Expand Down Expand Up @@ -476,6 +527,8 @@ std::optional<Document> TopicReference(std::string_view name) {
doc.sections.push_back(GrammarsSection());
} else if (name == "stats") {
doc.sections.push_back(StatsSection());
} else if (name == "config") {
doc.sections.push_back(ConfigSection());
} else {
return std::nullopt;
}
Expand Down Expand Up @@ -515,6 +568,7 @@ Document BuildReference() {
};

doc.sections.push_back(DescriptionSection());
doc.sections.push_back(ConfigSection());
doc.sections.push_back(OptionsSection(/*with_details=*/true));
doc.sections.push_back(ExpressionSection(/*with_details=*/true));

Expand Down
7 changes: 4 additions & 3 deletions xff/cli/help_build_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ TEST_F(BuildReferenceTest, PreambleComesFromTheSot) {

TEST_F(BuildReferenceTest, SectionsAppearInReferenceOrder) {
EXPECT_THAT(
SectionTitles(doc), ElementsAre(
"Description", "Options", "Expression", "Fields", "Printf directives", "Time formats",
"Size units", "Regex grammars", "Statistics", "Examples", "Exit status", "See also"));
SectionTitles(doc),
ElementsAre(
"Description", "Configuration", "Options", "Expression", "Fields", "Printf directives", "Time formats",
"Size units", "Regex grammars", "Statistics", "Examples", "Exit status", "See also"));
}

TEST_F(BuildReferenceTest, ExpressionHasTheThreeKindSubsections) {
Expand Down
12 changes: 11 additions & 1 deletion xff/cli/help_render_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ TEST_F(HelpTest, EveryAdvertisedTopicRendersAndAliasesAreSynonyms) {
// regex, or the shared DocRenderer walk that produces `fields`).
for (const HelpTopic& topic : HelpTopics()) {
if (topic.name == "styles" || topic.name == "fields" || topic.name == "printf" || topic.name == "time"
|| topic.name == "size" || topic.name == "grammars" || topic.name == "extras" || topic.name == "stats") {
|| topic.name == "size" || topic.name == "grammars" || topic.name == "extras" || topic.name == "stats"
|| topic.name == "config") {
continue; // rendered from the model (TopicReference) or the CLI facets, not RenderHelp
}
const absl::StatusOr<std::string> rendered = RenderHelp(topic.name);
Expand Down Expand Up @@ -206,6 +207,15 @@ TEST_F(HelpTest, StatsTopicDocumentsSummaryAndHistogram) {
HasSubstr("--summary"), HasSubstr("--histogram"), HasSubstr("sum(lines)"), HasSubstr("needs an aggregator")));
}

TEST_F(HelpTest, ConfigTopicDocumentsTiersStyleAndArming) {
// `--help=config` renders from the model (TopicReference): the layered tiers, style
// selection (--config / argv[0]), and the arming rule for dangerous --xffrc directives.
EXPECT_THAT(
RenderTopicDoc("config"), AllOf(
HasSubstr("system config"), HasSubstr("command line"), HasSubstr("--config"),
HasSubstr("argv[0]"), HasSubstr("--allow-exec")));
}

TEST_F(HelpTest, GlobalFlagTopicRendersWithGlobalTag) {
EXPECT_THAT(RenderEntry("--sort"), AllOf(HasSubstr("--sort"), HasSubstr("ordering"), HasSubstr("global")));
}
Expand Down
Loading