fix old nix manpages, strip priv esc

This commit is contained in:
atagen 2026-03-24 22:18:12 +11:00
parent bbd7c67d0c
commit 6da495dc59
2 changed files with 81 additions and 14 deletions

View file

@ -269,15 +269,23 @@ let classify_line line =
(* --- section extraction ---
* manpages are divided into sections by .SH macros. the options section
* contains the flag definitions we want. if there's no OPTIONS section,
* we fall back to DESCRIPTION (some simple tools put flags there). *)
* we fall back to DESCRIPTION (some simple tools put flags there).
*
* old-style nix manpages (nix-build, nix-env-install, etc.) split flags
* across multiple .SH sections with option-like names: e.g. "Options" for
* command-specific flags and "Common Options" for flags shared by all nix
* commands. collecting only the first such section misses the majority of
* flags, so we collect and concatenate ALL option-like sections. *)
let extract_options_section lines =
let classified = List.map classify_line lines in
let rec collect_until_next_sh lines acc =
(* collect lines until the next .SH header, returning (content, rest)
* where rest starts at the .SH line (or is empty if at end of file). *)
let rec collect_section lines acc =
match lines with
| [] -> List.rev acc
| Macro ("SH", _) :: _ -> List.rev acc
| line :: rest -> collect_until_next_sh rest (line :: acc)
| [] -> (List.rev acc, [])
| Macro ("SH", _) :: _ -> (List.rev acc, lines)
| line :: rest -> collect_section rest (line :: acc)
in
let is_options_section name =
let s = String.uppercase_ascii (String.trim name) in
@ -286,24 +294,33 @@ let extract_options_section lines =
try let _ = Str.search_forward (Str.regexp_string "OPTION") s 0 in true
with Not_found -> false)
in
(* First pass: look for OPTIONS section *)
let rec find_options = function
| [] -> None
(* Collect from ALL option-like .SH sections and concatenate them.
* handles the common nix pattern where "Options" and "Common Options"
* are separate .SH sections but both contain relevant flags.
*
* a synthetic Macro("SH","") separator is inserted between sections so
* that collect_desc_text (which stops on SH/SS) does not let a description
* from the last entry in one section bleed into the intro text of the next. *)
let rec find_all_options lines acc =
match lines with
| [] -> acc
| Macro ("SH", args) :: rest when is_options_section args ->
Some (collect_until_next_sh rest [])
| _ :: rest -> find_options rest
let (section, remaining) = collect_section rest [] in
let sep = if acc = [] then [] else [Macro ("SH", "")] in
find_all_options remaining (acc @ sep @ section)
| _ :: rest -> find_all_options rest acc
in
(* Fallback: DESCRIPTION section *)
let rec find_description = function
| [] -> []
| Macro ("SH", args) :: rest
when String.uppercase_ascii (String.trim args) = "DESCRIPTION" ->
collect_until_next_sh rest []
fst (collect_section rest [])
| _ :: rest -> find_description rest
in
match find_options classified with
| Some section -> section
| None -> find_description classified
match find_all_options classified [] with
| [] -> find_description classified
| sections -> sections
(* --- strategy-based entry extraction ---
* rather than a single monolithic parser, we use multiple "strategies" that