├── .ocamlformat ├── test ├── dune └── globlon_test.ml ├── .gitignore ├── lib ├── dune ├── glob.ml ├── glob.mli ├── globlon.ml ├── glob.c └── globlon.mli ├── README.md ├── .vscode ├── settings.json └── c_cpp_properties.json ├── dune-project └── globlon.opam /.ocamlformat: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name globlon_test) 3 | (libraries globlon)) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dune generated 2 | _build/ 3 | 4 | # opam generated 5 | _opam/ 6 | -------------------------------------------------------------------------------- /lib/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name globlon) 3 | (name globlon) 4 | (foreign_stubs 5 | (language c) 6 | (names glob))) 7 | -------------------------------------------------------------------------------- /test/globlon_test.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | Globlon.glob ~glob_nocheck:true "asd" |> Array.iter print_endline; 3 | Globlon.globs ~glob_nocheck:true [| "asd"; "**"; "!*" |] 4 | |> Array.iter print_endline 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Globlon - a globbing library for ocaml 2 | 3 | This library allows you to use unix shell style globbing to match against paths. 4 | 5 | We use Foreign Function Interface (FFI) to call `glob(3)`, and provide access to all of their flags. 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "alloc.h": "c", 4 | "memory.h": "c" 5 | }, 6 | "cSpell.words": [ 7 | "caml", 8 | "failwith", 9 | "globbing", 10 | "Globlon", 11 | "ONLYDIR", 12 | "Wosize" 13 | ] 14 | } -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.8) 2 | 3 | (name globlon) 4 | 5 | (generate_opam_files true) 6 | 7 | (source 8 | (github bleepbloopsify/globlon)) 9 | 10 | (license MIT) 11 | 12 | (authors "bleepbloopsify" "bleepbloopsify@gmail.com") 13 | 14 | (maintainers "bleepbloopsify" "bleepbloopsify@gmail.com") 15 | 16 | (package 17 | (name globlon) 18 | (synopsis "A globbing library for OCaml") 19 | (description "A globbing library for OCaml. We use it to match file paths against patterns.") 20 | (depends)) 21 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "_opam/lib/ocaml/*" 7 | ], 8 | "defines": [], 9 | "macFrameworkPath": [ 10 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" 11 | ], 12 | "compilerPath": "/usr/bin/clang", 13 | "cStandard": "c17", 14 | "cppStandard": "c++17", 15 | "intelliSenseMode": "macos-clang-x64" 16 | } 17 | ], 18 | "version": 4 19 | } -------------------------------------------------------------------------------- /lib/glob.ml: -------------------------------------------------------------------------------- 1 | external glob_err : unit -> int = "glob_err" 2 | external glob_mark : unit -> int = "glob_mark" 3 | external glob_nosort : unit -> int = "glob_nosort" 4 | external glob_nocheck : unit -> int = "glob_nocheck" 5 | external glob_noescape : unit -> int = "glob_noescape" 6 | external glob_period : unit -> int = "glob_period" 7 | external glob_altdirfunc : unit -> int = "glob_altdirfunc" 8 | external glob_nomagic : unit -> int = "glob_nomagic" 9 | external glob_brace : unit -> int = "glob_brace" 10 | external glob_tilde : unit -> int = "glob_tilde" 11 | external glob_onlydir : unit -> int = "glob_onlydir" 12 | external glob : string -> int -> string array = "ocaml_glob" 13 | external globs : string array -> int -> string array = "ocaml_globs" 14 | -------------------------------------------------------------------------------- /globlon.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: "A globbing library for OCaml" 4 | description: 5 | "A globbing library for OCaml. We use it to match file paths against patterns." 6 | maintainer: ["bleepbloopsify" "bleepbloopsify@gmail.com"] 7 | authors: ["bleepbloopsify" "bleepbloopsify@gmail.com"] 8 | license: "MIT" 9 | homepage: "https://github.com/bleepbloopsify/globlon" 10 | bug-reports: "https://github.com/bleepbloopsify/globlon/issues" 11 | depends: [ 12 | "dune" {>= "2.8"} 13 | "odoc" {with-doc} 14 | ] 15 | build: [ 16 | ["dune" "subst"] {dev} 17 | [ 18 | "dune" 19 | "build" 20 | "-p" 21 | name 22 | "-j" 23 | jobs 24 | "@install" 25 | "@runtest" {with-test} 26 | "@doc" {with-doc} 27 | ] 28 | ] 29 | dev-repo: "git+https://github.com/bleepbloopsify/globlon.git" 30 | -------------------------------------------------------------------------------- /lib/glob.mli: -------------------------------------------------------------------------------- 1 | external glob_err : unit -> int = "glob_err" 2 | (** [glob_err ()] returns the value of [GLOB_ERR] for the system *) 3 | 4 | external glob_mark : unit -> int = "glob_mark" 5 | (** [glob_mark ()] returns the value of [GLOB_MARK] for the system *) 6 | 7 | external glob_nosort : unit -> int = "glob_nosort" 8 | (** [glob_nosort ()] returns the value of [GLOB_NOSORT] for the system *) 9 | 10 | external glob_nocheck : unit -> int = "glob_nocheck" 11 | (** [glob_nocheck ()] returns the value of [GLOB_NOCHECK] for the system *) 12 | 13 | external glob_noescape : unit -> int = "glob_noescape" 14 | (** [glob_noescape ()] returns the value of [GLOB_NOESCAPE] for the system *) 15 | 16 | external glob_period : unit -> int = "glob_period" 17 | (** [glob_period ()] returns the value of [GLOB_PERIOD] for the system, and throws an exception if it is unsupported *) 18 | 19 | external glob_altdirfunc : unit -> int = "glob_altdirfunc" 20 | (** [glob_altdirfunc ()] returns the value of [GLOB_ALTDIRFUNC] for the system, and throws an exception if it is unsupported *) 21 | 22 | external glob_nomagic : unit -> int = "glob_nomagic" 23 | (** [glob_nomagic ()] returns the value of [GLOB_NOMAGIC] for the system, and throws an exception if it is unsupported *) 24 | 25 | external glob_brace : unit -> int = "glob_brace" 26 | (** [glob_brace ()] returns the value of [GLOB_BRACE] for the system, and throws an exception if it is unsupported *) 27 | 28 | external glob_tilde : unit -> int = "glob_tilde" 29 | (** [glob_tilde ()] returns the value of [GLOB_TILDE] for the system, and throws an exception if it is unsupported *) 30 | 31 | external glob_onlydir : unit -> int = "glob_onlydir" 32 | (** [glob_onlydir ()] returns the value of [GLOB_ONLYDIR] for the system, and throws an exception if it is unsuported *) 33 | 34 | external glob : string -> int -> string array = "ocaml_glob" 35 | (** [glob pattern flags] returns the list of files matching [pattern] with the given [flags] *) 36 | 37 | external globs : string array -> int -> string array = "ocaml_globs" 38 | (** [globs patterns flags] returns the list of files matching any of the [patterns] with the given [flags] *) 39 | -------------------------------------------------------------------------------- /lib/globlon.ml: -------------------------------------------------------------------------------- 1 | let calculate_flags ?(glob_err = false) ?(glob_mark = false) 2 | ?(glob_nosort = false) ?(glob_nocheck = false) ?(glob_noescape = false) 3 | ?(glob_period = false) ?(glob_altdirfunc = false) ?(glob_nomagic = false) 4 | ?(glob_brace = false) ?(glob_tilde = false) ?(glob_onlydir = false) () = 5 | let flags = 0 in 6 | let flags = if glob_err then flags lor Glob.glob_err () else flags in 7 | let flags = if glob_mark then flags lor Glob.glob_mark () else flags in 8 | let flags = if glob_nosort then flags lor Glob.glob_nosort () else flags in 9 | let flags = if glob_nocheck then flags lor Glob.glob_nocheck () else flags in 10 | let flags = 11 | if glob_noescape then flags lor Glob.glob_noescape () else flags 12 | in 13 | let flags = if glob_period then flags lor Glob.glob_period () else flags in 14 | let flags = 15 | if glob_altdirfunc then flags lor Glob.glob_altdirfunc () else flags 16 | in 17 | let flags = if glob_nomagic then flags lor Glob.glob_nomagic () else flags in 18 | let flags = if glob_brace then flags lor Glob.glob_brace () else flags in 19 | let flags = if glob_tilde then flags lor Glob.glob_tilde () else flags in 20 | let flags = if glob_onlydir then flags lor Glob.glob_onlydir () else flags in 21 | flags 22 | 23 | let glob ?glob_err ?glob_mark ?glob_nosort ?glob_nocheck ?glob_noescape 24 | ?glob_period ?glob_altdirfunc ?glob_nomagic ?glob_brace ?glob_tilde 25 | ?glob_onlydir pattern = 26 | let flags = 27 | calculate_flags ?glob_err ?glob_mark ?glob_nosort ?glob_nocheck 28 | ?glob_noescape ?glob_period ?glob_altdirfunc ?glob_nomagic ?glob_brace 29 | ?glob_tilde ?glob_onlydir () 30 | in 31 | Glob.glob pattern flags 32 | 33 | let globs ?glob_err ?glob_mark ?glob_nosort ?glob_nocheck ?glob_noescape 34 | ?glob_period ?glob_altdirfunc ?glob_nomagic ?glob_brace ?glob_tilde 35 | ?glob_onlydir patterns = 36 | let flags = 37 | calculate_flags ?glob_err ?glob_mark ?glob_nosort ?glob_nocheck 38 | ?glob_noescape ?glob_period ?glob_altdirfunc ?glob_nomagic ?glob_brace 39 | ?glob_tilde ?glob_onlydir () 40 | in 41 | Glob.globs patterns flags 42 | -------------------------------------------------------------------------------- /lib/glob.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define CAML_NAME_SPACE 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | CAMLprim value glob_err() { 10 | CAMLparam0(); 11 | CAMLreturn(Val_int(GLOB_ERR)); 12 | } 13 | CAMLprim value glob_mark() { 14 | CAMLparam0(); 15 | CAMLreturn(Val_int(GLOB_MARK)); 16 | } 17 | CAMLprim value glob_nosort() { 18 | CAMLparam0(); 19 | CAMLreturn(Val_int(GLOB_NOSORT)); 20 | } 21 | CAMLprim value glob_nocheck() { 22 | CAMLparam0(); 23 | CAMLreturn(Val_int(GLOB_NOCHECK)); 24 | } 25 | CAMLprim value glob_noescape() { 26 | CAMLparam0(); 27 | CAMLreturn(Val_int(GLOB_NOESCAPE)); 28 | } 29 | CAMLprim value glob_period() { 30 | CAMLparam0(); 31 | #ifdef GLOB_PERIOD 32 | CAMLreturn(Val_int(GLOB_PERIOD)); 33 | #else 34 | caml_failwith("GLOB_PERIOD not supported"); 35 | #endif 36 | } 37 | CAMLprim value glob_altdirfunc() { 38 | CAMLparam0(); 39 | #ifdef GLOB_ALTDIRFUNC 40 | CAMLreturn(Val_int(GLOB_ALTDIRFUNC)); 41 | #else 42 | caml_failwith("GLOB_ALTDIRFUNC not supported"); 43 | #endif 44 | } 45 | CAMLprim value glob_nomagic() { 46 | CAMLparam0(); 47 | #ifdef GLOB_NOMAGIC 48 | CAMLreturn(Val_int(GLOB_NOMAGIC)); 49 | #else 50 | caml_failwith("GLOB_NOMAGIC not supported"); 51 | #endif 52 | } 53 | CAMLprim value glob_brace() { 54 | CAMLparam0(); 55 | #ifdef GLOB_BRACE 56 | CAMLreturn(Val_int(GLOB_BRACE)); 57 | #else 58 | caml_failwith("GLOB_BRACE not supported"); 59 | #endif 60 | } 61 | CAMLprim value glob_tilde() { 62 | CAMLparam0(); 63 | #ifdef GLOB_TILDE 64 | CAMLreturn(Val_int(GLOB_TILDE)); 65 | #else 66 | caml_failwith("GLOB_TILDE not supported"); 67 | #endif 68 | } 69 | CAMLprim value glob_onlydir() { 70 | CAMLparam0(); 71 | #ifdef GLOB_ONLYDIR 72 | CAMLreturn(Val_int(GLOB_ONLYDIR)); 73 | #else 74 | caml_failwith("GLOB_ONLYDIR not supported"); 75 | #endif 76 | } 77 | 78 | void handleErrors(int r, glob_t *gl) { 79 | // if we have an error, we should free allocated memory before raising 80 | if (r != 0) { 81 | globfree(gl); 82 | } 83 | if (r == GLOB_NOMATCH) { 84 | caml_failwith("glob: no found matches"); 85 | } 86 | if (r == GLOB_ABORTED) { 87 | caml_failwith("glob: read error"); 88 | } 89 | if (r == GLOB_NOSPACE) { 90 | caml_failwith("glob: memory allocation error"); 91 | } 92 | if (r != 0) { 93 | caml_failwith("glob: unknown error"); 94 | } 95 | } 96 | 97 | CAMLprim value ocaml_glob(value pattern, value flags) { 98 | CAMLparam2(pattern, flags); 99 | CAMLlocal1(matches); 100 | 101 | glob_t gl; 102 | 103 | int r = glob(String_val(pattern), Int_val(flags), NULL, &gl); 104 | handleErrors(r, &gl); 105 | 106 | matches = caml_alloc(gl.gl_pathc, 0); 107 | for (int i = 0; i < gl.gl_pathc; i++) { 108 | Store_field(matches, i, caml_copy_string(gl.gl_pathv[i])); 109 | } 110 | 111 | globfree(&gl); 112 | CAMLreturn(matches); 113 | } 114 | 115 | CAMLprim value ocaml_globs(value patterns, value passed_flags) { 116 | CAMLparam2(patterns, passed_flags); 117 | CAMLlocal1(matches); 118 | 119 | glob_t gl; 120 | int flags = Int_val(passed_flags); 121 | 122 | for (int i = 0; i < Wosize_val(patterns); i++) { 123 | int r = glob(String_val(Field(patterns, i)), flags, NULL, &gl); 124 | handleErrors(r, &gl); 125 | flags |= GLOB_APPEND; 126 | } 127 | 128 | matches = caml_alloc(gl.gl_pathc, 0); 129 | for (int i = 0; i < gl.gl_pathc; i++) { 130 | Store_field(matches, i, caml_copy_string(gl.gl_pathv[i])); 131 | } 132 | 133 | globfree(&gl); 134 | CAMLreturn(matches); 135 | } -------------------------------------------------------------------------------- /lib/globlon.mli: -------------------------------------------------------------------------------- 1 | (** https://man7.org/linux/man-pages/man3/glob.3.html 2 | 3 | The argument flags is made up of the bitwise OR of zero or more of the following symbolic constants, which modify the behavior of glob(): 4 | 5 | GLOB_ERR 6 | Return upon a read error (because a directory does not 7 | have read permission, for example). By default, glob() 8 | attempts carry on despite errors, reading all of the 9 | directories that it can. 10 | 11 | GLOB_MARK 12 | Append a slash to each path which corresponds to a 13 | directory. 14 | 15 | GLOB_NOSORT 16 | Don't sort the returned pathnames. The only reason to do 17 | this is to save processing time. By default, the returned 18 | pathnames are sorted. 19 | 20 | GLOB_DOOFFS 21 | Reserve pglob->gl_offs slots at the beginning of the list 22 | of strings in pglob->pathv. The reserved slots contain 23 | null pointers. 24 | 25 | GLOB_NOCHECK 26 | If no pattern matches, return the original pattern. By 27 | default, glob() returns GLOB_NOMATCH if there are no 28 | matches. 29 | 30 | GLOB_APPEND 31 | Append the results of this call to the vector of results 32 | returned by a previous call to glob(). Do not set this 33 | flag on the first invocation of glob(). 34 | 35 | GLOB_NOESCAPE 36 | Don't allow backslash ('\') to be used as an escape 37 | character. Normally, a backslash can be used to quote the 38 | following character, providing a mechanism to turn off the 39 | special meaning metacharacters. 40 | 41 | flags may also include any of the following, which are GNU 42 | extensions and not defined by POSIX.2: 43 | 44 | GLOB_PERIOD 45 | Allow a leading period to be matched by metacharacters. 46 | By default, metacharacters can't match a leading period. 47 | 48 | GLOB_ALTDIRFUNC 49 | Use alternative functions pglob->gl_closedir, 50 | pglob->gl_readdir, pglob->gl_opendir, pglob->gl_lstat, and 51 | pglob->gl_stat for filesystem access instead of the normal 52 | library functions. 53 | 54 | GLOB_BRACE 55 | Expand csh(1) style brace expressions of the form {a,b}. 56 | Brace expressions can be nested. Thus, for example, 57 | specifying the pattern "{foo/{,cat,dog},bar}" would return 58 | the same results as four separate glob() calls using the 59 | strings: "foo/", "foo/cat", "foo/dog", and "bar". 60 | 61 | GLOB_NOMAGIC 62 | If the pattern contains no metacharacters, then it should 63 | be returned as the sole matching word, even if there is no 64 | file with that name. 65 | 66 | GLOB_TILDE 67 | Carry out tilde expansion. If a tilde ('~') is the only 68 | character in the pattern, or an initial tilde is followed 69 | immediately by a slash ('/'), then the home directory of 70 | the caller is substituted for the tilde. If an initial 71 | tilde is followed by a username (e.g., "~andrea/bin"), 72 | then the tilde and username are substituted by the home 73 | directory of that user. If the username is invalid, or 74 | the home directory cannot be determined, then no 75 | substitution is performed. 76 | 77 | GLOB_TILDE_CHECK 78 | This provides behavior similar to that of GLOB_TILDE. The 79 | difference is that if the username is invalid, or the home 80 | directory cannot be determined, then instead of using the 81 | pattern itself as the name, glob() returns GLOB_NOMATCH to 82 | indicate an error. 83 | 84 | GLOB_ONLYDIR 85 | This is a hint to glob() that the caller is interested 86 | only in directories that match the pattern. If the 87 | implementation can easily determine file-type information, 88 | then nondirectory files are not returned to the caller. 89 | However, the caller must still check that returned files 90 | are directories. (The purpose of this flag is merely to 91 | optimize performance when the caller is interested only in 92 | directories.) 93 | *) 94 | 95 | val glob : 96 | ?glob_err:bool -> 97 | ?glob_mark:bool -> 98 | ?glob_nosort:bool -> 99 | ?glob_nocheck:bool -> 100 | ?glob_noescape:bool -> 101 | ?glob_period:bool -> 102 | ?glob_altdirfunc:bool -> 103 | ?glob_nomagic:bool -> 104 | ?glob_brace:bool -> 105 | ?glob_tilde:bool -> 106 | ?glob_onlydir:bool -> 107 | string -> 108 | string array 109 | (** [glob ?[...flags] pattern] checks [pattern] for matches using [flags] *) 110 | 111 | val globs : 112 | ?glob_err:bool -> 113 | ?glob_mark:bool -> 114 | ?glob_nosort:bool -> 115 | ?glob_nocheck:bool -> 116 | ?glob_noescape:bool -> 117 | ?glob_period:bool -> 118 | ?glob_altdirfunc:bool -> 119 | ?glob_nomagic:bool -> 120 | ?glob_brace:bool -> 121 | ?glob_tilde:bool -> 122 | ?glob_onlydir:bool -> 123 | string array -> 124 | string array 125 | (** [globs ?[...flags] patterns] checks [patterns] for matches using [flags] *) 126 | --------------------------------------------------------------------------------