Skip to content

Commit

Permalink
Rework the OS.Path implementation to allow for multiple arc separator
Browse files Browse the repository at this point in the history
characters.  For Windows, we now recognize #"/" as a valid arc sepatator,
which addresses issue #280 (Support forward-slash ("/") as a separator
in the Windows implementation of OS.Path).
  • Loading branch information
JohnReppy committed Jan 30, 2024
1 parent 6f04e3a commit d6344fc
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 28 deletions.
21 changes: 12 additions & 9 deletions base/system/Basis/Implementation/OS/os-path-fn.sml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ functor OS_PathFn (OSPathBase : sig
val splitVolPath : string -> (bool * Substring.substring * Substring.substring)
(* Split a string into the volume part and arcs part and note whether it
* is absolute.
* Note: it is guaranteed that this is never called with "".
* Note: it is guaranteed that this function is never called with "".
*)
val joinVolPath : (bool * string * string) -> string
(* join a volume and path; raise Path on invalid volumes *)
val arcSepChar : char
(* the character used to separate arcs (e.g., #"/" on UNIX) *)
val arcSep : string
(* the arc separator (e.g., "/" on UNIX) *)
val isArchSepChar : char -> bool
(* a predicate to test if a character is an arc separator; by using a predicate
* for this purpose, we allow for multiple separator characters (e.g., both
* forward and backward slashs are valid on Windows.
*)
val sameVol : string * string -> bool

end) : OS_PATH = struct
Expand All @@ -46,8 +51,6 @@ functor OS_PathFn (OSPathBase : sig
(* check an arc to see if it is valid and raise InvalidArc if not *)
fun checkArc arc = if P.validArc arc then arc else raise InvalidArc

val arcSepStr = String.str P.arcSepChar

val parentArc = P.parentArc
val currentArc = P.currentArc

Expand All @@ -62,7 +65,7 @@ functor OS_PathFn (OSPathBase : sig

fun fromString "" = {isAbs = false, vol = "", arcs = []}
| fromString p = let
val fields = SS.fields (fn c => (c = P.arcSepChar))
val fields = SS.fields P.isArchSepChar
val (isAbs, vol, rest) = P.splitVolPath p
in
{ isAbs = isAbs,
Expand All @@ -75,7 +78,7 @@ functor OS_PathFn (OSPathBase : sig
| toString {isAbs, vol, arcs} = let
fun f [] = [""]
| f [a] = [checkArc a]
| f (a :: al) = (checkArc a) :: arcSepStr :: (f al)
| f (a :: al) = (checkArc a) :: P.arcSep :: (f al)
in
if validVolume{isAbs=isAbs, vol=vol}
then String.concat(P.joinVolPath(isAbs, vol, "") :: f arcs)
Expand Down Expand Up @@ -127,7 +130,7 @@ functor OS_PathFn (OSPathBase : sig
end
fun dir p = #dir(splitDirFile p)
fun file p = #file(splitDirFile p)

fun splitBaseExt p = let
val {dir, file} = splitDirFile p
val (file', ext') = SS.splitr (fn c => c <> #".") (SS.full file)
Expand Down Expand Up @@ -161,7 +164,7 @@ functor OS_PathFn (OSPathBase : sig
of (P.Arc _ :: r) => scanArcs(r, al)
| _ => scanArcs(P.Parent::l, al)
(* end case *))
| a' => scanArcs(a' :: l, al)
| a' => scanArcs(a' :: l, al)
(* end case *))
fun scanPath relPath = scanArcs([], relPath)
fun mkArc (P.Arc a) = a
Expand Down
4 changes: 3 additions & 1 deletion base/system/Basis/Implementation/Unix/os-path.sml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ structure OS_Path = OS_PathFn (
| joinVolPath (false, "", s) = s
| joinVolPath _ = raise Path (* invalid volume *)

val arcSepChar = #"/"
val arcSep = "/"

fun isArchSepChar #"/" = true | isArchSepChar _ = false

fun sameVol (v1, v2: string) = v1 = v2

Expand Down
8 changes: 4 additions & 4 deletions base/system/Basis/Implementation/Win32/os-filesys.sml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ structure OS_FileSys : OS_FILE_SYS =
fun openDir s = let
fun rse' s = rse "openDir" s
val _ = not (isDir s) andalso rse' "invalid directory"
fun mkValidDir s = if (S.sub(s,S.size s - 1) <> W32G.arcSepChar)
then s^(S.str W32G.arcSepChar)
else s
fun mkValidDir s = if WG.isArchSepChar(S.sub(s, S.size s - 1))
then s
else s ^ W32G.arcSep
val p = (mkValidDir s)^"*"
val (h,firstName) = W32FS.findFirstFile p
val (h, firstName) = W32FS.findFirstFile p
in
if not (Handle.isValid h)
then rse' "cannot find first file"
Expand Down
23 changes: 11 additions & 12 deletions base/system/Basis/Implementation/Win32/os-path.sml
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,28 @@ structure OS_Path = OS_PathFn (

val volSepChar = #":"

val arcSepChar = W32G.arcSepChar
val arcSep = S.str arcSepChar
val arcSep = W32G.arcSep
val isArchSepChar = W32G.isArchSepChar

fun volPresent vol =
fun volPresent vol =
(String.size vol >= 2) andalso
(C.isAlpha(S.sub(vol,0)) andalso (S.sub(vol,1) = volSepChar))

fun validVolume (_,vol) =
fun validVolume (_,vol) =
(SS.isEmpty vol) orelse volPresent(SS.string vol)

val emptySS = SS.full ""

fun splitPath (vol, s) =
if (SS.size s >= 1) andalso (SS.sub(s, 0) = arcSepChar) then
(true, vol, SS.triml 1 s)
else (false, vol, s)
fun splitPath (vol, s) = if (SS.size s > 0) andalso W32G.isArcSepChar(SS.sub(s, 0))
then (true, vol, SS.triml 1 s)
else (false, vol, s)

fun splitVolPath "" = (false, emptySS, emptySS)
| splitVolPath s =
if volPresent s then splitPath (SS.splitAt (SS.full s, 2))
else splitPath (emptySS, SS.full s)
| splitVolPath s = if volPresent s
then splitPath (SS.splitAt (SS.full s, 2))
else splitPath (emptySS, SS.full s)

fun joinVolPath arg =
fun joinVolPath arg =
let fun checkVol vol = if (volPresent vol) then vol else raise Path
fun aux (true,"","") = arcSep
| aux (true,"",s) = arcSep^s
Expand Down
3 changes: 2 additions & 1 deletion base/system/Basis/Implementation/Win32/win32-general.sig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ signature WIN32_GENERAL =

val isValidHandle : hndl -> bool

val arcSepChar : char
val arcSep : string
val isArchSepChar : char -> bool

val cfun : string -> string -> 'a -> 'b
val getConst : string -> string -> word
Expand Down
5 changes: 4 additions & 1 deletion base/system/Basis/Implementation/Win32/win32-general.sml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ structure Win32_General : WIN32_GENERAL =

val isValidHandle = Handle.isValid

val arcSepChar = #"\\"
val arcSep = "\\"
fun isArchSepChar #"/" = true
| isArchSepChar #"\\" = true
| isArchSepChar _ = false

val cfun = CInterface.c_function

Expand Down

0 comments on commit d6344fc

Please sign in to comment.