123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167(**************************************************************************)(* *)(* Copyright 2018-2020 OCamlPro *)(* *)(* 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. *)(* *)(**************************************************************************)moduleString=struct[@@@warning"-32"](** NOTE: OCaml >= 4.13 *)letexistsps=letn=String.lengthsinletrecloopi=ifi=nthenfalseelseifp(String.unsafe_getsi)thentrueelseloop(succi)inloop0includeStdlib.StringendmoduleSeq=struct[@@@warning"-32"](** NOTE: OCaml >= 4.14 *)letrecfind_mapfxs=matchxs()with|Seq.Nil->None|Seq.Cons(x,xs)->matchfxwith|None->find_mapfxs|Some_asresult->resultincludeSeqendmoduleEither=struct(** NOTE: OCaml >= 4.12 *)type('a,'b)t=|Leftof'a|Rightof'bendmoduleUnix=struct[@@@warning"-32"](** NOTE: OCaml >= 4.13 *)letrealpaths=letgetchdirs=letp=trySys.getcwd()withSys_error_->Filename.get_temp_dir_name()inUnix.chdirs;pintrygetchdir(getchdirs)withUnix.Unix_error_->sincludeUnixendmoduleLazy=struct[@@@warning"-32"](** NOTE: OCaml >= 4.13 *)letmapfx=lazy(f(Lazy.forcex))includeStdlib.LazyendmoduleFilename=struct[@@@warning"-32"]letquotes=letl=String.lengthsinletb=Buffer.create(l+20)inBuffer.add_charb'\"';letrecloopi=ifi=lthenBuffer.add_charb'\"'elsematch s.[i]with|'\"'->loop_bs0i;|'\\'->loop_bs0i;|c->Buffer.add_charbc;loop(i+1);andloop_bsni=ifi=lthenbeginBuffer.add_charb'\"';add_bs n;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.contains f'%'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)
*)(** NOTE: OCaml >= 4.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);"\""]include Stdlib.Filenameend