From 452ec3edef4b5bb58d205dc5b89d603d82e0b7d8 Mon Sep 17 00:00:00 2001 From: Patrick Ferris Date: Sat, 6 Jun 2026 16:45:30 +0100 Subject: [PATCH] Closing flows for compound commands --- src/lib/eval.ml | 70 +++++++++++++++++++++---------- vendor/eio.1.3/lib_eio/unix/fd.ml | 2 +- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/lib/eval.ml b/src/lib/eval.ml index c71299e..c0fee22 100644 --- a/src/lib/eval.ml +++ b/src/lib/eval.ml @@ -64,6 +64,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct in_double_quotes : bool; umask : int; current_pipeline : string option; + current_context : [ `Toplevel | `Function | `CompoundCommand ]; } exception Continue of int * ctx @@ -107,6 +108,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct in_double_quotes; umask; current_pipeline; + current_context = `Toplevel; } let state ctx = ctx.state @@ -258,7 +260,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct (ctx, Error 127) | _, Some full_path -> Debug.Log.debug (fun f -> - f "executing %a\n%a" + f "executing %a (rdr: %a)" Fmt.(list ~sep:(Fmt.any " ") (quote string)) (full_path :: args) Fmt.(list Types.pp_redirect) @@ -288,8 +290,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct match c with | Ast.SimpleCommand (Prefixed (prefix, None, _suffix)) :: rest -> Debug.Log.debug (fun f -> - f "assignment-only: %a" yojson_pp - (Ast.cmd_prefix_to_yojson prefix)); + f "assignment-only: %a" Ast_pp.cmd_prefix prefix); let ctx = collect_assignments initial_ctx prefix in let job = handle_job job (`Noop ctx) in loop pctx job rest @@ -473,11 +474,14 @@ module Make (S : Types.State) (E : Types.Exec) = struct Trace.name "built-in"; let bi = catch_execs_with_exit ctx @@ fun () -> - handle_built_in ~rdrs - ~stdout:some_write ctx bi + let blt = + handle_built_in ~rdrs + ~stdout:some_write ctx bi + in + close_flow ~is_global:is_stdout_global + some_write; + blt in - close_flow ~is_global:is_stdout_global - some_write; bi in let ctx = @@ -547,8 +551,6 @@ module Make (S : Types.State) (E : Types.Exec) = struct ~pgid:(job_pgid job) executable args in - close_flow ~is_global:is_stdout_global - some_write; Eio.Fiber.fork ~sw: (if async then ctx.async_switch @@ -559,6 +561,9 @@ module Make (S : Types.State) (E : Types.Exec) = struct (J.last_process job) with | Some _ -> + close_flow + ~is_global:is_stdout_global + some_write; close_flow ~is_global:is_stdin_global some_read @@ -574,9 +579,12 @@ module Make (S : Types.State) (E : Types.Exec) = struct Trace.name "built-in"; let bi = catch_execs_with_exit ctx @@ fun () -> - handle_built_in ~rdrs ~stdout:some_write ctx bi + let blt = + handle_built_in ~rdrs ~stdout:some_write ctx bi + in + close_flow ~is_global:is_stdout_global some_write; + blt in - close_flow ~is_global:is_stdout_global some_write; bi in let ctx = @@ -584,7 +592,6 @@ module Make (S : Types.State) (E : Types.Exec) = struct (Exit.map ~f:clear_local_state) ctx in - close_flow ~is_global:is_stdout_global some_write; let job = match bi with | Built_ins.Exit _ -> @@ -621,6 +628,8 @@ module Make (S : Types.State) (E : Types.Exec) = struct rdrs = rdrs @ saved_rdrs; stdout = some_write; stdin = pctx.stdin; + subshell = true; + current_context = `CompoundCommand; } in let ctx = @@ -628,18 +637,28 @@ module Make (S : Types.State) (E : Types.Exec) = struct Trace.name "compound-command"; with_pipeline_scope ctx @@ fun ctx -> let ctx = handle_compound_command ctx c in - close_flow ~is_global:is_stdout_global some_write; - Exit.map ~f:(fun c -> { c with rdrs = saved_rdrs }) ctx + Exit.map + ~f:(fun c -> + { + c with + rdrs = saved_rdrs; + stdout = initial_ctx.stdout; + stdin = initial_ctx.stdin; + current_context = initial_ctx.current_context; + subshell = initial_ctx.subshell; + }) + ctx in + let job = handle_job job (`Built_in ctx) in (* Close stdin *) Eio.Fiber.fork ~sw: (if async then initial_ctx.async_switch else pipeline_switch) (fun () -> - match Option.map E.await (J.last_process job) with - | Some _ -> close_flow ~is_global:is_stdin_global some_read - | None -> ()); - let job = handle_job job (`Built_in ctx) in + match Promise.await ctx with + | _ -> + close_flow ~is_global:is_stdin_global some_read; + close_flow ~is_global:is_stdout_global some_write); let pctx = { pctx with stdin = some_read } in loop pctx job rest) | FunctionDefinition (name, (body, _rdrs)) :: rest -> @@ -1268,6 +1287,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct match v with | Ast.For_Name_DoGroup (_, (term, sep)) -> exec ctx (term, Some sep) | Ast.For_Name_In_WordList_DoGroup (Name name, wdlist, (term, sep)) -> ( + Debug.Log.debug (fun f -> f "for-loop: start"); let wdlist = Nlist.map (word_expansion ctx) wdlist in try Nlist.fold_left @@ -1275,6 +1295,8 @@ module Make (S : Types.State) (E : Types.Exec) = struct Fiber.yield (); List.fold_left (fun _ word -> + Debug.Log.debug (fun f -> + f "for-loop: %s=%s" name word.Ast.txt); update ctx ~param:name word.Ast.txt >>= fun ctx -> try exec ctx (term, Some sep) with | Continue (1, ctx) -> Exit.zero ctx @@ -1375,7 +1397,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct | Exit.Nonzero _ -> exit_so_far (* TODO: Context? *) | Exit.Zero ctx -> (* Before we loop, we yield to other pipeline tasks. *) - Fiber.yield (); + (* Fiber.yield (); *) loop (try exec ctx (term', Some sep') with | Continue (1, ctx) -> Exit.zero ctx @@ -1686,9 +1708,13 @@ module Make (S : Types.State) (E : Types.Exec) = struct | Shift n -> let n = Option.value ~default:1 n in let new_len = Array.length ctx.argv - n in - assert (new_len >= 0); - let argv = Array.init new_len (fun i -> Array.get ctx.argv (i + n)) in - Exit.zero { ctx with argv } + if new_len < 0 then begin + Fmt.epr "Shift count out of range\n%!"; + Exit.nonzero ctx 1 + end + else + let argv = Array.init new_len (fun i -> Array.get ctx.argv (i + n)) in + Exit.zero { ctx with argv } | Read (_backslash, vars) -> ( let line = let buf = Cstruct.create 1 in diff --git a/vendor/eio.1.3/lib_eio/unix/fd.ml b/vendor/eio.1.3/lib_eio/unix/fd.ml index 56c9bd5..3e612ca 100644 --- a/vendor/eio.1.3/lib_eio/unix/fd.ml +++ b/vendor/eio.1.3/lib_eio/unix/fd.ml @@ -12,7 +12,7 @@ type t = { mutable release_hook : Eio.Switch.hook; (* Use this on close to remove switch's [on_release] hook. *) } -let err_closed op = Invalid_argument (op ^ ": file descriptor used after calling close!") +let err_closed ?(msg="") op = Invalid_argument (op ^ ": file descriptor used after calling close!" ^ msg) let use t f ~if_closed = Rcfd.use t.fd f ~if_closed -- 2.51.2