1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
open Common
let split (sep:char) (path:string): (string * string) option =
let len = String.length path
in
let len =
if 2 <= len && path.[len-1] = sep then
len - 1
else
len
in
let pos = String.find_bwd (fun c -> c = sep) len path
in
if pos = -1 || (pos = 0 && len = 1) then
None
else
let basename = String.sub path (pos+1) (len-pos-1)
and dirname =
if pos = 0 then
String.one sep
else
String.sub path 0 pos
in
Some (dirname, basename)
let normalize (sep:char) (path:string): string =
let rec norm lst nlst =
match lst with
| [] ->
String.concat (String.one sep) (List.rev nlst)
| "" :: tl ->
if nlst = [] || tl = [] then
norm tl ("" :: nlst)
else
norm tl nlst
| "." :: tl ->
norm tl nlst
| h :: _ :: ".." :: tl ->
norm (h :: tl) nlst
| _ :: ".." :: tl ->
norm tl nlst
| h :: tl ->
norm tl (h :: nlst)
in
if String.length path = 0 then
"."
else
norm (String.split_on_char sep path) []
let%test _ =
split '/' "" = None
let%test _ =
split '/' "/" = None
let%test _ =
split '/' "/hello" = Some ("/", "hello")
let%test _ =
split '/' "/User/name/xxx" = Some ("/User/name", "xxx")
let%test _ =
split '/' "/User/name/xxx/" = Some ("/User/name", "xxx")
let%test _ =
normalize '/' "/" = "/"
let%test _ =
normalize '/' "//" = "/"
let%test _ =
normalize '/' "" = "."
let%test _ =
normalize '/' "abc/def/ghi" = "abc/def/ghi"
let%test _ =
normalize '/' "abc/" = "abc/"
let%test _ =
normalize '/' "abc/def///ghi" = "abc/def/ghi"
let%test _ =
normalize '/' "abc/./def" = "abc/def"
let%test _ =
normalize '/' "a/../b" = "b"
let%test _ =
normalize '/' "../a" = "../a"
let%test _ =
normalize '/' "a/b/../../d" = "d"