1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174openImportletwith_lockmutex~f=Mutex.lockmutex;protectx()~finally:(fun()->Mutex.unlockmutex)~fmoduleContext=structmoduleState=structtypet=|ActiveofEnv.t|Disposedendtypet={mutablestate:State.t;mutablemutex:Mutex.t}moduleWorking_dir=Env.Working_dir_specletcreate?stdin?stdout?stderr?cwd?unix_env()={state=Active(Env.create?stdin?stdout?stderr?cwd?unix_env());mutex=Mutex.create()}letdisposet=matchwith_lockt.mutex~f:(fun()->matcht.statewith|Disposed->None|Activeenv->t.state<-Disposed;Someenv)with|None->()|Someenv->Env.deref_cwdenvletuse_envt=with_lockt.mutex~f:(fun()->matcht.statewith|Disposed->failwith"Shexp_process: \
trying to use a disposed context"|Activeenv->Env.add_cwd_refenv;env)endmoduleTemp0=struct(* Copied from filename.ml and adapted to pass O_CLOEXEC as well as protecting the
global random state *)letprng=lazy(Random.State.make_self_init())letprng_mutex=Mutex.create()letgen_name~temp_dir~prefix~suffix=letrnd=with_lockprng_mutex~f:(fun()->(Random.State.bits(Lazy.forceprng))land0xFFFFFF)intemp_dir^/(Printf.sprintf"%s%06x%s"prefixrndsuffix)letcreate~temp_dir~prefix~suffix~mk=letrectry_namecounter=letname=gen_name~temp_dir~prefix~suffixinmatchmknamewith|x->(x,name)|exception(Unix.Unix_error_)whencounter<1000->try_name(counter+1)intry_name0letwith_open_temp~prefix~suffix~f=letfinally(fd,name)=(tryUnix.closefdwith_->());(tryUnix.unlinknamewith_->())inprotectx~finally~f(create~temp_dir:(Filename.get_temp_dir_name())~prefix~suffix~mk:(funfn->Unix.openfilefn[O_WRONLY;O_CREAT;O_EXCL]0o600))endtype'at=|Returnof'a|Errorof{exn:exn;backtrace:Printexc.raw_backtrace}|Bind:'at*('a->'bt)->'bt|Protectof{finally:unitt;t:'at}(* Note: it is expected that the function in the following two constructors never
raise *)|Env_get:{prim:('a,'b)Prim.t;args:('a,'b)Prim.Args.t}->'bt|Env_set:{prim:('a,Env.t)Prim.t;args:('a,Env.t)Prim.Args.t;k:'bt}->'bt|Fork:'at*'bt->('a*'b)t|Prim:{prim:('a,'b)Prim.t;args:('a,'b)Prim.Args.t}->'bt|Chdirof{dir:string;k:'at}|Fold:{fold:'a.Env.t->init:'a->f:('a->'elt->'a)->'a;init:'acc;f:'acc->'elt->'acct;prim:('a,'eltoption)Prim.t;args:('a,'eltoption)Prim.Args.t}->'acctletrecmight_block:typea.at->bool=function|Bind_|Prim_|Chdir_|Fold_->true|Return_|Error_|Env_get_->false|Env_set{k;_}->might_blockk|Fork(a,b)->might_blocka||might_blockb|Protect{t;finally}->might_blockt||might_blockfinallyletderef_cwdenv~can_deref_cwd=ifcan_deref_cwdthenEnv.deref_cwdenvelse()letrecexec:typea.Env.t->can_deref_cwd:bool->at->a=funenv~can_deref_cwdt->matchtwith|Returnx->deref_cwdenv~can_deref_cwd;x|Error{exn;_}->deref_cwdenv~can_deref_cwd;reraiseexn|Env_get{prim;args}->deref_cwdenv~can_deref_cwd;Prim.runprimenvargs|Env_set{prim;args;k}->letenv=Prim.runprimenvargsinexecenvk~can_deref_cwd|Prim{prim;args}->ifcan_deref_cwdthenmatchPrim.runprimenvargswith|x->Env.deref_cwdenv;x|exceptionexn->Env.deref_cwdenv;reraiseexnelsePrim.runprimenvargs|Bind(t,f)->ifcan_deref_cwdthenmatchf(execenvt~can_deref_cwd:false)with|exceptionexn->Env.deref_cwdenv;reraiseexn|t->execenvt~can_deref_cwd:trueelselett=f(execenvt~can_deref_cwd:false)inexecenvt~can_deref_cwd:false|Protect{finally;t}->(matchexecenvt~can_deref_cwd:falsewith|x->execenvfinally~can_deref_cwd;x|exceptionexn->execenvfinally~can_deref_cwd;reraiseexn)|Chdir{dir;k}->ifcan_deref_cwdthenmatchEnv.chdirenvdirwith|new_env->Env.deref_cwdenv;execnew_envk~can_deref_cwd:true|exceptionexn->Env.deref_cwdenv;reraiseexnelseletnew_env=Env.chdirenvdirinexecnew_envk~can_deref_cwd:true|Fold{fold;f;init;_}->ifcan_deref_cwdthenmatchfoldenv~init~f:(funaccelt->execenv(faccelt)~can_deref_cwd:false)with|res->Env.deref_cwdenv;res|exceptionexn->Env.deref_cwdenv;reraiseexnelsefoldenv~init~f:(funaccelt->execenv(faccelt)~can_deref_cwd:false)|Fork(a,b)->ifmight_blocka&&might_blockbthenbeginifcan_deref_cwdthenEnv.add_cwd_refenv;letjob=Job.detach~f:(fun()->execenvb~can_deref_cwd)inleta_res=execenva~can_deref_cwdinletb_res=Job.waitjobin(a_res,b_res)endelseifcan_deref_cwdthenmatchexecenva~can_deref_cwd:falsewith|exceptionexn->Env.deref_cwdenv;reraiseexn|a_res->(a_res,execenvb~can_deref_cwd:true)elseleta_res=execenva~can_deref_cwd:falsein(a_res,execenvb~can_deref_cwd:false)leteval?contextt=letenv=matchcontextwith|None->Env.create()|Somectx->Context.use_envctxinexecenvt~can_deref_cwd:truemodulePrim=PrimmoduletypeDebugger=Debugger_intf.SmoduleWith_debug(D:Debugger)=structletchdir_prim=Prim.make"chdir"[Asexp_of_string]Env(fun__->assertfalse)moduleD=structtypecheckpoint=|Not_needed|Create_if_needed|Createdtypecapture_context={mutablepos:int;fdw:Unix.file_descr;fdr:Unix.file_descr}typet={dbg:D.t;capture:capture_contextoption;mutablecheckpoint:checkpoint}letneed_checkpointt=ift.checkpoint=Create_if_neededthenbeginD.enter_subt.dbg;t.checkpoint<-Created;endletbefore_primtprimargs=D.before_primt.dbgprimargsletafter_primtprimrestok=D.after_primt.dbgprimrestokletuser_exntexnbt=D.user_exnt.dbgexnbtletforce_threads=D.force_threadsletcapturet=matcht.capturewith|None->()|Somecap->letpos=Unix.lseekcap.fdw0SEEK_CURinletlen=pos-cap.posiniflen>0thenbeginlets=Bigstring.read_exactlycap.fdrlenincap.pos<-pos;D.outputt.dbgsendletenter_new_thread_with_captureenvdbg~parent_capture~f=letreplace_stdin,replace_stdout,replace_stderr=matchparent_capturewith|None->(false,true,true)|Someinfo->(Env.stdinenv==info.fdw,Env.stdoutenv==info.fdw,Env.stderrenv==info.fdw)inifnot(replace_stdin||replace_stdout||replace_stderr)thenfenv{dbg;capture=None;checkpoint=Not_needed}elseTemp0.with_open_temp~prefix:"shexp-process"~suffix:".output"~f:(fun(fdw,fn)->protectx(Unix.openfilefn[O_RDONLY]0)~finally:(funfd->tryUnix.closefdwith_->())~f:(funfdr->lett={dbg;capture=Some{pos=0;fdr;fdw};checkpoint=Not_needed}inletenv=Env.set_stdiosenv~stdin:(ifreplace_stdinthenfdwelseEnv.stdinenv)~stdout:(ifreplace_stdoutthenfdwelseEnv.stdoutenv)~stderr:(ifreplace_stderrthenfdwelseEnv.stderrenv)inletres=fenvtincapturet;res))letforkenvt~f=protectx(D.forkt.dbg)~finally:(fun(dbg_a,dbg_b)->D.end_forkt.dbgdbg_adbg_b)~f:(fun(dbg_a,dbg_b)->matcht.capturewith|None->fenv{twithdbg=dbg_a;checkpoint=Not_needed}env{twithdbg=dbg_b;checkpoint=Not_needed}|Some_->enter_new_thread_with_captureenvdbg_b~parent_capture:t.capture~f:(funenv't'->fenv{twithdbg=dbg_a;checkpoint=Not_needed}env't'))lettoplevelenvdbg~capture~f=ifcapturethenenter_new_thread_with_captureenvdbg~parent_capture:None~felsefenv{dbg;capture=None;checkpoint=Not_needed}letsubt~f=protectx{twithcheckpoint=Create_if_needed}~f~finally:(funt->ift.checkpoint=CreatedthenD.leave_subt.dbg)endletto_resultf=matchf()with|x->Okx|exceptione->Error(e,Printexc.get_raw_backtrace())letok_exn=function|Okx->x|Error(e,_)->raiseeletrecexec:typea.Env.t->D.t->can_deref_cwd:bool->at->a=funenvdbg~can_deref_cwdt->matchtwith|Returnx->deref_cwdenv~can_deref_cwd;x|Error{exn;backtrace}->deref_cwdenv~can_deref_cwd;D.user_exndbgexnbacktrace;raiseexn|Env_get{prim;args}->deref_cwdenv~can_deref_cwd;lettoken=D.before_primdbgprimargsinletres=Prim.runprimenvargsinD.after_primdbgprim(Okres)token;res|Env_set{prim;args;k}->D.need_checkpointdbg;lettoken=D.before_primdbgprimargsinletenv=Prim.runprimenvargsinD.after_primdbgprim(Okenv)token;execenvdbgk~can_deref_cwd|Prim{prim;args}->lettoken=D.before_primdbgprimargsinletres=to_result(fun()->Prim.runprimenvargs)inderef_cwdenv~can_deref_cwd;D.capturedbg;D.after_primdbgprimrestoken;ok_exnres|Bind(t,f)->(matchexec_subenvdbgtwith|exceptionexn->deref_cwdenv~can_deref_cwd;reraiseexn|x->matchfxwith|exceptionexn->letbacktrace=Printexc.get_raw_backtrace()inderef_cwdenv~can_deref_cwd;D.user_exndbgexnbacktrace;reraiseexn|t->execenvdbgt~can_deref_cwd)|Protect{finally;t}->(matchexec_subenvdbgtwith|x->execenvdbgfinally~can_deref_cwd;x|exceptionexn->execenvdbgfinally~can_deref_cwd;reraiseexn)|Chdir{dir;k}->D.need_checkpointdbg;lettoken=D.before_primdbgchdir_prim(A1dir)inletres=to_result(fun()->Env.chdirenvdir)inderef_cwdenv~can_deref_cwd;D.after_primdbgchdir_primrestoken;exec(ok_exnres)dbgk~can_deref_cwd:true|Fold{fold;f;init;prim;args}->lettoken=ref(D.before_primdbgprimargs)inletin_fold_impl=reftrueinletres=to_result(fun()->foldenv~init~f:(funaccelt->in_fold_impl:=false;D.capturedbg;D.after_primdbgprim(Ok(Someelt))!token;matchfacceltwith|exceptionexn->D.user_exndbgexn(Printexc.get_raw_backtrace());reraiseexn|t->letacc=execenvdbgt~can_deref_cwd:falseintoken:=D.before_primdbgprimargs;in_fold_impl:=true;acc))inderef_cwdenv~can_deref_cwd;if!in_fold_implthenbeginD.capturedbg;matchreswith|Okacc->D.after_primdbgprim(OkNone)!token;acc|Error(exn,_)ase->D.after_primdbgprime!token;reraiseexnendelsebeginmatchreswith|Ok_->assertfalse|Error(exn,_)->reraiseexnend|Fork(a,b)->D.forkenvdbg~f:(funenv_adbg_aenv_bdbg_b->ifD.force_threads||(might_blocka&&might_blockb)thenbeginifcan_deref_cwdthenEnv.add_cwd_refenv_a;letjob=Job.detach~f:(fun()->execenv_bdbg_bb~can_deref_cwd)inleta_res=to_result(fun()->execenv_adbg_aa~can_deref_cwd)inletb_res=Job.waitjobinmatcha_reswith|Oka_res->(a_res,b_res)|Error(exn,_)->reraiseexnendelseleta_res=to_result(fun()->execenvdbg_aa~can_deref_cwd:false)inletb_res=execenvdbg_bb~can_deref_cwdinmatcha_reswith|Oka_res->(a_res,b_res)|Error(exn,_)->reraiseexn)andexec_sub:typea.Env.t->D.t->at->a=funenvdbgt->D.subdbg~f:(fundbg->execenvdbgt~can_deref_cwd:false)leteval?context?(capture=false)t~debugger=letenv=matchcontextwith|None->Env.create()|Somectx->Context.use_envctxinD.toplevelenvdebugger~capture~f:(funenvdbg->execenvdbgt~can_deref_cwd:true)endmoduleLogged=structopenDebuggersmoduleM=With_debug(Logger)letlog_defaultsexp=prerr_endline(Sexp.to_stringsexp)leteval?context?capture?(log=log_default)t=M.eval?context?capturet~debugger:(Logger.createlog)endmoduleTraced=structopenDebuggersmoduleM=With_debug(Tracer)leteval?context?capturet=letdbg=Tracer.create()inletres=matchM.eval?context?capturet~debugger:dbgwith|x->Okx|exceptionexn->Errorexnin(res,Tracer.resultdbg)leteval_exn?context?capturet=letdbg=Tracer.create()inletres=M.eval?context?capturet~debugger:dbgin(res,Tracer.resultdbg)endletreturnx=Returnxletbindt~f=Bind(t,f)letmapt~f=Bind(t,funx->Return(fx))letforkab=Fork(a,b)letfork_unitab=map(forkab)~f:(fun(x,())->x)letprotect~finallyt=Protect{finally;t}letreify_exnfx=matchfxwith|t->t|exceptionexn->Error{exn;backtrace=Printexc.get_raw_backtrace()}letfailexn=Error{exn;backtrace=Printexc.get_raw_backtrace()}moduleInfix0=structlet(>>=)tf=bindt~flet(>>|)tf=mapt~flet(>>)ab=a>>=fun()->bendopenInfix0moduleList0=structletreciterl~f=matchlwith|[]->return()|x::l->fx>>=fun()->iterl~fendletquote_for_errorss=letneed_quoting=reffalsein(matchswith|""->need_quoting:=true|_->());String.iters~f:(function|'a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-'|':'|'.'|'/'|','|'+'|'='|'%'|'@'->()|_->need_quoting:=true);if!need_quotingthenFilename.quoteselsesletcmd_lineprogargs=List.map(prog::args)~f:quote_for_errors|>String.concat~sep:" "letpack0prim=Prim{prim;args=A0()}letpack1prima=Prim{prim;args=A1a}letpack2primab=Prim{prim;args=A2(a,b)}letpack3primabc=Prim{prim;args=A3(a,b,c)}moduleExit_status=Exit_statusletwaitpidpid:Exit_status.t=matchsnd(Unix.waitpid[]pid)with|WEXITEDn->Exitedn|WSIGNALEDn->Signaledn|WSTOPPED_->assertfalsemoduleBackground_command=structtypet={mutex:Mutex.t;pid:int;mutablewait:Exit_status.tLazy.t}letsexp_of_tt=sexp_of_string(Printf.sprintf"[%d]"t.pid)letcreatepid={mutex=Mutex.create();pid;wait=lazy(waitpidpid)}letpidt=t.pidletwaitt=Mutex.lockt.mutex;protectxt.mutex~finally:Mutex.unlock~f:(fun_->Lazy.forcet.wait)endletspawn=letprim=Prim.make"spawn"[Asexp_of_string;A(sexp_of_listsexp_of_string)](FBackground_command.sexp_of_t)(funenvprogargs->matchEnv.spawnenv~prog~argswith|Okpid->Background_command.createpid|ErrorCommand_not_found->Printf.ksprintffailwith"%s: command not found"(quote_for_errorsprog))infunprogargs->pack2primprogargsletwait=letprim=Prim.make"wait"[ABackground_command.sexp_of_t](FExit_status.sexp_of_t)(fun_bc->Background_command.waitbc)infunbc->pack1primbc(* This could be implemented in term of [spawn] followed by a [wait], but doing it in one
primitive improve traces. *)letrun_exit_status=letprim=Prim.make"run"[Asexp_of_string;A(sexp_of_listsexp_of_string)](FExit_status.sexp_of_t)(funenvprogargs->matchEnv.spawnenv~prog~argswith|Okpid->waitpidpid|ErrorCommand_not_found->Printf.ksprintffailwith"%s: command not found"(quote_for_errorsprog))infunprogargs->pack2primprogargsletrun_exit_codeprogargs=run_exit_statusprogargs>>|function|Exitedn->n|Signaledsignal->Printf.ksprintffailwith"Command got signal %d: %s"signal(cmd_lineprogargs)letrunprogargs=run_exit_codeprogargs>>|funcode->ifcode<>0thenPrintf.ksprintffailwith"Command exited with code %d: %s"code(cmd_lineprogargs)letrun_bool?(true_v=[0])?(false_v=[1])progargs=run_exit_codeprogargs>>|funcode->ifList.memcode~set:true_vthentrueelseifList.memcode~set:false_vthenfalseelsePrintf.ksprintffailwith"Command exited with unexpected code %d: %s"code(cmd_lineprogargs)letcall_exit_status=function|[]->failwith"call_exit_status: empty command"|prog::args->run_exit_statusprogargsletcall_exit_code=function|[]->failwith"call_exit_code: empty command"|prog::args->run_exit_codeprogargsletcall=function|[]->failwith"call: empty command"|prog::args->runprogargsletcall_bool?true_v?false_v=function|[]->failwith"call_bool: empty command"|prog::args->run_bool?true_v?false_vprogargsletfind_executable=letprim=Prim.make"find-executable"[Asexp_of_string](F(sexp_of_optionsexp_of_string))Env.find_executableinfunexe->pack1primexeletfind_executable_exnexe=find_executableexe>>|function|None->Printf.ksprintffailwith"command %S not found"exe|Somex->xletget_env=letprim=Prim.make"get-env"[Asexp_of_string](F(sexp_of_optionsexp_of_string))Env.get_envinfunvar->Env_get{prim;args=A1var}letget_env_exnvar=get_envvar>>|function|None->Printf.ksprintffailwith"environment variable %S not found"var|Somex->xletset_env=letprim={Prim.name="set-env";args=[Asexp_of_string;Asexp_of_string];result=Env;run=Env.set_env}infunvarvaluek->Env_set{k;prim;args=A2(var,value)}letunset_env=letprim={Prim.name="unset-env";args=[Asexp_of_string];result=Env;run=Env.unset_env}infunvark->Env_set{k;prim;args=A1var}letcwd_logical=letprim=Prim.make"cwd"[](Fsexp_of_string)Env.cwd_logicalinEnv_get{prim;args=A0()}letchdirdirk=Chdir{dir;k}moduleStd_io=Std_ioletecho=letprim=Prim.make"echo"[O("where",Std_io.sexp_of_t,Stdout);O("n",sexp_of_bool,false);Asexp_of_string]Unit(funenvwherenstr->letstr=ifnotnthenstr^"\n"elsestrinBigstring.write_all(Env.get_stdioenvwhere)str)infun?(where=Std_io.Stdout)?nstr->pack3primwhere(n<>None)strletprintstr=echostr~n:()~where:Stdoutleteprintstr=echostr~n:()~where:Stderrletprintffmt=Printf.ksprintfprintfmtleteprintffmt=Printf.ksprintfeprintfmtletread_all=letprim=Prim.make"read-all"[](Fsexp_of_string)(funenv->Bigstring.read_all(Env.stdinenv))inpack0primletfold_lines:typea.init:a->f:(a->string->at)->at=letprim={Prim.name="read-line";args=[];result=F(funx->sexp_of_optionsexp_of_stringx);run=fun_->assertfalse}infun~init~f->Fold{prim;args=A0();init;f;fold=funenv~init~f->Bigstring.fold_lines(Env.stdinenv)~init~f}letfold_chunks=letprim={Prim.name="read-chunk";args=[L("sep",sexp_of_char)];result=F(funx->sexp_of_optionsexp_of_stringx);run=fun_->assertfalse}infun~sep~init~f->Fold{prim;args=A1sep;init;f;fold=funenv~init~f->Bigstring.fold_chunks(Env.stdinenv)~sep~init~f}letiter_linesf=fold_lines~init:()~f:(fun()line->fline)letiter_chunks~sepf=fold_chunks~sep~init:()~f:(fun()line->fline)letcreate_pipe=letprim=Prim.make"create-pipe"[](F(Sexp.pairPosixat.Fd.sexp_of_tPosixat.Fd.sexp_of_t))(fun_->retry_eintr1W.(pairfdfd)Spawn.safe_pipe())inpack0primletclose_fd=letprim=Prim.make"close-fd"[APosixat.Fd.sexp_of_t]Unit(fun_fd->Unix.closefd)infunfd->pack1primfdletset_ios=letprim={Prim.name="set-ios";args=[A(funx->sexp_of_listStd_io.sexp_of_tx);APosixat.Fd.sexp_of_t];result=Env;run=funenviosfd->List.fold_leftios~init:env~f:(funenvwhere->Env.set_stdioenvwherefd)}infuniosfdk->Env_set{k;prim;args=A2(ios,fd)}letpipe_both?(connect=Std_io.([Stdout],Stdin))ab=create_pipe>>=fun(fdr,fdw)->let(aios,bio)=connectinfork(protect~finally:(close_fdfdw)(set_iosaiosfdwa))(protect~finally:(close_fdfdr)(set_ios[bio]fdrb))letpipe?connectab=pipe_both?connectab>>|sndletepipeab=pipe~connect:([Stderr],Stdin)abletepipe_bothab=pipe_both~connect:([Stderr],Stdin)abletcaptureiost=pipe_both~connect:(ios,Stdin)tread_allletcapture_unitiost=pipe~connect:(ios,Stdin)tread_allletopen_file=letprim=Prim.make"open-file"[O("perm",Posixat.File_perm.sexp_of_t,0);L("flags",sexp_of_listPosixat.Open_flag.sexp_of_t);Asexp_of_string](FPosixat.Fd.sexp_of_t)(funenvpermflagsfn->Env.open_fileenv~perm~flagsfn)infun?(perm=0)~flagsfn->pack3primpermflagsfnletredirectios?perm~flagsfnt=open_file?perm~flags:(List.map~f:Posixat.Open_flag.of_unix_open_flagflags)fn>>=funfd->protect~finally:(close_fdfd)(set_iosiosfdt)letstd_toios?appendfnt=redirectios~flags:[O_WRONLY;O_CREAT;ifappend=NonethenO_TRUNCelseO_APPEND]~perm:0o666fntletstdout_to?appendfnt=std_to?append[Stdout]fntletstderr_to?appendfnt=std_to?append[Stderr]fntletoutputs_to?appendfnt=std_to?append[Stdout;Stderr]fntletstdin_fromfnt=redirect[Stdin]~flags:[O_RDONLY]~perm:0fntletreplace_io=letprim={Prim.name="replace-io";args=[O("stdin",Std_io.sexp_of_t,Stdin);O("stdout",Std_io.sexp_of_t,Stdout);O("stderr",Std_io.sexp_of_t,Stderr)];result=Env;run=funenvstdinstdoutstderr->Env.set_stdiosenv~stdin:(Env.get_stdioenvstdin)~stdout:(Env.get_stdioenvstdout)~stderr:(Env.get_stdioenvstderr)}infun?(stdin=Std_io.Stdin)?(stdout=Std_io.Stdout)?(stderr=Std_io.Stderr)k->Env_set{k;prim;args=A3(stdin,stdout,stderr)}letout_to_errt=replace_io~stdout:Stderrtleterr_to_outt=replace_io~stderr:Stdouttletchmod=letprim=Prim.make"chmod"[Asexp_of_string;L("perm",Posixat.File_perm.sexp_of_t)]Unit(funenvfnperm->Env.chmodenvfn~perm)infunpath~perm->pack2primpathpermletchown=letprim=Prim.make"chown"[Asexp_of_string;L("uid",sexp_of_int);L("gid",sexp_of_int)]Unit(funenvfnuidgid->Env.chownenvfn~uid~gid)infunpath~uid~gid->pack3primpathuidgidletmkdir=letprim=Prim.make"mkdir"[O("perm",Posixat.File_perm.sexp_of_t,0o777);O("p",sexp_of_bool,false);Asexp_of_string]Unit(funenvpermppath->Env.mkdirenvpath~perm~p)infun?(perm=0o777)?ppath->pack3primperm(p<>None)pathletrm=letprim=Prim.make"rm"[Asexp_of_string]UnitEnv.rminfunfn->pack1primfnletrmdir=letprim=Prim.make"rmdir"[Asexp_of_string]UnitEnv.rmdirinfunfn->pack1primfnletmkfifo=ifSys.win32||Posixat.has_mkfifoatthenletprim=Prim.make"mkfifo"[O("perm",Posixat.File_perm.sexp_of_t,0o666);Asexp_of_string]Unit(funenvpermpath->Env.mkfifoenvpath~perm)infun?(perm=0o666)path->pack2primpermpathelsefun?(perm=0o666)path->run"/usr/bin/mkfifo"["-m";Printf.sprintf"0o%3o"perm;"--";path]letlink=letprim=Prim.make"link"[Asexp_of_string;Asexp_of_string]UnitEnv.linkinfunoldpathnewpath->pack2primoldpathnewpathletrename=letprim=Prim.make"rename"[Asexp_of_string;Asexp_of_string]UnitEnv.renameinfunoldpathnewpath->pack2primoldpathnewpathletsymlink=letprim=Prim.make"symlink"[Asexp_of_string;Asexp_of_string]UnitEnv.symlinkinfunoldpathnewpath->pack2primoldpathnewpathletstat=letprim=Prim.make"stat"[Asexp_of_string](FPosixat.Stats.sexp_of_t)Env.statinfunpath->pack1primpathletlstat=letprim=Prim.make"lstat"[Asexp_of_string](FPosixat.Stats.sexp_of_t)Env.lstatinfunpath->pack1primpathletreadlink=letprim=Prim.make"readlink"[Asexp_of_string](Fsexp_of_string)Env.readlinkinfunpath->pack1primpathletreaddir=letprim=Prim.make"readdir"[Asexp_of_string](F(sexp_of_listsexp_of_string))Env.readdirinfunpath->pack1primpathletfile_exists=letprim=Prim.make"file-exists"[Asexp_of_string](Fsexp_of_bool)(funenvpath->matchEnv.accessenvpath[F_OK]with|()->true|exception(Unix.Unix_error(ENOENT,_,_))->false)infunpath->pack1primpathletrecrm_rfpath=readdirpath>>=funl->List0.iterl~f:(funfname->(* readdir does not give us "." and ".." *)letfpath=path^/fnameinlstatfpath>>=funstat->matchstat.st_kindwith|S_DIR->rm_rffpath|_->rmfpath)>>=fun()->rmdirpathlettemp_dir_var=ifSys.win32then"TEMP"else"TMPDIR"lettemp_dir_default=ifSys.win32then"."else"/tmp"letget_temp_direnv=matchEnv.get_envenvtemp_dir_varwith|None->temp_dir_default|Somed->dlettemp_dir=letprim=Prim.make"temp-dir"[](Fsexp_of_string)get_temp_dirinpack0primletset_temp_dir=letprim={Prim.name="set-temp-dir";args=[Asexp_of_string];result=Env;run=funenvdir->Env.set_envenvtemp_dir_vardir}infundirk->Env_set{k;prim;args=A1dir}moduleTemp=structletcreatewhatmk=letprim=Prim.make("generate-temporary-"^what)[L("prefix",sexp_of_string);L("suffix",sexp_of_string)](Fsexp_of_string)(funenvprefixsuffix->lettemp_dir=get_temp_direnvinlet(),name=Temp0.create~temp_dir~prefix~suffix~mk:(funfn->mkenvfn)inname)infun~prefix~suffix->pack2primprefixsuffixletcreate_file=create"file"(funenvpath->Env.open_fileenvpath~perm:0o600~flags:[O_WRONLY;O_CREAT;O_EXCL]|>Unix.close)letcreate_dir=create"directory"(funenvpath->Env.mkdirenvpath~perm:0o700)endletwith_temp_file~prefix~suffixf=Temp.create_file~prefix~suffix>>=funfn->protect~finally:(rmfn)(reify_exnffn)letwith_temp_dir~prefix~suffixf=Temp.create_dir~prefix~suffix>>=fundn->protect~finally:(rm_rfdn)(reify_exnfdn)letsexp_of_any_=Sexp.Atom"_"letnew_channel=letprim={Prim.name="new-channel";args=[];result=Fsexp_of_any;run=fun_->Event.new_channel()}inPrim{prim;args=A0()}letsync=letprim={Prim.name="sync";args=[Asexp_of_any];result=Fsexp_of_any;run=fun_ev->Event.syncev}infunev->pack1primevletsleep=letprim=Prim.make"sleep"[Asexp_of_float]Unit(fun_d->Unix.sleepfd)infund->pack1primdmoduleInfix=structincludeInfix0let(|-)ab=pipeablet(|+)ab=pipe_bothabendmoduleList=List0moduleLet_syntax=structletreturn=returnincludeInfixmoduleLet_syntax=structletreturn=returnletbind=bindletmap=mapletboth=forkmoduleOpen_on_rhs=structendendend