Shells in OCaml
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add command built-in

+96 -27
+20 -1
src/lib/built_ins.ml
··· 36 36 type set = { update : Options.option list; print_options : bool } 37 37 type hash = Hash_remove | Hash_stats | Hash_add of string list 38 38 39 - (* Built-in Actions *) 40 39 type t = 40 + (* Built-in Actions *) 41 41 | Cd of { path : string option } 42 42 | Pwd 43 43 | Exit of int ··· 46 46 | Dot of string (* a.k.a source *) 47 47 | Unset of [ `Variables of string list | `Functions of string list ] 48 48 | Hash of hash 49 + | Command of string list 49 50 50 51 (* Change Directory *) 51 52 module Cd = struct ··· 191 192 Cmd.v info term 192 193 end 193 194 195 + module Command = struct 196 + open Cmdliner 197 + 198 + let args = 199 + let doc = "Arguments to command." in 200 + Arg.(value & pos_all string [] & info [] ~docv:"ARGS" ~doc) 201 + 202 + let t = 203 + let make_command pos_all = Command pos_all in 204 + let term = Term.(const make_command $ args) in 205 + let info = 206 + let doc = "Execute a simple command." in 207 + Cmd.info "command" ~doc 208 + in 209 + Cmd.v info term 210 + end 211 + 194 212 module Make_dot (T : sig 195 213 val name : string 196 214 end) = ··· 239 257 | "." :: _ as cmd -> exec_cmd cmd Dot.t 240 258 | "unset" :: _ as cmd -> exec_cmd cmd Unset.t 241 259 | "hash" :: _ as cmd -> exec_cmd cmd Hash.t 260 + | "command" :: _ as cmd -> exec_cmd cmd Command.t 242 261 | _ -> None
+1
src/lib/built_ins.mli
··· 23 23 | Dot of string 24 24 | Unset of [ `Variables of string list | `Functions of string list ] 25 25 | Hash of hash 26 + | Command of string list 26 27 27 28 val of_args : string list -> (t, string) result option 28 29 (** Parses a command-line to the built-ins, errors are returned if parsing. *)
+36 -14
src/lib/eval.ml
··· 291 291 |> List.rev 292 292 in 293 293 match Built_ins.of_args (executable :: args_as_strings) with 294 - | Some (Ok bi) -> 295 - let ctx = handle_built_in ~rdrs ~stdout:some_write ctx bi in 296 - close_stdout ~is_global some_write; 297 - let built_in = ctx >|= fun _ -> () in 298 - let job = handle_job ~pgid job (`Built_in built_in) in 299 - loop (Exit.value ctx) job (pgid, some_read) rest 300 294 | Some (Error _) -> 301 295 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1))) 302 - | None -> ( 296 + | (None | Some (Ok (Command _))) as v -> ( 297 + let is_command = 298 + match v with Some (Ok (Command _)) -> true | _ -> false 299 + in 303 300 (* We handle the [export] built_in explicitly as we need access to the 304 301 raw CST *) 305 302 match executable with ··· 311 308 loop (Exit.value updated) job (pgid, stdout_of_previous) rest 312 309 | _ -> ( 313 310 let saved_ctx = ctx in 314 - match 315 - let ctx = { ctx with stdout = some_write } in 316 - handle_function_application ctx ~name:executable 317 - (ctx.program :: args_as_strings) 318 - with 311 + let func_app = 312 + if is_command then None 313 + else 314 + let ctx = { ctx with stdout = some_write } in 315 + handle_function_application ctx ~name:executable 316 + (ctx.program :: args_as_strings) 317 + in 318 + match func_app with 319 319 | Some ctx -> 320 320 close_stdout ~is_global some_write; 321 321 (* TODO: Proper job stuff and redirects etc. *) ··· 324 324 in 325 325 loop saved_ctx job (pgid, some_read) rest 326 326 | None -> ( 327 + let executable, args = 328 + if is_command then begin 329 + match args_as_strings with 330 + | [] -> ("", []) 331 + | [ x ] -> (x, []) 332 + | x :: xs -> (x, xs) 333 + end 334 + else (executable, args_as_strings) 335 + in 327 336 match stdout_of_previous with 328 337 | None -> 329 338 let ctx, job = 330 339 exec_process ctx job ~fds:rdrs ~stdout:some_write 331 - ~pgid executable args_as_strings 340 + ~pgid executable args 332 341 in 333 342 close_stdout ~is_global some_write; 334 343 loop ctx job (pgid, some_read) rest ··· 339 348 args_as_strings 340 349 in 341 350 close_stdout ~is_global some_write; 342 - loop ctx job (pgid, some_read) rest)))) 351 + loop ctx job (pgid, some_read) rest))) 352 + | Some (Ok bi) -> 353 + let ctx = handle_built_in ~rdrs ~stdout:some_write ctx bi in 354 + close_stdout ~is_global some_write; 355 + let built_in = ctx >|= fun _ -> () in 356 + let job = handle_job ~pgid job (`Built_in built_in) in 357 + loop (Exit.value ctx) job (pgid, some_read) rest) 343 358 | CompoundCommand (c, rdrs) :: rest -> 344 359 let _rdrs = 345 360 List.map (handle_one_redirection ~sw:pipeline_switch ctx) rdrs ··· 636 651 | Ast.ForClause fc -> handle_for_clause ctx fc 637 652 | Ast.IfClause if_ -> handle_if_clause ctx if_ 638 653 | Ast.BraceGroup (term, sep) -> exec ctx (term, Some sep) 654 + | Ast.Subshell (term, sep) -> 655 + let saved_ctx = ctx in 656 + let e = exec ctx (term, Some sep) in 657 + e >|= fun _ -> saved_ctx 639 658 | _ as c -> 640 659 Fmt.epr "Compound command not supported: %a\n%!" yojson_pp 641 660 (Ast.compound_command_to_yojson c); ··· 807 826 Eio.Flow.copy_string (Fmt.str "%a" Hash.pp ctx.hash) stdout; 808 827 Exit.zero ctx 809 828 | _ -> assert false) 829 + | Command _ -> 830 + (* Handled separately *) 831 + assert false 810 832 811 833 and exec initial_ctx ((command, sep) : Ast.complete_command) = 812 834 let rec loop : Eio.Switch.t -> ctx -> Ast.clist -> ctx Exit.t =
+38 -12
test/built_ins.t
··· 96 96 97 97 5. Hash 98 98 99 + First clean up the directory a bit 100 + $ rm *.sh 101 + 99 102 $ cat > test.sh << EOF 100 103 > reproducible_hash () { 101 104 > hash | sed 's/|/ /' | awk '{print \$1, \$8}' ··· 109 112 110 113 $ sh test.sh 111 114 hello.txt 112 - run.sh 113 115 test.sh 114 - test_bad.sh 115 - test_good.sh 116 116 testing 117 117 hello.txt 118 - run.sh 119 118 test.sh 120 - test_bad.sh 121 - test_good.sh 122 119 testing 123 120 hits 124 121 2 125 122 $ msh test.sh 126 123 hello.txt 127 - run.sh 128 124 test.sh 129 - test_bad.sh 130 - test_good.sh 131 125 testing 132 126 hello.txt 133 - run.sh 134 127 test.sh 135 - test_bad.sh 136 - test_good.sh 137 128 testing 138 129 hits 139 130 2 131 + 140 132 6. Built-in redirection and pipelining 141 133 142 134 $ sh -c "FOO=\$(pwd | rev | rev); echo \$(basename \$FOO)" ··· 148 140 test 149 141 $ msh -c "pwd > out.txt; cat out.txt | xargs -- basename" 150 142 test 143 + 144 + 7. Command 145 + 146 + $ sh -c "command ls" 147 + hello.txt 148 + out.txt 149 + test.sh 150 + testing 151 + $ msh -c "command ls" 152 + hello.txt 153 + out.txt 154 + test.sh 155 + testing 156 + 157 + $ cat > test.sh << EOF 158 + > ls () { 159 + > echo "Fake LS!" 160 + > } 161 + > ls 162 + > command ls 163 + > EOF 164 + 165 + $ sh test.sh 166 + Fake LS! 167 + hello.txt 168 + out.txt 169 + test.sh 170 + testing 171 + $ msh test.sh 172 + Fake LS! 173 + hello.txt 174 + out.txt 175 + test.sh 176 + testing
+1
test/subshell.t
··· 12 12 hello 13 13 $ msh -c "`which echo` hello" 14 14 hello 15 +