Select the types of activity you want to include in your feed.
Add hash built-in
This also fixes the problem where the stdout of built-in functions was just going to the parent stdout instead of respecting things like pipelines etc.
···2727 argv = Array.of_list (pos_zero :: rest);
2828 program = pos_zero;
2929 functions = [];
3030+ hash = Merry.Hash.empty;
3031 }
3132 in
3233 match (file, command) with
+29-2
src/lib/built_ins.ml
···3434end
35353636type set = { update : Options.option list; print_options : bool }
3737+type hash = Hash_remove | Hash_stats | Hash_add of string list
37383839(* Built-in Actions *)
3940type t =
···4445 | Wait of int
4546 | Dot of string (* a.k.a source *)
4647 | Unset of [ `Variables of string list | `Functions of string list ]
4848+ | Hash of hash
47494850(* Change Directory *)
4951module Cd = struct
···159161 in
160162 let term = Term.(const make_unset $ kind $ names) in
161163 let info =
162162- let doc = "Wait for a particular PID (default is 0)" in
163163- Cmd.info "wait" ~doc
164164+ let doc = "Unset names of variables or functions." in
165165+ Cmd.info "unset" ~doc
166166+ in
167167+ Cmd.v info term
168168+end
169169+170170+module Hash = struct
171171+ open Cmdliner
172172+173173+ let remove =
174174+ let doc = "Empty the location table." in
175175+ Arg.(value & flag & info [ "r" ] ~docv:"REMOVE" ~doc)
176176+177177+ let utilities =
178178+ let doc = "Utilities to search for and add to the location table." in
179179+ Arg.(value & pos_all string [] & info [] ~docv:"UTILITIES" ~doc)
180180+181181+ let t =
182182+ let make_hash remove pos_all =
183183+ if remove then Hash Hash_remove
184184+ else match pos_all with [] -> Hash Hash_stats | us -> Hash (Hash_add us)
185185+ in
186186+ let term = Term.(const make_hash $ remove $ utilities) in
187187+ let info =
188188+ let doc = "Remember or report utility locations." in
189189+ Cmd.info "hash" ~doc
164190 in
165191 Cmd.v info term
166192end
···212238 | "source" :: _ as cmd -> exec_cmd cmd Source.t
213239 | "." :: _ as cmd -> exec_cmd cmd Dot.t
214240 | "unset" :: _ as cmd -> exec_cmd cmd Unset.t
241241+ | "hash" :: _ as cmd -> exec_cmd cmd Hash.t
215242 | _ -> None
+2
src/lib/built_ins.mli
···1111end
12121313type set = { update : Options.option list; print_options : bool }
1414+type hash = Hash_remove | Hash_stats | Hash_add of string list
14151516type t =
1617 | Cd of { path : string option }
···2122 | Wait of int
2223 | Dot of string
2324 | Unset of [ `Variables of string list | `Functions of string list ]
2525+ | Hash of hash
24262527val of_args : string list -> (t, string) result option
2628(** Parses a command-line to the built-ins, errors are returned if parsing. *)
+70-37
src/lib/eval.ml
···4343 program : string;
4444 argv : string array;
4545 functions : (string * Ast.compound_command) list;
4646+ hash : Hash.t;
4647 }
47484849 let clear_local_state ctx = { ctx with local_state = [] }
···157158 let apply_pair (a, b) f = f a b
158159 let ( ||> ) = apply_pair
159160161161+ let resolve_program ?(update = true) ctx name =
162162+ let v =
163163+ if not (String.contains name '/') then
164164+ Sys.getenv_opt "PATH"
165165+ |> Option.value ~default:"/bin:/usr/bin"
166166+ |> String.split_on_char ':'
167167+ |> List.find_map (fun dir ->
168168+ let p = Filename.concat dir name in
169169+ if Sys.file_exists p then Some p else None)
170170+ else if Sys.file_exists name then Some name
171171+ else None
172172+ in
173173+ match (update, v) with
174174+ | true, Some loc ->
175175+ let hash = Hash.add ~utility:name ~loc ctx.hash in
176176+ ({ ctx with hash }, Some loc)
177177+ | false, Some loc -> (ctx, Some loc)
178178+ | _, None -> (ctx, None)
179179+160180 let get_env ?(extra = []) ctx =
161181 let extra =
162182 extra
···193213 Eio.Flow.close some_write
194214 end
195215 in
196196- let exec_process ctx job ?fds ?stdin ~stdout ~pgid args =
197197- let process =
198198- E.exec ctx.executor ?fds ?stdin ~stdout ~pgid ~mode
199199- ~cwd:(cwd_of_ctx ctx)
200200- ~env:(get_env ~extra:ctx.local_state ctx)
201201- args
216216+ let exec_process ctx job ?fds ?stdin ~stdout ~pgid executable args =
217217+ let ctx, process =
218218+ match resolve_program ctx executable with
219219+ | ctx, None ->
220220+ Fmt.epr "msh: command not found: %s\n%!" executable;
221221+ (ctx, Error (127, `Not_found))
222222+ | ctx, Some full_path ->
223223+ ( ctx,
224224+ E.exec ctx.executor ?fds ?stdin ~stdout ~pgid ~mode
225225+ ~cwd:(cwd_of_ctx ctx)
226226+ ~env:(get_env ~extra:ctx.local_state ctx)
227227+ ~executable:full_path (executable :: args) )
202228 in
203229 match process with
204230 | Error (n, _) ->
···243269 in
244270 match Built_ins.of_args (executable :: args_as_strings) with
245271 | Some (Ok bi) ->
246246- let ctx = handle_built_in ctx bi in
272272+ let ctx = handle_built_in ~stdout:some_write ctx bi in
273273+ close_stdout ~is_global some_write;
247274 let built_in = ctx >|= fun _ -> () in
248275 let job = handle_job ~pgid job (`Built_in built_in) in
249249- loop (Exit.value ctx) job (pgid, stdout_of_previous) rest
276276+ loop (Exit.value ctx) job (pgid, some_read) rest
250277 | Some (Error _) ->
251278 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1)))
252279 | None -> (
···289316 | None ->
290317 let ctx, job =
291318 exec_process ctx job ~fds:redirect
292292- ~stdout:some_write ~pgid
293293- (executable :: args_as_strings)
319319+ ~stdout:some_write ~pgid executable
320320+ args_as_strings
294321 in
295322 close_stdout ~is_global some_write;
296323 loop ctx job (pgid, some_read) rest
297324 | Some stdout ->
298325 let ctx, job =
299326 exec_process ctx job ~fds:redirect ~stdin:stdout
300300- ~stdout:some_write ~pgid
301301- (executable :: args_as_strings)
327327+ ~stdout:some_write ~pgid executable
328328+ args_as_strings
302329 in
303330 close_stdout ~is_global some_write;
304331 loop ctx job (pgid, some_read) rest))))
···320347 process that last just until all of the processes are setup. *)
321348 let ctx, job =
322349 let ghost_process =
323323- E.exec ~mode:(Types.Switched pipeline_switch) ~pgid:0
324324- ~cwd:(cwd_of_ctx initial_ctx) initial_ctx.executor
325325- [ "sleep"; "99999999" ]
326326- |> function
327327- | Ok p -> p
328328- | Error (n, `Not_found) ->
329329- Fmt.epr "Interal error ghost process: not found";
330330- exit n
350350+ match resolve_program ~update:false initial_ctx "sleep" with
351351+ | _, None -> Fmt.failwith "Sleep not found\n%!"
352352+ | ctx, Some sleep -> (
353353+ E.exec ~mode:(Types.Switched pipeline_switch) ~pgid:0
354354+ ~cwd:(cwd_of_ctx ctx) ctx.executor ~executable:sleep
355355+ [ "sleep"; "99999999" ]
356356+ |> function
357357+ | Ok p -> p
358358+ | Error (n, `Not_found) ->
359359+ Fmt.epr "Interal error ghost process: not found";
360360+ exit n)
331361 in
332362 loop initial_ctx None (E.pid ghost_process, None) p
333363 in
···672702 | Ast.Prefix_assignment (Name param, v) ->
673703 (* Expand the values *)
674704 let ctx, v = expand_cst ctx v in
705705+ let v = handle_subshell ctx v in
675706 let state =
676707 if update then S.update ctx.state ~param v else ctx.state
677708 in
···693724 (ctx, acc @ word_glob_expand ctx cst))
694725 (ctx, []) swc
695726696696- and handle_built_in (ctx : ctx) = function
727727+ and handle_built_in ~(stdout : Eio_unix.sink_ty Eio.Flow.sink) (ctx : ctx) =
728728+ function
697729 | Built_ins.Cd { path } ->
698730 let cwd = S.cwd ctx.state in
699731 let+ state =
···708740 in
709741 { ctx with state }
710742 | Pwd ->
711711- Fmt.pr "%a\n%!" Fpath.pp (S.cwd ctx.state);
743743+ Eio.Flow.copy_string
744744+ (Fmt.str "%a\n%!" Fpath.pp (S.cwd ctx.state))
745745+ stdout;
712746 Exit.zero ctx
713747 | Exit n ->
714748 let should_exit =
···720754 Exit.zero
721755 { ctx with options = Built_ins.Options.update ctx.options update }
722756 in
723723- if print_options then Fmt.pr "%a%!" Built_ins.Options.pp ctx.options;
757757+ if print_options then
758758+ Eio.Flow.copy_string
759759+ (Fmt.str "%a" Built_ins.Options.pp ctx.options)
760760+ stdout;
724761 v
725762 | Wait i -> (
726763 match Unix.waitpid [] i with
727764 | _, WEXITED 0 -> Exit.zero ctx
728765 | _, (WEXITED n | WSIGNALED n | WSTOPPED n) -> Exit.nonzero ctx n)
729766 | Dot file -> (
730730- let resolve_program name =
731731- if not (String.contains name '/') then
732732- Sys.getenv_opt "PATH"
733733- |> Option.value ~default:"/bin:/usr/bin"
734734- |> String.split_on_char ':'
735735- |> List.find_map (fun dir ->
736736- let p = Filename.concat dir name in
737737- if Sys.file_exists p then Some p else None)
738738- else if Sys.file_exists name then Some name
739739- else None
740740- in
741741- match resolve_program file with
742742- | None -> Exit.nonzero ctx 127
743743- | Some f ->
767767+ match resolve_program ctx file with
768768+ | ctx, None -> Exit.nonzero ctx 127
769769+ | ctx, Some f ->
744770 let program = Ast.of_file (ctx.fs / f) in
745771 let ctx, _ = run (Exit.zero ctx) program in
746772 ctx)
···760786 ctx.functions names
761787 in
762788 Exit.zero { ctx with functions })
789789+ | Hash v -> (
790790+ match v with
791791+ | Built_ins.Hash_remove -> Exit.zero { ctx with hash = Hash.empty }
792792+ | Built_ins.Hash_stats ->
793793+ Eio.Flow.copy_string (Fmt.str "%a" Hash.pp ctx.hash) stdout;
794794+ Exit.zero ctx
795795+ | _ -> assert false)
763796764797 and exec initial_ctx ((command, sep) : Ast.complete_command) =
765798 let rec loop : Eio.Switch.t -> ctx -> Ast.clist -> ctx Exit.t =
+25
src/lib/hash.ml
···11+(* The hash table for utility locations *)
22+33+module M = Map.Make (String)
44+55+type entry = { hits : int; loc : string }
66+type t = entry M.t
77+88+let empty = M.empty
99+1010+let add ~utility ~loc t =
1111+ match M.find_opt utility t with
1212+ | Some { hits; loc = loc' } when String.equal loc loc' ->
1313+ M.add utility { hits = hits + 1; loc } t
1414+ | None | Some _ -> M.add utility { hits = 1; loc } t
1515+1616+let lookup ~utility t = M.find_opt utility t |> Option.map (fun v -> v.loc)
1717+1818+let pp ppf t =
1919+ let entries = M.to_list t in
2020+ match entries with
2121+ | [] -> ()
2222+ | _ ->
2323+ let pp_entry ppf (_, { hits; loc }) = Fmt.pf ppf "%-7i %s@." hits loc in
2424+ let pp_header ppf () = Fmt.pf ppf "%-7s %s@." "hits" "command" in
2525+ Fmt.pf ppf "@[<v>%a%a@]" pp_header () Fmt.(list pp_entry) entries
+14
src/lib/hash.mli
···11+type t
22+(** A lookup table for utilities *)
33+44+val empty : t
55+(** The empty table *)
66+77+val add : utility:string -> loc:string -> t -> t
88+(** [add ~utility ~loc t] adds the [utility] with [loc] location. *)
99+1010+val lookup : utility:string -> t -> string option
1111+(** [lookup ~utility t] will try to find [utility] in [t]. *)
1212+1313+val pp : t Fmt.t
1414+(** A pretty printer *)
···128128 let handler = Eio.Process.Pi.process (module Process_impl) in
129129 fun proc -> Eio.Resource.T (proc, handler)
130130131131-let resolve_program name =
132132- if not (String.contains name '/') then
133133- Sys.getenv_opt "PATH"
134134- |> Option.value ~default:"/bin:/usr/bin"
135135- |> String.split_on_char ':'
136136- |> List.find_map (fun dir ->
137137- let p = Filename.concat dir name in
138138- if Sys.file_exists p then Some p else None)
139139- else if Sys.file_exists name then Some name
140140- else None
141141-142131let read_of_fd ~mode ~default ~to_close v =
143132 match (mode, v) with
144133 | Merry.Types.Async, _ | _, None -> default
···186175 | None -> (
187176 match args with
188177 | [] -> invalid_arg "Arguments list is empty and no executable given!"
189189- | x :: _ -> (
190190- match resolve_program x with
191191- | Some x -> x
192192- | None -> raise (Eio.Process.err (Executable_not_found x))))
178178+ | x :: _ -> x)
193179194180let get_env = function Some e -> e | None -> Unix.environment ()
195181
+2-2
src/lib/posix/merry_posix.ml
···1616 | `Signaled n -> Merry.Exit.nonzero () n
17171818 let exec ?(fork_actions = []) ?(fds = []) ?stdin ?stdout ?stderr ?env ~mode
1919- ~pgid ~cwd t args =
1919+ ~pgid ~cwd ~executable t args =
2020 let env =
2121 Option.map
2222 (fun lst -> List.map (fun (a, b) -> a ^ "=" ^ b) lst |> Array.of_list)
···2525 try
2626 Ok
2727 (Exec.run ~fork_actions ~mode ~fds ~pgid ~cwd ?stdin ?stdout ?stderr
2828- ?env t args)
2828+ ?env t ~executable args)
2929 with Eio.Io (Eio.Process.E (Eio.Process.Executable_not_found m), _ctx) ->
3030 Fmt.epr "msh: command not found: %s\n%!" m;
3131 Error (127, `Not_found)
+1
src/lib/types.ml
···7070 mode:exec_mode ->
7171 pgid:int ->
7272 cwd:Eio.Fs.dir_ty Eio.Path.t ->
7373+ executable:string ->
7374 t ->
7475 string list ->
7576 (process, int * [ `Not_found ]) result