123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583(*---------------------------------------------------------------------------
Copyright (c) 2019 The b0 programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)openB0_stdopenResult.Syntaxletparse_changeslines=tryletparse_linel=matchString.cut_left~sep:" "lwith|None->Fmt.failwith"%S: can't parse log line"l|Somecut->cutinOk(List.(rev@@rev_mapparse_linelines))withFailuree->Erroreletparse_ptimeptime=tryOk(int_of_stringptime)with|Failure_->Fmt.error"Could not parse timestamp from %S"ptimeletparse_files~erro=letfile_of_stringl=Fpath.of_stringl|>Result.to_failureintryOk(List.mapfile_of_string(String.cuts_left~sep:"\n"o))with|Failuree->Fmt.error"%s: %s"erreletdirtifyid=id^"-dirty"(* VCS *)typecommit_ish=stringtypecommit_id=stringtypetag=stringtypekind=Git|Hgletpp_kindppfk=Fmt.stringppf(matchkwithGit->"git"|Hg->"hg")typer={kind:kind;cmd:Cmd.t;repo_dir:Fpath.t;work_dir:Fpath.t}moduletypeVCS=sigvalfind:?dir:Fpath.t->unit->(roption,string)resultvalrepo_cmd:r->Cmd.t(* Commits *)valcommit_id:r->dirty_mark:bool->commit_ish->(commit_id,string)resultvalcommit_ptime_s:r->commit_ish->(int,string)resultvaldescribe:r->dirty_mark:bool->commit_ish->(string,string)resultvalchanges:r->after:commit_ish->until:commit_ish->((string*string)list,string)resultvaltracked_files:r->tree_ish:string->(Fpath.tlist,string)resultvalcommit_files:?stdout:Os.Cmd.stdo->?stderr:Os.Cmd.stdo->?msg:string->r->Fpath.tlist->(unit,string)result(* Working directory *)valis_dirty:r->(bool,string)resultvalfile_is_dirty:r->Fpath.t->(bool,string)resultvalcheckout:?and_branch:string->r->commit_ish->(unit,string)resultvallocal_clone:r->dir:Fpath.t->(r,string)result(* Tags *)valtags:r->(taglist,string)resultvaltag:?msg:string->r->force:bool->sign:bool->commit_ish->tag->(unit,string)resultvaldelete_tag:r->tag->(unit,string)resultvallatest_tag:r->commit_ish->(tagoption,string)resultendtypet=r*(moduleVCS)leterrcmdstatus=Fmt.error"%a"Os.Cmd.pp_cmd_status(cmd,status)letrun?stderrrcmd_args=letvcs=Cmd.(r.cmd%%cmd_args)inResult.bind(Os.Cmd.run_status_out?stderr~trim:truevcs)@@function|`Exited0,v->Okv|status,_->errvcsstatusletrun_status?stdout?stderrrcmd_args=letvcs=Cmd.(r.cmd%%cmd_args)inResult.bind(Os.Cmd.run_status?stdout?stderrvcs)@@function|`Exited0->Ok()|status->errvcsstatus(* Git support *)moduleGit_vcs=structletgit=lazy(Os.Cmd.find(Cmd.atom"git"))letrepo_cmdgitrepo_dirwork_dir=Cmd.(git%"--git-dir"%%pathrepo_dir%"--work-tree"%%pathwork_dir)letfind?dir()=letget_vcs_pathcmd=letstderr=`StdoOs.Cmd.out_nullinlet*status,repo_dir=Os.Cmd.run_status_out~stderr~trim:truecmdinmatchstatuswith|`Exited0->ifrepo_dir=""(* that seems to be returned on bare repos *)thenOkNoneelseResult.mapOption.some(Fpath.of_stringrepo_dir)|status->OkNoneinlet*git=Lazy.forcegitinmatchgitwith|None->OkNone|Somegit->letcwd=matchdirwith|Somed->Cmd.(atom"-C"%%pathd)|None->Cmd.emptyinletrepo_dir=Cmd.(git%%cwd%"rev-parse"%"--absolute-git-dir")inletwork_dir=Cmd.(git%%cwd%"rev-parse"%"--show-toplevel")inlet*repo_dir=get_vcs_pathrepo_dirinmatchrepo_dirwith|None->OkNone|Somerepo_dir->let*work_dir=get_vcs_pathwork_dirinletdefault=Fpath.null(* for bare repos *)inletwork_dir=Option.value~defaultwork_dirinletcmd=repo_cmdgitrepo_dirwork_dirinOk(Some{kind=Git;cmd;repo_dir;work_dir})letrepo_cmdr=r.cmdletis_dirtyr=letstderr=`StdoOs.Cmd.out_nullinletstatus=Cmd.(atom"status"%"--porcelain")inResult.bind(run~stderrrstatus)@@function|""->Okfalse|_->Oktruelethandle_dirt~dirty_markcommit_ishrid=matchdirty_mark&&commit_ish="HEAD"with|false->Okid|true->Result.bind(is_dirtyr)@@function|true->Ok(dirtifyid)|false->Okid(* Commits *)letcommit_idr~dirty_markcommit_ish=lettyped_commit_ish=Fmt.str"%s^{commit}"commit_ishinletargs=Cmd.(atom"rev-parse"%"--verify"%typed_commit_ish)inResult.bind(runrargs)(handle_dirt~dirty_markcommit_ishr)letcommit_ptime_srcommit_ish=letargs=Cmd.(atom"show"%"-s"%"--format=%ct"%commit_ish)inResult.bind(runrargs)parse_ptimeletchangesr~after~until=letrange=ifafter=""thenuntilelseFmt.str"%s..%s"afteruntilinletargs=Cmd.(atom"log"%"--oneline"%"--no-decorate"%range)inResult.bind(runrargs)@@funo->parse_changes(String.cuts_left~sep:"\n"o)lettracked_filesr~tree_ish=letargs=Cmd.(atom"ls-tree"%"--name-only"%"-r"%tree_ish)inResult.bind(runrargs)(parse_files~err:"tracked files")letcommit_files?stdout?stderr?msg:mrfiles=letmsg=matchmwithNone->Cmd.empty|Somem->Cmd.(atom"-m"%m)inletargs=Cmd.(atom"commit"%%msg%%pathsfiles)inrun_status?stdout?stderrrargs(* Working directory *)letfile_is_dirtyrfile=letstderr=Os.Cmd.out_nullinletcmd=Cmd.(r.cmd%"diff-index"%"--quiet"%"HEAD"%%pathfile)inResult.bind(Os.Cmd.run_status~stderrcmd)@@function|`Exited0->Okfalse|`Exited1->Oktrue|_asstatus->errcmdstatusletcheckout?and_branch:brcommit_ish=letb=matchbwithNone->Cmd.empty|Someb->Cmd.(atom"-b"%b)inletargs=Cmd.(atom"checkout"%"--quiet"%%b%commit_ish)inrun_statusrargsletlocal_cloner~dir=letargs=Cmd.(atom"clone"%"--local"%%pathr.repo_dir%%pathdir)inResult.bind(run_statusrargs)@@fun()->Result.bind(find~dir())@@function|Somer->Okr|None->Fmt.error"%a: no clone found"Fpath.pp_quoteddir(* Tags *)lettagsr=letargs=Cmd.(atom"tag"%"--list")inResult.bind(runrargs)@@funo->Ok(String.cuts_left~sep:"\n"o)lettag?msg:mr~force~signcommit_ishtag=letmsg=matchmwithNone->Cmd.empty|Somem->Cmd.(atom"-m"%m)inletflags=Cmd.(if'force(atom"-f")%%if'sign(atom"-s"))inletargs=Cmd.(atom"tag"%"-a"%%flags%%msg%tag%commit_ish)inrun_statusrargsletdelete_tagrtag=letargs=Cmd.(atom"tag"%"-d"%tag)inrun_statusrargsletdescriber~dirty_markcommit_ish=letargs=Cmd.(atom"describe"%"--always"%commit_ish)inResult.bind(runrargs)(handle_dirt~dirty_markcommit_ishr)letlatest_tagrcommit_ish=letstderr=`StdoOs.Cmd.out_nullinletargs=Cmd.(atom"describe"%"--abbrev=0"%commit_ish)inletcmd=Cmd.(r.cmd%%args)inResult.bind(Os.Cmd.run_status_out~stderr~trim:truecmd)@@function|`Exited0,v->Ok(Somev)|`Exited128,_->OkNone|status,_->errcmdstatusend(* Hg support *)moduleHg_vcs=structlethg=lazy(Os.Cmd.find(Cmd.atom"hg"))letrepo_cmdhgrepo_dir=Cmd.(hg%"--repository"%%pathrepo_dir)letfind?dir()=matchdirwith|Somedir->beginletwork_dir=dirinletrepo_dir=Fpath.(work_dir/".hg")inResult.bind(Os.Dir.existsrepo_dir)@@function|false->OkNone|true->Result.bind(Lazy.forcehg)@@function|Somehg->Ok(Some{kind=Hg;cmd=repo_cmdhgrepo_dir;repo_dir;work_dir})|None->Fmt.error"%a: repo found but no hg executable in PATH"Fpath.pp_quotedrepo_dirend|None->Result.bind(Lazy.forcehg)@@function|None->OkNone|Somehg->lethg_root=Cmd.(hg%"root")inletstderr=`StdoOs.Cmd.out_nullinletres=Os.Cmd.run_status_out~stderr~trim:truehg_rootinResult.bindres@@function|`Exited0,repo_dir->Result.bind(Fpath.of_stringrepo_dir)@@funrepo_dir->letwork_dir=Fpath.parentrepo_dirinOk(Some{kind=Hg;cmd=repo_cmdhgrepo_dir;repo_dir;work_dir})|_->OkNoneletrepo_cmdr=r.cmdletrevisioncommit_ish=matchcommit_ishwith"HEAD"->"tip"|c->cletidr~rev=let*id=runrCmd.(atom"id"%"-i"%"--rev"%rev)inletlen=String.lengthidinletis_dirty=String.lengthid>0&&id.[len-1]='+'inletid=ifis_dirtythenString.subid0(len-1)elseidinOk(id,is_dirty)letis_dirtyr=Result.bind(idr~rev:"tip")@@function(_,is_dirty)->Okis_dirtylethandle_dirt~dirty_markcommit_ishrid=matchdirty_mark&&commit_ish="HEAD"with|false->Okid|true->Result.bind(is_dirtyr)@@function|true->Ok(dirtifyid)|false->Okid(* Commits *)letcommit_idr~dirty_markcommit_ish=letrev=revisioncommit_ishinResult.bind(idr~rev)@@fun(id,_)->handle_dirt~dirty_markcommit_ishridletcommit_ptime_srcommit_ish=letrev=revisioncommit_ishinletdate="{date(date, \"%s\")}"inletargs=Cmd.(atom"log"%"--template"%date%"--rev"%rev)inResult.bind(runrargs)parse_ptimeletchangesr~after~until=letafter=revisionafteranduntil=revisionuntilinletrev=Fmt.str"%s::%s"afteruntilinlettemplate="{node|short} {desc|firstline}\\n"inletargs=Cmd.(atom"log"%"--template"%template%"--rev"%rev)inResult.bind(runrargs)@@funo->Result.bind(parse_changes(String.cuts_left~sep:"\n"o))@@function|[]->Ok[]|after::rest->Ok(List.revrest)(* hg order is reverse from git *)lettracked_filesr~tree_ish=letrev=revisiontree_ishinletargs=Cmd.(atom"manifest"%"--rev"%rev)inResult.bind(runrargs)(parse_files~err:"tracked files")letcommit_files?stdout?stderr?msg:mrfiles=letmsg=matchmwithNone->Cmd.empty|Somem->Cmd.(atom"-m"%m)inletargs=Cmd.(atom"commit"%%msg%%pathsfiles)inrun_status?stdout?stderrrargs(* Working directory *)letfile_is_dirtyrfile=letargs=Cmd.(atom"status"%%pathfile)inResult.bind(runrargs)@@function""->Okfalse|_->Oktrueletcheckout?and_branch:branchrcommit_ish=letrev=revisioncommit_ishinletargs=Cmd.(atom"update"%"--rev"%rev)inResult.bind(runrargs)@@fun_->matchbranchwith|None->Ok()|Somebranch->letargs=Cmd.(atom"branch"%branch)inResult.bind(runrargs)@@fun_->Ok()letlocal_cloner~dir=letargs=Cmd.(atom"clone"%%pathr.repo_dir%%pathdir)inResult.bind(run_statusrargs)@@fun_->Result.bind(find~dir())@@function|Somer->Okr|None->Fmt.error"%a: no clone found"Fpath.pp_quoteddir(* Tags *)lettagsr=letargs=Cmd.(atom"tags"%"--quiet"(* sic *))inResult.bind(runrargs)@@funo->Ok(String.cuts_left~sep:"\n"o)lettag?msg:mr~force~signcommit_ishtag=ifsignthenError"Tag signing is not supported by hg"elseletrev=revisioncommit_ishinletmsg=matchmwithNone->Cmd.empty|Somem->Cmd.(atom"-m"%m)inletmay_force=Cmd.(if'force(atom"-f"))inletargs=Cmd.(atom"tag"%%may_force%%msg%"--rev"%rev%tag)inrun_statusrargsletdelete_tagrtag=letargs=Cmd.(atom"tag"%"--remove"%tag)inrun_statusrargsletdescriber~dirty_markcommit_ish=letget_distances=tryOk(int_of_strings)with|Failure_->Fmt.error"%a: Could not parse hg tag distance."Fpath.pp_quotedr.repo_dirinletrev=revisioncommit_ishinletparentt=Cmd.(atom"parent"%"--rev"%rev%"--template"%t)inResult.bind(runr(parent"{latesttagdistance}"))@@fundist->Result.bind(get_distancedist)@@fundist->lettemplate=matchdistwith|1->"{latesttag}"|n->"{latesttag}-{latesttagdistance}-{node|short}"inResult.bind(runr(parenttemplate))(handle_dirt~dirty_markcommit_ishr)letlatest_tagrcommit_ish=letrev=revisioncommit_ishinletargs=Cmd.(atom"parent"%"--rev"%rev%"--template"%"{latesttag}")inResult.mapOption.some(runrargs)endletkind(r,_)=r.kindletrepo_dir(r,_)=r.repo_dirletwork_dir(r,_)=r.work_dirletrepo_cmd(r,(moduleVcs:VCS))=Vcs.repo_cmdrletppppf(r,_)=Fmt.pfppf"(%a, %a)"pp_kindr.kindFpath.pp_quotedr.repo_dir(* Finding reposistories *)letfind?dir()=Result.bind(Git_vcs.find?dir())@@function|Somer->Ok(Some(r,(moduleGit_vcs:VCS)))|None->Result.bind(Hg_vcs.find())@@function|Somer->Ok(Some(r,(moduleHg_vcs:VCS)))|None->OkNoneletget?dir()=Result.bind(find?dir())@@function|Somer->Okr|None->letdir=matchdirwithNone->Os.Dir.cwd()|Somedir->OkdirinResult.binddir@@(Fmt.error"%a: No VCS repository found"Fpath.pp_quoted)(* Commits *)lethead="HEAD"letcommit_id(r,(moduleVcs:VCS))=Vcs.commit_idrletcommit_ptime_s(r,(moduleVcs:VCS))=Vcs.commit_ptime_srletchanges(r,(moduleVcs:VCS))=Vcs.changesrlettracked_files(r,(moduleVcs:VCS))=Vcs.tracked_filesrletcommit_files?stdout?stderr?msg(r,(moduleVcs:VCS))=Vcs.commit_files?stdout?stderr?msgr(* Working directory *)letis_dirty(r,(moduleVcs:VCS))=Vcs.is_dirtyrletnot_dirtyr=Result.bind(is_dirtyr)@@function|false->Ok()|true->Error"The VCS repository is dirty, commit or stash your changes."letfile_is_dirty(r,(moduleVcs:VCS))=Vcs.file_is_dirtyrletcheckout?and_branch(r,(moduleVcs:VCS))=Vcs.checkout?and_branchrletlocal_clone(r,(moduleVcs:VCS))~dir=Result.bind(Vcs.local_cloner~dir)@@funr->Ok(r,(moduleVcs:VCS))(* Tags *)lettags(r,(moduleVcs:VCS))=Vcs.tagsrlettag?msg(r,(moduleVcs:VCS))=Vcs.tag?msgrletdelete_tag(r,(moduleVcs:VCS))=Vcs.delete_tagrletdescribe(r,(moduleVcs:VCS))=Vcs.describerletlatest_tag(r,(moduleVcs:VCS))=Vcs.latest_tagr(* Git specific *)moduleGit=structletget_cmd?search?(cmd=Cmd.atom"git")()=Os.Cmd.get?searchcmdletfind?dir()=let*vcs=Git_vcs.find?dir()inOk(Option.map(funr->r,(moduleGit_vcs:VCS))vcs)letcheck_kind(r,_)=matchr.kindwith|Hg->Fmt.error"%a: not a git repository"Fpath.pp_quotedr.repo_dir|Git->Ok()(* Branches *)typeremote=stringtypebranch=stringletpp_branch=Fmt.tty_string[`Fg`Green;`Bold;]letpp_remote_branch=Fmt.tty[`Fg`Red;`Bold](funppf(r,b)->Fmt.pfppf"%s/%s"rb)letremote_branch_exists?env(r,_)~remote~branch=letui=Cmd.(atom"--exit-code"%"--quiet")inletstdout=Os.Cmd.out_null(* not so quiet *)inletls_remote=Cmd.(r.cmd%"ls-remote"%%ui%remote%branch)inResult.bind(Os.Cmd.run_status?env~stdoutls_remote)@@function|`Exited0->Oktrue|`Exited2->Okfalse|status->Fmt.error"%a"Os.Cmd.pp_cmd_status(ls_remote,status)letremote_branch_fetch?env?stdout?stderr(r,_)~remote~branch=letfetch=Cmd.(r.cmd%"fetch"%remote%branch)inOs.Cmd.run?env?stdout?stderrfetchletremote_branch_push?env?stdout?stderr(r,_)~force~src~remote~dst=letrefspec=Fmt.str"%s:%s"srcdstinletforce=Cmd.(if'force(atom"--force"))inletpush=Cmd.(r.cmd%"push"%%force%remote%refspec)inOs.Cmd.run?env?stdout?stderrpushletremote_branch_delete?env?stdout?stderr(r,_)~force~remote~branch=letforce=Cmd.(if'force(atom"--force"))inletpush=Cmd.(r.cmd%"push"%"--delete"%%force%remote%branch)inOs.Cmd.run?env?stdout?stderrpushletbranch_delete?env?stdout?stderr(r,_)~force~branch=letforce=Cmd.(if'force(atom"--force"))inletdel=Cmd.(r.cmd%"branch"%"-d"%%force%branch)inOs.Cmd.run?env?stdout?stderrdel(* Transient checkouts *)lettransient_checkout?stdout?stderr(r,_)~force~branchdir=function|Somecish->letb=ifforcethen"-B"else"-b"inletadd=Cmd.(atom"worktree"%"add"%b%branch%%pathdir%cish)inlet*()=Os.Cmd.run?stdout?stderrCmd.(r.cmd%%add)inget~dir()|None->(* worktree add has no --orphan option... The following is a contrived
way of getting an empty branch. *)letadd=Cmd.(r.cmd%"worktree"%"add"%"--detach"%%pathdir)inlet*()=Os.Cmd.run?stdout?stderraddinlet*repo=get~dir()inletorphan=Cmd.(r.cmd%"checkout"%"--orphan"%branch)inlet*()=Os.Cmd.run?stdout?stderrorphaninlet*()=Os.Cmd.run?stdout?stderrCmd.(r.cmd%"rm"%"-rf"%".")inOkrepolettransient_checkout_delete?stdout?stderr(r,_)~force=letforce=Cmd.(if'force(atom"--force"))inletrem=Cmd.(atom"worktree"%"remove"%%force%%pathr.work_dir)inOs.Cmd.run?stdout?stderrCmd.(r.cmd%%rem)letwith_transient_checkout?stdout?stderr?dirr~force~branchcishf=let*dir=matchdirwithNone->Os.Path.tmp()|Somed->Okdinlet*tr=transient_checkoutr?stdout?stderr~force~branchdircishinletcleanup()=Log.if_error~use:()(transient_checkout_delete?stdout?stderrtr~force)intryOs.Exit.on_sigint~hook:cleanup@@fun()->letv=ftrin(cleanup();Okv)withe->cleanup();raisee(* Working dir *)letadd?stdout?stderr(r,_)~forcefs=letforce=Cmd.(if'force(atom"--force"))inletadd=Cmd.(r.cmd%"add"%%force%"--"%%pathsfs)inOs.Cmd.run?stdout?stderraddlethas_staged_changes(r,_)=letdiff=Cmd.(r.cmd%"diff"%"--exit-code"%"--quiet"%"--staged")inResult.bind(Os.Cmd.run_statusdiff)@@function|`Exited0->Okfalse|`Exited1->Oktrue|status->Fmt.error"%a"Os.Cmd.pp_cmd_status(diff,status)letcommit?stdout?stderr?(sign=false)?(reset_author=false)?(amend=false)?msg:m(r,_)=letsign=Cmd.(if'sign(atom"--signoff"))inletreset_author=Cmd.(if'reset_author(atom"--reset-author"))inletamend=Cmd.(if'amend(atom"--amend"))inletmsg=matchmwithNone->Cmd.empty|Somem->Cmd.(atom"-m"%m)inletcommit=Cmd.(atom"commit"%%sign%%reset_author%%amend%%msg)inOs.Cmd.run?stdout?stderrCmd.(r.cmd%%commit)letcommit_exists(r,_)cish=letreflog=Cmd.(r.cmd%"reflog"%"exists"%cish)inResult.bind(Os.Cmd.run_statusreflog)@@function|`Exited0->Oktrue|`Exited1->Okfalse|status->Fmt.error"%a"Os.Cmd.pp_cmd_status(reflog,status)letrm?stdout?stderr(r,_)~force~recurse~ignore_unmatchfiles=letforce=Cmd.(if'force(atom"--force"))inletrecurse=Cmd.(if'recurse(atom"-r"))inletign=Cmd.(if'ignore_unmatch(atom"--ignore-unmatch"))inletrm=Cmd.(r.cmd%"rm"%%force%%recurse%%ign%%pathsfiles)inOs.Cmd.run?stdout?stderrrmendmoduleHg=structletget_cmd?search?(cmd=Cmd.atom"hg")()=Os.Cmd.get?searchcmdletfind?dir()=let*vcs=Hg_vcs.find?dir()inOk(Option.map(funr->r,(moduleHg_vcs:VCS))vcs)end(*---------------------------------------------------------------------------
Copyright (c) 2019 The b0 programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)