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 ()