123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118(**************************************************************************)(* *)(* OCaml *)(* *)(* Gabriel Scherer, projet Parsifal, INRIA Saclay *)(* *)(* Copyright 2017 Institut National de Recherche en Informatique et *)(* en Automatique. *)(* *)(* 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. *)(* *)(**************************************************************************)typepath=stringtypepath_prefix=stringtypeerror_message=stringleterrorffmt=Printf.ksprintf(funerr->Errorerr)fmtletencode_prefixstr=letbuf=Buffer.create(String.lengthstr)inletpush_char=function|'%'->Buffer.add_stringbuf"%#"|'='->Buffer.add_stringbuf"%+"|':'->Buffer.add_stringbuf"%."|c->Buffer.add_charbufcinString.iterpush_charstr;Buffer.contentsbufletdecode_prefixstr=letbuf=Buffer.create(String.lengthstr)inletrecloopi=ifi>=String.lengthstrthenOk(Buffer.contentsbuf)elsematchstr.[i]with|('='|':')asc->errorf"invalid character '%c' in key or value"c|'%'->letpushc=Buffer.add_charbufc;loop(i+2)inifi+1=String.lengthstrthenerrorf"invalid encoded string %S (trailing '%%')"strelsebeginmatchstr.[i+1]with|'#'->push'%'|'+'->push'='|'.'->push':'|c->errorf"invalid %%-escaped character '%c'"cend|c->Buffer.add_charbufc;loop(i+1)inloop0typepair={target:path_prefix;source:path_prefix}letencode_pair{target;source}=String.concat"="[encode_prefixtarget;encode_prefixsource]letdecode_pairstr=matchString.indexstr'='with|exceptionNot_found->errorf"invalid key/value pair %S, no '=' separator"str|equal_pos->letencoded_target=String.substr0equal_posinletencoded_source=String.substr(equal_pos+1)(String.lengthstr-equal_pos-1)inmatchdecode_prefixencoded_target,decode_prefixencoded_sourcewith|Oktarget,Oksource->Ok{target;source}|((Error_aserr),_)|(_,(Error_aserr))->errtypemap=pairoptionlistletencode_mapmap=letencode_elem=function|None->""|Somepair->encode_pairpairinList.mapencode_elemmap|>String.concat":"letdecode_mapstr=letexceptionShortcutoferror_messageinletdecode_or_empty=function|""->None|pair->beginmatchdecode_pairpairwith|Okstr->Somestr|Errorerr->raise(Shortcuterr)endinletpairs=String.split_on_char':'strinmatchList.mapdecode_or_emptypairswith|exception(Shortcuterr)->Errorerr|map->Okmapletmake_targetpath:pairoption->pathoption=function|None->None|Some{target;source}->letis_prefix=String.lengthsource<=String.lengthpath&&String.equalsource(String.subpath0(String.lengthsource))inifis_prefixthenSome(target^(String.subpath(String.lengthsource)(String.lengthpath-String.lengthsource)))elseNoneletrewrite_firstprefix_mappath=List.find_map(make_targetpath)(List.revprefix_map)letrewrite_allprefix_mappath=List.filter_map(make_targetpath)(List.revprefix_map)letrewriteprefix_mappath=matchrewrite_firstprefix_mappathwith|None->path|Somepath->path