Skip to content

engine: add -nouser / -nogroup predicates - #154

Merged
helly25 merged 2 commits into
mainfrom
feat/nouser-nogroup
Jun 27, 2026
Merged

engine: add -nouser / -nogroup predicates#154
helly25 merged 2 commits into
mainfrom
feat/nouser-nogroup

Conversation

@helly25

@helly25 helly25 commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Standard POSIX/GNU/BSD find predicates: -nouser (owner uid has no passwd entry) and -nogroup (group gid has no group entry). Backed by getpwuid/getgrgid. Tested: present id (root) vs unassigned high id. //... + asan green. Stacked on #153.

@helly25
helly25 changed the base branch from feat/traversal-synonyms to main June 27, 2026 18:10
@helly25
helly25 force-pushed the feat/nouser-nogroup branch from 3bddb9a to fe635c8 Compare June 27, 2026 18:10
@helly25
helly25 enabled auto-merge (squash) June 27, 2026 18:10
@helly25
helly25 force-pushed the feat/nouser-nogroup branch from fe635c8 to 9b6fc4d Compare June 27, 2026 18:18
Standard POSIX/GNU/BSD find predicates: -nouser matches when the entry's
owner uid has no passwd entry, -nogroup when its group gid has no group
entry (orphaned ids). Backed by getpwuid/getgrgid (already used by
%u/%g). Tested against a present id (root) and an unassigned high id.
@helly25
helly25 force-pushed the feat/nouser-nogroup branch from 9b6fc4d to c8a6253 Compare June 27, 2026 18:37
@helly25
helly25 merged commit ced79f4 into main Jun 27, 2026
8 checks passed
@helly25
helly25 deleted the feat/nouser-nogroup branch June 27, 2026 18:43
helly25 added a commit that referenced this pull request Aug 5, 2026
)

The first backend-facing half of the help-model epic: RenderDocument walks
a help_model Document and drives a HelpBackend, so a new output format is
just a new backend and no format re-implements the traversal. Unlike the
older DocRenderer walk, a backend receives the model's already-parsed
Inlines (styles + cross-reference targets), so highlighting and
cross-referencing are first-class rather than re-parsed from strings.

- help_backend.{h,cc}: the HelpBackend interface (Preamble, Begin/End
  Section/Subsection/Entry, Emit{Prose,Example,Bullets,Rows,SeeAlso}) and
  the RenderDocument walker (recurses subsection children + entry details).
- plain_backend.{h,cc}: PlainTextBackend + RenderInlinesPlain (drops
  emphasis markup, resolves a ref to its plain locator) + PlainRefLocator.

Additive only - not yet wired into the CLI. BuildReference() (build a
Document from the registry/globals SOT) and porting the roff / Markdown
backends to consume the model, byte-identical, are the following slices;
the plain layout here is provisional until it feeds real help data.
helly25 added a commit that referenced this pull request Aug 5, 2026
The structured counterpart to the plain-text backend: MarkdownBackend
renders a help_model Document to GitHub-flavored Markdown, exercising the
capabilities the eventual --markdown port needs and the plain backend did
not - a cross-reference becomes a real link (URL, or an in-document anchor
slug for a topic / flag / primary), and an Example becomes a fenced code
block carrying its info string. Inline emphasis maps to `code` / _italic_
/ **bold**.

RenderInlinesMarkdown + MarkdownRefLink (+ the GitHub anchor slug) are
exposed and unit-tested; a whole-document golden covers the block layout.
Additive, provisional layout - the byte-identical --markdown port (once
BuildReference feeds real data) is a later slice.
helly25 added a commit that referenced this pull request Aug 5, 2026
BuildReference() assembles the complete help_model Document from the single
source of truth - the global flags, the expression registry, and the
doc-source vocabularies (fields / printf / time / size / regex grammars) -
the model counterpart of the imperative WriteReference(DocRenderer&) walk.

Authored doc strings are parsed for their backtick / fence markup by
help_parse (ParseInline / ParseBlocks); structure (sections, grouped
subsections, entries, rows, the FIELDS braces / dynamic / qualifiers
blocks, the SEE ALSO man-page refs) is built as typed model nodes. Any
HelpBackend now renders the whole reference via RenderDocument.

