inshellah/bin/main.ml
2026-03-18 18:19:37 +11:00

112 lines
3.9 KiB
OCaml

open Inshellah.Parser
open Inshellah.Manpage
open Inshellah.Nushell
let usage () =
Printf.eprintf {|inshellah generate nushell completions from manpages and --help output
Usage:
inshellah manpage FILE Parse a manpage (.1, .1.gz) and emit nushell extern
inshellah manpage-dir DIR Batch-process all manpages under DIR/man1/
inshellah help CMD [ARGS...] Run CMD ARGS --help, parse output, emit nushell extern
inshellah parse-help CMD Read --help text from stdin, emit nushell extern for CMD
inshellah demo Run built-in demo
|};
exit 1
(* Extract command name from a manpage filename *)
let cmd_name_of_manpage path =
let base = Filename.basename path in
(* strip .gz if present *)
let base =
if Filename.check_suffix base ".gz" then Filename.chop_suffix base ".gz"
else base
in
(* strip .N section suffix *)
try Filename.chop_extension base
with Invalid_argument _ -> base
let cmd_manpage file =
let cmd = cmd_name_of_manpage file in
let entries = parse_manpage_file file in
if entries <> [] then
print_string (generate_extern_from_entries cmd entries)
let cmd_manpage_dir dir =
(* Walk man1/ through man9/ looking for manpages *)
for section = 1 to 9 do
let subdir = Filename.concat dir (Printf.sprintf "man%d" section) in
if Sys.file_exists subdir && Sys.is_directory subdir then begin
let files = Sys.readdir subdir in
Array.sort String.compare files;
Array.iter (fun file ->
let path = Filename.concat subdir file in
try cmd_manpage path
with _ -> () (* skip unparseable manpages *)
) files
end
done
let cmd_help args =
match args with
| [] -> Printf.eprintf "error: help mode requires a command name\n"; exit 1
| cmd :: rest ->
let full_cmd =
String.concat " " (List.map Filename.quote (cmd :: rest @ ["--help"]))
in
let ic = Unix.open_process_in (full_cmd ^ " 2>&1") in
let buf = Buffer.create 4096 in
(try while true do
let line = input_line ic in
Buffer.add_string buf line;
Buffer.add_char buf '\n'
done with End_of_file -> ());
let _ = Unix.close_process_in ic in
let text = Buffer.contents buf in
let cmd_name = Filename.basename cmd in
(match parse_help text with
| Ok r -> print_string (generate_extern cmd_name r)
| Error msg -> Printf.eprintf "parse error for %s: %s\n" cmd_name msg; exit 1)
let cmd_parse_help cmd =
let buf = Buffer.create 4096 in
(try while true do
let line = input_line stdin in
Buffer.add_string buf line;
Buffer.add_char buf '\n'
done with End_of_file -> ());
let text = Buffer.contents buf in
(match parse_help text with
| Ok r -> print_string (generate_extern cmd r)
| Error msg -> Printf.eprintf "parse error for %s: %s\n" cmd msg; exit 1)
let cmd_demo () =
let ls_help =
{|Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
-a, --all do not ignore entries starting with .
-A, --almost-all do not list implied . and ..
--block-size=SIZE with -l, scale sizes by SIZE when printing them;
e.g., '--block-size=M'; see SIZE format below
--color[=WHEN] color the output WHEN
-h, --human-readable with -l and -s, print sizes like 1K 234M 2G etc.
--help display this help and exit
--version output version information and exit
|}
in
Printf.printf "# Generated by: inshellah demo\n\n";
(match parse_help ls_help with
| Ok r -> print_string (generate_extern "ls" r)
| Error msg -> Printf.eprintf "parse error: %s\n" msg)
let () =
let args = Array.to_list Sys.argv |> List.tl in
match args with
| ["manpage"; file] -> cmd_manpage file
| ["manpage-dir"; dir] -> cmd_manpage_dir dir
| "help" :: rest -> cmd_help rest
| ["parse-help"; cmd] -> cmd_parse_help cmd
| ["demo"] -> cmd_demo ()
| _ -> usage ()