Shells in OCaml
3
fork

Configure Feed

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

Built-in: read

Initial support for the `read` built-in, no <backslash> escape handling
(TODO).

+100
+28
src/lib/built_ins.ml
··· 94 94 | Return of int 95 95 | Umask of int option 96 96 | Shift of int option 97 + | Read of bool * string list 97 98 98 99 let reserved = [ "fg"; "bg"; "jobs" ] 99 100 let pp_args = Fmt.(list ~sep:(Fmt.any " ") string) ··· 126 127 | Ignore -> "ignore" 127 128 | Default -> "default") 128 129 | Set _ -> "set" 130 + | Read (backslash, vars) -> 131 + Fmt.str "read%s %a" (if backslash then " -r" else " ") pp_args vars 129 132 130 133 (* Change Directory *) 131 134 module Cd = struct ··· 495 498 Cmd.v info term 496 499 end 497 500 501 + module Read = struct 502 + open Cmdliner 503 + 504 + let r = 505 + let doc = 506 + "Do not treat <backslash> character in any special way. Consider each \ 507 + <backslash> to be part of the input line." 508 + in 509 + Arg.(value & flag & info [ "r" ] ~doc) 510 + 511 + let args = 512 + let doc = "Names of existing or non-existing shell variables." in 513 + Arg.(value & pos_all string [] & info [] ~docv:"var" ~doc) 514 + 515 + let t = 516 + let make_read r args = Read (r, args) in 517 + let term = Term.(const make_read $ r $ args) in 518 + let info = 519 + let doc = "Read from standard input into shell variables." in 520 + Cmd.info "read" ~doc 521 + in 522 + Cmd.v info term 523 + end 524 + 498 525 let of_args (w : string list) = 499 526 let open Cmdliner in 500 527 let exec_cmd cmd v = ··· 524 551 | "return" :: _ as cmd -> exec_cmd cmd Return.t 525 552 | "umask" :: _ as cmd -> exec_cmd cmd Umask.t 526 553 | "shift" :: _ as cmd -> exec_cmd cmd Shift.t 554 + | "read" :: _ as cmd -> exec_cmd cmd Read.t 527 555 | cmd :: _ -> 528 556 if List.mem cmd reserved then begin 529 557 Debug.Log.err (fun f -> f "Unimplemented built-in: %s" cmd);
+1
src/lib/built_ins.mli
··· 53 53 | Return of int 54 54 | Umask of int option 55 55 | Shift of int option 56 + | Read of bool * string list 56 57 57 58 val to_string : t -> string 58 59 (** Serialises a built-in to a string *)
+36
src/lib/eval.ml
··· 1365 1365 assert (new_len >= 0); 1366 1366 let argv = Array.init new_len (fun i -> Array.get ctx.argv (i + n)) in 1367 1367 Exit.zero { ctx with argv } 1368 + | Read (_backslash, vars) -> ( 1369 + let line = 1370 + let buf = Cstruct.create 1 in 1371 + let rec loop acc = 1372 + match 1373 + Eio.Flow.read_exact ctx.stdin buf; 1374 + Cstruct.to_string buf 1375 + with 1376 + | "\n" -> Some acc 1377 + | c -> loop (acc ^ c) 1378 + | exception End_of_file -> None 1379 + in 1380 + loop "" 1381 + in 1382 + let rec loop acc = function 1383 + | v :: vars, Ast.{ txt; _ } :: fs -> loop ((v, txt) :: acc) (vars, fs) 1384 + | _, [] -> List.rev acc 1385 + | [], lines -> 1386 + let last_var, last_line = List.hd acc in 1387 + List.rev 1388 + ((last_var, last_line ^ Ast.Fragment.join_list ~sep:"" lines) 1389 + :: acc) 1390 + in 1391 + let fields = 1392 + Option.map (fun s -> field_splitting ctx [ Ast.Fragment.make s ]) line 1393 + in 1394 + match fields with 1395 + | None -> Exit.nonzero ctx 1 1396 + | Some fs -> 1397 + let vars = loop [] (vars, fs) in 1398 + let state = 1399 + List.fold_left 1400 + (fun st (k, v) -> S.update st ~param:k v |> Result.get_ok) 1401 + ctx.state vars 1402 + in 1403 + Exit.zero { ctx with state }) 1368 1404 | Command _ -> 1369 1405 (* Handled separately *) 1370 1406 assert false
+35
test/built_ins.t
··· 328 328 args: a b c d 329 329 args 2: c d 330 330 args 2: 2 331 + 332 + 15. Read 333 + 334 + 335 + $ cat > test.sh << EOF 336 + > echo "hello world" | read FOO BAR 337 + > echo "FOO is \$FOO" 338 + > echo "BAR is \$BAR" 339 + > EOF 340 + 341 + $ sh test.sh 342 + FOO is 343 + BAR is 344 + $ msh test.sh 345 + FOO is 346 + BAR is 347 + 348 + $ cat > test.sh << EOF 349 + > echo hello > hello.txt 350 + > echo world >> hello.txt 351 + > echo finished >> hello.txt 352 + > 353 + > while read line; do 354 + > echo "Got line: \$line" 355 + > done < hello.txt 356 + > EOF 357 + 358 + $ sh test.sh 359 + Got line: hello 360 + Got line: world 361 + Got line: finished 362 + $ msh test.sh 363 + Got line: hello 364 + Got line: world 365 + Got line: finished