Additive: the CLI still renders --man / --markdown / --help through the
DocRenderer walk. Porting those backends to render BuildReference()
byte-identically, then retiring the DocRenderer walk + Render* + kHelpText,
are the following slices. Tested structurally (section order, the three
expression kinds, grouped option entries, the FIELDS subsections, SEE ALSO).
helly25 added a commit that referenced this pull request Aug 5, 2026
Completes the backend trio (plain / markdown / roff) over help_model:
RoffBackend renders a Document to man-page source - .TH/.SH/.SS structure,
upper-cased section headings, .TP entries, code/strong -> \fB, emphasis ->
\fI, SEE ALSO -> .BR, and the RoffEscape() escaper (hyphens, backslashes,
leading control chars). The model counterpart of manpage.cc's RoffRenderer.

Additive: --man still uses the DocRenderer walk. Note the port will not be
byte-identical - BuildReference() parses backticks in entry details, which
the old RoffRenderer emitted literally, so the ported man page renders those
as bold (an improvement). Wiring --man/--markdown/--help onto the model
therefore regenerates XFF.md + the man golden, which is the next slice
(gated on the output-format decision).
helly25 added a commit that referenced this pull request Aug 7, 2026
/#164/#154) (#385)

* Add --width flag + help-width resolver, wired into model-rendered topics

New xff/cli/help_width lib: ResolveHelpWidth (pure, tested) maps the --width
value + detected terminal width to a wrap column; DetectTerminalWidth reads
$COLUMNS / the tty winsize. --width[=auto|none|COLS] is registered in globals
(auto = terminal width else 80; none/0 = no wrap; N = fixed). main resolves it
once and threads it into the model-rendered help topics (--help=fields, etc.)
via PlainTextBackend(width).

NOTE: bare --help and --help=FLAG still render through the imperative RenderHelp
path, which ignores --width; those wrap once that path is ported onto the help
model (the remaining help-epic piece).

* Hold render context (width/color) on the HelpBackend base; wrap rows

Introduce HelpRenderContext { width, color } held by the abstract HelpBackend
base as a constant, so every backend and the walk read one shared context via
Context() - not a bare width param on PlainTextBackend. main resolves it once
at the CLI boundary and threads a HelpRenderContext through the model-rendered
topics. color is plumbed (resolved later; ANSI emission is a follow-up).

Also make EmitRows wrap the description column with a hanging indent under the
description column when a width is set (the aligned vocabulary tables were the
one flowing element that did not wrap); width 0 keeps the verbatim table.

Supersedes the bare-width wiring from the previous commit. Groundwork for
retiring the imperative RenderHelp onto the model.

* Add a general Entry.tags list to the help model (port groundwork)

The model Entry gains a tags vector (parenthesized classification tokens, e.g.
{global, xff} for a flag). All three backends render it as "(a, b, c)" after
the term, falling back to the bare xff marker when tags is empty - so output is
byte-identical until BuildReference populates tags. Groundwork for retiring
RenderHelp: the (global, xff|find) flag tag and the primary kind/style/safety
tags move into the model so every renderer shows them uniformly.

* Enrich model flag/primary entries with tags + influence + not-built note

BuildReference now builds flags via FlagEntry and primaries via PrimaryEntry,
populating Entry.tags ((global, xff|find) / (kind, xff|find, [safety])), a
"not built into this binary" note when a flag's build extra is absent, and the
Affects / Affected-by influence detail lines (ported from help.cc's AffectsList
/ AffectedBy). Since BuildReference is the one SOT for --man / --markdown /
XFF.md, all three gain these uniformly (decision A); XFF.md regenerated and the
two tag unit tests updated. Groundwork for routing --help onto the model.

* Render --help=FLAG/PRIMARY and --help=full from the model (wrap via width)

RenderTopic now resolves a single primary/flag via the new EntryReference (the
same model entry BuildReference folds in) and renders it through
PlainTextBackend(context), so --help=--width, --help=-regex, etc. wrap. --help=full
renders the whole BuildReference() Document plain (the --man/--markdown content in
text). RenderHelp's single-entry branch is retired (returns NotFound); RenderTopic
runs RenderHelp's special topics first (so --help=config beats the --config flag),
then EntryReference. help_render_test's single-entry cases move to a RenderEntry
helper over the model.

Also: --width default is now no-wrap when the terminal width is unknown (a pipe /
file stays full-width and byte-stable; a real terminal still wraps), reverting the
80-column fallback. A title-less section renders headless (the single-entry doc).

* Structured, wrapping, indented Examples section (retire the verbatim blob)

