···3636type set = { update : Options.option list; print_options : bool }
3737type hash = Hash_remove | Hash_stats | Hash_add of string list
38383939-(* Built-in Actions *)
4039type t =
4040+ (* Built-in Actions *)
4141 | Cd of { path : string option }
4242 | Pwd
4343 | Exit of int
···4646 | Dot of string (* a.k.a source *)
4747 | Unset of [ `Variables of string list | `Functions of string list ]
4848 | Hash of hash
4949+ | Command of string list
49505051(* Change Directory *)
5152module Cd = struct
···191192 Cmd.v info term
192193end
193194195195+module Command = struct
196196+ open Cmdliner
197197+198198+ let args =
199199+ let doc = "Arguments to command." in
200200+ Arg.(value & pos_all string [] & info [] ~docv:"ARGS" ~doc)
201201+202202+ let t =
203203+ let make_command pos_all = Command pos_all in
204204+ let term = Term.(const make_command $ args) in
205205+ let info =
206206+ let doc = "Execute a simple command." in
207207+ Cmd.info "command" ~doc
208208+ in
209209+ Cmd.v info term
210210+end
211211+194212module Make_dot (T : sig
195213 val name : string
196214end) =
···239257 | "." :: _ as cmd -> exec_cmd cmd Dot.t
240258 | "unset" :: _ as cmd -> exec_cmd cmd Unset.t
241259 | "hash" :: _ as cmd -> exec_cmd cmd Hash.t
260260+ | "command" :: _ as cmd -> exec_cmd cmd Command.t
242261 | _ -> None
+1
src/lib/built_ins.mli
···2323 | Dot of string
2424 | Unset of [ `Variables of string list | `Functions of string list ]
2525 | Hash of hash
2626+ | Command of string list
26272728val of_args : string list -> (t, string) result option
2829(** Parses a command-line to the built-ins, errors are returned if parsing. *)
+36-14
src/lib/eval.ml
···291291 |> List.rev
292292 in
293293 match Built_ins.of_args (executable :: args_as_strings) with
294294- | Some (Ok bi) ->
295295- let ctx = handle_built_in ~rdrs ~stdout:some_write ctx bi in
296296- close_stdout ~is_global some_write;
297297- let built_in = ctx >|= fun _ -> () in
298298- let job = handle_job ~pgid job (`Built_in built_in) in
299299- loop (Exit.value ctx) job (pgid, some_read) rest
300294 | Some (Error _) ->
301295 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1)))
302302- | None -> (
296296+ | (None | Some (Ok (Command _))) as v -> (
297297+ let is_command =
298298+ match v with Some (Ok (Command _)) -> true | _ -> false
299299+ in
303300 (* We handle the [export] built_in explicitly as we need access to the
304301 raw CST *)
305302 match executable with
···311308 loop (Exit.value updated) job (pgid, stdout_of_previous) rest
312309 | _ -> (
313310 let saved_ctx = ctx in
314314- match
315315- let ctx = { ctx with stdout = some_write } in
316316- handle_function_application ctx ~name:executable
317317- (ctx.program :: args_as_strings)
318318- with
311311+ let func_app =
312312+ if is_command then None
313313+ else
314314+ let ctx = { ctx with stdout = some_write } in
315315+ handle_function_application ctx ~name:executable
316316+ (ctx.program :: args_as_strings)
317317+ in
318318+ match func_app with
319319 | Some ctx ->
320320 close_stdout ~is_global some_write;
321321 (* TODO: Proper job stuff and redirects etc. *)
···324324 in
325325 loop saved_ctx job (pgid, some_read) rest
326326 | None -> (
327327+ let executable, args =
328328+ if is_command then begin
329329+ match args_as_strings with
330330+ | [] -> ("", [])
331331+ | [ x ] -> (x, [])
332332+ | x :: xs -> (x, xs)
333333+ end
334334+ else (executable, args_as_strings)
335335+ in
327336 match stdout_of_previous with
328337 | None ->
329338 let ctx, job =
330339 exec_process ctx job ~fds:rdrs ~stdout:some_write
331331- ~pgid executable args_as_strings
340340+ ~pgid executable args
332341 in
333342 close_stdout ~is_global some_write;
334343 loop ctx job (pgid, some_read) rest
···339348 args_as_strings
340349 in
341350 close_stdout ~is_global some_write;
342342- loop ctx job (pgid, some_read) rest))))
351351+ loop ctx job (pgid, some_read) rest)))
352352+ | Some (Ok bi) ->
353353+ let ctx = handle_built_in ~rdrs ~stdout:some_write ctx bi in
354354+ close_stdout ~is_global some_write;
355355+ let built_in = ctx >|= fun _ -> () in
356356+ let job = handle_job ~pgid job (`Built_in built_in) in
357357+ loop (Exit.value ctx) job (pgid, some_read) rest)
343358 | CompoundCommand (c, rdrs) :: rest ->
344359 let _rdrs =
345360 List.map (handle_one_redirection ~sw:pipeline_switch ctx) rdrs
···636651 | Ast.ForClause fc -> handle_for_clause ctx fc
637652 | Ast.IfClause if_ -> handle_if_clause ctx if_
638653 | Ast.BraceGroup (term, sep) -> exec ctx (term, Some sep)
654654+ | Ast.Subshell (term, sep) ->
655655+ let saved_ctx = ctx in
656656+ let e = exec ctx (term, Some sep) in
657657+ e >|= fun _ -> saved_ctx
639658 | _ as c ->
640659 Fmt.epr "Compound command not supported: %a\n%!" yojson_pp
641660 (Ast.compound_command_to_yojson c);
···807826 Eio.Flow.copy_string (Fmt.str "%a" Hash.pp ctx.hash) stdout;
808827 Exit.zero ctx
809828 | _ -> assert false)
829829+ | Command _ ->
830830+ (* Handled separately *)
831831+ assert false
810832811833 and exec initial_ctx ((command, sep) : Ast.complete_command) =
812834 let rec loop : Eio.Switch.t -> ctx -> Ast.clist -> ctx Exit.t =