123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478openBaseopenUnixletwith_dirpathf=letdh=opendirpathinExn.protectfdh~finally:(fun_->closedirdh)(* run [f] on files in [path] *)letfold_dirpathinitf=with_dirpath&fundh->letrecloopst=tryletst'=fst(readdirdh)inloopst'with|End_of_file->stinloopinitmoduleInodes=Set.Make(structtypet=int*intletcompare:t->t->int=compareend)moduleFind=structexceptionPruneclasstypepath=objectmethodbase:stringmethoddepth:intmethoddev_inode:(int*int,[`Exnofexn])Vresult.tmethoddir:stringmethodis_dir:boolmethodis_ldir:boolmethodis_reg:boolmethodkind:(Unix.file_kind,[`Exnofexn])Vresult.tmethodlkind:(Unix.file_kind,[`Exnofexn])Vresult.tmethodpath:stringmethodstat:(Unix.stats,[`Exnofexn])Vresult.tmethodlstat:(Unix.stats,[`Exnofexn])Vresult.tendclasspath_~dir~base~depth=letpath=matchFilename.concatdirbasewith|"./."->"."|s->sinobject(self)methoddir=dirmethodbase=basemethodpath=pathmethoddepth:int=depthmethodstat:(_,[`Exnofexn])Vresult.t=tryOk(statpath)withe->Error(`Exne)methodlstat:(_,[`Exnofexn])Vresult.t=tryOk(lstatpath)withe->Error(`Exne)methodkind:(_,[`Exnofexn])Vresult.t=matchself#statwith|Errore->Errore|Okstat->Okstat.st_kindmethodlkind:(_,[`Exnofexn])Vresult.t=matchself#lstatwith|Errore->Errore|Okstat->Okstat.st_kindmethodis_dir=self#kind=OkS_DIRmethodis_ldir=self#lkind=OkS_DIRmethodis_reg=self#kind=OkS_REGmethoddev_inode:(_,[`Exnofexn])Vresult.t=matchself#statwith|Okstat->Ok(stat.st_dev,stat.st_ino)|Errore->Erroreendletprune()=raisePruneletfind?(follow_symlink=false)~ffnames=(* visited cache *)letvisited=refInodes.emptyinletif_not_visited_thenpath~f=matchpath#dev_inodewith|Error_->()|Okinode->ifInodes.meminode!visitedthen()elsebeginvisited:=Inodes.addinode!visited;fpathendinletrecfind_dirpth=tryfpth;letsubdirs=fold_dirpth#path[]&fundirs->function|"."|".."->dirs|name->letpth=newpath_~depth:(pth#depth+1)~dir:pth#path~base:nameiniftryiffollow_symlinkthenpth#is_direlsepth#is_ldirwith_->falsethenpth::dirselsebeginfind_non_dirpth;dirsendinList.iter(if_not_visited_then~f:find_dir)subdirswith|Prune->()andfind_non_dirpath=tryfpathwithPrune->()(* Even if path is a dangling symlink, f path is called *)(* CR jfuruse: if the initial argument contains non existent files,
they reach here. *)inList.iter(funfname->letpath=newpath_~depth:0~dir:(Filename.dirnamefname)~base:(Filename.basenamefname)inifpath#is_dirthenfind_dirpathelsefind_non_dirpath)fnamesletfold?(follow_symlink=false)fnamesinitf=letvisited=refInodes.emptyinletif_not_visited_thenpathstf=matchpath#dev_inodewith|Error_->`Continue,st(* Ignore the error *)|Okinode->ifInodes.meminode!visitedthen`Continue,stelsebeginvisited:=Inodes.addinode!visited;fstpathendinletsplit_non_dirs_and_dirspths=flipList.partitionpths&funpth->not&tryiffollow_symlinkthenpth#is_direlsepth#is_ldirwith_->falseinletget_dirpth=fold_dirpth#path[]&funpths->function|"."|".."->pths|name->letpth=newpath_~depth:(pth#depth+1)~dir:pth#path~base:nameinpth::pthsinletreclooppthsst=letnondirs,dirs=split_non_dirs_and_dirspthsinletrecloopgst=function|[]->`Continue,st|x::xs->matchgstxwith|`Continue,st->loopgstxs|(`Exit,_asres)->resinmatchloopfind_non_dirstnondirswith|`Continue,st->loopfind_dirstdirs|(`Exit,_asres)->resandfind_non_dirstpth=matchif_not_visited_thenpthstfwith|(`Continue|`Prune),st->`Continue,st(* /bin/find -prune is only meaningful aginst directories *)|(`Exit,_asres)->resandfind_dirstpth=matchif_not_visited_thenpthstfwith|(`Exit,_asres)->res|`Prune,st->`Continue,st|`Continue,st->loop(get_dirpth)stinletpths=flipList.mapfnames&funfname->newpath_~depth:0~dir:(Filename.dirnamefname)~base:(Filename.basenamefname)insnd&looppthsinitletfiles?follow_symlinkdirs=with_ref_[](funxs->find?follow_symlink~f:(funp->xs:=p::!xs)dirs)endlettry_set_close_on_execfd=tryset_close_on_execfd;truewithInvalid_argument_->falseletopen_proc_fullcmdargsinputoutputerrortoclose=letcmd=matchcmdargswith|x::_->x|_->invalid_arg"Xunix.gen_open_proc_full"inletcmdargs=Array.of_listcmdargsinletcloexec=List.for_alltry_set_close_on_exectocloseinmatchfork()with0->dup2inputstdin;closeinput;dup2outputstdout;closeoutput;dup2errorstderr;closeerror;ifnotcloexecthenList.iterclosetoclose;begintryexecvpcmdcmdargswith_->exit127end(* never return *)|id->idletopen_process_fullcmdargs=let(in_read,in_write)=pipe()inlet(out_read,out_write)=pipe()inlet(err_read,err_write)=pipe()inletpid=open_proc_fullcmdargsout_readin_writeerr_write[in_read;out_write;err_read]incloseout_read;closein_write;closeerr_write;pid,(in_read,out_write,err_read)letopen_shell_process_fullcmd=open_process_full["/bin/sh";"-c";cmd]letrecwaitpid_non_intrpid=trywaitpid[]pidwithUnix_error(EINTR,_,_)->waitpid_non_intrpidmoduleCommandDeprecated=structtype'aresult=Unix.process_status*'aletfail?name=letname=matchnamewithNone->""|Somen->n^": "infunction|WEXITEDn,_->Exn.failwithf"%sprocess exited with id %d"namen|WSIGNALEDn,_->Exn.failwithf"%sprocess killed by signal %d"namen|WSTOPPEDn,_->Exn.failwithf"%sprocess stopped by signal %d"namenletshould_exit_withn=function|(WEXITEDm,res)whenn=m->Okres|r->Errorrletmust_exit_with?namen=function|(WEXITEDm,res)whenn=m->res|r->fail?namerletfrom_exit?name=function|WEXITEDm,r->m,r|e->fail?nameeletbuf_flush_limit=100000letcommand_auxreadersstat=letread_buflen=4096inletread_buf=Bytes.createread_bufleninlettry_read_linesfdbuf:(stringlist*bool(* eof *))=letread_bytes=trySome(readfdread_buf0read_buflen)with|Unix_error((EAGAIN|EWOULDBLOCK),_,_)->Noneinmatchread_byteswith|None->[],false|Some0->(* eof *)lets=Buffer.contentsbufin(ifs=""then[]else[s]),true|Somelen->letbuffer_old_len=Buffer.lengthbufinBuffer.add_subbytesbufread_buf0len;letpos_in_bufferpos=buffer_old_len+posinletrecget_linesstfrom_in_bufferpos=matchifpos>=lenthenNoneelseXbytes.index_from_toread_bufpos(len-1)'\n'with|None->letrem=Buffer.subbuffrom_in_buffer(Buffer.lengthbuf-from_in_buffer)inBuffer.clearbuf;ifString.lengthrem>buf_flush_limitthenrem::stelsebeginBuffer.add_stringbufrem;stend|Somepos->letnext_from_in_buffer=pos_in_bufferpos+1inletline=Buffer.subbuffrom_in_buffer(next_from_in_buffer-from_in_buffer)inget_lines(line::st)next_from_in_buffer(pos+1)inList.rev(get_lines[]00),falseinletrecloopreadersstat=ifreaders=[]thenstat(* no more reader and no need to loop *)elsebeginletfds=List.map(fun(fd,_,_)->fd)readersinletreadables,_,_=selectfds[][](*?*)(-1.0)(*?*)inletreaders',stat=List.fold_right(fun(fd,buf,fsasreader)(st,stat)->ifnot(List.memfdreadables)then(reader::st,stat)elsebeginletrecloopstat=letlines,is_eof=try_read_linesfdbufiniflines<>[]thenbeginletstat=List.fold_left(funstatline->List.fold_left(funstatf->fstat(`Readline))statfs)statlinesinifnotis_eofthenloopstatelseis_eof,statendelseis_eof,statinmatchloopstatwith|true(*eof*),stat->(* reached eof. remove the reader *)letstat=List.fold_left(funstatf->fstat`EOF)statfsinclosefd;st,stat|false,stat->reader::st,statend)readers([],stat)inloopreaders'statendinloopreadersstatletcommand_wrapper(pid,(out,in_,err))~init:stat~f=tryclosein_;set_nonblockout;set_nonblockerr;letbuf_out=Buffer.createbuf_flush_limitinletbuf_err=Buffer.createbuf_flush_limitinletstat=command_aux[out,buf_out,[funstats->fstat(`Out,s)];err,buf_err,[funstats->fstat(`Err,s)]]statinsnd&waitpid_non_intrpid,statwith|e->(* kill really ? *)killpid9;ignore(waitpid_non_intrpid);raiseetype'stt=init:'st->f:('st->[`Out|`Err]*[`Readofstring|`EOF]->'st)->Unix.process_status*'stletexecvpcmd=command_wrapper(open_process_fullcmd)letshellcmd=command_wrapper(open_shell_process_fullcmd)letfoldcom=comletiter(com:_t)~f=com~init:()~f:(fun()i->fi)letprint?prefixcom=letwith_prefixs=lets=Xstring.chop_eolssinmatchprefixwith|None->s|Somep->p^": "^sinitercom~f:(function|`Err,`Reads->prerr_endline&with_prefixs|`Out,`Reads->print_endline&with_prefixs|_->())letignore_outputcom=itercom~f:(fun_->())letget_stdoutcom=letpst,rev=com~init:[]~f:(funrev->function|`Err,`Reads->prerr_endline&Xstring.chop_eolss;rev|`Out,`Reads->s::rev|_->rev)inpst,List.revrevletget_allcom=letpst,rev=com~init:[]~f:(funrev->function|_,`Reads->s::rev|_->rev)inpst,List.revrevendletgen_timedgetminusfv=lett1=get()inletres=fvinlett2=get()inres,minust2t1lettimedfv=gen_timedUnix.gettimeofday(-.)fvmoduleProcess_times=structtypet=process_timeslet(-)pt1pt2={tms_utime=pt1.tms_utime-.pt2.tms_utime;tms_stime=pt1.tms_stime-.pt2.tms_stime;tms_cutime=pt1.tms_utime-.pt2.tms_cutime;tms_cstime=pt1.tms_utime-.pt2.tms_cstime;}lettimedfv=gen_timedUnix.times(-)fvendletrecmkdir?(perm=0o700)?(recursive=false)s=matchFile.Test._d'swith|ErrorENOENT->beginmatchifrecursivethenbeginmatchswith|"."|"/"->Ok()|_->beginmatchmkdir~perm~recursive(Filename.dirnames)with|Ok()|Error(_,`Already_exists_)->Ok()|err->errendendelseOk()with|Ok()->begintryUnix.mkdirsperm;(* CR jfuruse: use umask? *)Ok()with|Unix_error(e,_,_)->Error(s,`Unixe)end|err->errend|Ok(`TRUEst)->Error(s,`Already_existsst)(* CR jfuruse: perm check ? *)|Ok(`FALSEst)->Error(s,`Not_a_directoryst)|Errore->Error(s,`Unixe)letmkdtemptemplate=matchXstring.is_postfix'"XXXXXX"templatewith|None->Exn.invalid_argf"Unix.mkdtemp must take an argument whose postfix is \"XXXXXX\""|Someprefix->letrecfind()=letd=!%"%s%06d"prefix&Random.int1000000inifSys.file_existsdthenfind()elsedinletd=find()inUnix.mkdird0o700;dletwith_dtemptemplatef=letd=mkdtemptemplateinExn.protectfd~finally:(fun_->ifksprintfSys.command"/bin/rm -rf %s"d<>0thenExn.failwithf"Unix.with_dtemp: cleaning tempdir %s failed"d)letwith_chdir?(at_failure=(funexn->raiseexn))dirf=letcwd=Unix.getcwd()inmatchExn.catchUnix.chdirdirwith|Error(`Exnexn)->at_failureexn|Ok()->Exn.protectf()~finally:(fun()->Unix.chdircwd)lettimed_messagemesfv=prerr_endline(mes^"...");letres,secs=timed(Exn.catchf)vinmatchreswith|Okv->!!%"%s: done (%.1f secs)@."messecs;v|Error(`Exne)->!!%"%s: raised an exception (%.1f secs)@."messecs;raiseemoduleStdlib=structlettimed_message=timed_messageend