The cookbook recipes are now the shared SOT (CookbookRecipes() in help.h): the
Examples section is built as structured model nodes - each recipe a subsection
with its command as a verbatim Example and its explanation as wrapping Prose -
instead of one preformatted RenderHelp("cookbook") blob. So the explanations
wrap to --width and --man/--markdown get real headings + fenced commands (#152).
The plain backend now indents subsection body content (an example's command and
note sit under their recipe heading).

Fixes a segfault: the recipe struct/accessor were duplicated (an anon-namespace
Recipe shadowed the header type and CookbookRecipes had internal linkage), so
BuildExamples called an undefined public symbol. Recipe + CookbookRecipes are
now the single public definitions. XFF.md regenerated; help_topic_test updated.

* Consistent nesting indentation in plain help + min wrap width (40)

The plain backend now tracks one nesting depth: every Begin* (section, subsection,
entry) indents its body two spaces and the matching End* dedents, so a heading
visibly owns the content beneath it (EXIT STATUS / SEE ALSO / Examples bodies, an
option under its group, an entry's summary/detail under its term) instead of
everything sitting flush-left. Rows, bullets, examples, prose and see-also all
render at the current body indent.

--width also clamps any positive width up to a 40-column minimum (below that,
wrapping degenerates); 0 / none (no wrap) is exempt. The doc-render golden is
updated; markdown / roff are unaffected (XFF.md unchanged).

* Render --help=printf/time/size/grammars from the model (wrap + indent)

The sub-vocabulary topics now render through TopicReference + PlainTextBackend
(the same VocabSection BuildReference folds into the full reference, factored into
named builders so they cannot drift), so they wrap to --width and pick up the
consistent nesting indentation. Retires the imperative RenderPrintfDocs /
RenderTimeDocs / RenderSizeDocs / RenderRegexDocs / RenderFieldsDocs (and drops
the now-unused datetime dep from the cli binary).

* Render bare --help (usage page) from the model in entry style

BuildUsage() renders the description, the whole-run options and expression
primaries as summary-only entries (no detail blocks), and the meta/doc flags +
--help=TOPIC index - all wrapping + indenting via the render context, replacing
the imperative kHelpText / kHelpTextExpression / RenderOptions path. The Options
and Expression section builders are factored so BuildReference and BuildUsage
share one definition (a with_details flag selects the terse vs full form). The
not-built-extra note shows even on the usage page. help_test / extras_test
updated to the model output.

* Render --help=list/all/expressions from the model (wrap + indent)

IndexReference routes the index topics through the shared section builders:
list = the usage page, all = every option + primary (summaries), expressions =
the primaries (summaries) without the global flags. They wrap + indent via the
context instead of the imperative RenderIndex / RenderFull / RenderExpressions.

* Format
helly25 added a commit that referenced this pull request Aug 8, 2026
…pager idea

Help / docs rendering section: structured examples, indent-aware width
control, and color are all shipped via the EPIC; F (cross-ref/index/
validation) and the HTML backend are parked as infra for unused features.

Add a design leaning for a git-style --pager=CMD (auto on a TTY, honor
$PAGER, fall back to less -FRX; pairs with help color) under deferred
ideas - confirm the default before building.
helly25 added a commit that referenced this pull request Aug 8, 2026
* help: colorize the plain backend (bold headings, cyan names/values, green examples)

Reuse xff/color's auto/always/never resolution at the CLI boundary
(color::Enabled(ResolveWhen(args), isatty, NO_COLOR)) and thread the
resolved bool through HelpRenderContext into the plain backend.

Palette (color on): section/subsection headings bold, flag/primary entry
terms bold-cyan, value-table terms cyan, verbatim example/command blocks
green. Color off renders byte-for-byte as before (guarded by a test).

Make WrapText ANSI-aware (VisibleWidth skips CSI escapes) so colored words
and indents wrap by their on-screen width, then unify plain EmitRows on
WrapText - dropping the now-dead RenderDocRows from help.{h,cc} and the
help.h include/dep from the plain backend.

XFF.md is unaffected (color is plain-only; the markdown backend renders
with color off).

* docs(TODO): mark help-model EPIC #154 done (color shipped); record --pager idea

Help / docs rendering section: structured examples, indent-aware width
control, and color are all shipped via the EPIC; F (cross-ref/index/
validation) and the HTML backend are parked as infra for unused features.

Add a design leaning for a git-style --pager=CMD (auto on a TTY, honor
$PAGER, fall back to less -FRX; pairs with help color) under deferred
ideas - confirm the default before building.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant