pwd
, cd
) à titre
d'illustration.
let tokenize s = let space_regexp = Str.regexp "[ \t\n]+" in Str.split space_regexp s;; type phrase = Com of bool * string list | Seq of phrase list | And of phrase list | Or of phrase list let rec quick_parse right = let rec split left right = let get () = match left with "&" :: cl -> Com (false, List.rev cl) | cl -> Com (true, List.rev cl) in match right with | ";" :: rest -> Seq [ get(); quick_parse rest ] | "&&" :: rest -> And [ get(); quick_parse rest ] | "||" :: rest -> Or [ get(); quick_parse rest ] | a :: rest -> split (a :: left) rest | [] -> get () in split [] right;; let internal_command f x = try f x; 0 with Unix_error (_,_,_) -> 1 (* Un ^C provoque Break, trois ^C de suite provoquent un exit 1 *) let breaks = ref 0;; let handle_int _ = if !breaks >= 3 then exit 1 else incr breaks;; let _ = signal sigint (Signal_handle handle_int);; let flatten = List.map let break_retcode = 129;; let rec command cmd = try match cmd with | Com (_, []) -> 0 | Com (_, "pwd" :: _) -> internal_command (fun () -> print_endline (getcwd())) () | Com (_, "cd" :: args) -> let p = match args with h::_ -> h | _ -> getenv "HOME" in internal_command chdir p; | Com (_, "jobs" :: _) -> jobs(); 0 | Com (_, "fg" :: _) -> fg() | Com (_, "bg" :: _) -> bg() | Com (fg, cmd :: args) -> let args' = Array.of_list args in if fg then command_wait cmd args' else command_bg cmd args' | Seq cmd_arg_list -> complex_command command_seq cmd_arg_list | And cmd_arg_list -> complex_command command_and cmd_arg_list | Or cmd_arg_list -> complex_command command_or cmd_arg_list with Sys.Break -> break_retcode and complex_command oper cmd_arg_list = let flat = function Com (_, com::args) -> com, Array.of_list args | _ -> eprintf "Embeded complex commands not implemented\n"; exit 1 in oper (List.map flat cmd_arg_list);; let eval_string s = command (quick_parse (tokenize s));; let prompt_string = "# ";; let rec eval_loop () = let last = ref 0 in try while true do clear_process_table(); print_string prompt_string; flush Pervasives.stdout; breaks := 0; last := eval_string (read_line()) done with End_of_file -> exit !last;; |
let main() = match Array.to_list Sys.argv with _ :: "-c" :: cmd :: _ -> exit (eval_string cmd) | [ _ ] -> eval_loop() | d -> eprintf "Usage: %s [ -c cmd ]\n" (if d = [] then "shell" else List.hd d); exit 2 let _ = handle_unix_error main();; |