1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 let win32 = Sys.os_type
= "Win32"
24 let slash = if win32 then '
\\'
else '
/'
25 let slash_s = String.make
1 slash
27 let normalize filename
=
28 let l = split filename
slash in
29 let is_absolute = match l with
38 | ".." :: l -> let l,_
= iter l in ("..":: l), false
40 let l,_
= iter l in l, true
42 let l, redo
= iter l in if redo
then iter (x
:: l) else (x
:: l), false
49 ".." :: l -> iter_abs l
55 let file = match l with
58 | _
-> unsplit
l slash
60 (* if file <> filename then begin
61 lprintf "[%s] normalized to [%s]" filename file; lprint_newline ();
67 let name = normalize name in
69 match String.rindex
name slash with
71 | n
-> String.sub
name 0 n
74 let last_extension file =
76 let pos = String.rindex
file '
.'
in
77 let pos2 = try String.rindex
file slash with Not_found
-> 0 in
78 if pos < pos2 then raise Not_found
;
79 String2.after
file pos
82 let last_extension2 file =
84 let pos = String.rindex
file '
.'
in
85 let pos2 = try String.rindex
file slash with Not_found
-> 0 in
86 if pos < pos2 then raise Not_found
;
87 String2.after
file (pos + 1)
92 let pos2 = try String.rindex
file slash with _
-> 0 in
93 let pos = String.index_from
file pos2 '
.'
in
94 let len = String.length
file in
95 String.sub
file pos (len -pos)
99 let ext = extension file in
100 let len = String.length
ext in
102 String2.split_simplify
(String.sub
ext 1 (len-1)) '
.'
105 let from_strings = ref []
106 let to_strings = ref []
108 let register_conversions from_string to_string
=
109 from_strings := from_string
:: !from_strings;
110 to_strings := to_string
:: !to_strings
112 let from_string filename
=
113 List.fold_left
(fun file f
-> f
file) filename
!from_strings
115 let to_string filename
=
116 List.fold_left
(fun file f
-> f
file) filename
!to_strings
118 let path_of_filename filename
=
119 let len = String.length filename
in
120 let filename = Bytes.of_string
filename in
121 for i
= 0 to len - 1 do
122 if Bytes.get
filename i
= '
\\'
then Bytes.set
filename i '
/'
;
125 if len > 2 && Bytes.get
filename 1 = '
:'
&&
126 match Bytes.get
filename 0 with
127 'a'
.. 'z'
| 'A'
.. 'Z'
-> true
129 Printf.sprintf
"%s/%s" (Bytes.sub_string
filename 0 2)
130 (Bytes.sub_string
filename 2 (len-2))
131 else Bytes.unsafe_to_string
filename
133 split_simplify
filename '
/'
135 let basename filename =
136 let rec iter list
name =
137 match list
with [] -> name | name :: tail
-> iter tail
name
139 iter (path_of_filename filename) filename
141 let filesystem_compliant name fstype namemax
=
142 (* replace all illegal characters with a valid one.
143 assumes all filesystems accept '_'s in filenames *)
144 let escape_chars p
filename =
145 let s = Bytes.of_string
filename in
146 for i
= 0 to String.length
filename - 1 do
147 if p
(Bytes.get
s i
) then Bytes.set
s i '_'
149 Bytes.unsafe_to_string
s
152 (* remove all illegal characters at the beginning of filename *)
153 let trim_left p
filename =
154 let len = String.length
filename in
157 if i
< len && p
filename.[i
] then aux (i
+1) else i
in
159 if left = 0 then filename
161 String.sub
filename left (len - left) in
163 (* remove all illegal characters at the end of filename *)
164 let trim_right p
filename =
165 let len = String.length
filename in
168 if i
> 0 && p
filename.[i
-1] then aux (i
-1) else i
in
170 if right = len then filename
172 String.sub
filename 0 right in
174 let minimal_filter c
=
176 | '
/'
| '
\\'
| '
<'
| '
>'
| '
"' -> true
179 let posix_compliant name =
180 escape_chars minimal_filter name in
182 let windows_compliant name =
183 (* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/creating__deleting__and_maintaining_files.asp *)
184 let windows_filter c =
187 | '*' | '?' | '|' | ':' | '"'
-> true
190 (* Windows has additional restrictions:
191 - filenames cannot start with a '.'
192 - filenames cannot end with '.' or space *)
193 let name = trim_left (fun c
-> c
= '
.'
) name in
194 let name = trim_right (fun c
-> c
= '
.'
|| c
= ' '
) name in
195 escape_chars windows_filter name in
197 let macosx_compliant name =
198 (* ':' is directory seperator on Mac OS X: http://www.comentum.com/File-Systems-HFS-FAT-UFS.html *)
199 let macosx_filter c
=
200 minimal_filter c
|| c
= '
:'
in
201 escape_chars macosx_filter name in
203 let sys_checked_name =
205 | `Win
-> windows_compliant name
206 | `Mac
-> macosx_compliant name
208 | `Unknown
-> posix_compliant name
211 let fs_checked_name =
212 let remove_last_spaces s =
213 let len = String.length
s in
218 if s.[n1] = ' '
then aux n1
220 let last_space = aux len in
221 if last_space = len then s
222 else String.sub
s 0 last_space
224 (* FAT filesystems do not allow files with space as last char *)
226 | `Win
-> remove_last_spaces sys_checked_name
227 | _
-> sys_checked_name
230 let length_checked_name =
231 if namemax
< 1 || String.length
sys_checked_name < namemax
then
234 let ext = extension fs_checked_name in
235 if String.length
ext > namemax
then
236 String.sub
fs_checked_name 0 namemax
238 String.sub
fs_checked_name 0 (namemax
- (String.length
ext)) ^
ext
242 let temp_dir_name () =
244 Sys.getenv
"MLDONKEY_TEMP"
246 (* kept for compatibility with Filename.temp_dir_name, this code
247 is never reached because $MLDONKEY_TEMP is filled in commonOptions.ml *)
248 match Sys.os_type
with
249 | "Unix" | "Cygwin" ->
250 (try Sys.getenv
"TMPDIR" with Not_found
-> "/tmp")
252 (try Sys.getenv
"TEMP" with Not_found
-> ".")
254 (* this code is copied from OCaml stdlib/filename.ml but
255 extended to respect runtime changes to $MLDONKEY_TEMP,
256 OCaml uses variable $TMPDIR/$TEMP instead *)
257 external open_desc
: string -> open_flag list
-> int -> int = "caml_sys_open"
258 external close_desc
: int -> unit = "caml_sys_close"
260 let prng = Random.State.make_self_init
();;
262 let temp_file_name prefix suffix
=
263 let rnd = (Random.State.bits
prng) land 0xFFFFFF in
264 Filename.concat
(temp_dir_name ()) (Printf.sprintf
"%s%06x%s" prefix
rnd suffix
)
266 let temp_file prefix suffix
=
267 let rec try_name counter
=
268 let name = temp_file_name prefix suffix
in
270 close_desc
(open_desc
name [Open_wronly
; Open_creat
; Open_excl
] 0o600
);
272 with Sys_error _
as e
->
273 if counter
>= 1000 then raise e
else try_name (counter
+ 1)
276 let _ = (* some assertions on these functions *)
277 assert (basename "c:\\Program Files\\Toto history.exe" = "Toto history.exe");
278 assert (path_of_filename
279 "c:\\Program Files\\Toto history.exe" =
280 [ "c:"; "Program Files"; "Toto history.exe"] );
281 assert (path_of_filename
282 "/home/bidule/mldonkey folder/toto" =
283 [ "home"; "bidule"; "mldonkey folder"; "toto"] );
284 assert (path_of_filename
285 "/home//bidule" = ["home"; "bidule"])