123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(* Files and load path. *)typephysical_path=stringtypeload_path=physical_pathlistletphysical_path_of_strings=sletstring_of_physical_pathp=pletescaped_string_of_physical_pathp=(* We assume a reasonable-enough path (typically utf8) and prevents
the presence of space; other escapings might be useful... *)ifString.containsp' 'then"\""^p^"\""elsepletpath_to_listp=letsep=Str.regexp(ifSys.os_type="Win32"then";"else":")inStr.splitsepp(* Some static definitions concerning filenames *)letdirsep=Filename.dir_sep(* Unix: "/" *)letdirsep_len=String.lengthdirsepletcurdir=Filename.concatFilename.current_dir_name""(* Unix: "./" *)letcurdir_len=String.lengthcurdir(* Hints to partially detects if two paths refer to the same directory *)(** cut path [p] after all the [/] that come at position [pos]. *)letreccut_after_dirsepppos=ifCString.is_subdirseppposthencut_after_dirsepp(pos+dirsep_len)elseString.subppos(String.lengthp-pos)(** remove all initial "./" in a path unless the path is exactly "./" *)letrecremove_path_dotp=ifCString.is_subcurdirp0thenifString.lengthp=curdir_lenthenFilename.current_dir_nameelseremove_path_dot(cut_after_dirseppcurdir_len)elsep(** If a path [p] starts with the current directory $PWD then
[strip_path p] returns the sub-path relative to $PWD.
Any leading "./" are also removed from the result. *)letstrip_pathp=letcwd=Filename.concat(Sys.getcwd())""in(* Unix: "`pwd`/" *)ifCString.is_subcwdp0thenremove_path_dot(cut_after_dirsepp(String.lengthcwd))elseremove_path_dotpletcanonical_path_namep=letcurrent=Sys.getcwd()intrySys.chdirp;letp'=Sys.getcwd()inSys.chdircurrent;p'withSys_error_->(* We give up to find a canonical name and just simplify it... *)current^dirsep^strip_pathpletmake_suffixnamesuffix=ifFilename.check_suffixnamesuffixthennameelse(name^suffix)letcorrect_pathfdir=ifFilename.is_relativefthenFilename.concatdirfelsefletfile_readable_pname=tryUnix.accessname[Unix.R_OK];truewithUnix.Unix_error(_,_,_)->false(* As for [Unix.close_process], a [Unix.waipid] that ignores all [EINTR] *)letrecwaitpid_non_intrpid=trysnd(Unix.waitpid[]pid)withUnix.Unix_error(Unix.EINTR,_,_)->waitpid_non_intrpid(** [run_command com] launches command [com] (via /bin/sh),
and returns the contents of stdout and stderr. If given, [~hook]
is called on each elements read on stdout or stderr. *)letrun_command?(hook=(fun_->()))c=letresult=Buffer.create127inletcin,cout,cerr=Unix.open_process_fullc(Unix.environment())inletbuff=Bytes.make127' 'inletbuffe=Bytes.make127' 'inletn=ref0inletne=ref0inwhilen:=inputcinbuff0127;ne:=inputcerrbuffe0127;!n+!ne<>0doletr=Bytes.subbuff0!nin(hookr;Buffer.add_bytesresultr);letr=Bytes.subbuffe0!nein(hookr;Buffer.add_bytesresultr);done;(Unix.close_process_full(cin,cout,cerr),Buffer.contentsresult)(** [sys_command] launches program [prog] with arguments [args].
It behaves like [Sys.command], except that we rely on
[Unix.create_process], it's hardly more complex and avoids dealing
with shells. In particular, no need to quote arguments
(against whitespace or other funny chars in paths), hence no need
to care about the different quoting conventions of /bin/sh and cmd.exe. *)letsys_commandprogargs=letargv=Array.of_list(prog::args)inletpid=Unix.create_processprogargvUnix.stdinUnix.stdoutUnix.stderrinwaitpid_non_intrpid(*
checks if two file names refer to the same (existing) file by
comparing their device and inode.
It seems that under Windows, inode is always 0, so we cannot
accurately check if
*)(* Optimised for partial application (in case many candidates must be
compared to f1). *)letsame_filef1=trylets1=Unix.statf1in(funf2->trylets2=Unix.statf2ins1.Unix.st_dev=s2.Unix.st_dev&&ifSys.os_type="Win32"thenf1=f2elses1.Unix.st_ino=s2.Unix.st_inowithUnix.Unix_error_->false)withUnix.Unix_error_->(fun_->false)(* Copied from ocaml filename.ml *)letprng=lazy(Random.State.make_self_init())lettemp_file_nametemp_dirprefixsuffix=letrnd=(Random.State.bits(Lazy.forceprng))land0xFFFFFFinFilename.concattemp_dir(Printf.sprintf"%s%06x%s"prefixrndsuffix)letmktemp_dir?(temp_dir=Filename.get_temp_dir_name())prefixsuffix=letrectry_namecounter=letname=temp_file_nametemp_dirprefixsuffixinmatchUnix.mkdirname0o700with|()->name|exception(Sys_error_ase)->ifcounter>=1000thenraiseeelsetry_name(counter+1)intry_name0