123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508(*{{{ Copyright (c) 2015 David Sheets <sheets@alum.mit.edu>
*
* Permission to use, copy, modify, and 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.
*
}}}*)openSexplib.Std(* From <https://tools.ietf.org/html/rfc5988> *)moduleRel=structtypet=|ExtensionofUri.t|Alternate|Appendix|Bookmark|Chapter|Contents|Copyright|Current|Described_by|Edit|Edit_media|Enclosure|First|Glossary|Help|Hub|Index|Last|Latest_version|License|Next|Next_archive|Payment|Predecessor_version|Prev|Prev_archive|Related|Replies|Section|Self|Service|Start|Stylesheet|Subsection|Successor_version|Up|Version_history|Via|Working_copy|Working_copy_of[@@derivingsexp]letextensionuri=Extensionuriletalternate=Alternateletappendix=Appendixletbookmark=Bookmarkletchapter=Chapterletcontents=Contentsletcopyright=Copyrightletcurrent=Currentletdescribed_by=Described_byletedit=Editletedit_media=Edit_medialetenclosure=Enclosureletfirst=Firstletglossary=Glossarylethelp=Helplethub=Hubletindex=Indexletlast=Lastletlatest_version=Latest_versionletlicense=Licenseletnext=Nextletnext_archive=Next_archiveletpayment=Paymentletpredecessor_version=Predecessor_versionletprev=Prevletprev_archive=Prev_archiveletrelated=Relatedletreplies=Repliesletsection=Sectionletself=Selfletservice=Serviceletstart=Startletstylesheet=Stylesheetletsubsection=Subsectionletsuccessor_version=Successor_versionletup=Upletversion_history=Version_historyletvia=Vialetworking_copy=Working_copyletworking_copy_of=Working_copy_ofendmoduleLanguage=structtypet=string[@@derivingsexp]letto_stringx=xletof_stringx=xendmoduleCharset=structtypet=string[@@derivingsexp]letto_stringx=xletof_stringx=xendmoduleExt=structtype'at={charset:Charset.t;language:Language.t;value:'a;}[@@derivingsexp,fields]letmake?(charset="")?(language="")value={charset;language;value}letmapfx={xwithvalue=fx.value}endmoduleArc=structtypet={reverse:bool;relation:Rel.tlist;hreflang:stringoption;media:stringoption;title:stringoption;title_ext:stringExt.toption;media_type:(string*string)option;extensions:(string*string)list;extension_exts:(string*stringExt.t)list;}[@@derivingsexp]letempty={reverse=false;relation=[];hreflang=None;media=None;title=None;title_ext=None;media_type=None;extensions=[];extension_exts=[];}endtypet={context:Uri.t;arc:Arc.t;target:Uri.t;}[@@derivingsexp](* TODO: this could be replaced with empty t/arc fupdate *)typeparam=|RelofRel.tlist|AnchorofUri.t|RevofRel.tlist|HreflangofLanguage.t|Mediaofstring|Titleofstring|StarofparamExt.t|Typeof(string*string)|Link_extensionofstring*stringletuntilsstartcl=letnextl=List.map(func->letpattern=String.make1cinStringext.find_from~starts~pattern)clinletmin=List.fold_left(funmin_opti_opt->matchmin_opt,i_optwith|None,None->None|Somei,None|None,Somei->Somei|Somei,Somej->Some(minij))Nonenextlinmatchminwith|None->Stringext.string_aftersstart,String.lengths|Somei->String.subsstart(i-start),iletstring_of_rel=Rel.(function|Alternate->"alternate"|Appendix->"appendix"|Bookmark->"bookmark"|Chapter->"chapter"|Contents->"contents"|Copyright->"copyright"|Current->"current"|Described_by->"describedby"|Edit->"edit"|Edit_media->"edit-media"|Enclosure->"enclosure"|First->"first"|Glossary->"glossary"|Help->"help"|Hub->"hub"|Index->"index"|Last->"last"|Latest_version->"latest-version"|License->"license"|Next->"next"|Next_archive->"next-archive"|Payment->"payment"|Predecessor_version->"predecessor-version"|Prev->"prev"|Prev_archive->"prev-archive"|Related->"related"|Replies->"replies"|Section->"section"|Self->"self"|Service->"service"|Start->"start"|Stylesheet->"stylesheet"|Subsection->"subsection"|Successor_version->"successor-version"|Up->"up"|Version_history->"version-history"|Via->"via"|Working_copy->"working-copy"|Working_copy_of->"working-copy-of"|Extensionuri->Uri.to_stringuri)letrel_of_strings=Rel.(tryignore(String.indexs':');Extension(Uri.of_strings)withNot_found->matchswith|"alternate"->Alternate|"appendix"->Appendix|"bookmark"->Bookmark|"chapter"->Chapter|"contents"->Contents|"copyright"->Copyright|"current"->Current|"describedby"->Described_by|"edit"->Edit|"edit-media"->Edit_media|"enclosure"->Enclosure|"first"->First|"glossary"->Glossary|"help"->Help|"hub"->Hub|"index"->Index|"last"->Last|"latest-version"->Latest_version|"license"->License|"next"->Next|"next-archive"->Next_archive|"payment"->Payment|"predecessor-version"->Predecessor_version|"prev"|"previous"->Prev|"prev-archive"->Prev_archive|"related"->Related|"replies"->Replies|"section"->Section|"self"->Self|"service"->Service|"start"->Start|"stylesheet"->Stylesheet|"subsection"->Subsection|"successor-version"->Successor_version|"up"->Up|"version-history"->Version_history|"via"->Via|"working-copy"->Working_copy|"working-copy-of"->Working_copy_of|_->Extension(Uri.of_strings))letquoted_string_of_stringsq=letrecfirst_quoteq=matchString.getsqwith|' '->first_quote(q+1)|'"'->letq=q+1inbeginmatchStringext.find_from~start:qs~pattern:"\""with|None->Stringext.string_aftersq,String.lengths|Someq'->String.subsq(q'-q),q'+1end|_->untilsq[';';',']infirst_quoteqletrels_of_string_sq=letqs,i=quoted_string_of_stringsqinletrels=Stringext.splitqs~on:' 'inList.maprel_of_string(List.filter(funs->String.lengths>0)rels),iletrels_of_stringsi=matchStringext.find_from~start:is~pattern:"\"",untilsi[';';',']with|Someq,(_,d)whenq<d->rels_of_string_sq|_,(s,d)->[rel_of_strings],dletanchor_of_stringsi=letqs,i=quoted_string_of_stringsiinUri.of_stringqs,iletstar_of_stringsi=matchStringext.find_from~start:is~pattern:"'"with|None->lets,i=quoted_string_of_stringsiin"","",s,i|Somea->letcharset=String.subsi(a-i)inleti=a+1inmatchStringext.find_from~start:is~pattern:"'"with|None->lets,i=quoted_string_of_stringsiincharset,"",s,i|Somea->letlanguage=String.subsi(a-i)inleti=a+1inlets,i=quoted_string_of_stringsiincharset,language,s,iletmedia_type_of_stringsi=letmt,i=quoted_string_of_stringsiinmatchStringext.split~max:2mt~on:'/'with|[]->("",""),i|[t]->(t,""),i|t::st::_->(t,st),iletrecparams_of_stringsips=let_,d=untilsi[';';',']inifd=String.lengthsthenps,NoneelseifString.getsd=','thenps,Somedelseleti=d+1inletparam,i=untilsi['=']inleti=i+1inmatchString.trimparamwith|"rel"->letrels,i=rels_of_stringsiinparams_of_stringsi((Relrels)::ps)|"anchor"->leturi,i=anchor_of_stringsiinparams_of_stringsi((Anchoruri)::ps)|"rev"->letrels,i=rels_of_stringsiinparams_of_stringsi((Revrels)::ps)|"hreflang"->lethreflang,i=untilsi[',';';']inparams_of_stringsi((Hreflanghreflang)::ps)|"media"->letmedia,i=quoted_string_of_stringsiinparams_of_stringsi((Mediamedia)::ps)|"title"->lettitle,i=quoted_string_of_stringsiinparams_of_stringsi((Titletitle)::ps)|"title*"->letcharset,language,v,i=star_of_stringsiinparams_of_stringsi((Star{Ext.charset;language;value=Titlev})::ps)|"type"->letmedia_type,i=media_type_of_stringsiinparams_of_stringsi((Typemedia_type)::ps)|otherwhenString.lengthother=0->lets,i=quoted_string_of_stringsiinparams_of_stringsi((Link_extension("",s))::ps)|other->letlast=String.lengthother-1inifString.getotherlast='*'thenletmain=String.subother0lastinletcharset,language,v,i=star_of_stringsiinparams_of_stringsi((Star{Ext.charset;language;value=Link_extension(main,v)})::ps)elseletv,i=quoted_string_of_stringsiinparams_of_stringsi((Link_extension(other,v))::ps)letrecfind_or_defaultfd=function|[]->d|h::t->matchfhwith|None->find_or_defaultfdt|Somev->vletarc_of_relation_params?(reverse=false)relationparams=letextensions,extension_exts=List.fold_left(fun(x,xx)->function|Link_extension(k,v)->((k,v)::x,xx)|Star{Ext.charset;language;value=Link_extension(k,value)}->(x,(k,{Ext.charset;language;value})::xx)|_->(x,xx))([],[])paramsin{Arc.reverse;relation;hreflang=find_or_default(functionHreflangl->Some(Somel)|_->None)Noneparams;media=find_or_default(functionMediam->Some(Somem)|_->None)Noneparams;title=find_or_default(functionTitlet->Some(Somet)|_->None)Noneparams;title_ext=find_or_default(function|Star{Ext.charset;language;value=Titlet}->Some(Some{Ext.charset;language;value=t})|_->None)Noneparams;media_type=find_or_default(functionTypemt->Some(Somemt)|_->None)Noneparams;extensions;extension_exts;}letempty={context=Uri.of_string"";arc=Arc.empty;target=Uri.of_string"";}letrecunfoldsliststart=matchStringext.find_from~starts~pattern:"<"with|None->list|Somei->leturi_ref,i=untils(i+1)['>']inleti=i+1inlettarget=Uri.of_stringuri_refinletparams,c_opt=params_of_stringsi[]inletparams=List.revparamsinletcontext=find_or_default(functionAnchoruri->Someuri|_->None)(Uri.of_string"")paramsinletlink=matchfind_or_default(functionRelrels->Somerels|_->None)[]paramswith|(_::_)asrelation->letarc=arc_of_relation_paramsrelationparamsin{context;arc;target}|[]->matchfind_or_default(functionRevrels->Somerels|_->None)[]paramswith|[]->letarc=arc_of_relation_params[]paramsin{context;arc;target}|rev->letarc=arc_of_relation_params~reverse:truerevparamsin{context=target;arc;target=context}inletlist=link::listinmatchc_optwith|None->list|Somec->unfoldslistcletof_strings=List.rev(unfolds[]0)openPrintfletarc_to_stringcontextarc=Arc.(letattrs=matcharc.relationwith|[]->[]|rels->[sprintf"%s=\"%s\""(ifarc.reversethen"rev"else"rel")(String.concat" "(List.mapstring_of_relrels))]inletattrs=matcharc.hreflangwith|None->attrs|Somes->("hreflang="^s)::attrsinletattrs=matcharc.mediawith|None->attrs|Somes->(sprintf"media=\"%s\""s)::attrsinletattrs=matcharc.titlewith|None->attrs|Somes->(sprintf"title=%S"s)::attrs(* TODO: this isn't quite right...*)inletattrs=matcharc.title_extwith|None->attrs|Some{Ext.charset;language;value}->(sprintf"title*=%s'%s'%s"charsetlanguagevalue)::attrsinletattrs=matcharc.media_typewith|None->attrs|Some(typ,sub)->(sprintf"type=%s/%s"typsub)::attrsinletattrs=(List.map(fun(k,v)->sprintf"%s=%S"kv)arc.extensions)@attrsinletattrs=(List.map(fun(k,{Ext.charset;language;value})->sprintf"%s=%s'%s'%s"kcharsetlanguagevalue)arc.extension_exts)@attrsinletattrs=ifcontext=Uri.of_string""thenattrselse(sprintf"anchor=\"%s\""(Uri.to_stringcontext))::attrsinString.concat"; "attrs)letto_string({context;arc;target})=sprintf"<%s>; %s"(Uri.to_stringtarget)(arc_to_stringcontextarc)