From e30d98a59da80fd9feb8532fca68f363ecb62f31 Mon Sep 17 00:00:00 2001 From: David Declerck Date: Thu, 28 May 2026 16:03:30 +0200 Subject: [PATCH] Add option to normalize path separators to Unix on Windows --- .drom | 24 ++++++------ .ocamlformat-ignore | 6 +-- LICENSE.md | 2 +- Makefile | 6 +++ drom.toml | 11 +++++- dune-project | 5 ++- opam/ez_file.opam | 6 +-- scripts/after.sh | 2 +- scripts/before.sh | 2 +- sphinx/license.rst | 2 +- src/ez_file/fileAbstract.ml | 18 ++++----- src/ez_file/fileDirMaker.ml | 8 ++-- src/ez_file/fileString.ml | 20 +++++----- src/ez_file/package.toml | 10 +++++ src/ez_file/slashifier.ml | 73 +++++++++++++++++++++++++++++++++++++ src/ez_file/slashifier.mli | 19 ++++++++++ src/ez_file/v1.ml | 2 + test/expect-tests/test.ml | 2 + 18 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 src/ez_file/slashifier.ml create mode 100644 src/ez_file/slashifier.mli diff --git a/.drom b/.drom index 195792f..7932006 100644 --- a/.drom +++ b/.drom @@ -1,11 +1,11 @@ # Keep this file in your GIT repo to help drom track generated files # begin version -version:0.6.0~dev +version:0.9.0 # end version # hash of toml configuration files # used for generation of all files -38d7ee132fdca58e40e8593ebb731eb5:. +9aa97f19d5f4d407d8d3ee24f5aab0c5:. # end context for . # begin context for .github/workflows/ci.ml @@ -30,7 +30,7 @@ be2d226b0dc658a425e86af1847a2316:.ocamlformat # begin context for .ocamlformat-ignore # file .ocamlformat-ignore -a8d1bcd6f62c6b813b77d3ff8959d8d2:.ocamlformat-ignore +c8f70450e7d78c0387644864ffe8cbd5:.ocamlformat-ignore # end context for .ocamlformat-ignore # begin context for .ocp-indent @@ -45,12 +45,12 @@ a8d1bcd6f62c6b813b77d3ff8959d8d2:.ocamlformat-ignore # begin context for LICENSE.md # file LICENSE.md -3e8c72433d2b8531f0e9716ab0e5fc8a:LICENSE.md +cdfde087b1ceb33b9c5f9d01ddc08d62:LICENSE.md # end context for LICENSE.md # begin context for Makefile # file Makefile -79c6863d340b7c60145053ba884feebd:Makefile +af1157fb3599c96e00152d564db64b64:Makefile # end context for Makefile # begin context for README.md @@ -95,7 +95,7 @@ c8281f46ba9a11d0b61bc8ef67eaa357:docs/style.css # begin context for drom.toml # file drom.toml -4680d74fae3ab56021b78667d5b0fc25:drom.toml +61839e8d3bf7209159a62ad2a75f2c66:drom.toml # end context for drom.toml # begin context for dune @@ -105,22 +105,22 @@ df544cfaf9078d1bba2f90ed1b5adf5f:dune # begin context for dune-project # file dune-project -d19df50bc2a2b6d306ddde718c10f7bf:dune-project +6d4cd5636f52fc7edf771a588766bb6c:dune-project # end context for dune-project # begin context for opam/ez_file.opam # file opam/ez_file.opam -89634f5f8677617a9a6b729cbdfd3ca9:opam/ez_file.opam +181b3a7dd59114806144fa4c1b4a01b3:opam/ez_file.opam # end context for opam/ez_file.opam # begin context for scripts/after.sh # file scripts/after.sh -1112fbe7067fce342365a25613b5659d:scripts/after.sh +62c6489217feef26dac343e709b89c1a:scripts/after.sh # end context for scripts/after.sh # begin context for scripts/before.sh # file scripts/before.sh -ae050b3099ac0b43d7c7f4295f6b2319:scripts/before.sh +371e627833d9ac7ae2f906b5ec97aae2:scripts/before.sh # end context for scripts/before.sh # begin context for scripts/copy-bin.sh @@ -155,7 +155,7 @@ bb3a9d286f0dc64021db4194427263ee:scripts/copy-bin.sh # begin context for sphinx/license.rst # file sphinx/license.rst -abc6ae223dbcd2a710665e948f370518:sphinx/license.rst +49671db676482a5b8d9fc14297699d21:sphinx/license.rst # end context for sphinx/license.rst # begin context for src/ez_file/dune @@ -170,7 +170,7 @@ abc6ae223dbcd2a710665e948f370518:sphinx/license.rst # begin context for src/ez_file/package.toml # file src/ez_file/package.toml -45c89f9c025ffd8438e7ba68ad24ab10:src/ez_file/package.toml +d084e0c0b356453f70a0ab97451d4863:src/ez_file/package.toml # end context for src/ez_file/package.toml # begin context for test/expect-tests/dune diff --git a/.ocamlformat-ignore b/.ocamlformat-ignore index ad47dda..ab9b3d1 100644 --- a/.ocamlformat-ignore +++ b/.ocamlformat-ignore @@ -1,3 +1,3 @@ -vendor/*/* -vendor/*/*/* -vendor/*/*/*/* +vendor/** +share/** + diff --git a/LICENSE.md b/LICENSE.md index 59295f5..2b58694 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2022 OCamlPro SAS +Copyright (c) 2026 OCamlPro SAS This software is distributed under the terms of the GNU Lesser General Public License (LGPL) version 2.1 (included below). diff --git a/Makefile b/Makefile index ad577a8..ab94d1a 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,11 @@ SPHINX_TARGET:=_drom/docs/sphinx ODOC_TARGET:=_drom/docs/doc/. +# Use these non-generated files to include more rules here (and +# Makefile.trailer at the end) +-include Makefile.header +-include Makefile.config + all: build build: @@ -79,4 +84,5 @@ distclean: clean rm -rf _opam _drom ./scripts/after.sh distclean +-include Makefile.trailer diff --git a/drom.toml b/drom.toml index 33dc8a2..c88bf14 100644 --- a/drom.toml +++ b/drom.toml @@ -10,8 +10,9 @@ github-organization = "ocamlpro" license = "LGPL2" min-edition = "4.07.0" name = "ez_file" +skeleton = "program" synopsis = "Easy file manipulation (read_file, write_file, etc.)" -version = "0.3.0" +version = "0.4.0" # keys that you could also define: # odoc-target = "...odoc-target..." @@ -24,7 +25,8 @@ version = "0.3.0" # homepage = "...homepage..." [project] -description = """This library provides some modules to read and write files, and create +description = """ +This library provides some modules to read and write files, and create and scan directories. """ @@ -65,6 +67,11 @@ share-dirs = ["share"] # ... # project-wide fields (depends on project skeleton) +# examples: +# docker-alpine-image = "ocamlpro/ocaml:4.13" +# dune-lang = "2.1" +# readme-trailer = "..." +# dot-gitignore-trailer = "..." [fields] # ... diff --git a/dune-project b/dune-project index 8d76b72..2100e3b 100644 --- a/dune-project +++ b/dune-project @@ -1,17 +1,18 @@ -(lang dune 2.7) +(lang dune 2.8) ; This file was generated by drom, using drom.toml (cram enable) (name ez_file) (allow_approximate_merlin) (generate_opam_files false) -(version 0.3.0) +(version 0.4.0) (formatting (enabled_for ocaml reason)) (package (name ez_file) (synopsis "Easy file manipulation (read_file, write_file, etc.)") (description "This library provides some modules to read and write files, and create \nand scan directories.\n") + (depends (ocaml (>= 4.07.0)) (re (and (>= 1.8.0) (< 2.0.0))) diff --git a/opam/ez_file.opam b/opam/ez_file.opam index 3a38ec7..3214b82 100644 --- a/opam/ez_file.opam +++ b/opam/ez_file.opam @@ -2,8 +2,8 @@ # Do not modify, or add to the `skip` field of `drom.toml`. opam-version: "2.0" name: "ez_file" -version: "0.3.0" -license: "LGPL-2.1-only with OCaml-LGPL-linking-exception" +version: "0.4.0" +license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" synopsis: "Easy file manipulation (read_file, write_file, etc.)" description: """\ This library provides some modules to read and write files, and create @@ -37,7 +37,7 @@ install: [ ] depends: [ "ocaml" {>= "4.07.0"} - "dune" {>= "2.7.0"} + "dune" {>= "2.8.0"} "re" {>= "1.8.0" & < "2.0.0"} "ocplib_stuff" {>= "0.1.0" & < "1.0.0"} "base-unix" {>= "base"} diff --git a/scripts/after.sh b/scripts/after.sh index a8bd2bf..53b51db 100755 --- a/scripts/after.sh +++ b/scripts/after.sh @@ -6,6 +6,6 @@ COMMAND=$1 shift SCRIPT=./scripts/after-${COMMAND}.sh -if [ -e ${SCRIPT} ]; then +if [ -f ${SCRIPT} ]; then exec ${SCRIPT} $* fi diff --git a/scripts/before.sh b/scripts/before.sh index c9856bd..6984b56 100755 --- a/scripts/before.sh +++ b/scripts/before.sh @@ -24,6 +24,6 @@ COMMAND=$1 shift SCRIPT=./scripts/before-${COMMAND}.sh -if [ -e ${SCRIPT} ]; then +if [ -f ${SCRIPT} ]; then exec ${SCRIPT} $* fi diff --git a/sphinx/license.rst b/sphinx/license.rst index 71378d5..b48198e 100644 --- a/sphinx/license.rst +++ b/sphinx/license.rst @@ -1,7 +1,7 @@ Copyright and License ===================== -Copyright (c) 2022 OCamlPro SAS +Copyright (c) 2026 OCamlPro SAS This software is distributed under the terms of the GNU Lesser General Public License (LGPL) version 2.1 (included below). diff --git a/src/ez_file/fileAbstract.ml b/src/ez_file/fileAbstract.ml index 63ff19f..d0aea9f 100755 --- a/src/ez_file/fileAbstract.ml +++ b/src/ez_file/fileAbstract.ml @@ -56,7 +56,7 @@ type t = { file_partition : string; } -let root_basename = FileOS.dir_separator_string +let root_basename () = Slashifier.get_dir_separator_string () let basename t = t.file_basename @@ -64,7 +64,7 @@ let rec is_absolute t = if t.file_dir != t then is_absolute t.file_dir else - t.file_basename = root_basename + t.file_basename = root_basename () let is_relative t = not (is_absolute t) let is_implicit t = @@ -87,9 +87,9 @@ let is_implicit t = let to_root_dir t = let rec root = { file_dir = root; - file_basename = root_basename; + file_basename = root_basename (); file_partition = t.file_partition; - file_string = t.file_partition ^ root_basename; + file_string = t.file_partition ^ root_basename (); } in root *) @@ -142,7 +142,7 @@ let add_basename_string dir basename = | "" | "/" | "\\" -> dir.file_partition ^ dir.file_basename ^ basename | _ -> - dir.file_string ^ FileOS.dir_separator_string ^ basename + dir.file_string ^ Slashifier.get_dir_separator_string () ^ basename let add_basename_simple dir basename = { @@ -260,9 +260,9 @@ let of_path part path = if kind = Absolute then let rec root = { - file_basename = FileOS.dir_separator_string; + file_basename = Slashifier.get_dir_separator_string (); file_dir = root; - file_string = part ^ FileOS.dir_separator_string; + file_string = part ^ Slashifier.get_dir_separator_string (); file_partition = part; } in make root path @@ -519,7 +519,7 @@ let equal t1 t2 = let temp_file t ext = - of_string (Filename.temp_file (to_string t) ext) + of_string (Slashifier.temp_file (to_string t) ext) let current_dir_name = of_string "." @@ -527,7 +527,7 @@ let to_rooted_string t = if is_absolute t then t.file_string else - Printf.sprintf ".%c%s" FileOS.dir_separator t.file_string + Printf.sprintf ".%c%s" (Slashifier.get_dir_separator ()) t.file_string (* module String = FileString diff --git a/src/ez_file/fileDirMaker.ml b/src/ez_file/fileDirMaker.ml index 0de5522..da0a113 100644 --- a/src/ez_file/fileDirMaker.ml +++ b/src/ez_file/fileDirMaker.ml @@ -111,7 +111,7 @@ module Make(M : sig for i = 0 to Array.length array - 1 do let basename = array.(i) in let filename = M.add_basename dirname basename in - let filepath = Filename.concat dirpath basename in + let filepath = Slashifier.concat dirpath basename in let (keep, recurse) = check select ~filepath ~filename in if keep then f filepath; if recurse then @@ -126,7 +126,7 @@ module Make(M : sig for i = 0 to Array.length array - 1 do let basename = array.(i) in let filename = M.add_basename dirname basename in - let filepath = Filename.concat dirpath basename in + let filepath = Slashifier.concat dirpath basename in let ( keep, recurse ) = check select ~filepath ~filename in match dft with | `Before -> @@ -182,7 +182,7 @@ module Make(M : sig end else begin let basename = array.(!i) in let filename = M.add_basename dirname basename in - let filepath = Filename.concat dirpath basename in + let filepath = Slashifier.concat dirpath basename in incr i; let (keep, recurse) = check select ~filepath ~filename in if recurse then Queue.add (filename,filepath) dirs; @@ -215,7 +215,7 @@ module Make(M : sig else let basename = array.(!i) in let filename = M.add_basename dirname basename in - let filepath = Filename.concat dirpath basename in + let filepath = Slashifier.concat dirpath basename in incr i; let ( keep, recurse ) = check select ~filepath ~filename in if recurse then enter_dir filename filepath keep; diff --git a/src/ez_file/fileString.ml b/src/ez_file/fileString.ml index 65e657e..0bee493 100644 --- a/src/ez_file/fileString.ml +++ b/src/ez_file/fileString.ml @@ -26,10 +26,10 @@ let cut_extensions file = let is_absolute file = not (Filename.is_relative file) let is_relative = Filename.is_relative let is_implicit = Filename.is_implicit -let concat = Filename.concat -let add_path = Filename.concat -let add_basename = Filename.concat -let add_basenames = List.fold_left Filename.concat +let concat = Slashifier.concat +let add_path = Slashifier.concat +let add_basename = Slashifier.concat +let add_basenames = List.fold_left Slashifier.concat let dirname = Filename.dirname let basename = Filename.basename let check_suffix = Filename.check_suffix @@ -152,7 +152,7 @@ let size64 filename = module OP = struct - let (//) = Filename.concat + let (//) = Slashifier.concat end @@ -184,8 +184,8 @@ let rec copy_rec src dst = | MinUnix.S_DIR -> make_dir ~p:true dst; iter_dir ~f:(fun file -> - copy_rec (Filename.concat src file) - (Filename.concat dst file)) src + copy_rec (Slashifier.concat src file) + (Slashifier.concat dst file)) src | MinUnix.S_REG -> copy_file src dst | _ -> @@ -202,8 +202,8 @@ let rec uncopy_rec src dst = | _, None -> () | Some MinUnix.S_DIR, Some MinUnix.S_DIR -> iter_dir ~f:(fun file -> - uncopy_rec (Filename.concat src file) - (Filename.concat dst file)) src; + uncopy_rec (Slashifier.concat src file) + (Slashifier.concat dst file)) src; (try MinUnix.rmdir dst with _ -> ()) | Some MinUnix.S_REG, Some MinUnix.S_REG -> Sys.remove dst @@ -219,7 +219,7 @@ let find_in_path path name = let rec try_dir = function [] -> raise Not_found | dir::rem -> - let fullname = Filename.concat dir name in + let fullname = Slashifier.concat dir name in if Sys.file_exists fullname then fullname else try_dir rem in try_dir path end diff --git a/src/ez_file/package.toml b/src/ez_file/package.toml index 8633b0a..f0321f7 100644 --- a/src/ez_file/package.toml +++ b/src/ez_file/package.toml @@ -25,6 +25,13 @@ kind = "library" # default is [ "ocamllex", "ocamlyacc" ] # generators = [ "ocamllex", "menhir" ] +# menhir options for the package +#Example: +#version = "2.0" +#parser = { modules = ["parser"]; tokens = "Tokens" } +#tokens = { modules = ["tokens"]} +# menhir = ... + # whether all modules should be packed/wrapped (default is true) # pack-modules = false @@ -65,5 +72,8 @@ version = ">=base" # no-opam-test = "yes" # no-opam-doc = "yes" # gen-opam = "some" | "all" +# dune-flags = "(:standard (:include linking.sexp))" +# dune-stanzas = "(foreign_stubs (language c) (names mapfile_stubs))" +# static-clibs = "unix" [fields] # ... diff --git a/src/ez_file/slashifier.ml b/src/ez_file/slashifier.ml new file mode 100644 index 0000000..33c9546 --- /dev/null +++ b/src/ez_file/slashifier.ml @@ -0,0 +1,73 @@ +(**************************************************************************) +(* *) +(* Typerex Libraries *) +(* *) +(* Copyright 2011-2026 OCamlPro SAS *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Lesser General Public License version 2.1, with the *) +(* special exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +let enabled = ref false +let enable () = enabled := true +let disable () = enabled := false + +module type IMPLEM = sig + val get_dir_separator : unit -> char + val get_dir_separator_string : unit -> string + val slashify : string -> string + val concat : string -> string -> string + val temp_file : ?temp_dir: string -> string -> string -> string +end + +module Unix = struct + let get_dir_separator () = FileOS.dir_separator + let get_dir_separator_string () = FileOS.dir_separator_string + let slashify s = s + let concat = Filename.concat + let temp_file = Filename.temp_file +end + +module Win32 = struct + + let get_dir_separator () = + if !enabled then '/' + else FileOS.dir_separator + + let get_dir_separator_string () = + if !enabled then "/" + else FileOS.dir_separator_string + + let slashify s = + if !enabled && String.contains s '\\' then + String.map (function '\\' -> '/' | c -> c) s + else + s + + let is_dir_sep s i = + match s.[i] with + | '/' | '\\' | ':' -> true + | _ -> false + + let concat dirname filename = + let l = String.length dirname in + if l = 0 then + filename + else + if is_dir_sep dirname (l-1) + then dirname ^ filename + else Printf.sprintf "%s%c%s" dirname (get_dir_separator ()) filename + + let temp_file ?temp_dir prefix suffix = + Filename.temp_file ?temp_dir prefix suffix |> slashify + +end + +module Implem = + (val (match Sys.os_type with + | "Win32" | "Cygwin" -> (module Win32: IMPLEM) + | _ -> (module Unix: IMPLEM))) + +include Implem diff --git a/src/ez_file/slashifier.mli b/src/ez_file/slashifier.mli new file mode 100644 index 0000000..24edf82 --- /dev/null +++ b/src/ez_file/slashifier.mli @@ -0,0 +1,19 @@ +(**************************************************************************) +(* *) +(* Typerex Libraries *) +(* *) +(* Copyright 2011-2026 OCamlPro SAS *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Lesser General Public License version 2.1, with the *) +(* special exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +val enable : unit -> unit +val disable : unit -> unit +val get_dir_separator : unit -> char +val get_dir_separator_string : unit -> string +val slashify : string -> string +val concat : string -> string -> string +val temp_file : ?temp_dir: string -> string -> string -> string diff --git a/src/ez_file/v1.ml b/src/ez_file/v1.ml index 4d11772..da657ad 100644 --- a/src/ez_file/v1.ml +++ b/src/ez_file/v1.ml @@ -18,3 +18,5 @@ module FileSig = FileSig module EzFile = FileString module FileAbstract = FileAbstract module FileChannel = FileChannel + +module Slashifier = Slashifier diff --git a/test/expect-tests/test.ml b/test/expect-tests/test.ml index b039baa..9f9cb6b 100644 --- a/test/expect-tests/test.ml +++ b/test/expect-tests/test.ml @@ -38,6 +38,8 @@ let create_files dir = let test () = + Slashifier.enable (); + let _ret = Sys.command "rm -rf tmpx" in let dir = "tmpx" in create_files dir;