···738738let rec word_component_to_string : word_component -> string = function
739739 | WordName s -> s
740740 | WordLiteral s -> s
741741- | WordDoubleQuoted s -> String.concat "" (List.map word_component_to_string s)
742742- | WordSingleQuoted s -> String.concat "" (List.map word_component_to_string s)
741741+ | WordDoubleQuoted s -> word_components_to_string s
742742+ | WordSingleQuoted s -> word_components_to_string s
743743 | WordGlobAll -> "*"
744744 | WordGlobAny -> "?"
745745+ | WordAssignmentWord (Name p, v) -> p ^ "=" ^ word_components_to_string v
745746 | WordSubshell _ ->
746747 Fmt.failwith
747748 "This is an error in Merry, subshells should already have been \
···750751 Fmt.failwith "Conversion of %a" Yojson.Safe.pp
751752 (word_component_to_yojson v)
752753753753-let word_components_to_string ws =
754754+and word_components_to_string ws =
754755 String.concat "" (List.map word_component_to_string ws)
755756756757class check_ast =
···787788 end
788789 in
789790 o#complete_command ast false
791791+792792+let has_glob ast =
793793+ let o =
794794+ object
795795+ inherit check_ast as super
796796+797797+ method! word_component v ctx =
798798+ match v with
799799+ | WordGlobAll | WordGlobAny -> true
800800+ | _ -> super#word_component v ctx
801801+ end
802802+ in
803803+ o#word_cst ast false
+3
src/lib/ast.mli
···2626(** Checks, recursively, the command to see if there is any use of the async
2727 operator [&] *)
28282929+val has_glob : word_cst -> bool
3030+(** Checks whether or not any glob patterns exist in a given word_cst *)
3131+2932module Dump : sig
3033 val pp : t Fmt.t
3134 (** Dump the program *)
+88-47
src/lib/eval.ml
···217217 let apply_pair (a, b) f = f a b
218218 let ( ||> ) = apply_pair
219219220220- let get_env ?(extra = []) () =
220220+ let get_env ?(extra = []) ctx =
221221+ let extra =
222222+ extra
223223+ @ List.map (fun (k, v) -> (k, Ast.word_components_to_string v))
224224+ @@ S.exports ctx.state
225225+ in
221226 let env = Eunix.env () in
222227 List.fold_left (fun acc (k, _) -> List.remove_assoc k acc) env extra
223228 |> List.append extra
···252257 let process =
253258 E.exec ctx.executor ?fds ?stdin ~stdout ~pgid ~mode
254259 ~cwd:(cwd_of_ctx ctx)
255255- ~env:(get_env ~extra:ctx.local_state ())
260260+ ~env:(get_env ~extra:ctx.local_state ctx)
256261 args
257262 in
258263 match process with
···279284 (Ast.SimpleCommand (Named (executable, suffix)) :: rest)
280285 | Ast.SimpleCommand (Named (executable, suffix)) :: rest -> (
281286 let ctx, executable = expand_cst ctx executable in
282282- let executable = handle_word_components_to_string ctx executable in
287287+ let executable = handle_word_cst_subshell ctx executable in
288288+ let executable = Ast.word_components_to_string executable in
283289 let ctx, suffix =
284290 match suffix with
285291 | None -> (ctx, [])
286292 | Some suffix -> expand_redirects (ctx, []) suffix
287293 in
288294 let args = args ctx suffix in
295295+ let args_as_strings = List.map Ast.word_components_to_string args in
289296 let some_read, some_write =
290297 stdout_for_pipeline ~sw:pipeline_switch ctx rest
291298 in
···294301 | `Global p -> (true, p)
295302 | `Local p -> (false, p)
296303 in
297297- match Built_ins.of_args (executable :: args) with
304304+ match Built_ins.of_args (executable :: args_as_strings) with
298305 | Some (Ok bi) ->
299306 let ctx = handle_built_in ctx bi in
300307 let built_in = ctx >|= fun _ -> () in
···303310 | Some (Error _) ->
304311 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1)))
305312 | None -> (
306306- let saved_ctx = ctx in
307307- match
308308- let ctx = { ctx with stdout = some_write } in
309309- handle_function_application ctx ~name:executable
310310- (ctx.program :: args)
311311- with
312312- | Some ctx ->
313313- close_stdout ~is_global some_write;
314314- (* TODO: Proper job stuff and redirects etc. *)
313313+ (* We handle the [export] built_in explicitly as we need access to the
314314+ raw CST *)
315315+ match executable with
316316+ | "export" ->
317317+ let updated = handle_export ctx args in
315318 let job =
316316- handle_job ~pgid job (`Built_in (ctx >|= fun _ -> ()))
317317- in
318318- loop saved_ctx job (pgid, some_read) rest
319319- | None -> (
320320- let redirect =
321321- List.fold_left
322322- (fun acc -> function
323323- | Ast.Suffix_word _ -> acc
324324- | Ast.Suffix_redirect rdr ->
325325- handle_one_redirection ~sw:pipeline_switch ctx rdr
326326- :: acc)
327327- [] suffix
328328- |> List.rev |> List.filter_map Fun.id
319319+ handle_job ~pgid job (`Built_in (updated >|= fun _ -> ()))
329320 in
330330- match stdout_of_previous with
331331- | None ->
332332- let ctx, job =
333333- exec_process ctx job ~fds:redirect ~stdout:some_write
334334- ~pgid (executable :: args)
321321+ loop (Exit.value updated) job (pgid, stdout_of_previous) rest
322322+ | _ -> (
323323+ let saved_ctx = ctx in
324324+ match
325325+ let ctx = { ctx with stdout = some_write } in
326326+ handle_function_application ctx ~name:executable
327327+ (ctx.program :: args_as_strings)
328328+ with
329329+ | Some ctx ->
330330+ close_stdout ~is_global some_write;
331331+ (* TODO: Proper job stuff and redirects etc. *)
332332+ let job =
333333+ handle_job ~pgid job (`Built_in (ctx >|= fun _ -> ()))
335334 in
336336- close_stdout ~is_global some_write;
337337- loop ctx job (pgid, some_read) rest
338338- | Some stdout ->
339339- let ctx, job =
340340- exec_process ctx job ~fds:redirect ~stdin:stdout
341341- ~stdout:some_write ~pgid (executable :: args)
335335+ loop saved_ctx job (pgid, some_read) rest
336336+ | None -> (
337337+ let redirect =
338338+ List.fold_left
339339+ (fun acc -> function
340340+ | Ast.Suffix_word _ -> acc
341341+ | Ast.Suffix_redirect rdr ->
342342+ handle_one_redirection ~sw:pipeline_switch ctx
343343+ rdr
344344+ :: acc)
345345+ [] suffix
346346+ |> List.rev |> List.filter_map Fun.id
342347 in
343343- close_stdout ~is_global some_write;
344344- loop ctx job (pgid, some_read) rest)))
348348+ match stdout_of_previous with
349349+ | None ->
350350+ let ctx, job =
351351+ exec_process ctx job ~fds:redirect
352352+ ~stdout:some_write ~pgid
353353+ (executable :: args_as_strings)
354354+ in
355355+ close_stdout ~is_global some_write;
356356+ loop ctx job (pgid, some_read) rest
357357+ | Some stdout ->
358358+ let ctx, job =
359359+ exec_process ctx job ~fds:redirect ~stdin:stdout
360360+ ~stdout:some_write ~pgid
361361+ (executable :: args_as_strings)
362362+ in
363363+ close_stdout ~is_global some_write;
364364+ loop ctx job (pgid, some_read) rest))))
345365 | CompoundCommand (c, rdrs) :: rest ->
346366 let _rdrs =
347367 List.map (handle_one_redirection ~sw:pipeline_switch ctx) rdrs
···379399 Exit.zero { ctx with background_jobs = job :: ctx.background_jobs }
380400 end
381401402402+ and handle_export ctx (assignments : Ast.word_cst list) =
403403+ let rec loop acc_ctx = function
404404+ | [] -> Exit.zero acc_ctx
405405+ | Ast.WordAssignmentWord (Name param, v) :: rest ->
406406+ loop
407407+ {
408408+ acc_ctx with
409409+ state = S.update ~export:true acc_ctx.state ~param v;
410410+ }
411411+ rest
412412+ | _ :: _ -> Exit.nonzero_msg acc_ctx "export weird arguments"
413413+ in
414414+ List.fold_left
415415+ (fun ctx w -> match ctx with Exit.Zero ctx -> loop ctx w | _ -> ctx)
416416+ (Exit.zero ctx) assignments
417417+382418 and expand_cst (ctx : ctx) cst : ctx * Ast.word_cst =
383419 let cst = tilde_expansion ctx cst in
384420 let _, o = parameter_expansion' ctx in
···447483 let wdlist = Nlist.flatten @@ Nlist.map (word_glob_expand ctx) wdlist in
448484 Nlist.fold_left
449485 (fun _ word ->
450450- let s = S.update ctx.state ~param:name [ Ast.WordLiteral word ] in
486486+ let s = S.update ctx.state ~param:name word in
451487 let ctx = { ctx with state = s } in
452488 exec ctx (term, Some sep))
453489 (Exit.zero ctx) wdlist
···534570 in
535571 Eio.Switch.run @@ fun sw -> run_subshells ~sw (ref false) wcs
536572537537- and handle_word_components_to_string (ctx : ctx) wcs : string =
573573+ and handle_word_cst_subshell (ctx : ctx) wcs : Ast.word_cst =
538574 if needs_subshelling wcs then begin
539575 let wcs = handle_subshell ctx wcs in
540540- Ast.word_components_to_string wcs
576576+ wcs
541577 end
542542- else Ast.word_components_to_string wcs
578578+ else wcs
543579544580 and glob_expand ctx wc =
545545- handle_word_components_to_string ctx wc |> Globlon.glob |> Array.to_list
581581+ let wc = handle_word_cst_subshell ctx wc in
582582+ if Ast.has_glob wc then
583583+ Ast.word_components_to_string wc
584584+ |> Globlon.glob |> Array.to_list
585585+ |> List.map (fun w -> [ Ast.WordName w ])
586586+ else [ wc ]
546587547547- and word_glob_expand (ctx : ctx) wc =
588588+ and word_glob_expand (ctx : ctx) wc : Ast.word_cst list =
548589 if List.exists needs_glob_expansion wc then glob_expand ctx wc
549549- else [ handle_word_components_to_string ctx wc ]
590590+ else [ handle_word_cst_subshell ctx wc ]
550591551592 and collect_assignments ?(update = true) ctx =
552593 List.fold_left
···566607 | _ -> ctx)
567608 ctx
568609569569- and args ctx swc =
610610+ and args ctx swc : Ast.word_cst list =
570611 List.concat_map
571612 (function
572613 | Ast.Suffix_redirect _ -> []
+9-5
src/lib/posix/state.ml
···66 root : int;
77 outermost : bool;
88 home : string;
99- variables : Merry.Ast.word_cst Variables.t;
99+ variables : (bool * Merry.Ast.word_cst) Variables.t;
1010}
11111212let make ?(functions = []) ?(root = 0) ?(outermost = true) ?(home = "/root")
···1616let cwd t = t.cwd
1717let set_cwd t cwd = { t with cwd }
1818let expand t = function `Tilde -> t.home
1919-let lookup t ~param = Variables.find_opt param t.variables
1919+let lookup t ~param = Variables.find_opt param t.variables |> Option.map snd
20202121-let update t ~param v =
2222- let variables' = Variables.add param v t.variables in
2121+let update ?(export = false) t ~param v =
2222+ let variables' = Variables.add param (export, v) t.variables in
2323 { t with variables = variables' }
24242525+let exports t =
2626+ Variables.to_list t.variables
2727+ |> List.filter_map (function p, (true, v) -> Some (p, v) | _ -> None)
2828+2529let dump ppf s =
2630 Fmt.pf ppf "Variables:[%a]"
2731 Fmt.(list ~sep:Fmt.comma (pair string Yojson.Safe.pp))
2832 (Variables.to_list s.variables
2929- |> List.map (fun (s, v) -> (s, Merry.Ast.word_cst_to_yojson v)))
3333+ |> List.map (fun (s, (_, v)) -> (s, Merry.Ast.word_cst_to_yojson v)))
+6-2
src/lib/types.ml
···2323 val lookup : t -> param:string -> Ast.word_cst option
2424 (** Parameter lookup. [None] means [unset]. *)
25252626- val update : t -> param:string -> Ast.word_cst -> t
2727- (** Update the state with a new parameter mapping *)
2626+ val update : ?export:bool -> t -> param:string -> Ast.word_cst -> t
2727+ (** Update the state with a new parameter mapping and whether or not it should
2828+ exported to the environment (default false). *)
2929+3030+ val exports : t -> (string * Ast.word_cst) list
3131+ (** All of the variables that must be exported to the environment *)
28322933 val dump : t Fmt.t
3034end