diff --git a/ChangeLog b/ChangeLog index 565ee05af..8f8b7268f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -47,6 +47,45 @@ Changelog #679 (Cedric Cellier) +## v2.11.0 (minor release) + +This minor release fixes a few bugs or interface mismatch with OCaml stdlib, +and is compatible with BER MetaOCaml. + +This is the last planned release of the v2 series. +Next planned release (v3.0.0) will introduce some API changes. + +Notable changes: + +- Add Unix.with_locked_file + #904 + (Simon Cruanes, Cedric Cellier, review by Francois Berenger) + +- Build with -strict-sequence + #927 + (Armaël Guéneau, review by Francois Berenger) + +- Add Legacy.Result for OCaml >= 4.8.0 + #913 + (Cedric Cellier, review by Francois Berenger) + +- Remove BatOo + #915 + (Cedric Cellier, review by Francois Berenger) + +- Add BatFilename + #910 + (Cedric Cellier, review by Francois Berenger) + +- Make batteries usable with BER MetaOCaml + #909 + (Cedric Cellier, review by Francois Berenger and Gabriel Scherer) + +- Unix.sleepf is provided across all OCaml versions; + previously it was only for OCaml >= 4.03.0 + #930 + (Francois Berenger, review by Cedric Cellier) + ## v2.10.0 (minor release) This minor release adds support for OCaml 4.08.0. @@ -79,6 +118,10 @@ recommend trying the 'stdcompat' library. #891 (Cedric Cellier, Francois Berenger, Gabriel Scherer) +- added Unix.with_locked_file + #904 + (Cedric Cellier, Francois Berenger) + ## v2.9.0 (minor release) This minor release adds support for OCaml 4.07.0, as well as a certain diff --git a/_tags b/_tags index c74904414..2e8fc763f 100644 --- a/_tags +++ b/_tags @@ -12,4 +12,4 @@ true: package(bytes), warn_-3, bin_annot : opaque true: safe_string true: no_alias_deps - +true: strict_sequence diff --git a/opam b/opam index f40435a67..3e0aa5f1f 100644 --- a/opam +++ b/opam @@ -9,7 +9,7 @@ authors: "OCaml batteries-included team" homepage: "http://batteries.forge.ocamlcore.org/" bug-reports: "https://github.com/ocaml-batteries-team/batteries-included/issues" dev-repo: "git://github.com/ocaml-batteries-team/batteries-included.git" -license: "LGPL-2.1+ with OCaml linking exception" +license: "LGPL-2.1-or-later with OCaml-LGPL-linking-exception" doc: "http://ocaml-batteries-team.github.io/batteries-included/hdoc2/" build: [ ["ocaml" "setup.ml" "-configure" "--prefix" prefix] @@ -18,7 +18,7 @@ build: [ install: [make "install"] remove: ["ocamlfind" "remove" "batteries"] depends: [ - "ocaml" {>= "3.12.1"} + "ocaml" {>= "4.00.0" & < "4.10.0"} "ocamlfind" {build & >= "1.5.3"} "ocamlbuild" {build} "qtest" {with-test & >= "2.5"} diff --git a/setup.ml b/setup.ml index 8df1e0dde..4dc94f0b1 100644 --- a/setup.ml +++ b/setup.ml @@ -1,7 +1,7 @@ (* setup.ml generated for the first time by OASIS v0.2.0 *) (* OASIS_START *) -(* DO NOT EDIT (digest: 8eacb5fc3c01b3f2ec2fa94f8db2c52a) *) +(* DO NOT EDIT (digest: c538dc9cea7562212bf9319fabb10941) *) (* Regenerated by OASIS v0.4.11 Visit http://oasis.forge.ocamlcore.org for more information and @@ -6742,7 +6742,7 @@ let setup_t = { oasis_version = "0.4"; ocaml_version = Some (OASISVersion.VGreaterEqual "3.12.1"); - version = "2.10.0"; + version = "2.11.0"; license = OASISLicense.DEP5License (OASISLicense.DEP5Unit @@ -7019,7 +7019,7 @@ let setup_t = }; oasis_fn = Some "_oasis"; oasis_version = "0.4.11"; - oasis_digest = Some "\031B\"\198\141\157`Yd\200\159F\169\162\127\022"; + oasis_digest = Some "\197\026\147\198c\239\0223\026_)\201>2\152\150"; oasis_exec = None; oasis_setup_args = []; setup_update = false diff --git a/src/batBytes.mliv b/src/batBytes.mliv index 386a7b72f..a5ebb8f9d 100644 --- a/src/batBytes.mliv +++ b/src/batBytes.mliv @@ -666,3 +666,5 @@ let s = Bytes.of_string "hello" ##V<4.4##external unsafe_fill : t -> int -> int -> char -> unit = "caml_fill_string" "noalloc" ##V>=4.4##external unsafe_fill: t -> int -> int -> char -> unit = "caml_fill_bytes" "noalloc" + +##V>=4.09##external unsafe_blit_string : string -> int -> bytes -> int -> int -> unit = "caml_blit_string" "noalloc" diff --git a/src/batFilename.mliv b/src/batFilename.mliv index 0bcec2e32..8b274755f 100644 --- a/src/batFilename.mliv +++ b/src/batFilename.mliv @@ -30,7 +30,7 @@ val parent_dir_name : string val dir_sep : string (** The directory separator (e.g. [/] in Unix). - @since NEXT_RELEASE and OCaml 3.11.2 *) + @since 2.11.0 and OCaml 3.11.2 *) val concat : string -> string -> string (** [concat dir file] returns a file name that designates file @@ -73,7 +73,7 @@ val chop_suffix : string -> string -> string ##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## @since 2.11.0 and OCaml 4.08 ##V>=4.8##*) @@ -89,7 +89,7 @@ val chop_suffix : string -> string -> string ##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## @since 2.11.0 and OCaml 4.04 ##V>=4.4##*) ##V>=4.4##val remove_extension : string -> string @@ -101,7 +101,7 @@ val chop_suffix : string -> string -> string ##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## @since 2.11.0 and OCaml 4.04 ##V>=4.4##*) val chop_extension : string -> string @@ -141,8 +141,10 @@ val temp_file : ?temp_dir: string -> string -> string -> string *) val open_temp_file : - ?mode: open_flag list -> ?perms: int -> ?temp_dir: string -> string -> - string -> string * out_channel + ?mode: open_flag list -> +##V>4.2## ?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 @@ -150,13 +152,11 @@ val open_temp_file : 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]). + and [Open_text]. The default is [[Open_text]] (open in text mode). +##V>4.2## The file is created with permissions [perms] (defaults to readable and +##V>4.2## 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 @@ -166,24 +166,24 @@ val open_temp_file : ##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## @since 2.11.0 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## @since 2.11.0 and OCaml 4.00.0 ##V>=4.0##*) val temp_dir_name : string - [@@ocaml.deprecated "Use Filename.get_temp_dir_name instead"] +##V>=4.2## [@@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 + @since 2.11.0 and OCaml 3.09.1 *) val quote : string -> string @@ -226,9 +226,26 @@ val quote : string -> string ##V>4.9## Raise [Failure] if the command cannot be escaped on the current platform. ##V>4.9##*) +##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 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 in name0. +##V<4.4## If such a suffix does not exist, extension name is the empty string. +##V<4.4## +##V<4.4## @since 2.11.0 *) + +##V<4.4## val remove_extension : string -> string +##V<4.4##(* Return the given file name without its extension, as defined in +##V<4.4## Filename.extension. If the extension is empty, the function returns +##V<4.4## the given file name. +##V<4.4## +##V<4.4## @since 2.11.0 *) + 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 *) + @since 2.11.0 *) diff --git a/src/batFilename.ml b/src/batFilename.mlv similarity index 55% rename from src/batFilename.ml rename to src/batFilename.mlv index 82c2e1b12..258808f53 100644 --- a/src/batFilename.ml +++ b/src/batFilename.mlv @@ -21,6 +21,37 @@ include Filename +##V<4.4## let is_dir_sep name i = +##V<4.4## try +##V<4.4## for j = 0 to String.length dir_sep - 1 do +##V<4.4## if i + j >= String.length name || +##V<4.4## name.[i + j] != dir_sep.[j] then raise Exit +##V<4.4## done; +##V<4.4## true +##V<4.4## with Exit -> +##V<4.4## false +##V<4.4## +##V<4.4## let extension_len name = +##V<4.4## let rec check i0 i = +##V<4.4## if i < 0 || is_dir_sep name i then 0 +##V<4.4## else if name.[i] = '.' then check i0 (i - 1) +##V<4.4## else String.length name - i0 +##V<4.4## in +##V<4.4## let rec search_dot i = +##V<4.4## if i < 0 || is_dir_sep name i then 0 +##V<4.4## else if name.[i] = '.' then check i (i - 1) +##V<4.4## else search_dot (i - 1) +##V<4.4## in +##V<4.4## search_dot (String.length name - 1) +##V<4.4## +##V<4.4## let remove_extension name = +##V<4.4## let l = extension_len name in +##V<4.4## if l = 0 then name else String.sub name 0 (String.length name - l) +##V<4.4## +##V<4.4## let extension name = +##V<4.4## let l = extension_len name in +##V<4.4## if l = 0 then "" else String.sub name (String.length name - l) l + let split_extension s = remove_extension s, extension s diff --git a/src/batGc.mliv b/src/batGc.mliv index de5554e90..3b452135e 100644 --- a/src/batGc.mliv +++ b/src/batGc.mliv @@ -356,7 +356,7 @@ val finalise : ('a -> unit) -> 'a -> unit ##V>=4.4## finalisation function attached with `GC.finalise` are always ##V>=4.4## called before the finalisation function attached with `GC.finalise_last`. ##V>=4.4## -##V>=4.4## @since NEXT_RELEASE and OCaml 4.04 +##V>=4.4## @since 2.11.0 and OCaml 4.04 ##V>=4.4##*) val finalise_release : unit -> unit;; diff --git a/src/batIO.mli b/src/batIO.mli index 7a9d3561c..02dd94db3 100644 --- a/src/batIO.mli +++ b/src/batIO.mli @@ -951,7 +951,7 @@ module Incubator : sig ?last:string -> ?sep:string -> ?indent:int -> - (Format.formatter -> 'a -> 'b) -> Format.formatter -> 'a array -> unit + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a array -> unit (** Print the contents of an array, with [first] preceding the first item (default: ["\[|"]), [last] following the last item (default: ["|\]"]) and [sep] separating items (default: ["; "]). A printing function must @@ -972,7 +972,7 @@ module Incubator : sig ?last:string -> ?sep:string -> ?indent:int -> - (Format.formatter -> 'a -> 'b) -> Format.formatter -> 'a BatEnum.t -> unit + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a BatEnum.t -> unit (** Print the contents of an enum, with [first] preceding the first item (default: [""]), [last] following the last item (default: [""]) and [sep] separating items (default: [" "]). A printing function must @@ -992,7 +992,7 @@ module Incubator : sig ?last:string -> ?sep:string -> ?indent:int -> - (Format.formatter -> 'a -> 'b) -> Format.formatter -> 'a list -> unit + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a list -> unit (** Print the contents of a list, with [first] preceding the first item (default: ["\["]), [last] following the last item (default: ["\]"]) and [sep] separating items (default: ["; "]). A printing function must diff --git a/src/batInnerPervasives.mlv b/src/batInnerPervasives.mlv index 7394c6792..088c104a7 100644 --- a/src/batInnerPervasives.mlv +++ b/src/batInnerPervasives.mlv @@ -58,7 +58,7 @@ let ok = function let wrap f x = try Ok (f x) with ex -> Error ex -let forever f x = ignore (while true do f x done) +let forever f x = ignore (while true do ignore (f x) done) let ignore_exceptions f x = try ignore (f x) with _ -> () diff --git a/src/batLazyList.ml b/src/batLazyList.ml index 6c4fe715e..c5c48e7ac 100644 --- a/src/batLazyList.ml +++ b/src/batLazyList.ml @@ -106,7 +106,7 @@ let make n x = let iter f l = let rec aux l = match next l with - | Cons (x, t) -> (f x; aux t) + | Cons (x, t) -> (ignore (f x); aux t) | Nil -> () in aux l diff --git a/src/batList.mliv b/src/batList.mliv index 560b4db63..1fb2534aa 100644 --- a/src/batList.mliv +++ b/src/batList.mliv @@ -299,13 +299,13 @@ val min : 'a list -> 'a val sum : int list -> int (** [sum l] returns the sum of the integers of [l]. Returns [0] on the empty list. - Note: prior to NEXT_RELEASE, used to raise Invalid_argument on the empty list. + Note: prior to 2.11.0, used to raise Invalid_argument on the empty list. *) val fsum : float list -> float (** [fsum l] returns the sum of the floats of [l]. Returns [0.] on the empty list. - Note: prior to NEXT_RELEASE, used to raise Invalid_argument on the empty list. + Note: prior to 2.11.0, used to raise Invalid_argument on the empty list. *) val favg : float list -> float diff --git a/src/batPrintexc.mliv b/src/batPrintexc.mliv index 2ea7f3bf8..0f3e20b91 100644 --- a/src/batPrintexc.mliv +++ b/src/batPrintexc.mliv @@ -325,7 +325,7 @@ val print : _ BatInnerIO.output -> exn -> unit ##V>=4.4## raw_backtrace_slot -> raw_backtrace_slot option ##V>=4.4##(** [get_raw_backtrace_next_slot slot] returns the next slot inlined, if any. ##V>=4.4## -##V>=4.4## @since NEXT_RELEASE and OCaml 4.04 +##V>=4.4## @since 2.11.0 and OCaml 4.04 ##V>=4.4##*) @@ -348,3 +348,17 @@ val print : _ BatInnerIO.output -> exn -> unit ##V>=4.08##type t = exn = .. ##V>=4.08##(** The type of exception values. *) + +##V>=4.09##val use_printers: exn -> string option +##V>=4.09##(** [Printexc.use_printers e] returns [None] if there are no registered +##V>=4.09## printers and [Some s] with else as the resulting string otherwise. +##V>=4.09## +##V>=4.09## @since 2.11.0 and OCaml 4.09 +##V>=4.09##*) + +##V>=4.09##val to_string_default: exn -> string +##V>=4.09##(** [Printexc.to_string_default e] returns a string representation of the +##V>=4.09## exception [e], ignoring all registered exception printers. +##V>=4.09## +##V>=4.09## @since 2.11.0 and OCaml 4.09 +##V>=4.09##*) diff --git a/src/batRefList.ml b/src/batRefList.ml index 36bb5446e..84150b9a4 100644 --- a/src/batRefList.ml +++ b/src/batRefList.ml @@ -121,12 +121,12 @@ module Index = struct let index pred rl = let index = ref (-1) in - List.find (fun it -> incr index; pred it; ) !rl; + ignore (List.find (fun it -> incr index; pred it; ) !rl); !index let index_of rl item = let index = ref (-1) in - List.find (fun it -> incr index; it = item; ) !rl; + ignore (List.find (fun it -> incr index; it = item; ) !rl); !index let at_index rl pos = List.nth !rl pos diff --git a/src/batString.mliv b/src/batString.mliv index 70c32e54c..9a79d7293 100644 --- a/src/batString.mliv +++ b/src/batString.mliv @@ -745,7 +745,7 @@ val split_on_char: char -> string -> string list (String.split_on_char sep s) = s]). - No string in the result contains the [sep] character. - Note: prior to NEXT_RELEASE [split_on_char _ ""] used to return an empty list. + Note: prior to 2.11.0 [split_on_char _ ""] used to return an empty list. @since 2.5.3 *) @@ -790,7 +790,8 @@ val nsplit : string -> by:string -> string list ##V>=4.2## [@@ocaml.deprecated "Use split_on_string instead."] (** [nsplit s sep] splits the string [s] into a list of strings which are separated by [sep] (excluded). - [nsplit "" _] returns an empty string. + [nsplit "" _] returns a single empty string. + Note: prior to 2.11.0 [nsplit "" _] used to return an empty list. Example: [String.nsplit "abcabcabc" "bc" = ["a"; "a"; "a"; ""]] @@ -829,6 +830,29 @@ val cut_on_char : char -> int -> string -> string @since 2.9.0 *) +val cut_on_char : char -> int -> string -> string +(** + Similar to Unix [cut]. [cut_on_char chr n str] returns the substring of + [str] located strictly between the [n]-th occurrence of [chr] and + the [n+1]-th one. + + - {b Occurrences of [chr] are numbered from 1}. + - If [n = 0], returns the substring from the beginning of + [str] to the first occurrence of [chr]. + - If there are exactly [n] occurrences of [chr] in [str], returns the + substring between the last occurrence of [chr] and the end of [str]. + - These behaviours cumulate: if [n] equals [0] and [chr] is + absent from [str], returns the full string [str]. + + {b Remark:} [cut_on_char] can return the empty string. Examples of this + behaviour are [cut_on_char ',' 1 "foo,,bar"] and [cut_on_char ',' 0 ",foo"]. + + @raise Not_found if there are strictly less than [n] occurrences of [chr] in str. + @raise Invalid_argument if [n < 0]. + + @since 2.9.0 +*) + val join : string -> string list -> string (** Same as {!concat} *) diff --git a/src/batSys.mliv b/src/batSys.mliv index b7a4f9333..7df166a78 100644 --- a/src/batSys.mliv +++ b/src/batSys.mliv @@ -29,7 +29,8 @@ @author David Teller *) -val argv : string array +##V>=4.09##external argv : string array = "%sys_argv" +##V<4.09##val argv : string array (** The command line arguments given to the process. The first element is the command name used to invoke the program. The following elements are the command-line arguments diff --git a/src/batUnix.mliv b/src/batUnix.mliv index 4e7dd990e..017cade4b 100644 --- a/src/batUnix.mliv +++ b/src/batUnix.mliv @@ -1041,6 +1041,16 @@ val lockf : file_descr -> lock_command -> int -> unit acquired on the specified region, without actually putting a lock. It returns immediately if successful, or fails otherwise. *) +val with_locked_file : kind:[`Read|`Write] -> string -> (file_descr -> 'a) -> 'a +(** [with_locked_file ~kind filename f] puts a lock (using lockf) on the whole + file named [filename], calls [f] with the file descriptor, and returns + its result after the file is unlocked. + The file is opened with permissions matching [kind], and created if it + does not exist yet. + If [f ()] raises an exception the exception is re-raised after the file + is unlocked. + + @param kind specifies whether the lock is read-only or read-write. *) (** {6 Signals} Note: installation of signal handlers is performed via @@ -1134,10 +1144,10 @@ val alarm : int -> int val sleep : int -> unit (** Stop execution for the given number of seconds. *) -##V>=4.3##val sleepf : float -> unit -##V>=4.3##(** Stop execution for the given number of seconds. Like [sleep], -##V>=4.3## but fractions of seconds are supported. -##V>=4.3## @since 2.5.0 and OCaml 4.03 *) +val sleepf : float -> unit +(** Stop execution for the given number of seconds. Like [sleep], + but fractions of seconds are supported. + @since 2.5.0 *) val times : unit -> process_times (** Return the execution times of the process. *) diff --git a/src/batUnix.mlv b/src/batUnix.mlv index 19ac50dc4..c17b630d5 100644 --- a/src/batUnix.mlv +++ b/src/batUnix.mlv @@ -30,6 +30,37 @@ include Unix ##V<4.2##let send_substring = send ##V<4.2##let sendto_substring = sendto +##V<4.3##let sleepf (timeout: float): unit = +##V<4.3## let elapsed = ref 0.0 in +##V<4.3## while !elapsed < timeout do +##V<4.3## let start = gettimeofday () in +##V<4.3## begin +##V<4.3## try ignore(select [] [] [] (timeout -. !elapsed)) +##V<4.3## with Unix_error(EINTR, _, _) -> () +##V<4.3## end; +##V<4.3## let stop = gettimeofday () in +##V<4.3## let dt = stop -. start in +##V<4.3## elapsed := !elapsed +. dt +##V<4.3## done; +##V<4.3## () + +(* chronometer is useful to test sleepf *) +(*$inject +let chronometer f = + let start = gettimeofday () in + let res = f () in + let stop = gettimeofday () in + let dt = stop -. start in + (dt, res) ;; +*) + +(* do not underestimate the imprecission of sleepf + and so don't be too harsh when testing it *) +(*$T sleepf +let dt, _ = chronometer (fun () -> sleepf 0.002) in \ +0.001 <= dt && dt <= 0.003 +*) + let run_and_read cmd = (* This code is before the open of BatInnerIO to avoid using batteries' wrapped IOs *) @@ -215,3 +246,26 @@ let is_directory fn = (lstat fn).st_kind = S_DIR let rec restart_on_EINTR f x = try f x with Unix_error(EINTR, _, _) -> restart_on_EINTR f x + +(** + {6 Locking} +*) + +let with_locked_file ~kind filename f = + let perms = + [O_CREAT ; match kind with `Read -> O_RDONLY | `Write -> O_RDWR] in + let lock_file = openfile filename perms 0o644 in + let lock_action = match kind with + | `Read -> F_RLOCK + | `Write -> F_LOCK + in + lockf lock_file lock_action 0; + BatInnerPervasives.finally + (fun () -> + (* Although the user might expect EINTR to interrupt locking, we must + * not allow such interrupt here since there is no way to restart the + * unlock: *) + ignore (restart_on_EINTR (lseek lock_file 0) SEEK_SET); + restart_on_EINTR (lockf lock_file F_ULOCK) 0; + restart_on_EINTR close lock_file) + f lock_file diff --git a/src/batteries.mlv b/src/batteries.mlv index 37e804185..ecbb5801d 100644 --- a/src/batteries.mlv +++ b/src/batteries.mlv @@ -34,7 +34,7 @@ module Legacy = struct module Random = Random module Scanf = Scanf module Set = Set - module Sort = Sort +##V<4.8## module Sort = Sort module Stack = Stack module StdLabels = StdLabels module Stream = Stream