123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128(* This file is part of Markup.ml, released under the MIT license. See
LICENSE.md for details, or visit https://github.com/aantron/markup.ml. *)letescapes=letbuffer=Buffer.create(String.lengths)inString.iter(function|'"'->Buffer.add_stringbuffer"""|'&'->Buffer.add_stringbuffer"&"|'\''->Buffer.add_stringbuffer"'"|'<'->Buffer.add_stringbuffer"<"|'>'->Buffer.add_stringbuffer">"|c->Buffer.add_charbufferc)s;Buffer.contentsbufferletattribute_stringsend_attributes=letrecprepend_attributeswords=function|[]->words|(name,value)::more->prepend_attributes(" "::name::"=\""::(escapevalue)::"\""::words)moreinprepend_attributes[end_](List.revattributes)openCommonopenKstreamletwritereportprefixsignals=letsignals=enumeratesignalsinletopen_elements=ref[]inletnamespaces=Namespace.Writing.initprefixinletrecqueue=refnext_signalandemit_listlthrowek=matchlwith|[]->next_signalthrowek|s::more->queue:=emit_listmore;ksandnext_signalthrowek=nextsignalsthrowebeginfunction|i,(`Start_element(name,attributes)assignal)->(funk'->nextsignalsthrow(fun()->k'false)(funs->matchswith|_,`End_element->k'true|_,(`Text_|`Start_element_|`Comment_|`PI_|`Doctype_|`Xml_)->pushsignalss;k'false))(funself_closing->Namespace.Writing.push(fun()->report(signal,i))namespacesnameattributesthrow(fun(formatted_name,formatted_attributes)->open_elements:=formatted_name::!open_elements;ifself_closingthenbeginNamespace.Writing.popnamespaces;open_elements:=match!open_elementswith|[]->[]|_::rest->restend;letend_=ifself_closingthen"/>"else">"inlettag="<"::formatted_name::(attribute_stringsend_formatted_attributes)inemit_listtagthrowek))|_,`End_element->Namespace.Writing.popnamespaces;beginmatch!open_elementswith|[]->next_signalthrowek|name::rest->open_elements:=rest;emit_list["</";name;">"]throwekend|_,`Textss->ifList.for_all(funs->String.lengths=0)ssthennext_signalthrowekelseemit_list(List.mapescapess)throwek|_,`Xml{version;encoding;standalone}->letattributes=matchstandalonewith|None->[]|Sometrue->["standalone","yes"]|Somefalse->["standalone","no"]inletattributes=matchencodingwith|None->attributes|Someencoding->("encoding",encoding)::attributesinletattributes=("version",version)::attributesinletdeclaration="<?xml"::(attribute_strings"?>"attributes)inemit_listdeclarationthrowek|_,`Doctype{raw_text}->beginmatchraw_textwith|None->next_signalthrowek|Sometext->emit_list["<!DOCTYPE ";text;">"]throwekend|_,`PI(target,s)->emit_list["<?";target;" ";s;"?>"]throwek|_,`Comments->emit_list["<!--";s;"-->"]throwekendin(funthrowek->!queuethrowek)|>make