123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359(**************************************************************************)(* *)(* OCaml *)(* *)(* Xavier Leroy and Damien Doligez, INRIA Rocquencourt *)(* *)(* Copyright 1996 Institut National de Recherche en Informatique et *)(* en Automatique. *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)letgeneric_quotequotequotes=letl=String.lengthsinletb=Buffer.create(l+20)inBuffer.add_charb'\'';fori=0tol-1doifs.[i]='\''thenBuffer.add_stringbquotequoteelseBuffer.add_charbs.[i]done;Buffer.add_charb'\'';Buffer.contentsb(* This function implements the Open Group specification found here:
[[1]] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html
In step 1 of [[1]], we choose to return "." for empty input.
(for compatibility with previous versions of OCaml)
In step 2, we choose to process "//" normally.
Step 6 is not implemented: we consider that the [suffix] operand is
always absent. Suffixes are handled by [chop_suffix] and [chop_extension].
*)letgeneric_basenameis_dir_sepcurrent_dir_namename=letrecfind_endn=ifn<0thenString.subname01elseifis_dir_sepnamenthenfind_end(n-1)elsefind_begn(n+1)andfind_begnp=ifn<0thenString.subname0pelseifis_dir_sepnamenthenString.subname(n+1)(p-n-1)elsefind_beg(n-1)pinifname=""thencurrent_dir_nameelsefind_end(String.lengthname-1)(* This function implements the Open Group specification found here:
[[2]] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
In step 6 of [[2]], we choose to process "//" normally.
*)letgeneric_dirnameis_dir_sepcurrent_dir_namename=letrectrailing_sepn=ifn<0thenString.subname01elseifis_dir_sepnamenthentrailing_sep(n-1)elsebasenandbasen=ifn<0thencurrent_dir_nameelseifis_dir_sepnamenthenintermediate_sepnelsebase(n-1)andintermediate_sepn=ifn<0thenString.subname01elseifis_dir_sepnamenthenintermediate_sep(n-1)elseString.subname0(n+1)inifname=""thencurrent_dir_nameelsetrailing_sep(String.lengthname-1)moduletypeSYSDEPS=sigvalnull:stringvalcurrent_dir_name:stringvalparent_dir_name:stringvaldir_sep:stringvalis_dir_sep:string->int->boolvalis_relative:string->boolvalis_implicit:string->boolvalcheck_suffix:string->string->boolvalchop_suffix_opt:suffix:string->string->stringoptionvaltemp_dir_name:stringvalquote:string->stringvalquote_command:string->?stdin:string->?stdout:string->?stderr:string->stringlist->stringvalbasename:string->stringvaldirname:string->stringendmoduleUnix:SYSDEPS=structletnull="/dev/null"letcurrent_dir_name="."letparent_dir_name=".."letdir_sep="/"letis_dir_sepsi=s.[i]='/'letis_relativen=String.lengthn<1||n.[0]<>'/'letis_implicitn=is_relativen&&(String.lengthn<2||String.subn02<>"./")&&(String.lengthn<3||String.subn03<>"../")letcheck_suffixnamesuff=String.ends_with~suffix:suffnameletchop_suffix_opt~suffixfilename=letlen_s=String.lengthsuffixandlen_f=String.lengthfilenameiniflen_f>=len_sthenletr=String.subfilename(len_f-len_s)len_sinifr=suffixthenSome(String.subfilename0(len_f-len_s))elseNoneelseNonelettemp_dir_name=trySys.getenv"TMPDIR"withNot_found->"/tmp"letquote=generic_quote"'\\''"letquote_commandcmd?stdin?stdout?stderrargs=String.concat" "(List.mapquote(cmd::args))^(matchstdinwithNone->""|Somef->" <"^quotef)^(matchstdoutwithNone->""|Somef->" >"^quotef)^(matchstderrwithNone->""|Somef->ifstderr=stdoutthen" 2>&1"else" 2>"^quotef)letbasename=generic_basenameis_dir_sepcurrent_dir_nameletdirname=generic_dirnameis_dir_sepcurrent_dir_nameendmoduleWin32:SYSDEPS=structletnull="NUL"letcurrent_dir_name="."letparent_dir_name=".."letdir_sep="\\"letis_dir_sepsi=letc=s.[i]inc='/'||c='\\'||c=':'letis_relativen=(String.lengthn<1||n.[0]<>'/')&&(String.lengthn<1||n.[0]<>'\\')&&(String.lengthn<2||n.[1]<>':')letis_implicitn=is_relativen&&(String.lengthn<2||String.subn02<>"./")&&(String.lengthn<2||String.subn02<>".\\")&&(String.lengthn<3||String.subn03<>"../")&&(String.lengthn<3||String.subn03<>"..\\")letcheck_suffixnamesuff=String.lengthname>=String.lengthsuff&&(lets=String.subname(String.lengthname-String.lengthsuff)(String.lengthsuff)inString.lowercase_asciis=String.lowercase_asciisuff)letchop_suffix_opt~suffixfilename=letlen_s=String.lengthsuffixandlen_f=String.lengthfilenameiniflen_f>=len_sthenletr=String.subfilename(len_f-len_s)len_sinifString.lowercase_asciir=String.lowercase_asciisuffixthenSome(String.subfilename0(len_f-len_s))elseNoneelseNonelettemp_dir_name=trySys.getenv"TEMP"withNot_found->"."letquotes=letl=String.lengthsinletb=Buffer.create(l+20)inBuffer.add_charb'\"';letrecloopi=ifi=lthenBuffer.add_charb'\"'elsematchs.[i]with|'\"'->loop_bs0i;|'\\'->loop_bs0i;|c->Buffer.add_charbc;loop(i+1);andloop_bsni=ifi=lthenbeginBuffer.add_charb'\"';add_bsn;endelsebeginmatchs.[i]with|'\"'->add_bs(2*n+1);Buffer.add_charb'\"';loop(i+1);|'\\'->loop_bs(n+1)(i+1);|_->add_bsn;loopiendandadd_bsn=for_j=1tondoBuffer.add_charb'\\';doneinloop0;Buffer.contentsb(*
Quoting commands for execution by cmd.exe is difficult.
1- Each argument is first quoted using the "quote" function above, to
protect it against the processing performed by the C runtime system,
then cmd.exe's special characters are escaped with '^', using
the "quote_cmd" function below. For more details, see
https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23
2- The command and the redirection files, if any, must be double-quoted
in case they contain spaces. This quoting is interpreted by cmd.exe,
not by the C runtime system, hence the "quote" function above
cannot be used. The two characters we don't know how to quote
inside a double-quoted cmd.exe string are double-quote and percent.
We just fail if the command name or the redirection file names
contain a double quote (not allowed in Windows file names, anyway)
or a percent. See function "quote_cmd_filename" below.
3- The whole string passed to Sys.command is then enclosed in double
quotes, which are immediately stripped by cmd.exe. Otherwise,
some of the double quotes from step 2 above can be misparsed.
See e.g. https://stackoverflow.com/a/9965141
*)letquote_cmds=letb=Buffer.create(String.lengths+20)inString.iter(func->matchcwith|'('|')'|'!'|'^'|'%'|'\"'|'<'|'>'|'&'|'|'->Buffer.add_charb'^';Buffer.add_charbc|_->Buffer.add_charbc)s;Buffer.contentsbletquote_cmd_filenamef=ifString.containsf'\"'||String.containsf'%'thenfailwith("Filename.quote_command: bad file name "^f)elseifString.containsf' 'then"\""^f^"\""elsef(* Redirections in cmd.exe: see https://ss64.com/nt/syntax-redirection.html
and https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490982(v=technet.10)
*)letquote_commandcmd?stdin?stdout?stderrargs=String.concat""["\"";quote_cmd_filenamecmd;" ";quote_cmd(String.concat" "(List.mapquoteargs));(matchstdinwithNone->""|Somef->" <"^quote_cmd_filenamef);(matchstdoutwithNone->""|Somef->" >"^quote_cmd_filenamef);(matchstderrwithNone->""|Somef->ifstderr=stdoutthen" 2>&1"else" 2>"^quote_cmd_filenamef);"\""]lethas_drives=letis_letter=function|'A'..'Z'|'a'..'z'->true|_->falseinString.lengths>=2&&is_letters.[0]&&s.[1]=':'letdrive_and_paths=ifhas_drivesthen(String.subs02,String.subs2(String.lengths-2))else("",s)letdirnames=let(drive,path)=drive_and_pathsinletdir=generic_dirnameis_dir_sepcurrent_dir_namepathindrive^dirletbasenames=let(_drive,path)=drive_and_pathsingeneric_basenameis_dir_sepcurrent_dir_namepathendmoduleCygwin:SYSDEPS=structletnull="/dev/null"letcurrent_dir_name="."letparent_dir_name=".."letdir_sep="/"letis_dir_sep=Win32.is_dir_sepletis_relative=Win32.is_relativeletis_implicit=Win32.is_implicitletcheck_suffix=Win32.check_suffixletchop_suffix_opt=Win32.chop_suffix_optlettemp_dir_name=Unix.temp_dir_nameletquote=Unix.quoteletquote_command=Unix.quote_commandletbasename=generic_basenameis_dir_sepcurrent_dir_nameletdirname=generic_dirnameis_dir_sepcurrent_dir_nameendmoduleSysdeps=(val(matchSys.os_typewith|"Win32"->(moduleWin32:SYSDEPS)|"Cygwin"->(moduleCygwin:SYSDEPS)|_->(moduleUnix:SYSDEPS)))includeSysdepsletconcatdirnamefilename=letl=String.lengthdirnameinifl=0||is_dir_sepdirname(l-1)thendirname^filenameelsedirname^dir_sep^filenameletchop_suffixnamesuff=letn=String.lengthname-String.lengthsuffinifn<0theninvalid_arg"Filename.chop_suffix"elseString.subname0nletextension_lenname=letrecchecki0i=ifi<0||is_dir_sepnameithen0elseifname.[i]='.'thenchecki0(i-1)elseString.lengthname-i0inletrecsearch_doti=ifi<0||is_dir_sepnameithen0elseifname.[i]='.'thenchecki(i-1)elsesearch_dot(i-1)insearch_dot(String.lengthname-1)letextensionname=letl=extension_lennameinifl=0then""elseString.subname(String.lengthname-l)lletchop_extensionname=letl=extension_lennameinifl=0theninvalid_arg"Filename.chop_extension"elseString.subname0(String.lengthname-l)letremove_extensionname=letl=extension_lennameinifl=0thennameelseString.subname0(String.lengthname-l)externalopen_desc:string->open_flaglist->int->int="caml_sys_open"externalclose_desc:int->unit="caml_sys_close"letprng=lazy(Random.State.make_self_init())lettemp_file_nametemp_dirprefixsuffix=letrnd=(Random.State.bits(Lazy.forceprng))land0xFFFFFFinconcattemp_dir(Printf.sprintf"%s%06x%s"prefixrndsuffix)letcurrent_temp_dir_name=reftemp_dir_nameletset_temp_dir_names=current_temp_dir_name:=sletget_temp_dir_name()=!current_temp_dir_namelettemp_file?(temp_dir=!current_temp_dir_name)prefixsuffix=letrectry_namecounter=letname=temp_file_nametemp_dirprefixsuffixintryclose_desc(open_descname[Open_wronly;Open_creat;Open_excl]0o600);namewithSys_error_ase->ifcounter>=1000thenraiseeelsetry_name(counter+1)intry_name0letopen_temp_file?(mode=[Open_text])?(perms=0o600)?(temp_dir=!current_temp_dir_name)prefixsuffix=letrectry_namecounter=letname=temp_file_nametemp_dirprefixsuffixintry(name,open_out_gen(Open_wronly::Open_creat::Open_excl::mode)permsname)withSys_error_ase->ifcounter>=1000thenraiseeelsetry_name(counter+1)intry_name0