package fileutils
Source file FilePath.ml
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605(******************************************************************************) (* ocaml-fileutils: files and filenames common operations *) (* *) (* Copyright (C) 2003-2014, Sylvain Le Gall *) (* *) (* This library is free software; you can redistribute it and/or modify it *) (* under the terms of the GNU Lesser General Public License as published by *) (* the Free Software Foundation; either version 2.1 of the License, or (at *) (* your option) any later version, with the OCaml static compilation *) (* exception. *) (* *) (* This library is distributed in the hope that it will be useful, but *) (* WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *) (* COPYING for more details. *) (* *) (* You should have received a copy of the GNU Lesser General Public License *) (* along with this library; if not, write to the Free Software Foundation, *) (* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *) (******************************************************************************) open FilePath_type exception BaseFilenameRelative of filename exception UnrecognizedOS of string exception EmptyFilename exception NoExtension of filename exception InvalidFilename of filename module type OS_SPECIFICATION = sig val dir_writer: (filename_part list) -> filename val dir_reader: filename -> (filename_part list) val path_writer: (filename list) -> string val path_reader: string -> (filename list) val fast_concat: filename -> filename -> filename val fast_basename: filename -> filename val fast_dirname: filename -> filename val fast_is_relative: filename -> bool val fast_is_current: filename -> bool val fast_is_parent: filename -> bool end module type PATH_SPECIFICATION = sig type filename type extension val string_of_filename: filename -> string val filename_of_string: string -> filename val extension_of_string: string -> extension val string_of_extension: extension -> string val make_filename: string list -> filename val is_subdir: filename -> filename -> bool val is_updir: filename -> filename -> bool val compare: filename -> filename -> int val basename: filename -> filename val dirname: filename -> filename val concat: filename -> filename -> filename val reduce: ?no_symlink:bool -> filename -> filename val make_absolute: filename -> filename -> filename val make_relative: filename -> filename -> filename val reparent: filename -> filename -> filename -> filename val identity: filename -> filename val is_valid: filename -> bool val is_relative: filename -> bool val is_current: filename -> bool val is_parent: filename -> bool val chop_extension: filename -> filename val get_extension: filename -> extension val check_extension: filename -> extension -> bool val add_extension: filename -> extension -> filename val replace_extension: filename -> extension -> filename val string_of_path: filename list -> string val path_of_string: string -> filename list val current_dir: filename val parent_dir: filename end module type PATH_STRING_SPECIFICATION = sig module Abstract: PATH_SPECIFICATION include PATH_SPECIFICATION with type filename = string and type extension = string end (* Convert an OS_SPECIFICATION to PATH_SPECIFICATION *) module GenericPath = functor (OsOperation: OS_SPECIFICATION) -> struct type filename = FilePath_type.filename_part list type extension = FilePath_type.extension (* Filename_from_string *) let filename_of_string str = try OsOperation.dir_reader str with Parsing.Parse_error -> raise (InvalidFilename str) (* String_from_filename *) let string_of_filename path = OsOperation.dir_writer path (* Reduce *) let reduce ?(no_symlink=false) path = (* TODO: not tail recursive ! *) let rec reduce_aux lst = match lst with | ParentDir :: tl when no_symlink -> begin match reduce_aux tl with | Root s :: tl -> Root s :: tl | ParentDir :: tl -> ParentDir :: ParentDir :: tl | [] -> ParentDir :: tl | _ :: tl -> tl end | ParentDir :: tl -> ParentDir :: (reduce_aux tl) | CurrentDir _ :: tl | Component "" :: tl -> (reduce_aux tl) | Component s :: tl -> Component s :: (reduce_aux tl) | Root s :: tl -> Root s :: (reduce_aux tl) | [] -> [] in let rev_path = List.rev path in match reduce_aux rev_path with | [] when no_symlink = false-> (* assert * ( List.for_all ( function | Component "" * | CurrentDir _ -> true | _ -> false ) rev_path ) *) (try (* use last CurrentDir _ *) [ List.find ( function | CurrentDir _ -> true | _ -> false ) rev_path ] with | Not_found -> [] ) (* Only Component "" *) |l -> List.rev l (* Compare, subdir, updir *) type filename_relation = SubDir | UpDir | Equal | NoRelation of int let relation_of_filename path1 path2 = let rec relation_of_filename_aux path1 path2 = match (path1, path2) with ([], []) -> Equal | (hd1 :: tl1, hd2 :: tl2) -> if hd1 = hd2 then relation_of_filename_aux tl1 tl2 else begin NoRelation (String.compare (string_of_filename [hd1]) (string_of_filename [hd2]) ) end | (_, []) -> SubDir | ([], _) -> UpDir in relation_of_filename_aux path1 path2 let is_subdir path1 path2 = match relation_of_filename path1 path2 with SubDir -> true | _ -> false let is_updir path1 path2 = match relation_of_filename path1 path2 with UpDir -> true | _ -> false let compare path1 path2 = match relation_of_filename path1 path2 with SubDir -> -1 | UpDir -> 1 | Equal -> 0 | NoRelation i -> i (* Concat *) let concat lst_path1 lst_path2 = reduce (match lst_path2 with | CurrentDir Short :: tl_path2 -> lst_path1 @ tl_path2 | _ -> lst_path1 @ lst_path2) (* Is_relative *) let is_relative lst_path = match lst_path with (Root _) :: _ -> false | _ -> true (* Is_valid *) let is_valid _ = (* As we are manipulating abstract filename, and that it has been parsed, we are sure that all is correct *) true let is_current path = match path with [ (CurrentDir _) ] -> true | _ -> false let is_parent path = match path with [ ParentDir ] -> true | _ -> false (* Basename *) let basename path = match List.rev path with | hd :: _ -> [hd] | [] -> raise EmptyFilename (* Dirname *) let dirname path = match List.rev path with | _ :: tl -> List.rev tl | [] -> raise EmptyFilename (* Extension manipulation *) let wrap_extension f path = match basename path with | [Component fn] -> f fn | _ -> raise (NoExtension (string_of_filename path)) let check_extension path ext = wrap_extension (fun fn -> ExtensionPath.check fn ext) path let get_extension path = wrap_extension (fun fn -> ExtensionPath.get fn) path let chop_extension path = wrap_extension (fun fn -> concat (dirname path) [Component (ExtensionPath.chop fn)]) path let add_extension path ext = wrap_extension (fun fn -> concat (dirname path) [Component (ExtensionPath.add fn ext)]) path let replace_extension path ext = wrap_extension (fun fn -> concat (dirname path) [Component (ExtensionPath.replace fn ext)]) path let extension_of_string x = x let string_of_extension x = x (* Make_asbolute *) let make_absolute path_base path_path = reduce (if is_relative path_base then raise (BaseFilenameRelative (string_of_filename path_base)) else if is_relative path_path then path_base @ path_path else path_path) (* Make_relative *) let make_relative path_base path_path = let rec make_relative_aux lst_base lst_path = match (lst_base, lst_path) with x :: tl_base, a :: tl_path when x = a -> make_relative_aux tl_base tl_path | _, _ -> let back_to_base = List.rev_map (fun _ -> ParentDir) lst_base in back_to_base @ lst_path in reduce (if is_relative path_base then raise (BaseFilenameRelative (string_of_filename path_base)) else if is_relative path_path then path_path else make_relative_aux path_base path_path) (* Make_filename *) let make_filename lst_path = reduce (List.flatten (List.map filename_of_string lst_path)) (* Reparent *) let reparent path_src path_dst path = let path_relative = make_relative path_src path in make_absolute path_dst path_relative (* Identity *) let identity path = path (* Manipulate path like variable *) let string_of_path lst = OsOperation.path_writer (List.map string_of_filename lst) let path_of_string str = List.map filename_of_string (OsOperation.path_reader str) (* Generic filename component *) let current_dir = [ CurrentDir Long ] let parent_dir = [ ParentDir ] end (* Convert an OS_SPECIFICATION to PATH_STRING_SPECIFICATION *) module GenericStringPath = functor (OsOperation: OS_SPECIFICATION) -> struct module Abstract = GenericPath(OsOperation) type filename = string type extension = string let string_of_filename path = path let filename_of_string path = path let string_of_extension ext = ext let extension_of_string str = str let f2s = Abstract.string_of_filename let s2f = Abstract.filename_of_string let e2s = Abstract.string_of_extension let s2e = Abstract.extension_of_string let is_subdir path1 path2 = Abstract.is_subdir (s2f path1) (s2f path2) let is_updir path1 path2 = Abstract.is_updir (s2f path1) (s2f path2) let compare path1 path2 = Abstract.compare (s2f path1) (s2f path2) let basename path = try OsOperation.fast_basename path with CommonPath.CannotHandleFast -> f2s (Abstract.basename (s2f path)) let dirname path = try OsOperation.fast_dirname path with CommonPath.CannotHandleFast -> f2s (Abstract.dirname (s2f path)) let concat path1 path2 = try OsOperation.fast_concat path1 path2 with CommonPath.CannotHandleFast -> f2s (Abstract.concat (s2f path1) (s2f path2)) let make_filename path_lst = f2s (Abstract.make_filename path_lst) let reduce ?no_symlink path = f2s (Abstract.reduce ?no_symlink (s2f path)) let make_absolute base_path path = f2s (Abstract.make_absolute (s2f base_path) (s2f path)) let make_relative base_path path = f2s (Abstract.make_relative (s2f base_path) (s2f path)) let reparent path_src path_dst path = f2s (Abstract.reparent (s2f path_src) (s2f path_dst) (s2f path)) let identity path = f2s (Abstract.identity (s2f path)) let is_valid path = try Abstract.is_valid (s2f path) with InvalidFilename _ -> false let is_relative path = try OsOperation.fast_is_relative path with CommonPath.CannotHandleFast -> Abstract.is_relative (s2f path) let is_current path = try OsOperation.fast_is_current path with CommonPath.CannotHandleFast -> Abstract.is_current (s2f path) let is_parent path = try OsOperation.fast_is_parent path with CommonPath.CannotHandleFast -> Abstract.is_parent (s2f path) let wrap_extension f path = let bfn = OsOperation.fast_basename path in if OsOperation.fast_is_parent bfn || OsOperation.fast_is_current bfn || not (OsOperation.fast_is_relative bfn) then raise (NoExtension path) else f bfn let chop_extension path = try wrap_extension (fun fn -> OsOperation.fast_concat (OsOperation.fast_dirname path) (ExtensionPath.chop fn)) path with CommonPath.CannotHandleFast -> f2s (Abstract.chop_extension (s2f path)) let get_extension path = try wrap_extension (fun fn -> ExtensionPath.get fn) path with CommonPath.CannotHandleFast -> e2s (Abstract.get_extension (s2f path)) let check_extension path ext = try wrap_extension (fun fn -> ExtensionPath.check fn ext) path with CommonPath.CannotHandleFast -> Abstract.check_extension (s2f path) (s2e ext) let add_extension path ext = try wrap_extension (fun fn -> OsOperation.fast_concat (OsOperation.fast_dirname path) (ExtensionPath.add fn ext)) path with CommonPath.CannotHandleFast -> f2s (Abstract.add_extension (s2f path) (s2e ext)) let replace_extension path ext = try wrap_extension (fun fn -> OsOperation.fast_concat (OsOperation.fast_dirname path) (ExtensionPath.replace fn ext)) path with CommonPath.CannotHandleFast -> f2s (Abstract.replace_extension (s2f path) (s2e ext)) let string_of_path path_lst = Abstract.string_of_path (List.map s2f path_lst) let path_of_string str = List.map f2s (Abstract.path_of_string str) let current_dir = f2s (Abstract.current_dir) let parent_dir = f2s (Abstract.parent_dir) end module DefaultPath = GenericStringPath(struct let os_depend unix win32 = match Sys.os_type with "Unix" | "Cygwin" -> unix | "Win32" -> win32 | s -> raise (UnrecognizedOS s) let dir_writer = os_depend UnixPath.dir_writer Win32Path.dir_writer let dir_reader = os_depend UnixPath.dir_reader Win32Path.dir_reader let path_writer = os_depend UnixPath.path_writer Win32Path.path_writer let path_reader = os_depend UnixPath.path_reader Win32Path.path_reader let fast_concat = os_depend UnixPath.fast_concat Win32Path.fast_concat let fast_basename = os_depend UnixPath.fast_basename Win32Path.fast_basename let fast_dirname = os_depend UnixPath.fast_dirname Win32Path.fast_dirname let fast_is_relative = os_depend UnixPath.fast_is_relative Win32Path.fast_is_relative let fast_is_current = os_depend UnixPath.fast_is_current Win32Path.fast_is_current let fast_is_parent = os_depend UnixPath.fast_is_parent Win32Path.fast_is_parent end) module UnixPath = GenericStringPath(UnixPath) module Win32Path = GenericStringPath(Win32Path) module CygwinPath = UnixPath include DefaultPath