123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143(******************************************************************************)(* This file is a modified version of the one found in the Dose library *)(* See https://gitlab.com/irill/dose3/ *)(* The original can be found in src/versioning/debian.ml *)(* *)(* Copyright (C) 2011 Ralf Treinen <ralf.treinen@pps.jussieu.fr> *)(* *)(* 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. *)(* *)(* Work developed with the support of the Mancoosi Project *)(* http://www.mancoosi.org *)(* *)(******************************************************************************)letis_digit=function|'0'..'9'->true|_->false(* [skip_while_from i f w m] yields the index of the leftmost character
* in the string [s], starting from [i], and ending at [m], that does
* not satisfy the predicate [f], or [length w] if no such index exists. *)letrecskip_while_fromifwm=if(i:int)=mthenielseiffw.[i]thenskip_while_from(i+1)fwmelsei(* splits a version into (prefix,revision). The revision starts on the
* right-most occurrence of '-', or is empty in case the version does
* not contain '-'. *)letextract_revisionx=tryletdi=String.rindexx'-'inletbefore=String.subx0diinletafter=String.subx(di+1)(String.lengthx-di-1)in(before,after)with|Not_found->(x,"")(* character comparison uses a modified character ordering: '~' first,
then letters, then anything else *)letcompare_charsc1c2=matchc1with|'~'->(matchc2with|'~'->0|_->-1)|'a'..'z'|'A'..'Z'->(matchc2with|'~'->1|'a'..'z'|'A'..'Z'->Char.comparec1c2|_->-1)|_->(matchc2with|'~'|'a'..'z'|'A'..'Z'->1|_->Char.comparec1c2)(* return the first index of x, starting from xi, of a nun-null
* character in x. or (length x) in case x contains only 0's starting
* from xi on. *)letskip_zerosxxixl=skip_while_fromxi(func->c='0')xxl(* compare versions chunks, that is parts of version strings that are
* epoch, upstream version, or revisision. Alternates string comparison
* and numerical comparison. *)letcompare_chunksxy=(* x and y may be empty *)letxl=String.lengthxandyl=String.lengthyinletrecloop_lexicalxiyi=assert((xi:int)<=xl&&(yi:int)<=yl);match((xi:int)=xl,(yi:int)=yl)with(* which of x and y is exhausted? *)|true,true->0|true,false->(* if y continues numerically than we have to continue by
* comparing numerically. In this case the x part is
* interpreted as 0 (since empty). If the y part consists
* only of 0's then both parts are equal, otherwise the y
* part is larger. If y continues non-numerically then y is
* larger anyway, so we only have to skip 0's in the y part
* and check whether this exhausts the y part. *)letys=skip_zerosyyiylinif(ys:int)=ylthen0elseify.[ys]='~'then1else-1|false,true->(* symmetric to the preceding case *)letxs=skip_zerosxxixlinif(xs:int)=xlthen0elseifx.[xs]='~'then-1else1|false,false->(* which of x and y continues numerically? *)match(is_digitx.[xi],is_digity.[yi])with|true,true->(* both continue numerically. Skip leading zeros in the
* remaining parts, and then continue by
* comparing numerically. *)compare_numerical(skip_zerosxxixl)(skip_zerosyyiyl)|true,false->(* '~' is smaller than any numeric part *)ify.[yi]='~'then1else-1|false,true->(* '~' is smaller than any numeric part *)ifx.[xi]='~'then-1else1|false,false->(* continue comparing lexically *)letcomp=compare_charsx.[xi]y.[yi]inifcomp=0thenloop_lexical(xi+1)(yi+1)elsecompandcompare_numericalxiyi=assert((xi:int)=xl||((xi:int)<xl&&x.[xi]<>'0'));(* leading zeros have been stripped *)assert((yi:int)=yl||((yi:int)<yl&&y.[yi]<>'0'));(* leading zeros have been stripped *)letxn=skip_while_fromxiis_digitxxl(* length of numerical part *)andyn=skip_while_fromyiis_digityyl(* length of numerical part *)inletcomp=Int.compare(xn-xi)(yn-yi)inifcomp=0then(* both numerical parts have same length: compare digit by digit *)loop_numericalxiyiynelse(* if one numerical part is longer than the other we have found the
* answer since leading 0 have been striped when switching
* to numerical comparison. *)compandloop_numericalxiyiyn=assert((xi:int)<=xl&&(yi:int)<=yn&&(yn:int)<=yl);(* invariant: the two numerical parts that remain to compare are
of the same length *)if(yi:int)=ynthen(* both numerical parts are exhausted, we switch to lexical
comparison *)loop_lexicalxiyielse(* both numerical parts are not exhausted, we continue comparing
digit by digit *)letcomp=Char.comparex.[xi]y.[yi]inifcomp=0thenloop_numerical(xi+1)(yi+1)ynelsecompinloop_lexical00letcompare(x:string)(y:string)=letnormalize_comp_resultx=ifx=0then0elseifx<0then-1else1inif(x:string)=ythen0elselet(u1,r1)=extract_revisionxand(u2,r2)=extract_revisionyinletu_comp=compare_chunksu1u2inifu_comp<>0thennormalize_comp_resultu_compelsenormalize_comp_result(compare_chunksr1r2)letequal(x:string)(y:string)=if(x:string)=ythentrueelse(comparexy)=0