···4343 let update t options =
4444 List.fold_left
4545 (fun d -> function
4646- | `Pipefail -> with_options ~pipefail:true d
4747- | `Noclobber -> with_options ~noclobber:true d
4848- | `Noglob -> with_options ~no_path_expansion:true d
4949- | `Errexit -> with_options ~errexit:true d
5050- | `Nounset -> with_options ~no_unset:true d
5151- | `Async -> with_options ~async:true d)
4646+ | `Pipefail, pipefail -> with_options ~pipefail d
4747+ | `Noclobber, noclobber -> with_options ~noclobber d
4848+ | `Noglob, no_path_expansion -> with_options ~no_path_expansion d
4949+ | `Errexit, errexit -> with_options ~errexit d
5050+ | `Nounset, no_unset -> with_options ~no_unset d
5151+ | `Async, async -> with_options ~async d)
5252 t options
53535454 let pp ppf opt =
···7171 Fmt.pf ppf "@[<v>%a@]" Fmt.(list pp_option) opts
7272end
73737474-type set = { update : Options.option list; print_options : bool }
7474+type set = { update : (Options.option * bool) list; print_options : bool }
7575type hash = Hash_remove | Hash_stats | Hash_add of string list
7676type trap = Int of int | Action of string | Ignore | Default
7777···213213 let doc = "No unset, like -o nounset." in
214214 Arg.(value & flag & info [ "u" ] ~docv:"NOUNSET" ~doc)
215215216216+ let rest =
217217+ let doc = "Arguments" in
218218+ Arg.(value & pos_all string [] & info [] ~docv:"ARGUMENTS" ~doc)
219219+220220+ let classify_args args =
221221+ let plus_flags, args =
222222+ List.fold_left
223223+ (fun (p, a) v ->
224224+ if String.starts_with ~prefix:"+" v then (v :: p, a) else (p, v :: a))
225225+ ([], []) args
226226+ in
227227+ (List.rev plus_flags, List.rev args)
228228+216229 let t =
217217- let make_set update noglob noclobber nounset errexit =
218218- let extra = if noglob then [ `Noglob ] else [] in
219219- let extra = if noclobber then `Noclobber :: extra else extra in
220220- let extra = if nounset then `Nounset :: extra else extra in
221221- let extra = if errexit then `Errexit :: extra else extra in
230230+ let make_set update noglob noclobber nounset errexit rest =
231231+ let update = List.map (fun u -> (u, true)) update in
232232+ let extra = if noglob then [ (`Noglob, true) ] else [] in
233233+ let extra = if noclobber then (`Noclobber, true) :: extra else extra in
234234+ let extra = if nounset then (`Nounset, true) :: extra else extra in
235235+ let extra = if errexit then (`Errexit, true) :: extra else extra in
222236 let update = extra @ update in
237237+ let unset, _args = classify_args rest in
238238+ let unset =
239239+ List.filter_map
240240+ (function
241241+ | "+f" -> Some (`Noglob, false)
242242+ | "+u" -> Some (`Nounset, false)
243243+ | "+e" -> Some (`Errexit, false)
244244+ | e ->
245245+ Debug.Log.err (fun f -> f "Missed set arg: %s" e);
246246+ None)
247247+ unset
248248+ in
249249+ let update = update @ unset in
223250 Set { update; print_options = false }
224251 in
225252 let term =
226226- Term.(const make_set $ option $ noglob $ noclobber $ nounset $ errexit)
253253+ Term.(
254254+ const make_set $ option $ noglob $ noclobber $ nounset $ errexit $ rest)
227255 in
228256 let info =
229257 let doc = "Set or unset options and positional parameters." in
+2-2
src/lib/built_ins.mli
···2626 t ->
2727 t
28282929- val update : t -> option list -> t
2929+ val update : t -> (option * bool) list -> t
3030 val pp : t Fmt.t
3131end
32323333-type set = { update : Options.option list; print_options : bool }
3333+type set = { update : (Options.option * bool) list; print_options : bool }
3434type hash = Hash_remove | Hash_stats | Hash_add of string list
3535type trap = Int of int | Action of string | Ignore | Default
3636
···11-(* From dune-glob library
22-33-The MIT License
44-55-Copyright (c) 2016 Jane Street Group, LLC <opensource@janestreet.com>
66-77-Permission is hereby granted, free of charge, to any person obtaining a copy
88-of this software and associated documentation files (the "Software"), to deal
99-in the Software without restriction, including without limitation the rights
1010-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1111-copies of the Software, and to permit persons to whom the Software is
1212-furnished to do so, subject to the following conditions:
1313-1414-The above copyright notice and this permission notice shall be included in all
1515-copies or substantial portions of the Software.
1616-1717-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1818-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1919-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2020-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2121-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2222-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323-SOFTWARE. *)
2424-2525-type t = Re of { re : Re.re; repr : string } | Literal of string
2626-2727-let test t s =
2828- match t with
2929- | Literal t -> String.equal t s
3030- | Re { re; repr = _ } -> Re.execp re s
3131-3232-let empty = Re { re = Re.compile Re.empty; repr = "\000" }
3333-let universal = Re { re = Re.compile (Re.rep Re.any); repr = "**" }
3434-3535-let of_string_result repr =
3636- Lexer.parse_string repr
3737- |> Result.map (function
3838- | Lexer.Literal s -> Literal s
3939- | Re re -> Re { re = Re.compile re; repr })
4040-4141-let of_string repr =
4242- match of_string_result repr with
4343- | Error (_, msg) -> invalid_arg (Printf.sprintf "invalid glob: :%s" msg)
4444- | Ok t -> t
4545-4646-let to_string t = match t with Re { repr; re = _ } -> repr | Literal s -> s
4747-let hash t = String.hash (to_string t)
-21
src/lib/glob/glob.mli
···11-(** Simple glob support library. *)
22-33-type t
44-55-val empty : t
66-(** A glob that matches nothing *)
77-88-val universal : t
99-(** A glob that matches anything (including the strings starting with a ".") *)
1010-1111-val test : t -> string -> bool
1212-(** Tests if string matches the glob. *)
1313-1414-val to_string : t -> string
1515-(** Returns textual representation of a glob. *)
1616-1717-val of_string : string -> t
1818-(** Converts string to glob. Throws [Invalid_argument] exception if string is
1919- not a valid glob. *)
2020-2121-val hash : t -> int
-91
src/lib/glob/lexer.mll
···11-{
22-open Re
33-44-let string_of_list chars =
55- let s = Bytes.make (List.length chars) '0' in
66- List.iteri (fun i c -> Bytes.set s i c) chars;
77- Bytes.to_string s
88-99-type t =
1010- | Literal of string
1111- | Re of Re.t
1212-1313-let no_slash = diff any (char '/')
1414-let no_slash_no_dot = diff any (set "./")
1515-1616-type stack =
1717- | Bottom
1818- | Lbrace of stack
1919- | Char of char * stack
2020- | Re of Re.t * stack
2121- | Comma of stack
2222-2323-let make_group st =
2424- let rec loop current_re full_res st =
2525- match st with
2626- | Bottom -> failwith "'}' without opening '{'"
2727- | Re (re, st) -> loop (re :: current_re) full_res st
2828- | Char (c, st) -> loop (char c :: current_re) full_res st
2929- | Comma st -> loop [] (seq current_re :: full_res) st
3030- | Lbrace st -> Re (alt (seq current_re :: full_res), st)
3131- in
3232- loop [] [] st
3333-3434-let finalize st =
3535- let rec loop acc st =
3636- match st with
3737- | Bottom -> seq (start :: acc)
3838- | Re (re, st) -> loop (re :: acc) st
3939- | Char (c, st) -> loop (char c :: acc) st
4040- | Comma st -> loop (char ',' :: acc) st
4141- | Lbrace _ -> failwith "unclosed '{'"
4242- in
4343- let rec try_str (acc : char list) st =
4444- match st with
4545- | Bottom -> Literal (string_of_list acc)
4646- | Comma st -> try_str (',' :: acc) st
4747- | Char (c, st) -> try_str (c :: acc) st
4848- | st ->
4949- let re =
5050- let re = [stop] in
5151- match acc with
5252- | [] -> re
5353- | _ :: _ -> str (string_of_list acc) :: re
5454- in
5555- Re (loop re st)
5656- in
5757- try_str [] st
5858-}
5959-6060-rule initial = parse
6161- (* | "**" { glob (Re (rep any, Bottom)) lexbuf } *)
6262- | "*" { glob (Re (rep any, Bottom)) lexbuf }
6363- | "" { glob Bottom lexbuf }
6464-6565-and glob st = parse
6666- | eof
6767- | '\\' eof { finalize st }
6868- | '\\' (_ as c) { glob (Char (c , st)) lexbuf }
6969- | "**" { glob (Re (seq [no_slash_no_dot; rep no_slash] , st)) lexbuf }
7070- | '*' { glob (Re (rep no_slash , st)) lexbuf }
7171- | '?' { glob (Re (no_slash , st)) lexbuf }
7272- | '{' { glob (Lbrace st ) lexbuf }
7373- | ',' { glob (Comma st ) lexbuf }
7474- | '}' { glob (make_group st) lexbuf }
7575- | '[' { char_set st lexbuf }
7676- | ']' { failwith "']' without opening '['" }
7777- | _ as c { glob (Char (c , st)) lexbuf }
7878-7979-and char_set st = parse
8080- | '!' ([^ ']']* as s) "]" { glob (Re (diff any (set s) , st)) lexbuf }
8181- | ([^ ']']* as s) "]" { glob (Re (set s , st)) lexbuf }
8282- | "" { failwith "unclosed character set" }
8383-8484-{
8585- let parse_string s =
8686- let lb = Lexing.from_string s in
8787- match initial lb with
8888- | re -> Result.Ok re
8989- | exception Failure msg ->
9090- Error (Lexing.lexeme_start lb, msg)
9191-}
+5-4
src/lib/import.ml
···7575end
76767777module Glob = struct
7878- let tests ~pattern s = List.filter Glob.(test (of_string pattern)) s
7878+ let glob_dir pattern = Globlon.glob pattern |> Array.to_list
79798080 let test ~pattern s =
8181- match tests ~pattern [ s ] with [ _ ] -> true | _ :: _ | [] -> false
8282-8383- let glob_dir ~pattern dir = tests ~pattern (Eio.Path.read_dir dir)
8181+ let pat =
8282+ Re.Glob.glob ~anchored:true ~pathname:false pattern |> Re.compile
8383+ in
8484+ Re.execp pat s
8485end
+38
test/non-posix.t
···11+Some non-POSIX pieces that we've brought into merry to make
22+it more useful.
33+44+1. Continue
55+66+ $ cat > test.sh << EOF
77+ > for i in "hello" "the" "world"; do
88+ > if [ \${#i} -lt 4 ]; then
99+ > continue
1010+ > fi
1111+ > echo "Got \$i"
1212+ > done
1313+ > EOF
1414+1515+ $ sh test.sh
1616+ Got hello
1717+ Got world
1818+ $ msh test.sh
1919+ Got hello
2020+ Got world
2121+2222+A very specific example coming from /usr/sbin/update-shells
2323+2424+ $ cat > test.sh << EOF
2525+ > echo "# /etc/shells: valud login shells" > file.txt
2626+ > echo "/bin/sh" >> file.txt
2727+ >
2828+ > while IFS='#' read -r line _; do
2929+ > echo "Line \$line"
3030+ > done < file.txt
3131+ > EOF
3232+3333+ $ sh test.sh
3434+ Line
3535+ Line /bin/sh
3636+ $ msh test.sh
3737+ Line
3838+ Line /bin/sh
+2-2
test/wordexp.ml
···9191 let actual = expand ctx cargs in
9292 Alcotest.check fragments "same fragments" expected actual
93939494-let _test_glob env () =
9494+let test_glob env () =
9595 let cargs = W.[ glob_all; lit ".ml" ] in
9696 with_default_ctx ~args:[ "*.ml" ] env @@ fun ctx ->
9797 let expected = Ast.Fragment.[ make "test_merry.ml"; make "wordexp.ml" ] in
···113113 ("single expansion", `Quick, test_single_expansion env);
114114 ("argv expansion", `Quick, test_argv_expansion env);
115115 ("argv expansion dquote", `Quick, test_argv_in_quotes_expansion env);
116116- (* ("glob all", `Quick, test_glob env); *)
116116+ ("glob all", `Quick, test_glob env);
117117 ("tilde", `Quick, test_tilde env);
118118 ]
119119