From da3bb6c6c05bcf6bff671bbe8c3e6c08d65ddfdd Mon Sep 17 00:00:00 2001 From: Cedric Cellier Date: Fri, 27 Sep 2019 11:45:32 +0200 Subject: [PATCH] Add BatFilename Also with `Filename.split_extension`, provided I've understood the requirement for this function. Closes #445 --- .gitignore | 1 + src/batFilename.ml | 33 ++++++ src/batFilename.mliv | 234 +++++++++++++++++++++++++++++++++++++++++++ src/batteries.mllib | 1 + src/batteries.mlv | 1 + 5 files changed, 270 insertions(+) create mode 100644 src/batFilename.ml create mode 100644 src/batFilename.mliv diff --git a/.gitignore b/.gitignore index 3b6a13063..230f587a9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ src/batPrintf.mli src/batFormat.mli src/batSys.mli src/batBigarray.mli +src/batFilename.mli diff --git a/src/batFilename.ml b/src/batFilename.ml new file mode 100644 index 000000000..82c2e1b12 --- /dev/null +++ b/src/batFilename.ml @@ -0,0 +1,33 @@ +(* + * BatFilename - Extended Filename module + * Copyright (C) 1996 Xavier Leroy + * 2008 David Teller, LIFO, Universite d'Orleans + * + * 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 special exception on linking described in file LICENSE. + * + * 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 GNU + * Lesser General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *) + +include Filename + +let split_extension s = + remove_extension s, extension s + +(*$= split_extension & ~printer:(IO.to_string (Tuple2.print String.print String.print)) + ("/foo/bar", ".baz") (split_extension "/foo/bar.baz") + ("/foo/bar", "") (split_extension "/foo/bar") + ("/foo/bar", ".") (split_extension "/foo/bar.") + ("/foo/.rc", "") (split_extension "/foo/.rc") + ("", "") (split_extension "") +*) diff --git a/src/batFilename.mliv b/src/batFilename.mliv new file mode 100644 index 000000000..0bcec2e32 --- /dev/null +++ b/src/batFilename.mliv @@ -0,0 +1,234 @@ +(* + * BatFilename - Extended Filename module + * Copyright (C) 1996 Xavier Leroy + * 2008 David Teller + * + * 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 special exception on linking described in file LICENSE. + * + * 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 GNU + * Lesser General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *) + +(** Operations on file names. *) + +val current_dir_name : string +(** The conventional name for the current directory (e.g. [.] in Unix). *) + +val parent_dir_name : string +(** The conventional name for the parent of the current directory + (e.g. [..] in Unix). *) + +val dir_sep : string +(** The directory separator (e.g. [/] in Unix). + @since NEXT_RELEASE and OCaml 3.11.2 *) + +val concat : string -> string -> string +(** [concat dir file] returns a file name that designates file + [file] in directory [dir]. *) + +val is_relative : string -> bool +(** Return [true] if the file name is relative to the current + directory, [false] if it is absolute (i.e. in Unix, starts + with [/]). *) + +val is_implicit : string -> bool +(** Return [true] if the file name is relative and does not start + with an explicit reference to the current directory ([./] or + [../] in Unix), [false] if it starts with an explicit reference + to the root directory or the current directory. *) + +val check_suffix : string -> string -> bool +(** [check_suffix name suff] returns [true] if the filename [name] + ends with the suffix [suff]. + + Under Windows ports (including Cygwin), comparison is + case-insensitive, relying on [String.lowercase_ascii]. Note that + this does not match exactly the interpretation of case-insensitive + filename equivalence from Windows. *) + +val chop_suffix : string -> string -> string +(** [chop_suffix name suff] removes the suffix [suff] from + the filename [name]. The behavior is undefined if [name] does not + end with the suffix [suff]. [chop_suffix_opt] is thus recommended + instead. +*) + +##V>=4.8##val chop_suffix_opt: suffix:string -> string -> string option +##V>=4.8##(** [chop_suffix_opt ~suffix filename] removes the suffix from +##V>=4.8## the [filename] if possible, or returns [None] if the +##V>=4.8## filename does not end with the suffix. +##V>=4.8## +##V>=4.8## Under Windows ports (including Cygwin), comparison is +##V>=4.8## case-insensitive, relying on [String.lowercase_ascii]. Note that +##V>=4.8## this does not match exactly the interpretation of case-insensitive +##V>=4.8## filename equivalence from Windows. +##V>=4.8## +##V>=4.8## @since NEXT_RELEASE and OCaml 4.08 +##V>=4.8##*) + + +##V>=4.4##val extension : string -> string +##V>=4.4##(** [extension name] is the shortest suffix [ext] of [name0] where: +##V>=4.4## +##V>=4.4## - [name0] is the longest suffix of [name] that does not +##V>=4.4## contain a directory separator; +##V>=4.4## - [ext] starts with a period; +##V>=4.4## - [ext] is preceded by at least one non-period character +##V>=4.4## in [name0]. +##V>=4.4## +##V>=4.4## If such a suffix does not exist, [extension name] is the empty +##V>=4.4## string. +##V>=4.4## +##V>=4.4## @since NEXT_RELEASE and OCaml 4.04 +##V>=4.4##*) + +##V>=4.4##val remove_extension : string -> string +##V>=4.4##(** Return the given file name without its extension, as defined +##V>=4.4## in {!Filename.extension}. If the extension is empty, the function +##V>=4.4## returns the given file name. +##V>=4.4## +##V>=4.4## The following invariant holds for any file name [s]: +##V>=4.4## +##V>=4.4## [remove_extension s ^ extension s = s] +##V>=4.4## +##V>=4.4## @since NEXT_RELEASE and OCaml 4.04 +##V>=4.4##*) + +val chop_extension : string -> string +(** Same as {!Filename.remove_extension}, but raise [Invalid_argument] + if the given name has an empty extension. *) + + +val basename : string -> string +(** Split a file name into directory name / base file name. + If [name] is a valid file name, then [concat (dirname name) (basename name)] + returns a file name which is equivalent to [name]. Moreover, + after setting the current directory to [dirname name] (with {!Sys.chdir}), + references to [basename name] (which is a relative file name) + designate the same file as [name] before the call to {!Sys.chdir}. + + This function conforms to the specification of POSIX.1-2008 for the + [basename] utility. *) + +val dirname : string -> string +(** See {!Filename.basename}. + This function conforms to the specification of POSIX.1-2008 for the + [dirname] utility. *) + +val temp_file : ?temp_dir: string -> string -> string -> string +(** [temp_file prefix suffix] returns the name of a + fresh temporary file in the temporary directory. + The base name of the temporary file is formed by concatenating + [prefix], then a suitably chosen integer number, then [suffix]. + The optional argument [temp_dir] indicates the temporary directory + to use, defaulting to the current result of {!Filename.get_temp_dir_name}. + The temporary file is created empty, with permissions [0o600] + (readable and writable only by the file owner). The file is + guaranteed to be different from any other file that existed when + [temp_file] was called. + Raise [Sys_error] if the file could not be created. + @before 3.11.2 no ?temp_dir optional argument +*) + +val open_temp_file : + ?mode: open_flag list -> ?perms: int -> ?temp_dir: string -> string -> + string -> string * out_channel +(** Same as {!Filename.temp_file}, but returns both the name of a fresh + temporary file, and an output channel opened (atomically) on + this file. This function is more secure than [temp_file]: there + is no risk that the temporary file will be modified (e.g. replaced + by a symbolic link) before the program opens it. The optional argument + [mode] is a list of additional flags to control the opening of the file. + It can contain one or several of [Open_append], [Open_binary], + and [Open_text]. The default is [[Open_text]] (open in text mode). The + file is created with permissions [perms] (defaults to readable and + writable only by the file owner, [0o600]). + + @raise Sys_error if the file could not be opened. + @before 4.03.0 no ?perms optional argument + @before 3.11.2 no ?temp_dir optional argument +*) + +##V>=4.0##val get_temp_dir_name : unit -> string +##V>=4.0##(** The name of the temporary directory: +##V>=4.0## Under Unix, the value of the [TMPDIR] environment variable, or "/tmp" +##V>=4.0## if the variable is not set. +##V>=4.0## Under Windows, the value of the [TEMP] environment variable, or "." +##V>=4.0## if the variable is not set. +##V>=4.0## The temporary directory can be changed with {!Filename.set_temp_dir_name}. +##V>=4.0## @since NEXT_RELEASE and OCaml 4.00.0 +##V>=4.0##*) + +##V>=4.0##val set_temp_dir_name : string -> unit +##V>=4.0##(** Change the temporary directory returned by {!Filename.get_temp_dir_name} +##V>=4.0## and used by {!Filename.temp_file} and {!Filename.open_temp_file}. +##V>=4.0## @since NEXT_RELEASE and OCaml 4.00.0 +##V>=4.0##*) + +val temp_dir_name : string + [@@ocaml.deprecated "Use Filename.get_temp_dir_name instead"] +(** The name of the initial temporary directory: + Under Unix, the value of the [TMPDIR] environment variable, or "/tmp" + if the variable is not set. + Under Windows, the value of the [TEMP] environment variable, or "." + if the variable is not set. + @deprecated You should use {!Filename.get_temp_dir_name} instead. + @since NEXT_RELEASE and OCaml 3.09.1 +*) + +val quote : string -> string +(** Return a quoted version of a file name, suitable for use as + one argument in a command line, escaping all meta-characters. + Warning: under Windows, the output is only suitable for use + with programs that follow the standard Windows quoting + conventions. + *) + +##V>4.9##val quote_command : +##V>4.9## string -> ?stdin:string -> ?stdout:string -> ?stderr:string +##V>4.9## -> string list -> string +##V>4.9##(** [quote_command cmd args] returns a quoted command line, suitable +##V>4.9## for use as an argument to {!Sys.command}, {!Unix.system}, and the +##V>4.9## {!Unix.open_process} functions. +##V>4.9## +##V>4.9## The string [cmd] is the command to call. The list [args] is +##V>4.9## the list of arguments to pass to this command. It can be empty. +##V>4.9## +##V>4.9## The optional arguments [?stdin] and [?stdout] and [?stderr] are +##V>4.9## file names used to redirect the standard input, the standard +##V>4.9## output, or the standard error of the command. +##V>4.9## If [~stdin:f] is given, a redirection [< f] is performed and the +##V>4.9## standard input of the command reads from file [f]. +##V>4.9## If [~stdout:f] is given, a redirection [> f] is performed and the +##V>4.9## standard output of the command is written to file [f]. +##V>4.9## If [~stderr:f] is given, a redirection [2> f] is performed and the +##V>4.9## standard error of the command is written to file [f]. +##V>4.9## If both [~stdout:f] and [~stderr:f] are given, with the exact +##V>4.9## same file name [f], a [2>&1] redirection is performed so that the +##V>4.9## standard output and the standard error of the command are interleaved +##V>4.9## and redirected to the same file [f]. +##V>4.9## +##V>4.9## Under Unix and Cygwin, the command, the arguments, and the redirections +##V>4.9## if any are quoted using {!Filename.quote}, then concatenated. +##V>4.9## Under Win32, additional quoting is performed as required by the +##V>4.9## [cmd.exe] shell that is called by {!Sys.command}. +##V>4.9## +##V>4.9## Raise [Failure] if the command cannot be escaped on the current platform. +##V>4.9##*) + +val split_extension : string -> string * string +(** [split_extension s] returns both the filename [s] without its extension + and its extension in two distinct strings. + For instance, [split_extension "foo.bar"] returns the pair ["foo",".bar"]. + + @since NEXT_RELEASE *) diff --git a/src/batteries.mllib b/src/batteries.mllib index 30ab679a6..cd4df595e 100644 --- a/src/batteries.mllib +++ b/src/batteries.mllib @@ -13,6 +13,7 @@ BatInnerPervasives BatDeque BatDigest BatEnum + BatFilename BatFingerTree BatFloat BatFormat diff --git a/src/batteries.mlv b/src/batteries.mlv index e4c6e6993..81047a896 100644 --- a/src/batteries.mlv +++ b/src/batteries.mlv @@ -162,6 +162,7 @@ module Int = BatInt module Bool = BatBool module Unit = BatUnit (*module Int63 = BatInt63*) +module Filename = BatFilename (* Modules in-progress, API stability not guaranteed *) module Incubator = struct