xmlread.pp 105 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800
  1. {
  2. This file is part of the Free Component Library
  3. XML reading routines.
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. Modified in 2006 by Sergei Gorelkin, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit XMLRead;
  13. {$ifdef fpc}
  14. {$MODE objfpc}{$H+}
  15. {$endif}
  16. interface
  17. uses
  18. SysUtils, Classes, DOM;
  19. type
  20. TErrorSeverity = (esWarning, esError, esFatal);
  21. EXMLReadError = class(Exception)
  22. private
  23. FSeverity: TErrorSeverity;
  24. FErrorMessage: string;
  25. FLine: Integer;
  26. FLinePos: Integer;
  27. public
  28. property Severity: TErrorSeverity read FSeverity;
  29. property ErrorMessage: string read FErrorMessage;
  30. property Line: Integer read FLine;
  31. property LinePos: Integer read FLinePos;
  32. end;
  33. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  34. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text); overload;
  35. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream); overload;
  36. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String); overload;
  37. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String); overload;
  38. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text); overload;
  39. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream); overload;
  40. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream; const ABaseURI: String); overload;
  41. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  42. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text); overload;
  43. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream); overload;
  44. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String); overload;
  45. type
  46. TDOMParseOptions = class(TObject)
  47. private
  48. FValidate: Boolean;
  49. FPreserveWhitespace: Boolean;
  50. FExpandEntities: Boolean;
  51. FIgnoreComments: Boolean;
  52. FCDSectionsAsText: Boolean;
  53. FResolveExternals: Boolean;
  54. FNamespaces: Boolean;
  55. FDisallowDoctype: Boolean;
  56. FCanonical: Boolean;
  57. FMaxChars: Cardinal;
  58. function GetCanonical: Boolean;
  59. procedure SetCanonical(aValue: Boolean);
  60. public
  61. property Validate: Boolean read FValidate write FValidate;
  62. property PreserveWhitespace: Boolean read FPreserveWhitespace write FPreserveWhitespace;
  63. property ExpandEntities: Boolean read FExpandEntities write FExpandEntities;
  64. property IgnoreComments: Boolean read FIgnoreComments write FIgnoreComments;
  65. property CDSectionsAsText: Boolean read FCDSectionsAsText write FCDSectionsAsText;
  66. property ResolveExternals: Boolean read FResolveExternals write FResolveExternals;
  67. property Namespaces: Boolean read FNamespaces write FNamespaces;
  68. property DisallowDoctype: Boolean read FDisallowDoctype write FDisallowDoctype;
  69. property MaxChars: Cardinal read FMaxChars write FMaxChars;
  70. property CanonicalForm: Boolean read GetCanonical write SetCanonical;
  71. end;
  72. // NOTE: DOM 3 LS ACTION_TYPE enumeration starts at 1
  73. TXMLContextAction = (
  74. xaAppendAsChildren = 1,
  75. xaReplaceChildren,
  76. xaInsertBefore,
  77. xaInsertAfter,
  78. xaReplace);
  79. TXMLErrorEvent = procedure(Error: EXMLReadError) of object;
  80. TXMLInputSource = class(TObject)
  81. private
  82. FStream: TStream;
  83. FStringData: string;
  84. FBaseURI: WideString;
  85. FSystemID: WideString;
  86. FPublicID: WideString;
  87. // FEncoding: string;
  88. public
  89. constructor Create(AStream: TStream); overload;
  90. constructor Create(const AStringData: string); overload;
  91. property Stream: TStream read FStream;
  92. property StringData: string read FStringData;
  93. property BaseURI: WideString read FBaseURI write FBaseURI;
  94. property SystemID: WideString read FSystemID write FSystemID;
  95. property PublicID: WideString read FPublicID write FPublicID;
  96. // property Encoding: string read FEncoding write FEncoding;
  97. end;
  98. TDOMParser = class(TObject)
  99. private
  100. FOptions: TDOMParseOptions;
  101. FOnError: TXMLErrorEvent;
  102. public
  103. constructor Create;
  104. destructor Destroy; override;
  105. procedure Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  106. procedure ParseUri(const URI: WideString; out ADoc: TXMLDocument);
  107. function ParseWithContext(Src: TXMLInputSource; Context: TDOMNode;
  108. Action: TXMLContextAction): TDOMNode;
  109. property Options: TDOMParseOptions read FOptions;
  110. property OnError: TXMLErrorEvent read FOnError write FOnError;
  111. end;
  112. TDecoder = record
  113. Context: Pointer;
  114. Decode: function(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  115. Cleanup: procedure(Context: Pointer); stdcall;
  116. end;
  117. TGetDecoderProc = function(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
  118. procedure RegisterDecoder(Proc: TGetDecoderProc);
  119. // =======================================================
  120. implementation
  121. uses
  122. UriParser, xmlutils, dtdmodel;
  123. const
  124. PubidChars: TSetOfChar = [' ', #13, #10, 'a'..'z', 'A'..'Z', '0'..'9',
  125. '-', '''', '(', ')', '+', ',', '.', '/', ':', '=', '?', ';', '!', '*',
  126. '#', '@', '$', '_', '%'];
  127. type
  128. TDOMNotationEx = class(TDOMNotation);
  129. TDOMDocumentTypeEx = class(TDOMDocumentType);
  130. TDOMTopNodeEx = class(TDOMNode_TopLevel);
  131. TDTDSubsetType = (dsNone, dsInternal, dsExternal);
  132. // This may be augmented with ByteOffset, UTF8Offset, etc.
  133. TLocation = record
  134. Line: Integer;
  135. LinePos: Integer;
  136. end;
  137. TDOMEntityEx = class(TDOMEntity)
  138. protected
  139. FExternallyDeclared: Boolean;
  140. FPrefetched: Boolean;
  141. FResolved: Boolean;
  142. FOnStack: Boolean;
  143. FBetweenDecls: Boolean;
  144. FIsPE: Boolean;
  145. FReplacementText: DOMString;
  146. FStartLocation: TLocation;
  147. FCharCount: Cardinal;
  148. end;
  149. TXMLReader = class;
  150. TXMLCharSource = class(TObject)
  151. private
  152. FBuf: PWideChar;
  153. FBufEnd: PWideChar;
  154. FReader: TXMLReader;
  155. FParent: TXMLCharSource;
  156. FEntity: TObject; // weak reference
  157. FLineNo: Integer;
  158. LFPos: PWideChar;
  159. FXML11Rules: Boolean;
  160. FSystemID: WideString;
  161. FCharCount: Cardinal;
  162. FStartNesting: Integer;
  163. function GetSystemID: WideString;
  164. protected
  165. function Reload: Boolean; virtual;
  166. public
  167. DTDSubsetType: TDTDSubsetType;
  168. constructor Create(const AData: WideString);
  169. procedure NextChar;
  170. procedure NewLine; virtual;
  171. function SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  172. wsflag: PBoolean = nil): WideChar; virtual;
  173. procedure Initialize; virtual;
  174. function SetEncoding(const AEncoding: string): Boolean; virtual;
  175. function Matches(const arg: WideString): Boolean;
  176. property SystemID: WideString read GetSystemID write FSystemID;
  177. end;
  178. TXMLDecodingSource = class(TXMLCharSource)
  179. private
  180. FCharBuf: PChar;
  181. FCharBufEnd: PChar;
  182. FBufStart: PWideChar;
  183. FDecoder: TDecoder;
  184. FHasBOM: Boolean;
  185. FFixedUCS2: string;
  186. FBufSize: Integer;
  187. procedure DecodingError(const Msg: string);
  188. protected
  189. function Reload: Boolean; override;
  190. procedure FetchData; virtual;
  191. public
  192. procedure AfterConstruction; override;
  193. destructor Destroy; override;
  194. function SetEncoding(const AEncoding: string): Boolean; override;
  195. procedure NewLine; override;
  196. function SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  197. wsflag: PBoolean = nil): WideChar; override;
  198. procedure Initialize; override;
  199. end;
  200. TXMLStreamInputSource = class(TXMLDecodingSource)
  201. private
  202. FAllocated: PChar;
  203. FStream: TStream;
  204. FCapacity: Integer;
  205. FOwnStream: Boolean;
  206. FEof: Boolean;
  207. public
  208. constructor Create(AStream: TStream; AOwnStream: Boolean);
  209. destructor Destroy; override;
  210. procedure FetchData; override;
  211. end;
  212. TXMLFileInputSource = class(TXMLDecodingSource)
  213. private
  214. FFile: ^Text;
  215. FString: string;
  216. FTmp: string;
  217. public
  218. constructor Create(var AFile: Text);
  219. procedure FetchData; override;
  220. end;
  221. PForwardRef = ^TForwardRef;
  222. TForwardRef = record
  223. Value: WideString;
  224. Loc: TLocation;
  225. end;
  226. TElementValidator = object
  227. FElementDef: TElementDecl;
  228. FCurCP: TContentParticle;
  229. FFailed: Boolean;
  230. function IsElementAllowed(Def: TElementDecl): Boolean;
  231. function Incomplete: Boolean;
  232. end;
  233. TNodeDataDynArray = array of TNodeData;
  234. TDOMNodeDynArray = array of TDOMNode_WithChildren;
  235. TValidatorDynArray = array of TElementValidator;
  236. TXMLReadState = (rsProlog, rsDTD, rsAfterDTD, rsRoot, rsEpilog);
  237. TCheckNameFlags = set of (cnOptional, cnToken);
  238. TXMLToken = (xtNone, xtEOF, xtText, xtWhitespace, xtElement, xtEndElement,
  239. xtCDSect, xtComment, xtPI, xtDoctype, xtEntity, xtEntityEnd, xtPopElement,
  240. xtPopEmptyElement, xtPushElement);
  241. TLiteralType = (ltPlain, ltPubid, ltEntity);
  242. TXMLReader = class
  243. private
  244. FSource: TXMLCharSource;
  245. FCtrl: TDOMParser;
  246. FXML11: Boolean;
  247. FState: TXMLReadState;
  248. FHavePERefs: Boolean;
  249. FInsideDecl: Boolean;
  250. FValue: TWideCharBuf;
  251. FEntityValue: TWideCharBuf;
  252. FName: TWideCharBuf;
  253. FTokenStart: TLocation;
  254. FStandalone: Boolean; // property of Doc ?
  255. FNamePages: PByteArray;
  256. FDocType: TDOMDocumentTypeEx; // a shortcut
  257. FPEMap: THashTable;
  258. FGEMap: THashTable;
  259. FIDRefs: TFPList;
  260. FNotationRefs: TFPList;
  261. FCurrContentType: TElementContentType;
  262. FSaViolation: Boolean;
  263. FDTDStartPos: PWideChar;
  264. FIntSubset: TWideCharBuf;
  265. FAttrTag: Cardinal;
  266. FOwnsDoctype: Boolean;
  267. FDTDProcessed: Boolean;
  268. FToken: TXMLToken;
  269. FNext: TXMLToken;
  270. FCurrEntity: TDOMEntityEx;
  271. FNSHelper: TNSSupport;
  272. FNsAttHash: TDblHashArray;
  273. FStdPrefix_xml: PHashItem;
  274. FStdPrefix_xmlns: PHashItem;
  275. FStdUri_xml: PHashItem;
  276. FStdUri_xmlns: PHashItem;
  277. FColonPos: Integer;
  278. FValidate: Boolean; // parsing options, copy of FCtrl.Options
  279. FPreserveWhitespace: Boolean;
  280. FExpandEntities: Boolean;
  281. FIgnoreComments: Boolean;
  282. FCDSectionsAsText: Boolean;
  283. FResolveExternals: Boolean;
  284. FNamespaces: Boolean;
  285. FDisallowDoctype: Boolean;
  286. FCanonical: Boolean;
  287. FMaxChars: Cardinal;
  288. procedure SkipQuote(out Delim: WideChar; required: Boolean = True);
  289. procedure Initialize(ASource: TXMLCharSource);
  290. procedure NSPrepare;
  291. procedure EntityToSource(AEntity: TDOMEntityEx; out Src: TXMLCharSource);
  292. function ContextPush(AEntity: TDOMEntityEx): Boolean;
  293. function ContextPop(Forced: Boolean = False): Boolean;
  294. procedure XML11_BuildTables;
  295. function ParseQuantity: TCPQuant;
  296. procedure StoreLocation(out Loc: TLocation);
  297. function ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: WideString): Boolean;
  298. procedure ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
  299. procedure AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer);
  300. procedure ClearRefs(aList: TFPList);
  301. procedure ValidateIdRefs;
  302. procedure StandaloneError(LineOffs: Integer = 0);
  303. procedure CallErrorHandler(E: EXMLReadError);
  304. function FindOrCreateElDef: TElementDecl;
  305. function SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean;
  306. procedure CheckMaxChars;
  307. function AllocNodeData(AIndex: Integer): PNodeData;
  308. function AllocAttributeData(AName: PHashItem): PNodeData;
  309. function AllocAttributeValueChunk(APrev: PNodeData): PNodeData;
  310. procedure CleanupAttributeData;
  311. procedure SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
  312. protected
  313. FNesting: Integer;
  314. FCurrNode: PNodeData;
  315. FAttrCount: Integer;
  316. FPrefixedAttrs: Integer;
  317. FNodeStack: TNodeDataDynArray;
  318. FCursorStack: TDOMNodeDynArray;
  319. FValidators: TValidatorDynArray;
  320. FAttrChunks: TFPList;
  321. FFreeAttrChunk: PNodeData;
  322. FAttrCleanupFlag: Boolean;
  323. procedure DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer=0);
  324. procedure DoErrorPos(Severity: TErrorSeverity; const descr: string;
  325. const ErrPos: TLocation);
  326. procedure FatalError(const descr: String; LineOffs: Integer=0); overload;
  327. procedure FatalError(const descr: string; const args: array of const; LineOffs: Integer=0); overload;
  328. procedure FatalError(Expected: WideChar); overload;
  329. function SkipWhitespace(PercentAloneIsOk: Boolean = False): Boolean;
  330. function SkipS(required: Boolean = False): Boolean;
  331. procedure ExpectWhitespace;
  332. procedure ExpectString(const s: String);
  333. procedure ExpectChar(wc: WideChar);
  334. function CheckForChar(c: WideChar): Boolean;
  335. procedure RaiseNameNotFound;
  336. function CheckName(aFlags: TCheckNameFlags = []): Boolean;
  337. procedure CheckNCName;
  338. function ExpectName: WideString; // [5]
  339. function ParseLiteral(var ToFill: TWideCharBuf; aType: TLiteralType;
  340. Required: Boolean): Boolean;
  341. function ExpectAttValue(attrData: PNodeData; NonCDATA: Boolean): Boolean; // [10]
  342. procedure ParseComment(discard: Boolean); // [15]
  343. procedure ParsePI; // [16]
  344. procedure CreatePINode;
  345. procedure ParseXmlOrTextDecl(TextDecl: Boolean);
  346. procedure ExpectEq;
  347. procedure ParseDoctypeDecl; // [28]
  348. procedure ParseMarkupDecl; // [29]
  349. procedure ParseStartTag; // [39]
  350. procedure ParseEndTag; // [42]
  351. procedure DoEndElement;
  352. procedure ParseAttribute(ElDef: TElementDecl);
  353. procedure ParseContent; // [43]
  354. function Read: Boolean;
  355. function ResolvePredefined: Boolean;
  356. function EntityCheck(NoExternals: Boolean = False): TDOMEntityEx;
  357. procedure AppendReference(cur: TDOMNode; AEntity: TDOMEntityEx);
  358. function PrefetchEntity(AEntity: TDOMEntityEx): Boolean;
  359. procedure StartPE;
  360. function ParseRef(var ToFill: TWideCharBuf): Boolean; // [67]
  361. function ParseExternalID(out SysID, PubID: WideString; // [75]
  362. SysIdOptional: Boolean): Boolean;
  363. procedure BadPENesting(S: TErrorSeverity = esError);
  364. procedure ParseEntityDecl;
  365. procedure ParseAttlistDecl;
  366. procedure ExpectChoiceOrSeq(CP: TContentParticle);
  367. procedure ParseElementDecl;
  368. procedure ParseNotationDecl;
  369. function ResolveEntity(const ASystemID, APublicID, ABaseURI: WideString; out Source: TXMLCharSource): Boolean;
  370. procedure ProcessDefaultAttributes(ElDef: TElementDecl);
  371. procedure ProcessNamespaceAtts;
  372. procedure AddBinding(attrData: PNodeData);
  373. procedure PushVC(aElDef: TElementDecl);
  374. procedure PopVC;
  375. procedure UpdateConstraints;
  376. procedure ValidateDTD;
  377. procedure ValidateRoot;
  378. procedure ValidationError(const Msg: string; const args: array of const; LineOffs: Integer = -1);
  379. procedure ValidationErrorWithName(const Msg: string; LineOffs: Integer = -1);
  380. procedure DTDReloadHook;
  381. procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
  382. // Some SAX-alike stuff (at a very early stage)
  383. procedure DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean=False);
  384. procedure DoComment(ch: PWideChar; Count: Integer);
  385. procedure DoCDSect(ch: PWideChar; Count: Integer);
  386. procedure DoNotationDecl(const aName, aPubID, aSysID: WideString);
  387. public
  388. doc: TDOMDocument;
  389. constructor Create; overload;
  390. constructor Create(AParser: TDOMParser); overload;
  391. destructor Destroy; override;
  392. procedure ProcessXML(ASource: TXMLCharSource); // [1]
  393. procedure ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
  394. procedure ProcessDTD(ASource: TXMLCharSource); // ([29])
  395. end;
  396. const
  397. NullLocation: TLocation = (Line: 0; LinePos: 0);
  398. { Decoders }
  399. var
  400. Decoders: array of TGetDecoderProc;
  401. procedure RegisterDecoder(Proc: TGetDecoderProc);
  402. var
  403. L: Integer;
  404. begin
  405. L := Length(Decoders);
  406. SetLength(Decoders, L+1);
  407. Decoders[L] := Proc;
  408. end;
  409. function FindDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean;
  410. var
  411. I: Integer;
  412. begin
  413. Result := False;
  414. for I := 0 to High(Decoders) do
  415. if Decoders[I](AEncoding, Decoder) then
  416. begin
  417. Result := True;
  418. Exit;
  419. end;
  420. end;
  421. function Is_8859_1(const AEncoding: string): Boolean;
  422. begin
  423. Result := SameText(AEncoding, 'ISO-8859-1') or
  424. SameText(AEncoding, 'ISO_8859-1') or
  425. SameText(AEncoding, 'latin1') or
  426. SameText(AEncoding, 'iso-ir-100') or
  427. SameText(AEncoding, 'l1') or
  428. SameText(AEncoding, 'IBM819') or
  429. SameText(AEncoding, 'CP819') or
  430. SameText(AEncoding, 'csISOLatin1') or
  431. // This one is not in character-sets.txt, but was used in FPC documentation,
  432. // and still being used in fcl-registry package
  433. SameText(AEncoding, 'ISO8859-1');
  434. end;
  435. { TDOMParseOptions }
  436. function TDOMParseOptions.GetCanonical: Boolean;
  437. begin
  438. Result := FCanonical and FExpandEntities and FCDSectionsAsText and
  439. { (not normalizeCharacters) and } FNamespaces and
  440. { namespaceDeclarations and } FPreserveWhitespace;
  441. end;
  442. procedure TDOMParseOptions.SetCanonical(aValue: Boolean);
  443. begin
  444. FCanonical := aValue;
  445. if aValue then
  446. begin
  447. FExpandEntities := True;
  448. FCDSectionsAsText := True;
  449. FNamespaces := True;
  450. FPreserveWhitespace := True;
  451. { normalizeCharacters := False; }
  452. { namespaceDeclarations := True; }
  453. { wellFormed := True; }
  454. end;
  455. end;
  456. { TXMLInputSource }
  457. constructor TXMLInputSource.Create(AStream: TStream);
  458. begin
  459. inherited Create;
  460. FStream := AStream;
  461. end;
  462. constructor TXMLInputSource.Create(const AStringData: string);
  463. begin
  464. inherited Create;
  465. FStringData := AStringData;
  466. end;
  467. { TDOMParser }
  468. constructor TDOMParser.Create;
  469. begin
  470. FOptions := TDOMParseOptions.Create;
  471. end;
  472. destructor TDOMParser.Destroy;
  473. begin
  474. FOptions.Free;
  475. inherited Destroy;
  476. end;
  477. procedure TDOMParser.Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  478. var
  479. InputSrc: TXMLCharSource;
  480. begin
  481. with TXMLReader.Create(Self) do
  482. try
  483. ConvertSource(Src, InputSrc); // handles 'no-input-specified' case
  484. ProcessXML(InputSrc)
  485. finally
  486. ADoc := TXMLDocument(doc);
  487. Free;
  488. end;
  489. end;
  490. procedure TDOMParser.ParseUri(const URI: WideString; out ADoc: TXMLDocument);
  491. var
  492. Src: TXMLCharSource;
  493. begin
  494. ADoc := nil;
  495. with TXMLReader.Create(Self) do
  496. try
  497. if ResolveEntity(URI, '', '', Src) then
  498. ProcessXML(Src)
  499. else
  500. DoErrorPos(esFatal, 'The specified URI could not be resolved', NullLocation);
  501. finally
  502. ADoc := TXMLDocument(doc);
  503. Free;
  504. end;
  505. end;
  506. function TDOMParser.ParseWithContext(Src: TXMLInputSource;
  507. Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
  508. var
  509. InputSrc: TXMLCharSource;
  510. Frag: TDOMDocumentFragment;
  511. node: TDOMNode;
  512. begin
  513. if Action in [xaInsertBefore, xaInsertAfter, xaReplace] then
  514. node := Context.ParentNode
  515. else
  516. node := Context;
  517. // TODO: replacing document isn't yet supported
  518. if (Action = xaReplaceChildren) and (node.NodeType = DOCUMENT_NODE) then
  519. raise EDOMNotSupported.Create('DOMParser.ParseWithContext');
  520. if not (node.NodeType in [ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE]) then
  521. raise EDOMHierarchyRequest.Create('DOMParser.ParseWithContext');
  522. with TXMLReader.Create(Self) do
  523. try
  524. ConvertSource(Src, InputSrc); // handles 'no-input-specified' case
  525. Frag := Context.OwnerDocument.CreateDocumentFragment;
  526. try
  527. ProcessFragment(InputSrc, Frag);
  528. Result := Frag.FirstChild;
  529. case Action of
  530. xaAppendAsChildren: Context.AppendChild(Frag);
  531. xaReplaceChildren: begin
  532. Context.TextContent := ''; // removes children
  533. Context.ReplaceChild(Frag, Context.FirstChild);
  534. end;
  535. xaInsertBefore: node.InsertBefore(Frag, Context);
  536. xaInsertAfter: node.InsertBefore(Frag, Context.NextSibling);
  537. xaReplace: node.ReplaceChild(Frag, Context);
  538. end;
  539. finally
  540. Frag.Free;
  541. end;
  542. finally
  543. Free;
  544. end;
  545. end;
  546. { TXMLCharSource }
  547. constructor TXMLCharSource.Create(const AData: WideString);
  548. begin
  549. inherited Create;
  550. FLineNo := 1;
  551. FBuf := PWideChar(AData);
  552. FBufEnd := FBuf + Length(AData);
  553. LFPos := FBuf-1;
  554. FCharCount := Length(AData);
  555. end;
  556. procedure TXMLCharSource.Initialize;
  557. begin
  558. end;
  559. function TXMLCharSource.SetEncoding(const AEncoding: string): Boolean;
  560. begin
  561. Result := True; // always succeed
  562. end;
  563. function TXMLCharSource.GetSystemID: WideString;
  564. begin
  565. if FSystemID <> '' then
  566. Result := FSystemID
  567. else if Assigned(FParent) then
  568. Result := FParent.SystemID
  569. else
  570. Result := '';
  571. end;
  572. function TXMLCharSource.Reload: Boolean;
  573. begin
  574. Result := False;
  575. end;
  576. procedure TXMLCharSource.NewLine;
  577. begin
  578. Inc(FLineNo);
  579. LFPos := FBuf;
  580. end;
  581. function TXMLCharSource.SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  582. wsflag: PBoolean): WideChar;
  583. var
  584. old: PWideChar;
  585. nonws: Boolean;
  586. begin
  587. old := FBuf;
  588. nonws := False;
  589. repeat
  590. if FBuf^ = #10 then
  591. NewLine;
  592. if (FBuf^ < #255) and (Char(ord(FBuf^)) in Delim) then
  593. Break;
  594. if (FBuf^ > #32) or not (Char(ord(FBuf^)) in [#32, #9, #10, #13]) then
  595. nonws := True;
  596. Inc(FBuf);
  597. until False;
  598. Result := FBuf^;
  599. BufAppendChunk(ToFill, old, FBuf);
  600. if Assigned(wsflag) then
  601. wsflag^ := wsflag^ or nonws;
  602. end;
  603. function TXMLCharSource.Matches(const arg: WideString): Boolean;
  604. begin
  605. Result := False;
  606. if (FBufEnd >= FBuf + Length(arg)) or Reload then
  607. Result := CompareMem(Pointer(arg), FBuf, Length(arg)*sizeof(WideChar));
  608. if Result then
  609. begin
  610. Inc(FBuf, Length(arg));
  611. if FBuf >= FBufEnd then
  612. Reload;
  613. end;
  614. end;
  615. { TXMLDecodingSource }
  616. procedure TXMLDecodingSource.AfterConstruction;
  617. begin
  618. inherited AfterConstruction;
  619. FBufStart := AllocMem(4096);
  620. FBuf := FBufStart;
  621. FBufEnd := FBuf;
  622. LFPos := FBuf-1;
  623. end;
  624. destructor TXMLDecodingSource.Destroy;
  625. begin
  626. FreeMem(FBufStart);
  627. if Assigned(FDecoder.Cleanup) then
  628. FDecoder.Cleanup(FDecoder.Context);
  629. inherited Destroy;
  630. end;
  631. procedure TXMLDecodingSource.FetchData;
  632. begin
  633. end;
  634. procedure TXMLDecodingSource.DecodingError(const Msg: string);
  635. begin
  636. // count line endings to obtain correct error location
  637. while FBuf < FBufEnd do
  638. begin
  639. if (FBuf^ = #10) or (FBuf^ = #13) or (FXML11Rules and ((FBuf^ = #$85) or (FBuf^ = #$2028))) then
  640. begin
  641. if (FBuf^ = #13) and (FBuf < FBufEnd-1) and
  642. ((FBuf[1] = #10) or (FXML11Rules and (FBuf[1] = #$85))) then
  643. Inc(FBuf);
  644. LFPos := FBuf;
  645. Inc(FLineNo);
  646. end;
  647. Inc(FBuf);
  648. end;
  649. FReader.FatalError(Msg);
  650. end;
  651. function TXMLDecodingSource.Reload: Boolean;
  652. var
  653. Remainder: PtrInt;
  654. r, inLeft: Cardinal;
  655. rslt: Integer;
  656. begin
  657. if DTDSubsetType = dsInternal then
  658. FReader.DTDReloadHook;
  659. Remainder := FBufEnd - FBuf;
  660. if Remainder > 0 then
  661. Move(FBuf^, FBufStart^, Remainder * sizeof(WideChar));
  662. Dec(LFPos, FBuf-FBufStart);
  663. FBuf := FBufStart;
  664. FBufEnd := FBufStart + Remainder;
  665. repeat
  666. inLeft := FCharBufEnd - FCharBuf;
  667. if inLeft < 4 then // may contain an incomplete char
  668. begin
  669. FetchData;
  670. inLeft := FCharBufEnd - FCharBuf;
  671. if inLeft <= 0 then
  672. Break;
  673. end;
  674. r := FBufStart + FBufSize - FBufEnd;
  675. if r = 0 then
  676. Break;
  677. rslt := FDecoder.Decode(FDecoder.Context, FCharBuf, inLeft, FBufEnd, r);
  678. { Sanity checks: r and inLeft must not increase. }
  679. if inLeft + FCharBuf <= FCharBufEnd then
  680. FCharBuf := FCharBufEnd - inLeft
  681. else
  682. DecodingError('Decoder error: input byte count out of bounds');
  683. if r + FBufEnd <= FBufStart + FBufSize then
  684. FBufEnd := FBufStart + FBufSize - r
  685. else
  686. DecodingError('Decoder error: output char count out of bounds');
  687. if rslt = 0 then
  688. Break
  689. else if rslt < 0 then
  690. DecodingError('Invalid character in input stream')
  691. else
  692. begin
  693. Inc(FCharCount, rslt);
  694. FReader.CheckMaxChars;
  695. end;
  696. until False;
  697. FBufEnd^ := #0;
  698. Result := FBuf < FBufEnd;
  699. end;
  700. const
  701. XmlSign: array [0..4] of WideChar = ('<', '?', 'x', 'm', 'l');
  702. procedure TXMLDecodingSource.Initialize;
  703. begin
  704. inherited;
  705. FLineNo := 1;
  706. FDecoder.Decode := @Decode_UTF8;
  707. FFixedUCS2 := '';
  708. if FCharBufEnd-FCharBuf > 1 then
  709. begin
  710. if (FCharBuf[0] = #$FE) and (FCharBuf[1] = #$FF) then
  711. begin
  712. FFixedUCS2 := 'UTF-16BE';
  713. FDecoder.Decode := {$IFNDEF ENDIAN_BIG} @Decode_UCS2_Swapped {$ELSE} @Decode_UCS2 {$ENDIF};
  714. end
  715. else if (FCharBuf[0] = #$FF) and (FCharBuf[1] = #$FE) then
  716. begin
  717. FFixedUCS2 := 'UTF-16LE';
  718. FDecoder.Decode := {$IFDEF ENDIAN_BIG} @Decode_UCS2_Swapped {$ELSE} @Decode_UCS2 {$ENDIF};
  719. end;
  720. end;
  721. FBufSize := 6; // possible BOM and '<?xml'
  722. Reload;
  723. if FBuf^ = #$FEFF then
  724. begin
  725. FHasBOM := True;
  726. Inc(FBuf);
  727. end;
  728. LFPos := FBuf-1;
  729. if CompareMem(FBuf, @XmlSign[0], sizeof(XmlSign)) then
  730. begin
  731. FBufSize := 3; // don't decode past XML declaration
  732. Inc(FBuf, Length(XmlSign));
  733. FReader.ParseXmlOrTextDecl((FParent <> nil) or (FReader.FState <> rsProlog));
  734. end;
  735. FBufSize := 2047;
  736. if FReader.FXML11 then
  737. FReader.XML11_BuildTables;
  738. end;
  739. function TXMLDecodingSource.SetEncoding(const AEncoding: string): Boolean;
  740. var
  741. NewDecoder: TDecoder;
  742. begin
  743. Result := True;
  744. if (FFixedUCS2 = '') and SameText(AEncoding, 'UTF-8') then
  745. Exit;
  746. if FFixedUCS2 <> '' then
  747. begin
  748. Result := SameText(AEncoding, FFixedUCS2) or
  749. SameText(AEncoding, 'UTF-16') or
  750. SameText(AEncoding, 'unicode');
  751. Exit;
  752. end;
  753. // TODO: must fail when a byte-based stream is labeled as word-based.
  754. // see rmt-e2e-61, it now fails but for a completely different reason.
  755. FillChar(NewDecoder, sizeof(TDecoder), 0);
  756. if Is_8859_1(AEncoding) then
  757. FDecoder.Decode := @Decode_8859_1
  758. else if FindDecoder(AEncoding, NewDecoder) then
  759. FDecoder := NewDecoder
  760. else
  761. Result := False;
  762. end;
  763. procedure TXMLDecodingSource.NewLine;
  764. begin
  765. case FBuf^ of
  766. #10: ;
  767. #13: begin
  768. // Reload trashes the buffer, it should be consumed beforehand
  769. if (FBufEnd >= FBuf+2) or Reload then
  770. begin
  771. if (FBuf[1] = #10) or (FXML11Rules and (FBuf[1] = #$85)) then
  772. Inc(FBuf);
  773. end;
  774. FBuf^ := #10;
  775. end;
  776. #$85, #$2028: if FXML11Rules then
  777. FBuf^ := #10
  778. else
  779. Exit;
  780. else
  781. Exit;
  782. end;
  783. Inc(FLineNo);
  784. LFPos := FBuf;
  785. end;
  786. { TXMLStreamInputSource }
  787. const
  788. Slack = 16;
  789. constructor TXMLStreamInputSource.Create(AStream: TStream; AOwnStream: Boolean);
  790. begin
  791. FStream := AStream;
  792. FCapacity := 4096;
  793. GetMem(FAllocated, FCapacity+Slack);
  794. FCharBuf := FAllocated+(Slack-4);
  795. FCharBufEnd := FCharBuf;
  796. FOwnStream := AOwnStream;
  797. FetchData;
  798. end;
  799. destructor TXMLStreamInputSource.Destroy;
  800. begin
  801. FreeMem(FAllocated);
  802. if FOwnStream then
  803. FStream.Free;
  804. inherited Destroy;
  805. end;
  806. procedure TXMLStreamInputSource.FetchData;
  807. var
  808. Remainder, BytesRead: Integer;
  809. OldBuf: PChar;
  810. begin
  811. Assert(FCharBufEnd - FCharBuf < Slack-4);
  812. if FEof then
  813. Exit;
  814. OldBuf := FCharBuf;
  815. Remainder := FCharBufEnd - FCharBuf;
  816. if Remainder < 0 then
  817. Remainder := 0;
  818. FCharBuf := FAllocated+Slack-4-Remainder;
  819. if Remainder > 0 then
  820. Move(OldBuf^, FCharBuf^, Remainder);
  821. BytesRead := FStream.Read(FAllocated[Slack-4], FCapacity);
  822. if BytesRead < FCapacity then
  823. FEof := True;
  824. FCharBufEnd := FAllocated + (Slack-4) + BytesRead;
  825. { Null-termination has been removed:
  826. 1) Built-in decoders don't need it because they respect the buffer length.
  827. 2) It was causing unaligned access errors on ARM CPUs.
  828. }
  829. //PWideChar(FCharBufEnd)^ := #0;
  830. end;
  831. { TXMLFileInputSource }
  832. constructor TXMLFileInputSource.Create(var AFile: Text);
  833. begin
  834. FFile := @AFile;
  835. SystemID := FilenameToURI(TTextRec(AFile).Name);
  836. FetchData;
  837. end;
  838. procedure TXMLFileInputSource.FetchData;
  839. var
  840. Remainder: Integer;
  841. begin
  842. if not Eof(FFile^) then
  843. begin
  844. Remainder := FCharBufEnd - FCharBuf;
  845. if Remainder > 0 then
  846. SetString(FTmp, FCharBuf, Remainder);
  847. ReadLn(FFile^, FString);
  848. FString := FString + #10; // bad solution...
  849. if Remainder > 0 then
  850. Insert(FTmp, FString, 1);
  851. FCharBuf := PChar(FString);
  852. FCharBufEnd := FCharBuf + Length(FString);
  853. end;
  854. end;
  855. { helper that closes handle upon destruction }
  856. type
  857. THandleOwnerStream = class(THandleStream)
  858. public
  859. destructor Destroy; override;
  860. end;
  861. destructor THandleOwnerStream.Destroy;
  862. begin
  863. if Handle >= 0 then FileClose(Handle);
  864. inherited Destroy;
  865. end;
  866. { TXMLReader }
  867. procedure TXMLReader.ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
  868. begin
  869. SrcOut := nil;
  870. if Assigned(SrcIn) then
  871. begin
  872. if Assigned(SrcIn.FStream) then
  873. SrcOut := TXMLStreamInputSource.Create(SrcIn.FStream, False)
  874. else if SrcIn.FStringData <> '' then
  875. SrcOut := TXMLStreamInputSource.Create(TStringStream.Create(SrcIn.FStringData), True)
  876. else if (SrcIn.SystemID <> '') then
  877. ResolveEntity(SrcIn.SystemID, SrcIn.PublicID, SrcIn.BaseURI, SrcOut);
  878. end;
  879. if (SrcOut = nil) and (FSource = nil) then
  880. DoErrorPos(esFatal, 'No input source specified', NullLocation);
  881. end;
  882. procedure TXMLReader.StoreLocation(out Loc: TLocation);
  883. begin
  884. Loc.Line := FSource.FLineNo;
  885. Loc.LinePos := FSource.FBuf-FSource.LFPos;
  886. end;
  887. function TXMLReader.ResolveEntity(const ASystemID, APublicID, ABaseURI: WideString; out Source: TXMLCharSource): Boolean;
  888. var
  889. AbsSysID: WideString;
  890. Filename: string;
  891. Stream: TStream;
  892. fd: THandle;
  893. begin
  894. Source := nil;
  895. Result := False;
  896. if not ResolveRelativeURI(ABaseURI, ASystemID, AbsSysID) then
  897. Exit;
  898. { TODO: alternative resolvers
  899. These may be 'internal' resolvers or a handler set by application.
  900. Internal resolvers should probably produce a TStream
  901. ( so that internal classes need not be exported ).
  902. External resolver will produce TXMLInputSource that should be converted.
  903. External resolver must NOT be called for root entity.
  904. External resolver can return nil, in which case we do the default }
  905. if URIToFilename(AbsSysID, Filename) then
  906. begin
  907. fd := FileOpen(Filename, fmOpenRead + fmShareDenyWrite);
  908. if fd <> THandle(-1) then
  909. begin
  910. Stream := THandleOwnerStream.Create(fd);
  911. Source := TXMLStreamInputSource.Create(Stream, True);
  912. Source.SystemID := AbsSysID; // <- Revisit: Really need absolute sysID?
  913. end;
  914. end;
  915. Result := Assigned(Source);
  916. end;
  917. procedure TXMLReader.Initialize(ASource: TXMLCharSource);
  918. begin
  919. ASource.FParent := FSource;
  920. FSource := ASource;
  921. FSource.FReader := Self;
  922. FSource.FStartNesting := FNesting;
  923. FSource.Initialize;
  924. end;
  925. procedure TXMLReader.FatalError(Expected: WideChar);
  926. begin
  927. // FIX: don't output what is found - anything may be found, including exploits...
  928. FatalError('Expected "%1s"', [string(Expected)]);
  929. end;
  930. procedure TXMLReader.FatalError(const descr: String; LineOffs: Integer);
  931. begin
  932. DoError(esFatal, descr, LineOffs);
  933. end;
  934. procedure TXMLReader.FatalError(const descr: string; const args: array of const; LineOffs: Integer);
  935. begin
  936. DoError(esFatal, Format(descr, args), LineOffs);
  937. end;
  938. procedure TXMLReader.ValidationError(const Msg: string; const Args: array of const; LineOffs: Integer);
  939. begin
  940. if FValidate then
  941. DoError(esError, Format(Msg, Args), LineOffs);
  942. end;
  943. procedure TXMLReader.ValidationErrorWithName(const Msg: string; LineOffs: Integer);
  944. var
  945. ws: WideString;
  946. begin
  947. SetString(ws, FName.Buffer, FName.Length);
  948. ValidationError(Msg, [ws], LineOffs);
  949. end;
  950. procedure TXMLReader.DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer);
  951. var
  952. Loc: TLocation;
  953. begin
  954. StoreLocation(Loc);
  955. if LineOffs >= 0 then
  956. begin
  957. Dec(Loc.LinePos, LineOffs);
  958. DoErrorPos(Severity, descr, Loc);
  959. end
  960. else
  961. DoErrorPos(Severity, descr, FTokenStart);
  962. end;
  963. procedure TXMLReader.DoErrorPos(Severity: TErrorSeverity; const descr: string; const ErrPos: TLocation);
  964. var
  965. E: EXMLReadError;
  966. sysid: WideString;
  967. begin
  968. if Assigned(FSource) then
  969. begin
  970. sysid := FSource.FSystemID;
  971. if (sysid = '') and Assigned(FSource.FEntity) then
  972. sysid := TDOMEntityEx(FSource.FEntity).FURI;
  973. E := EXMLReadError.CreateFmt('In ''%s'' (line %d pos %d): %s', [sysid, ErrPos.Line, ErrPos.LinePos, descr]);
  974. end
  975. else
  976. E := EXMLReadError.Create(descr);
  977. E.FSeverity := Severity;
  978. E.FErrorMessage := descr;
  979. E.FLine := ErrPos.Line;
  980. E.FLinePos := ErrPos.LinePos;
  981. CallErrorHandler(E);
  982. // No 'finally'! If user handler raises exception, control should not get here
  983. // and the exception will be freed in CallErrorHandler (below)
  984. E.Free;
  985. end;
  986. procedure TXMLReader.CheckMaxChars;
  987. var
  988. src: TXMLCharSource;
  989. total: Cardinal;
  990. begin
  991. if FMaxChars = 0 then
  992. Exit;
  993. src := FSource;
  994. total := 0;
  995. repeat
  996. Inc(total, src.FCharCount);
  997. if total > FMaxChars then
  998. FatalError('Exceeded character count limit');
  999. src := src.FParent;
  1000. until src = nil;
  1001. end;
  1002. procedure TXMLReader.CallErrorHandler(E: EXMLReadError);
  1003. begin
  1004. try
  1005. if Assigned(FCtrl) and Assigned(FCtrl.FOnError) then
  1006. FCtrl.FOnError(E);
  1007. if E.Severity = esFatal then
  1008. raise E;
  1009. except
  1010. if ExceptObject <> E then
  1011. E.Free;
  1012. raise;
  1013. end;
  1014. end;
  1015. function TXMLReader.SkipWhitespace(PercentAloneIsOk: Boolean): Boolean;
  1016. begin
  1017. Result := False;
  1018. repeat
  1019. Result := SkipS or Result;
  1020. if FSource.FBuf^ = #0 then
  1021. begin
  1022. Result := True; // report whitespace upon exiting the PE
  1023. if not ContextPop then
  1024. Break;
  1025. end
  1026. else if FSource.FBuf^ = '%' then
  1027. begin
  1028. if (FState <> rsDTD) or ((FSource.DTDSubsetType = dsInternal) and FInsideDecl) then
  1029. Break;
  1030. // This is the only case where look-ahead is needed
  1031. if FSource.FBuf > FSource.FBufEnd-2 then
  1032. FSource.Reload;
  1033. if (not PercentAloneIsOk) or (Byte(FSource.FBuf[1]) in NamingBitmap[FNamePages^[$100+hi(Word(FSource.FBuf[1]))]]) or
  1034. (FXML11 and (FSource.FBuf[1] >= #$D800) and (FSource.FBuf[1] <= #$DB7F)) then
  1035. begin
  1036. Inc(FSource.FBuf); // skip '%'
  1037. CheckName;
  1038. ExpectChar(';');
  1039. StartPE;
  1040. Result := True; // report whitespace upon entering the PE
  1041. end
  1042. else Break;
  1043. end
  1044. else
  1045. Break;
  1046. until False;
  1047. end;
  1048. procedure TXMLReader.ExpectWhitespace;
  1049. begin
  1050. if not SkipWhitespace then
  1051. FatalError('Expected whitespace');
  1052. end;
  1053. function TXMLReader.SkipS(Required: Boolean): Boolean;
  1054. var
  1055. p: PWideChar;
  1056. begin
  1057. Result := False;
  1058. repeat
  1059. p := FSource.FBuf;
  1060. repeat
  1061. if (p^ = #10) or (p^ = #13) or (FXML11 and ((p^ = #$85) or (p^ = #$2028))) then
  1062. begin
  1063. FSource.FBuf := p;
  1064. FSource.NewLine;
  1065. p := FSource.FBuf;
  1066. end
  1067. else if (p^ <> #32) and (p^ <> #9) then
  1068. Break;
  1069. Inc(p);
  1070. Result := True;
  1071. until False;
  1072. FSource.FBuf := p;
  1073. until (p^ <> #0) or (not FSource.Reload);
  1074. if (not Result) and Required then
  1075. FatalError('Expected whitespace');
  1076. end;
  1077. procedure TXMLReader.ExpectString(const s: String);
  1078. var
  1079. I: Integer;
  1080. begin
  1081. for I := 1 to Length(s) do
  1082. begin
  1083. if FSource.FBuf^ <> WideChar(ord(s[i])) then
  1084. FatalError('Expected "%s"', [s], i-1);
  1085. FSource.NextChar;
  1086. end;
  1087. end;
  1088. function TXMLReader.CheckForChar(c: WideChar): Boolean;
  1089. begin
  1090. Result := (FSource.FBuf^ = c);
  1091. if Result then
  1092. begin
  1093. Inc(FSource.FBuf);
  1094. if FSource.FBuf >= FSource.FBufEnd then
  1095. FSource.Reload;
  1096. end;
  1097. end;
  1098. procedure TXMLReader.SkipQuote(out Delim: WideChar; required: Boolean);
  1099. begin
  1100. Delim := #0;
  1101. if (FSource.FBuf^ = '''') or (FSource.FBuf^ = '"') then
  1102. begin
  1103. Delim := FSource.FBuf^;
  1104. FSource.NextChar; // skip quote
  1105. StoreLocation(FTokenStart);
  1106. end
  1107. else if required then
  1108. FatalError('Expected single or double quote');
  1109. end;
  1110. const
  1111. PrefixDefault: array[0..4] of WideChar = ('x','m','l','n','s');
  1112. constructor TXMLReader.Create;
  1113. begin
  1114. inherited Create;
  1115. BufAllocate(FName, 128);
  1116. BufAllocate(FValue, 512);
  1117. FIDRefs := TFPList.Create;
  1118. FNotationRefs := TFPList.Create;
  1119. FAttrChunks := TFPList.Create;
  1120. // Set char rules to XML 1.0
  1121. FNamePages := @NamePages;
  1122. SetLength(FNodeStack, 16);
  1123. SetLength(FValidators, 16);
  1124. SetLength(FCursorStack, 16);
  1125. end;
  1126. constructor TXMLReader.Create(AParser: TDOMParser);
  1127. begin
  1128. Create;
  1129. FCtrl := AParser;
  1130. if FCtrl = nil then
  1131. Exit;
  1132. FValidate := FCtrl.Options.Validate;
  1133. FPreserveWhitespace := FCtrl.Options.PreserveWhitespace;
  1134. FExpandEntities := FCtrl.Options.ExpandEntities;
  1135. FCDSectionsAsText := FCtrl.Options.CDSectionsAsText;
  1136. FIgnoreComments := FCtrl.Options.IgnoreComments;
  1137. FResolveExternals := FCtrl.Options.ResolveExternals;
  1138. FNamespaces := FCtrl.Options.Namespaces;
  1139. FDisallowDoctype := FCtrl.Options.DisallowDoctype;
  1140. FCanonical := FCtrl.Options.CanonicalForm;
  1141. FMaxChars := FCtrl.Options.MaxChars;
  1142. end;
  1143. destructor TXMLReader.Destroy;
  1144. var
  1145. i: Integer;
  1146. begin
  1147. for i := FAttrChunks.Count-1 downto 0 do
  1148. Dispose(PNodeData(FAttrChunks.List^[i]));
  1149. if Assigned(FEntityValue.Buffer) then
  1150. FreeMem(FEntityValue.Buffer);
  1151. FreeMem(FName.Buffer);
  1152. FreeMem(FValue.Buffer);
  1153. if Assigned(FSource) then
  1154. while ContextPop(True) do; // clean input stack
  1155. FSource.Free;
  1156. FPEMap.Free;
  1157. FGEMap.Free;
  1158. ClearRefs(FNotationRefs);
  1159. ClearRefs(FIDRefs);
  1160. FNsAttHash.Free;
  1161. FNSHelper.Free;
  1162. if FOwnsDoctype then
  1163. FDocType.Free;
  1164. FNotationRefs.Free;
  1165. FIDRefs.Free;
  1166. FAttrChunks.Free;
  1167. inherited Destroy;
  1168. end;
  1169. procedure TXMLReader.XML11_BuildTables;
  1170. begin
  1171. FNamePages := Xml11NamePages;
  1172. FXML11 := True;
  1173. FSource.FXml11Rules := True;
  1174. end;
  1175. { Must be executed after doc has been set.
  1176. After introducing own NameTable, merge this into constructor }
  1177. procedure TXMLReader.NSPrepare;
  1178. begin
  1179. if FNamespaces then
  1180. begin
  1181. FNSHelper := TNSSupport.Create;
  1182. FNsAttHash := TDblHashArray.Create;
  1183. FStdPrefix_xml := FNSHelper.GetPrefix(@PrefixDefault, 3);
  1184. FStdPrefix_xmlns := FNSHelper.GetPrefix(@PrefixDefault, 5);
  1185. FStdUri_xmlns := doc.Names.FindOrAdd(PWideChar(stduri_xmlns), Length(stduri_xmlns));
  1186. FStdUri_xml := doc.Names.FindOrAdd(PWideChar(stduri_xml), Length(stduri_xml));
  1187. end;
  1188. end;
  1189. procedure TXMLReader.ProcessXML(ASource: TXMLCharSource);
  1190. begin
  1191. doc := TXMLDocument.Create;
  1192. doc.documentURI := ASource.SystemID; // TODO: to be changed to URI or BaseURI
  1193. FState := rsProlog;
  1194. FNesting := 0;
  1195. FCurrNode := @FNodeStack[0];
  1196. FCursorStack[0] := doc;
  1197. NSPrepare;
  1198. Initialize(ASource);
  1199. ParseContent;
  1200. if FState < rsRoot then
  1201. FatalError('Root element is missing');
  1202. if FValidate and Assigned(FDocType) then
  1203. ValidateIdRefs;
  1204. end;
  1205. procedure TXMLReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
  1206. begin
  1207. doc := AOwner.OwnerDocument;
  1208. FState := rsRoot;
  1209. FNesting := 0;
  1210. FCurrNode := @FNodeStack[0];
  1211. FCursorStack[0] := AOwner as TDOMNode_WithChildren;
  1212. FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1');
  1213. NSPrepare;
  1214. Initialize(ASource);
  1215. FDocType := TDOMDocumentTypeEx(doc.DocType);
  1216. ParseContent;
  1217. end;
  1218. function TXMLReader.CheckName(aFlags: TCheckNameFlags): Boolean;
  1219. var
  1220. p: PWideChar;
  1221. NameStartFlag: Boolean;
  1222. begin
  1223. p := FSource.FBuf;
  1224. FName.Length := 0;
  1225. FColonPos := -1;
  1226. NameStartFlag := not (cnToken in aFlags);
  1227. repeat
  1228. if NameStartFlag then
  1229. begin
  1230. if (Byte(p^) in NamingBitmap[FNamePages^[hi(Word(p^))]]) or
  1231. ((p^ = ':') and (not FNamespaces)) then
  1232. Inc(p)
  1233. else if FXML11 and ((p^ >= #$D800) and (p^ <= #$DB7F) and
  1234. (p[1] >= #$DC00) and (p[1] <= #$DFFF)) then
  1235. Inc(p, 2)
  1236. else
  1237. begin
  1238. // here we come either when first char of name is bad (it may be a colon),
  1239. // or when a colon is not followed by a valid NameStartChar
  1240. FSource.FBuf := p;
  1241. Result := False;
  1242. Break;
  1243. end;
  1244. NameStartFlag := False;
  1245. end;
  1246. if FXML11 then
  1247. repeat
  1248. if Byte(p^) in NamingBitmap[FNamePages^[$100+hi(Word(p^))]] then
  1249. Inc(p)
  1250. else if ((p^ >= #$D800) and (p^ <= #$DB7F) and
  1251. (p[1] >= #$DC00) and (p[1] <= #$DFFF)) then
  1252. Inc(p,2)
  1253. else
  1254. Break;
  1255. until False
  1256. else
  1257. while Byte(p^) in NamingBitmap[FNamePages^[$100+hi(Word(p^))]] do
  1258. Inc(p);
  1259. if p^ = ':' then
  1260. begin
  1261. if (cnToken in aFlags) or not FNamespaces then // colon has no specific meaning
  1262. begin
  1263. Inc(p);
  1264. if p^ <> #0 then Continue;
  1265. end
  1266. else if FColonPos = -1 then // this is the first colon, remember it
  1267. begin
  1268. FColonPos := p-FSource.FBuf+FName.Length;
  1269. NameStartFlag := True;
  1270. Inc(p);
  1271. if p^ <> #0 then Continue;
  1272. end;
  1273. end;
  1274. BufAppendChunk(FName, FSource.FBuf, p);
  1275. Result := (FName.Length > 0);
  1276. FSource.FBuf := p;
  1277. if (p^ <> #0) or not FSource.Reload then
  1278. Break;
  1279. p := FSource.FBuf;
  1280. until False;
  1281. if not (Result or (cnOptional in aFlags)) then
  1282. RaiseNameNotFound;
  1283. end;
  1284. procedure TXMLReader.CheckNCName;
  1285. begin
  1286. if FNamespaces and (FColonPos <> -1) then
  1287. FatalError('Names of entities, notations and processing instructions may not contain colons', FName.Length);
  1288. end;
  1289. procedure TXMLReader.RaiseNameNotFound;
  1290. begin
  1291. if FColonPos <> -1 then
  1292. FatalError('Bad QName syntax, local part is missing')
  1293. else
  1294. // Coming at no cost, this allows more user-friendly error messages
  1295. with FSource do
  1296. if (FBuf^ = #32) or (FBuf^ = #10) or (FBuf^ = #9) or (FBuf^ = #13) then
  1297. FatalError('Whitespace is not allowed here')
  1298. else
  1299. FatalError('Name starts with invalid character');
  1300. end;
  1301. function TXMLReader.ExpectName: WideString;
  1302. begin
  1303. CheckName;
  1304. SetString(Result, FName.Buffer, FName.Length);
  1305. end;
  1306. function TXMLReader.ResolvePredefined: Boolean;
  1307. var
  1308. wc: WideChar;
  1309. begin
  1310. Result := False;
  1311. with FName do
  1312. begin
  1313. if (Length = 2) and (Buffer[1] = 't') then
  1314. begin
  1315. if Buffer[0] = 'l' then
  1316. wc := '<'
  1317. else if Buffer[0] = 'g' then
  1318. wc := '>'
  1319. else Exit;
  1320. end
  1321. else if Buffer[0] = 'a' then
  1322. begin
  1323. if (Length = 3) and (Buffer[1] = 'm') and (Buffer[2] = 'p') then
  1324. wc := '&'
  1325. else if (Length = 4) and (Buffer[1] = 'p') and (Buffer[2] = 'o') and
  1326. (Buffer[3] = 's') then
  1327. wc := ''''
  1328. else Exit;
  1329. end
  1330. else if (Length = 4) and (Buffer[0] = 'q') and (Buffer[1] = 'u') and
  1331. (Buffer[2] = 'o') and (Buffer[3] ='t') then
  1332. wc := '"'
  1333. else
  1334. Exit;
  1335. end; // with
  1336. BufAppend(FValue, wc);
  1337. Result := True;
  1338. end;
  1339. function TXMLReader.ParseRef(var ToFill: TWideCharBuf): Boolean; // [67]
  1340. var
  1341. Code: Integer;
  1342. begin
  1343. FSource.NextChar; // skip '&'
  1344. Result := CheckForChar('#');
  1345. if Result then
  1346. begin
  1347. Code := 0;
  1348. if CheckForChar('x') then
  1349. repeat
  1350. case FSource.FBuf^ of
  1351. '0'..'9': Code := Code * 16 + Ord(FSource.FBuf^) - Ord('0');
  1352. 'a'..'f': Code := Code * 16 + Ord(FSource.FBuf^) - (Ord('a') - 10);
  1353. 'A'..'F': Code := Code * 16 + Ord(FSource.FBuf^) - (Ord('A') - 10);
  1354. else
  1355. Break;
  1356. end;
  1357. FSource.NextChar;
  1358. until Code > $10FFFF
  1359. else
  1360. repeat
  1361. case FSource.FBuf^ of
  1362. '0'..'9': Code := Code * 10 + Ord(FSource.FBuf^) - Ord('0');
  1363. else
  1364. Break;
  1365. end;
  1366. FSource.NextChar;
  1367. until Code > $10FFFF;
  1368. case Code of
  1369. $01..$08, $0B..$0C, $0E..$1F:
  1370. if FXML11 then
  1371. BufAppend(ToFill, WideChar(Code))
  1372. else
  1373. FatalError('Invalid character reference');
  1374. $09, $0A, $0D, $20..$D7FF, $E000..$FFFD:
  1375. BufAppend(ToFill, WideChar(Code));
  1376. $10000..$10FFFF:
  1377. begin
  1378. BufAppend(ToFill, WideChar($D7C0 + (Code shr 10)));
  1379. BufAppend(ToFill, WideChar($DC00 xor (Code and $3FF)));
  1380. end;
  1381. else
  1382. FatalError('Invalid character reference');
  1383. end;
  1384. end
  1385. else CheckName;
  1386. ExpectChar(';');
  1387. end;
  1388. const
  1389. AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13];
  1390. GT_Delim: TSetOfChar = [#0, '>'];
  1391. { Parse attribute literal, producing plain string value in AttrData.FValueStr.
  1392. If entity references are encountered and FExpandEntities=False, also builds
  1393. a node chain starting from AttrData.FNext. Node chain is built only for the
  1394. first level. If NonCDATA=True, additionally normalizes whitespace in string value.
  1395. Returns True if value actually needed normalization }
  1396. function TXMLReader.ExpectAttValue(AttrData: PNodeData; NonCDATA: Boolean): Boolean;
  1397. var
  1398. wc: WideChar;
  1399. Delim: WideChar;
  1400. ent: TDOMEntityEx;
  1401. start: TObject;
  1402. curr: PNodeData;
  1403. StartPos: Integer;
  1404. begin
  1405. SkipQuote(Delim);
  1406. curr := AttrData;
  1407. FValue.Length := 0;
  1408. StartPos := 0;
  1409. start := FSource.FEntity;
  1410. repeat
  1411. wc := FSource.SkipUntil(FValue, AttrDelims);
  1412. if wc = '<' then
  1413. FatalError('Character ''<'' is not allowed in attribute value')
  1414. else if wc = '&' then
  1415. begin
  1416. if ParseRef(FValue) or ResolvePredefined then
  1417. Continue;
  1418. ent := EntityCheck(True);
  1419. if ((ent = nil) or (not FExpandEntities)) and (FSource.FEntity = start) then
  1420. begin
  1421. if FValue.Length > StartPos then
  1422. begin
  1423. curr := AllocAttributeValueChunk(curr);
  1424. curr^.FNodeType := ntText;
  1425. // without PWideChar typecast and in {$T-}, FPC treats '@' result as PAnsiChar...
  1426. SetString(curr^.FValueStr, PWideChar(@FValue.Buffer[StartPos]), FValue.Length-StartPos);
  1427. end;
  1428. curr := AllocAttributeValueChunk(curr);
  1429. curr^.FNodeType := ntEntityReference;
  1430. // TODO: this probably should be placed to 'name'
  1431. if ent = nil then
  1432. SetString(curr^.FValueStr, FName.Buffer, FName.Length)
  1433. else
  1434. curr^.FValueStr := ent.FName;
  1435. end;
  1436. StartPos := FValue.Length;
  1437. if Assigned(ent) then
  1438. ContextPush(ent);
  1439. end
  1440. else if wc <> #0 then
  1441. begin
  1442. FSource.NextChar;
  1443. if (wc = Delim) and (FSource.FEntity = start) then
  1444. Break;
  1445. if (wc = #10) or (wc = #9) or (wc = #13) then
  1446. wc := #32;
  1447. BufAppend(FValue, wc);
  1448. end
  1449. else
  1450. begin
  1451. if (FSource.FEntity = start) or not ContextPop then // #0
  1452. FatalError('Literal has no closing quote', -1);
  1453. StartPos := FValue.Length;
  1454. end;
  1455. until False;
  1456. if Assigned(attrData^.FNext) then
  1457. begin
  1458. FAttrCleanupFlag := True;
  1459. if FValue.Length > StartPos then
  1460. begin
  1461. curr := AllocAttributeValueChunk(curr);
  1462. curr^.FNodeType := ntText;
  1463. SetString(curr^.FValueStr, PWideChar(@FValue.Buffer[StartPos]), FValue.Length-StartPos);
  1464. end;
  1465. end;
  1466. if nonCDATA then
  1467. BufNormalize(FValue, Result)
  1468. else
  1469. Result := False;
  1470. SetString(attrData^.FValueStr, FValue.Buffer, FValue.Length);
  1471. end;
  1472. const
  1473. PrefixChar: array[Boolean] of string = ('', '%');
  1474. procedure TXMLReader.EntityToSource(AEntity: TDOMEntityEx; out Src: TXMLCharSource);
  1475. begin
  1476. if AEntity.FOnStack then
  1477. FatalError('Entity ''%s%s'' recursively references itself', [PrefixChar[AEntity.FIsPE], AEntity.FName]);
  1478. if (AEntity.SystemID <> '') and not AEntity.FPrefetched then
  1479. begin
  1480. if not ResolveEntity(AEntity.SystemID, AEntity.PublicID, AEntity.FURI, Src) then
  1481. begin
  1482. // TODO: a detailed message like SysErrorMessage(GetLastError) would be great here
  1483. ValidationError('Unable to resolve external entity ''%s''', [AEntity.FName]);
  1484. Src := nil;
  1485. Exit;
  1486. end;
  1487. end
  1488. else
  1489. begin
  1490. Src := TXMLCharSource.Create(AEntity.FReplacementText);
  1491. Src.FLineNo := AEntity.FStartLocation.Line;
  1492. Src.LFPos := Src.FBuf - AEntity.FStartLocation.LinePos;
  1493. // needed in case of prefetched external PE
  1494. if AEntity.SystemID <> '' then
  1495. Src.SystemID := AEntity.FURI;
  1496. end;
  1497. AEntity.FOnStack := True;
  1498. Src.FEntity := AEntity;
  1499. end;
  1500. function TXMLReader.ContextPush(AEntity: TDOMEntityEx): Boolean;
  1501. var
  1502. Src: TXMLCharSource;
  1503. begin
  1504. EntityToSource(AEntity, Src);
  1505. Result := Assigned(Src);
  1506. if Result then
  1507. Initialize(Src);
  1508. end;
  1509. function TXMLReader.ContextPop(Forced: Boolean): Boolean;
  1510. var
  1511. Src: TXMLCharSource;
  1512. Error: Boolean;
  1513. begin
  1514. Result := Assigned(FSource.FParent) and (Forced or (FSource.DTDSubsetType = dsNone));
  1515. if Result then
  1516. begin
  1517. Src := FSource.FParent;
  1518. Error := False;
  1519. if Assigned(FSource.FEntity) then
  1520. begin
  1521. TDOMEntityEx(FSource.FEntity).FOnStack := False;
  1522. TDOMEntityEx(FSource.FEntity).FCharCount := FSource.FCharCount;
  1523. // [28a] PE that was started between MarkupDecls may not end inside MarkupDecl
  1524. Error := TDOMEntityEx(FSource.FEntity).FBetweenDecls and FInsideDecl;
  1525. end;
  1526. FSource.Free;
  1527. FSource := Src;
  1528. // correct position of this error is after PE reference
  1529. if Error then
  1530. BadPENesting(esFatal);
  1531. end;
  1532. end;
  1533. function TXMLReader.EntityCheck(NoExternals: Boolean): TDOMEntityEx;
  1534. var
  1535. RefName: WideString;
  1536. cnt: Integer;
  1537. InnerReader: TXMLReader;
  1538. Src: TXMLCharSource;
  1539. begin
  1540. Result := nil;
  1541. SetString(RefName, FName.Buffer, FName.Length);
  1542. cnt := FName.Length+2;
  1543. if Assigned(FDocType) then
  1544. Result := FDocType.Entities.GetNamedItem(RefName) as TDOMEntityEx;
  1545. if Result = nil then
  1546. begin
  1547. if FStandalone or (FDocType = nil) or not (FHavePERefs or (FDocType.SystemID <> '')) then
  1548. FatalError('Reference to undefined entity ''%s''', [RefName], cnt)
  1549. else
  1550. ValidationError('Undefined entity ''%s'' referenced', [RefName], cnt);
  1551. Exit;
  1552. end;
  1553. if FStandalone and Result.FExternallyDeclared then
  1554. FatalError('Standalone constraint violation', cnt);
  1555. if Result.NotationName <> '' then
  1556. FatalError('Reference to unparsed entity ''%s''', [RefName], cnt);
  1557. if NoExternals and (Result.SystemID <> '') then
  1558. FatalError('External entity reference is not allowed in attribute value', cnt);
  1559. if not Result.FResolved then
  1560. begin
  1561. // To build children of the entity itself, we must parse it "out of context"
  1562. InnerReader := TXMLReader.Create(FCtrl);
  1563. try
  1564. InnerReader.FAttrTag := FAttrTag;
  1565. EntityToSource(Result, Src);
  1566. Result.SetReadOnly(False);
  1567. if Assigned(Src) then
  1568. InnerReader.ProcessFragment(Src, Result);
  1569. Result.FResolved := True;
  1570. finally
  1571. FAttrTag := InnerReader.FAttrTag;
  1572. InnerReader.Free;
  1573. Result.FOnStack := False;
  1574. Result.SetReadOnly(True);
  1575. end;
  1576. end;
  1577. // at this point we know the charcount of the entity being included
  1578. Inc(FSource.FCharCount, Result.FCharCount - cnt);
  1579. CheckMaxChars;
  1580. end;
  1581. procedure TXMLReader.StartPE;
  1582. var
  1583. PEnt: TDOMEntityEx;
  1584. begin
  1585. PEnt := nil;
  1586. if Assigned(FPEMap) then
  1587. PEnt := FPEMap.Get(FName.Buffer, FName.Length) as TDOMEntityEx;
  1588. if PEnt = nil then
  1589. begin
  1590. ValidationErrorWithName('Undefined parameter entity ''%s'' referenced', FName.Length+2);
  1591. // cease processing declarations, unless document is standalone.
  1592. FDTDProcessed := FStandalone;
  1593. Exit;
  1594. end;
  1595. { cache an external PE so it's only fetched once }
  1596. if (PEnt.SystemID <> '') and (not PEnt.FPrefetched) and (not PrefetchEntity(PEnt)) then
  1597. begin
  1598. FDTDProcessed := FStandalone;
  1599. Exit;
  1600. end;
  1601. Inc(FSource.FCharCount, PEnt.FCharCount);
  1602. CheckMaxChars;
  1603. PEnt.FBetweenDecls := not FInsideDecl;
  1604. ContextPush(PEnt);
  1605. FHavePERefs := True;
  1606. end;
  1607. function TXMLReader.PrefetchEntity(AEntity: TDOMEntityEx): Boolean;
  1608. begin
  1609. Result := ContextPush(AEntity);
  1610. if Result then
  1611. try
  1612. FValue.Length := 0;
  1613. FSource.SkipUntil(FValue, [#0]);
  1614. SetString(AEntity.FReplacementText, FValue.Buffer, FValue.Length);
  1615. AEntity.FCharCount := FValue.Length;
  1616. AEntity.FStartLocation.Line := 1;
  1617. AEntity.FStartLocation.LinePos := 1;
  1618. AEntity.FURI := FSource.SystemID; // replace base URI with absolute one
  1619. finally
  1620. ContextPop;
  1621. AEntity.FPrefetched := True;
  1622. FValue.Length := 0;
  1623. end;
  1624. end;
  1625. const
  1626. LiteralDelims: array[TLiteralType] of TSetOfChar = (
  1627. [#0, '''', '"'], // ltPlain
  1628. [#0, '''', '"', #13, #10], // ltPubid
  1629. [#0, '%', '&', '''', '"'] // ltEntity
  1630. );
  1631. function TXMLReader.ParseLiteral(var ToFill: TWideCharBuf; aType: TLiteralType;
  1632. Required: Boolean): Boolean;
  1633. var
  1634. start: TObject;
  1635. wc, Delim: WideChar;
  1636. dummy: Boolean;
  1637. begin
  1638. SkipQuote(Delim, Required);
  1639. Result := (Delim <> #0);
  1640. if not Result then
  1641. Exit;
  1642. ToFill.Length := 0;
  1643. start := FSource.FEntity;
  1644. repeat
  1645. wc := FSource.SkipUntil(ToFill, LiteralDelims[aType]);
  1646. if wc = '%' then { ltEntity only }
  1647. begin
  1648. FSource.NextChar;
  1649. CheckName;
  1650. ExpectChar(';');
  1651. if FSource.DTDSubsetType = dsInternal then
  1652. FatalError('PE reference not allowed here in internal subset', FName.Length+2);
  1653. StartPE;
  1654. end
  1655. else if wc = '&' then { ltEntity }
  1656. begin
  1657. if ParseRef(ToFill) then // charRefs always expanded
  1658. Continue;
  1659. BufAppend(ToFill, '&');
  1660. BufAppendChunk(ToFill, FName.Buffer, FName.Buffer + FName.Length);
  1661. BufAppend(ToFill, ';');
  1662. end
  1663. else if wc <> #0 then
  1664. begin
  1665. FSource.NextChar;
  1666. if (wc = #10) or (wc = #13) then
  1667. wc := #32
  1668. // terminating delimiter must be in the same context as the starting one
  1669. else if (wc = Delim) and (start = FSource.FEntity) then
  1670. Break;
  1671. BufAppend(ToFill, wc);
  1672. end
  1673. else if (FSource.FEntity = start) or not ContextPop then // #0
  1674. FatalError('Literal has no closing quote', -1);
  1675. until False;
  1676. if aType = ltPubid then
  1677. BufNormalize(ToFill, dummy);
  1678. end;
  1679. function TXMLReader.SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean;
  1680. var
  1681. wc: WideChar;
  1682. begin
  1683. Result := False;
  1684. StoreLocation(FTokenStart);
  1685. repeat
  1686. wc := FSource.SkipUntil(FValue, Delim);
  1687. if wc <> #0 then
  1688. begin
  1689. FSource.NextChar;
  1690. if (FValue.Length > ord(c2 <> #0)) then
  1691. begin
  1692. if (FValue.Buffer[FValue.Length-1] = c1) and
  1693. ((c2 = #0) or ((c2 <> #0) and (FValue.Buffer[FValue.Length-2] = c2))) then
  1694. begin
  1695. Dec(FValue.Length, ord(c2 <> #0) + 1);
  1696. Result := True;
  1697. Exit;
  1698. end;
  1699. end;
  1700. BufAppend(FValue, wc);
  1701. end;
  1702. until wc = #0;
  1703. end;
  1704. procedure TXMLReader.ParseComment(discard: Boolean); // [15]
  1705. var
  1706. SaveLength: Integer;
  1707. begin
  1708. ExpectString('--');
  1709. SaveLength := FValue.Length;
  1710. if not SkipUntilSeq([#0, '-'], '-') then
  1711. FatalError('Unterminated comment', -1);
  1712. ExpectChar('>');
  1713. if not discard then
  1714. begin
  1715. FCurrNode := @FNodeStack[FNesting+1];
  1716. FCurrNode^.FNodeType := ntComment;
  1717. FCurrNode^.FQName := nil;
  1718. FCurrNode^.FValueStart := @FValue.Buffer[SaveLength];
  1719. FCurrNode^.FValueLength := FValue.Length-SaveLength;
  1720. end;
  1721. FValue.Length := SaveLength;
  1722. end;
  1723. procedure TXMLReader.ParsePI; // [16]
  1724. begin
  1725. FSource.NextChar; // skip '?'
  1726. CheckName;
  1727. CheckNCName;
  1728. with FName do
  1729. if (Length = 3) and
  1730. ((Buffer[0] = 'X') or (Buffer[0] = 'x')) and
  1731. ((Buffer[1] = 'M') or (Buffer[1] = 'm')) and
  1732. ((Buffer[2] = 'L') or (Buffer[2] = 'l')) then
  1733. begin
  1734. if not BufEquals(FName, 'xml') then
  1735. FatalError('''xml'' is a reserved word; it must be lowercase', FName.Length)
  1736. else
  1737. FatalError('XML declaration is not allowed here', FName.Length);
  1738. end;
  1739. if FSource.FBuf^ <> '?' then
  1740. SkipS(True);
  1741. FValue.Length := 0;
  1742. if not SkipUntilSeq(GT_Delim, '?') then
  1743. FatalError('Unterminated processing instruction', -1);
  1744. SetNodeInfoWithValue(ntProcessingInstruction,
  1745. doc.Names.FindOrAdd(FName.Buffer, FName.Length));
  1746. end;
  1747. procedure TXMLReader.CreatePINode;
  1748. var
  1749. NameStr, ValueStr: WideString;
  1750. PINode: TDOMProcessingInstruction;
  1751. begin
  1752. SetString(NameStr, FName.Buffer, FName.Length);
  1753. SetString(ValueStr, FValue.Buffer, FValue.Length);
  1754. // SAX: ContentHandler.ProcessingInstruction(Name, Value);
  1755. if FCurrContentType = ctEmpty then
  1756. ValidationError('Processing instructions are not allowed within EMPTY elements', []);
  1757. PINode := Doc.CreateProcessingInstruction(NameStr, ValueStr);
  1758. FCursorStack[FNesting].InternalAppend(PINode);
  1759. end;
  1760. const
  1761. vers: array[Boolean] of TXMLVersion = (xmlVersion10, xmlVersion11);
  1762. procedure TXMLReader.ParseXmlOrTextDecl(TextDecl: Boolean);
  1763. var
  1764. TmpStr: WideString;
  1765. Ver: TXMLVersion;
  1766. Delim: WideChar;
  1767. buf: array[0..31] of WideChar;
  1768. I: Integer;
  1769. node: TDOMNode;
  1770. begin
  1771. SkipS(True);
  1772. if TextDecl then
  1773. node := TDOMNode(FSource.FEntity)
  1774. else
  1775. node := doc;
  1776. // [24] VersionInfo: optional in TextDecl, required in XmlDecl
  1777. if (not TextDecl) or (FSource.FBuf^ = 'v') then
  1778. begin
  1779. ExpectString('version');
  1780. ExpectEq;
  1781. SkipQuote(Delim);
  1782. I := 0;
  1783. while (I < 3) and (FSource.FBuf^ <> Delim) do
  1784. begin
  1785. buf[I] := FSource.FBuf^;
  1786. Inc(I);
  1787. FSource.NextChar;
  1788. end;
  1789. if (I <> 3) or (buf[0] <> '1') or (buf[1] <> '.') or
  1790. ((buf[2] <> '0') and (buf[2] <> '1')) then
  1791. FatalError('Illegal version number', -1);
  1792. ExpectChar(Delim);
  1793. Ver := vers[buf[2] = '1'];
  1794. if TextDecl and (Ver = xmlVersion11) and not FXML11 then
  1795. FatalError('XML 1.0 document cannot invoke XML 1.1 entities', -1);
  1796. if Assigned(node) then { it is nil for external DTD subset }
  1797. TDOMTopNodeEx(node).FXMLVersion := Ver;
  1798. if TextDecl or (FSource.FBuf^ <> '?') then
  1799. SkipS(True);
  1800. end;
  1801. // [80] EncodingDecl: required in TextDecl, optional in XmlDecl
  1802. if TextDecl or (FSource.FBuf^ = 'e') then
  1803. begin
  1804. ExpectString('encoding');
  1805. ExpectEq;
  1806. SkipQuote(Delim);
  1807. I := 0;
  1808. while (I < 30) and (FSource.FBuf^ <> Delim) and (FSource.FBuf^ < #127) and
  1809. ((Char(ord(FSource.FBuf^)) in ['A'..'Z', 'a'..'z']) or
  1810. ((I > 0) and (Char(ord(FSource.FBuf^)) in ['0'..'9', '.', '-', '_']))) do
  1811. begin
  1812. buf[I] := FSource.FBuf^;
  1813. Inc(I);
  1814. FSource.NextChar;
  1815. end;
  1816. if not CheckForChar(Delim) then
  1817. FatalError('Illegal encoding name', i);
  1818. SetString(TmpStr, buf, i);
  1819. if not FSource.SetEncoding(TmpStr) then // <-- Wide2Ansi conversion here
  1820. FatalError('Encoding ''%s'' is not supported', [TmpStr], i+1);
  1821. // getting here means that specified encoding is supported
  1822. // TODO: maybe assign the 'preferred' encoding name?
  1823. if Assigned(node) then
  1824. TDOMTopNodeEx(node).FXMLEncoding := TmpStr;
  1825. if FSource.FBuf^ <> '?' then
  1826. SkipS(not TextDecl);
  1827. end;
  1828. // [32] SDDecl: forbidden in TextDecl, optional in XmlDecl
  1829. if (not TextDecl) and (FSource.FBuf^ = 's') then
  1830. begin
  1831. ExpectString('standalone');
  1832. ExpectEq;
  1833. SkipQuote(Delim);
  1834. if FSource.Matches('yes') then
  1835. FStandalone := True
  1836. else if not FSource.Matches('no') then
  1837. FatalError('Only "yes" or "no" are permitted as values of "standalone"', -1);
  1838. ExpectChar(Delim);
  1839. SkipS;
  1840. end;
  1841. ExpectString('?>');
  1842. { Switch to 1.1 rules only after declaration is parsed completely. This is to
  1843. ensure that NEL and LSEP within declaration are rejected (rmt-056, rmt-057) }
  1844. if Ver = xmlVersion11 then
  1845. FXML11 := True;
  1846. end;
  1847. procedure TXMLReader.DTDReloadHook;
  1848. var
  1849. p: PWideChar;
  1850. begin
  1851. { FSource converts CR, NEL and LSEP linebreaks to LF, and CR-NEL sequences to CR-LF.
  1852. We must further remove the CR chars and have only LF's left. }
  1853. p := FDTDStartPos;
  1854. while p < FSource.FBuf do
  1855. begin
  1856. while (p < FSource.FBuf) and (p^ <> #13) do
  1857. Inc(p);
  1858. BufAppendChunk(FIntSubset, FDTDStartPos, p);
  1859. if p^ = #13 then
  1860. Inc(p);
  1861. FDTDStartPos := p;
  1862. end;
  1863. FDTDStartPos := TXMLDecodingSource(FSource).FBufStart;
  1864. end;
  1865. procedure TXMLReader.ParseDoctypeDecl; // [28]
  1866. var
  1867. Src: TXMLCharSource;
  1868. begin
  1869. if FState >= rsDTD then
  1870. FatalError('Markup declaration is not allowed here');
  1871. if FDisallowDoctype then
  1872. FatalError('Document type is prohibited by parser settings');
  1873. ExpectString('DOCTYPE');
  1874. SkipS(True);
  1875. FDocType := TDOMDocumentTypeEx(TDOMDocumentType.Create(doc));
  1876. FDTDProcessed := True; // assume success
  1877. FOwnsDoctype := True;
  1878. FState := rsDTD;
  1879. FDocType.FName := ExpectName;
  1880. SkipS(True);
  1881. ParseExternalID(FDocType.FSystemID, FDocType.FPublicID, False);
  1882. SkipS;
  1883. if CheckForChar('[') then
  1884. begin
  1885. BufAllocate(FIntSubset, 256);
  1886. FSource.DTDSubsetType := dsInternal;
  1887. try
  1888. FDTDStartPos := FSource.FBuf;
  1889. ParseMarkupDecl;
  1890. DTDReloadHook; // fetch last chunk
  1891. SetString(FDocType.FInternalSubset, FIntSubset.Buffer, FIntSubset.Length);
  1892. finally
  1893. FreeMem(FIntSubset.Buffer);
  1894. FSource.DTDSubsetType := dsNone;
  1895. end;
  1896. ExpectChar(']');
  1897. SkipS;
  1898. end;
  1899. ExpectChar('>');
  1900. if (FDocType.SystemID <> '') then
  1901. begin
  1902. if ResolveEntity(FDocType.SystemID, FDocType.PublicID, FSource.SystemID, Src) then
  1903. begin
  1904. Initialize(Src);
  1905. try
  1906. Src.DTDSubsetType := dsExternal;
  1907. ParseMarkupDecl;
  1908. finally
  1909. ContextPop(True);
  1910. end;
  1911. end
  1912. else
  1913. begin
  1914. ValidationError('Unable to resolve external DTD subset', []);
  1915. FDTDProcessed := FStandalone;
  1916. end;
  1917. end;
  1918. ValidateDTD;
  1919. FDocType.SetReadOnly(True);
  1920. FState := rsAfterDTD;
  1921. end;
  1922. procedure TXMLReader.ExpectEq; // [25]
  1923. begin
  1924. if FSource.FBuf^ <> '=' then
  1925. SkipS;
  1926. if FSource.FBuf^ <> '=' then
  1927. FatalError('Expected "="');
  1928. FSource.NextChar;
  1929. SkipS;
  1930. end;
  1931. { DTD stuff }
  1932. procedure TXMLReader.BadPENesting(S: TErrorSeverity);
  1933. begin
  1934. if (S = esFatal) or FValidate then
  1935. DoError(S, 'Parameter entities must be properly nested');
  1936. end;
  1937. procedure TXMLReader.StandaloneError(LineOffs: Integer);
  1938. begin
  1939. ValidationError('Standalone constriant violation', [], LineOffs);
  1940. end;
  1941. function TXMLReader.ParseQuantity: TCPQuant;
  1942. begin
  1943. case FSource.FBuf^ of
  1944. '?': Result := cqZeroOrOnce;
  1945. '*': Result := cqZeroOrMore;
  1946. '+': Result := cqOnceOrMore;
  1947. else
  1948. Result := cqOnce;
  1949. Exit;
  1950. end;
  1951. FSource.NextChar;
  1952. end;
  1953. function TXMLReader.FindOrCreateElDef: TElementDecl;
  1954. var
  1955. p: PHashItem;
  1956. begin
  1957. CheckName;
  1958. p := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
  1959. Result := TElementDecl(p^.Data);
  1960. if Result = nil then
  1961. begin
  1962. Result := TElementDecl.Create;
  1963. p^.Data := Result;
  1964. end;
  1965. end;
  1966. procedure TXMLReader.ExpectChoiceOrSeq(CP: TContentParticle); // [49], [50]
  1967. var
  1968. Delim: WideChar;
  1969. CurrentEntity: TObject;
  1970. CurrentCP: TContentParticle;
  1971. begin
  1972. Delim := #0;
  1973. repeat
  1974. CurrentCP := CP.Add;
  1975. SkipWhitespace;
  1976. if CheckForChar('(') then
  1977. begin
  1978. CurrentEntity := FSource.FEntity;
  1979. ExpectChoiceOrSeq(CurrentCP);
  1980. if CurrentEntity <> FSource.FEntity then
  1981. BadPENesting;
  1982. FSource.NextChar;
  1983. end
  1984. else
  1985. CurrentCP.Def := FindOrCreateElDef;
  1986. CurrentCP.CPQuant := ParseQuantity;
  1987. SkipWhitespace;
  1988. if FSource.FBuf^ = ')' then
  1989. Break;
  1990. if Delim = #0 then
  1991. begin
  1992. if (FSource.FBuf^ = '|') or (FSource.FBuf^ = ',') then
  1993. Delim := FSource.FBuf^
  1994. else
  1995. FatalError('Expected pipe or comma delimiter');
  1996. end
  1997. else
  1998. if FSource.FBuf^ <> Delim then
  1999. FatalError(Delim);
  2000. FSource.NextChar; // skip delimiter
  2001. until False;
  2002. if Delim = '|' then
  2003. CP.CPType := ctChoice
  2004. else
  2005. CP.CPType := ctSeq; // '(foo)' is a sequence!
  2006. end;
  2007. procedure TXMLReader.ParseElementDecl; // [45]
  2008. var
  2009. ElDef: TElementDecl;
  2010. CurrentEntity: TObject;
  2011. I: Integer;
  2012. CP: TContentParticle;
  2013. Typ: TElementContentType;
  2014. ExtDecl: Boolean;
  2015. begin
  2016. CP := nil;
  2017. Typ := ctUndeclared; // satisfy compiler
  2018. ExpectWhitespace;
  2019. ElDef := FindOrCreateElDef;
  2020. if ElDef.ContentType <> ctUndeclared then
  2021. ValidationErrorWithName('Duplicate declaration of element ''%s''', FName.Length);
  2022. ExtDecl := FSource.DTDSubsetType <> dsInternal;
  2023. ExpectWhitespace;
  2024. if FSource.Matches('EMPTY') then
  2025. Typ := ctEmpty
  2026. else if FSource.Matches('ANY') then
  2027. Typ := ctAny
  2028. else if CheckForChar('(') then
  2029. begin
  2030. CP := TContentParticle.Create;
  2031. try
  2032. CurrentEntity := FSource.FEntity;
  2033. SkipWhitespace;
  2034. if FSource.Matches('#PCDATA') then // Mixed section [51]
  2035. begin
  2036. SkipWhitespace;
  2037. Typ := ctMixed;
  2038. while FSource.FBuf^ <> ')' do
  2039. begin
  2040. ExpectChar('|');
  2041. SkipWhitespace;
  2042. with CP.Add do
  2043. begin
  2044. Def := FindOrCreateElDef;
  2045. for I := CP.ChildCount-2 downto 0 do
  2046. if Def = CP.Children[I].Def then
  2047. ValidationError('Duplicate token in mixed section', [], FName.Length);
  2048. end;
  2049. SkipWhitespace;
  2050. end;
  2051. if CurrentEntity <> FSource.FEntity then
  2052. BadPENesting;
  2053. FSource.NextChar;
  2054. if (not CheckForChar('*')) and (CP.ChildCount > 0) then
  2055. FatalError(WideChar('*'));
  2056. CP.CPQuant := cqZeroOrMore;
  2057. CP.CPType := ctChoice;
  2058. end
  2059. else // Children section [47]
  2060. begin
  2061. Typ := ctChildren;
  2062. ExpectChoiceOrSeq(CP);
  2063. if CurrentEntity <> FSource.FEntity then
  2064. BadPENesting;
  2065. FSource.NextChar;
  2066. CP.CPQuant := ParseQuantity;
  2067. end;
  2068. except
  2069. CP.Free;
  2070. raise;
  2071. end;
  2072. end
  2073. else
  2074. FatalError('Invalid content specification');
  2075. // SAX: DeclHandler.ElementDecl(name, model);
  2076. if FDTDProcessed and (ElDef.ContentType = ctUndeclared) then
  2077. begin
  2078. ElDef.ExternallyDeclared := ExtDecl;
  2079. ElDef.ContentType := Typ;
  2080. ElDef.RootCP := CP;
  2081. end
  2082. else
  2083. CP.Free;
  2084. end;
  2085. procedure TXMLReader.ParseNotationDecl; // [82]
  2086. var
  2087. NameStr, SysID, PubID: WideString;
  2088. begin
  2089. ExpectWhitespace;
  2090. NameStr := ExpectName;
  2091. CheckNCName;
  2092. ExpectWhitespace;
  2093. if not ParseExternalID(SysID, PubID, True) then
  2094. FatalError('Expected external or public ID');
  2095. if FDTDProcessed then
  2096. DoNotationDecl(NameStr, PubID, SysID);
  2097. end;
  2098. const
  2099. AttrDataTypeNames: array[TAttrDataType] of WideString = (
  2100. 'CDATA',
  2101. 'ID',
  2102. 'IDREF',
  2103. 'IDREFS',
  2104. 'ENTITY',
  2105. 'ENTITIES',
  2106. 'NMTOKEN',
  2107. 'NMTOKENS',
  2108. 'NOTATION'
  2109. );
  2110. procedure TXMLReader.ParseAttlistDecl; // [52]
  2111. var
  2112. ElDef: TElementDecl;
  2113. AttDef: TAttributeDef;
  2114. dt: TAttrDataType;
  2115. Found, DiscardIt: Boolean;
  2116. Offsets: array [Boolean] of Integer;
  2117. attrName: PHashItem;
  2118. begin
  2119. ExpectWhitespace;
  2120. ElDef := FindOrCreateElDef;
  2121. SkipWhitespace;
  2122. while FSource.FBuf^ <> '>' do
  2123. begin
  2124. CheckName;
  2125. ExpectWhitespace;
  2126. attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
  2127. AttDef := TAttributeDef.Create(attrName, FColonPos);
  2128. try
  2129. AttDef.ExternallyDeclared := FSource.DTDSubsetType <> dsInternal;
  2130. // In case of duplicate declaration of the same attribute, we must discard it,
  2131. // not modifying ElDef, and suppressing certain validation errors.
  2132. DiscardIt := (not FDTDProcessed) or Assigned(ElDef.GetAttrDef(attrName));
  2133. if CheckForChar('(') then // [59]
  2134. begin
  2135. AttDef.DataType := dtNmToken;
  2136. repeat
  2137. SkipWhitespace;
  2138. CheckName([cnToken]);
  2139. if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
  2140. ValidationError('Duplicate token in enumerated attibute declaration', [], FName.Length);
  2141. SkipWhitespace;
  2142. until not CheckForChar('|');
  2143. ExpectChar(')');
  2144. ExpectWhitespace;
  2145. end
  2146. else
  2147. begin
  2148. StoreLocation(FTokenStart);
  2149. // search topside-up so that e.g. NMTOKENS is matched before NMTOKEN
  2150. for dt := dtNotation downto dtCData do
  2151. begin
  2152. Found := FSource.Matches(AttrDataTypeNames[dt]);
  2153. if Found then
  2154. Break;
  2155. end;
  2156. if Found and SkipWhitespace then
  2157. begin
  2158. AttDef.DataType := dt;
  2159. if (dt = dtId) and not DiscardIt then
  2160. begin
  2161. if Assigned(ElDef.IDAttr) then
  2162. ValidationError('Only one attribute of type ID is allowed per element',[])
  2163. else
  2164. ElDef.IDAttr := AttDef;
  2165. end
  2166. else if dt = dtNotation then // no test cases for these ?!
  2167. begin
  2168. if not DiscardIt then
  2169. begin
  2170. if Assigned(ElDef.NotationAttr) then
  2171. ValidationError('Only one attribute of type NOTATION is allowed per element',[])
  2172. else
  2173. ElDef.NotationAttr := AttDef;
  2174. if ElDef.ContentType = ctEmpty then
  2175. ValidationError('NOTATION attributes are not allowed on EMPTY elements',[]);
  2176. end;
  2177. ExpectChar('(');
  2178. repeat
  2179. SkipWhitespace;
  2180. StoreLocation(FTokenStart);
  2181. CheckName;
  2182. CheckNCName;
  2183. if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
  2184. ValidationError('Duplicate token in NOTATION attribute declaration',[], FName.Length);
  2185. if not DiscardIt then
  2186. AddForwardRef(FNotationRefs, FName.Buffer, FName.Length);
  2187. SkipWhitespace;
  2188. until not CheckForChar('|');
  2189. ExpectChar(')');
  2190. ExpectWhitespace;
  2191. end;
  2192. end
  2193. else
  2194. begin
  2195. // don't report 'expected whitespace' if token does not match completely
  2196. Offsets[False] := 0;
  2197. Offsets[True] := Length(AttrDataTypeNames[dt]);
  2198. if Found and (FSource.FBuf^ < 'A') then
  2199. ExpectWhitespace
  2200. else
  2201. FatalError('Illegal attribute type for ''%s''', [attrName^.Key], Offsets[Found]);
  2202. end;
  2203. end;
  2204. StoreLocation(FTokenStart);
  2205. if FSource.Matches('#REQUIRED') then
  2206. AttDef.Default := adRequired
  2207. else if FSource.Matches('#IMPLIED') then
  2208. AttDef.Default := adImplied
  2209. else if FSource.Matches('#FIXED') then
  2210. begin
  2211. AttDef.Default := adFixed;
  2212. ExpectWhitespace;
  2213. end
  2214. else
  2215. AttDef.Default := adDefault;
  2216. if AttDef.Default in [adDefault, adFixed] then
  2217. begin
  2218. if AttDef.DataType = dtId then
  2219. ValidationError('An attribute of type ID cannot have a default value',[]);
  2220. // See comments to valid-sa-094: PE expansion should be disabled in AttDef.
  2221. ExpectAttValue(AttDef.Data, dt <> dtCDATA);
  2222. if not ValidateAttrSyntax(AttDef, AttDef.Data^.FValueStr) then
  2223. ValidationError('Default value for attribute ''%s'' has wrong syntax', [attrName^.Key]);
  2224. end;
  2225. // SAX: DeclHandler.AttributeDecl(...)
  2226. if DiscardIt then
  2227. AttDef.Free
  2228. else
  2229. ElDef.AddAttrDef(AttDef);
  2230. except
  2231. AttDef.Free;
  2232. raise;
  2233. end;
  2234. SkipWhitespace;
  2235. end;
  2236. end;
  2237. procedure TXMLReader.ParseEntityDecl; // [70]
  2238. var
  2239. IsPE, Exists: Boolean;
  2240. Entity: TDOMEntityEx;
  2241. Map: THashTable;
  2242. Item: PHashItem;
  2243. begin
  2244. if not SkipWhitespace(True) then
  2245. FatalError('Expected whitespace');
  2246. IsPE := CheckForChar('%');
  2247. if IsPE then // [72]
  2248. begin
  2249. ExpectWhitespace;
  2250. if FPEMap = nil then
  2251. FPEMap := THashTable.Create(64, True);
  2252. Map := FPEMap;
  2253. end
  2254. else
  2255. begin
  2256. if FGEMap = nil then
  2257. FGEMap := THashTable.Create(64, False);
  2258. Map := FGEMap;
  2259. end;
  2260. Entity := TDOMEntityEx.Create(Doc);
  2261. Entity.SetReadOnly(True);
  2262. try
  2263. Entity.FExternallyDeclared := FSource.DTDSubsetType <> dsInternal;
  2264. Entity.FIsPE := IsPE;
  2265. Entity.FName := ExpectName;
  2266. CheckNCName;
  2267. Item := Map.FindOrAdd(FName.Buffer, FName.Length, Exists);
  2268. ExpectWhitespace;
  2269. // remember where the entity is declared
  2270. Entity.FURI := FSource.SystemID;
  2271. if FEntityValue.Buffer = nil then
  2272. BufAllocate(FEntityValue, 256);
  2273. if ParseLiteral(FEntityValue, ltEntity, False) then
  2274. begin
  2275. SetString(Entity.FReplacementText, FEntityValue.Buffer, FEntityValue.Length);
  2276. Entity.FCharCount := FEntityValue.Length;
  2277. Entity.FStartLocation := FTokenStart;
  2278. end
  2279. else
  2280. begin
  2281. if not ParseExternalID(Entity.FSystemID, Entity.FPublicID, False) then
  2282. FatalError('Expected entity value or external ID');
  2283. if not IsPE then // [76]
  2284. begin
  2285. if FSource.FBuf^ <> '>' then
  2286. ExpectWhitespace;
  2287. if FSource.Matches('NDATA') then
  2288. begin
  2289. ExpectWhitespace;
  2290. StoreLocation(FTokenStart);
  2291. Entity.FNotationName := ExpectName;
  2292. AddForwardRef(FNotationRefs, FName.Buffer, FName.Length);
  2293. // SAX: DTDHandler.UnparsedEntityDecl(...);
  2294. end;
  2295. end;
  2296. end;
  2297. except
  2298. Entity.Free;
  2299. raise;
  2300. end;
  2301. // Repeated declarations of same entity are legal but must be ignored
  2302. if FDTDProcessed and not Exists then
  2303. begin
  2304. Item^.Data := Entity;
  2305. if not IsPE then
  2306. FDocType.Entities.SetNamedItem(Entity);
  2307. end
  2308. else
  2309. Entity.Free;
  2310. end;
  2311. procedure TXMLReader.ParseMarkupDecl; // [29]
  2312. var
  2313. IncludeLevel: Integer;
  2314. IgnoreLevel: Integer;
  2315. CurrentEntity: TObject;
  2316. IncludeLoc: TLocation;
  2317. IgnoreLoc: TLocation;
  2318. wc: WideChar;
  2319. CondType: (ctUnknown, ctInclude, ctIgnore);
  2320. begin
  2321. IncludeLevel := 0;
  2322. IgnoreLevel := 0;
  2323. repeat
  2324. SkipWhitespace;
  2325. if (FSource.FBuf^ = ']') and (IncludeLevel > 0) then
  2326. begin
  2327. ExpectString(']]>');
  2328. Dec(IncludeLevel);
  2329. Continue;
  2330. end;
  2331. if not CheckForChar('<') then
  2332. Break;
  2333. CurrentEntity := FSource.FEntity;
  2334. if FSource.FBuf^ = '?' then
  2335. begin
  2336. ParsePI;
  2337. CreatePINode;
  2338. end
  2339. else
  2340. begin
  2341. ExpectChar('!');
  2342. if FSource.FBuf^ = '-' then
  2343. ParseComment(True)
  2344. else if CheckForChar('[') then
  2345. begin
  2346. if FSource.DTDSubsetType = dsInternal then
  2347. FatalError('Conditional sections are not allowed in internal subset', 1);
  2348. SkipWhitespace;
  2349. CondType := ctUnknown; // satisfy compiler
  2350. if FSource.Matches('INCLUDE') then
  2351. CondType := ctInclude
  2352. else if FSource.Matches('IGNORE') then
  2353. CondType := ctIgnore
  2354. else
  2355. FatalError('Expected "INCLUDE" or "IGNORE"');
  2356. SkipWhitespace;
  2357. if CurrentEntity <> FSource.FEntity then
  2358. BadPENesting;
  2359. ExpectChar('[');
  2360. if CondType = ctInclude then
  2361. begin
  2362. if IncludeLevel = 0 then
  2363. StoreLocation(IncludeLoc);
  2364. Inc(IncludeLevel);
  2365. end
  2366. else if CondType = ctIgnore then
  2367. begin
  2368. StoreLocation(IgnoreLoc);
  2369. IgnoreLevel := 1;
  2370. repeat
  2371. FValue.Length := 0;
  2372. wc := FSource.SkipUntil(FValue, [#0, '<', ']']);
  2373. if FSource.Matches('<![') then
  2374. Inc(IgnoreLevel)
  2375. else if FSource.Matches(']]>') then
  2376. Dec(IgnoreLevel)
  2377. else if wc <> #0 then
  2378. FSource.NextChar
  2379. else // PE's aren't recognized in ignore section, cannot ContextPop()
  2380. DoErrorPos(esFatal, 'IGNORE section is not closed', IgnoreLoc);
  2381. until IgnoreLevel=0;
  2382. end;
  2383. end
  2384. else
  2385. begin
  2386. FInsideDecl := True;
  2387. if FSource.Matches('ELEMENT') then
  2388. ParseElementDecl
  2389. else if FSource.Matches('ENTITY') then
  2390. ParseEntityDecl
  2391. else if FSource.Matches('ATTLIST') then
  2392. ParseAttlistDecl
  2393. else if FSource.Matches('NOTATION') then
  2394. ParseNotationDecl
  2395. else
  2396. FatalError('Illegal markup declaration');
  2397. SkipWhitespace;
  2398. if CurrentEntity <> FSource.FEntity then
  2399. BadPENesting;
  2400. ExpectChar('>');
  2401. FInsideDecl := False;
  2402. end;
  2403. end;
  2404. until False;
  2405. if IncludeLevel > 0 then
  2406. DoErrorPos(esFatal, 'INCLUDE section is not closed', IncludeLoc);
  2407. if (FSource.DTDSubsetType = dsInternal) and (FSource.FBuf^ = ']') then
  2408. Exit;
  2409. if FSource.FBuf^ <> #0 then
  2410. FatalError('Illegal character in DTD');
  2411. end;
  2412. procedure TXMLReader.ProcessDTD(ASource: TXMLCharSource);
  2413. begin
  2414. doc := TXMLDocument.Create;
  2415. FDocType := TDOMDocumentTypeEx.Create(doc);
  2416. // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
  2417. doc.AppendChild(FDocType);
  2418. NSPrepare;
  2419. Initialize(ASource);
  2420. ParseMarkupDecl;
  2421. end;
  2422. procedure TXMLReader.AppendReference(cur: TDOMNode; AEntity: TDOMEntityEx);
  2423. var
  2424. s: WideString;
  2425. begin
  2426. if AEntity = nil then
  2427. SetString(s, FName.Buffer, FName.Length)
  2428. else
  2429. s := AEntity.nodeName;
  2430. cur.AppendChild(doc.CreateEntityReference(s));
  2431. end;
  2432. // The code below does the bulk of the parsing, and must be as fast as possible.
  2433. // To minimize CPU cache effects, methods from different classes are kept together
  2434. function TXMLDecodingSource.SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  2435. wsflag: PBoolean): WideChar;
  2436. var
  2437. old: PWideChar;
  2438. nonws: Boolean;
  2439. wc: WideChar;
  2440. begin
  2441. nonws := False;
  2442. repeat
  2443. old := FBuf;
  2444. repeat
  2445. wc := FBuf^;
  2446. if (wc = #10) or (wc = #13) or (FXML11Rules and ((wc = #$85) or
  2447. (wc = #$2028))) then
  2448. begin
  2449. // strictly this is needed only for 2-byte lineendings
  2450. BufAppendChunk(ToFill, old, FBuf);
  2451. NewLine;
  2452. old := FBuf;
  2453. wc := FBuf^
  2454. end
  2455. else if ((wc < #32) and (not ((wc = #0) and (FBuf >= FBufEnd))) and
  2456. (wc <> #9)) or (wc > #$FFFD) or
  2457. (FXML11Rules and (wc >= #$7F) and (wc <= #$9F)) then
  2458. FReader.FatalError('Invalid character');
  2459. if (wc < #255) and (Char(ord(wc)) in Delim) then
  2460. Break;
  2461. // the checks above filter away everything below #32 that isn't a whitespace
  2462. if wc > #32 then
  2463. nonws := True;
  2464. Inc(FBuf);
  2465. until False;
  2466. Result := wc;
  2467. BufAppendChunk(ToFill, old, FBuf);
  2468. until (Result <> #0) or (not Reload);
  2469. if Assigned(wsflag) then
  2470. wsflag^ := wsflag^ or nonws;
  2471. end;
  2472. const
  2473. TextDelims: array[Boolean] of TSetOfChar = (
  2474. [#0, '<', '&', '>'],
  2475. [#0, '>']
  2476. );
  2477. textNodeTypes: array[Boolean] of TXMLNodeType = (
  2478. ntText,
  2479. ntWhitespace
  2480. );
  2481. procedure TXMLReader.ParseContent;
  2482. begin
  2483. FNext := xtText;
  2484. while Read do
  2485. begin
  2486. case FToken of
  2487. xtText, xtWhitespace:
  2488. DoText(FValue.Buffer, FValue.Length, FToken = xtWhitespace);
  2489. xtCDSect:
  2490. DoCDSect(FValue.Buffer, FValue.Length);
  2491. xtPI:
  2492. CreatePINode;
  2493. xtComment:
  2494. DoComment(FCurrNode^.FValueStart, FCurrNode^.FValueLength);
  2495. xtEndElement:
  2496. DoEndElement;
  2497. xtDoctype:
  2498. if not FCanonical then
  2499. begin
  2500. doc.AppendChild(FDocType);
  2501. FOwnsDoctype := False;
  2502. end;
  2503. end;
  2504. end;
  2505. end;
  2506. function TXMLReader.Read: Boolean;
  2507. var
  2508. nonWs: Boolean;
  2509. wc: WideChar;
  2510. InCDATA: Boolean;
  2511. tok: TXMLToken;
  2512. begin
  2513. if FNext = xtPopEmptyElement then
  2514. begin
  2515. FNext := xtPopElement;
  2516. FToken := xtEndElement;
  2517. FCurrNode^.FNodeType := ntEndElement;
  2518. if FAttrCleanupFlag then
  2519. CleanupAttributeData;
  2520. FAttrCount := 0;
  2521. Result := True;
  2522. Exit;
  2523. end;
  2524. if FNext = xtPushElement then
  2525. begin
  2526. if FAttrCleanupFlag then
  2527. CleanupAttributeData;
  2528. FAttrCount := 0;
  2529. FNext := xtText;
  2530. end
  2531. else if FNext = xtPopElement then
  2532. begin
  2533. if FNamespaces then
  2534. FNSHelper.EndElement;
  2535. PopVC;
  2536. FNext := xtText;
  2537. end;
  2538. InCDATA := (FNext = xtCDSect);
  2539. StoreLocation(FTokenStart);
  2540. nonWs := False;
  2541. FValue.Length := 0;
  2542. if FNext in [xtCDSect, xtText] then
  2543. repeat
  2544. wc := FSource.SkipUntil(FValue, TextDelims[InCDATA], @nonWs);
  2545. if wc = '<' then
  2546. begin
  2547. Inc(FSource.FBuf);
  2548. if FSource.FBufEnd < FSource.FBuf + 2 then
  2549. FSource.Reload;
  2550. if FSource.FBuf^ = '/' then
  2551. tok := xtEndElement
  2552. else if CheckName([cnOptional]) then
  2553. tok := xtElement
  2554. else if FSource.FBuf^ = '!' then
  2555. begin
  2556. Inc(FSource.FBuf);
  2557. if FSource.FBuf^ = '[' then
  2558. begin
  2559. ExpectString('[CDATA[');
  2560. if FState <> rsRoot then
  2561. FatalError('Illegal at document level');
  2562. StoreLocation(FTokenStart);
  2563. InCDATA := True;
  2564. if FCDSectionsAsText or (FValue.Length = 0) then
  2565. Continue;
  2566. tok := xtCDSect;
  2567. end
  2568. else if FSource.FBuf^ = '-' then
  2569. begin
  2570. { Ignoring comments is tricky in validating mode; discarding a comment which
  2571. is the only child of an EMPTY element will make that element erroneously appear
  2572. as valid. Therefore, at this point we discard only comments which are preceded
  2573. by some text (since presence of text already renders an EMPTY element invalid).
  2574. Other comments should be reported to validation part and discarded there. }
  2575. if FIgnoreComments and (FValue.Length > 0) then
  2576. begin
  2577. ParseComment(True);
  2578. Continue;
  2579. end;
  2580. tok := xtComment;
  2581. end
  2582. else
  2583. tok := xtDoctype;
  2584. end
  2585. else if FSource.FBuf^ = '?' then
  2586. tok := xtPI
  2587. else
  2588. RaiseNameNotFound;
  2589. end
  2590. else if wc = #0 then
  2591. begin
  2592. if InCDATA then
  2593. FatalError('Unterminated CDATA section', -1);
  2594. if FNesting > FSource.FStartNesting then
  2595. FatalError('End-tag is missing for ''%s''', [FNodeStack[FNesting].FQName^.Key]);
  2596. if ContextPop then Continue;
  2597. tok := xtEOF;
  2598. end
  2599. else if wc = '>' then
  2600. begin
  2601. BufAppend(FValue, wc);
  2602. FSource.NextChar;
  2603. if (FValue.Length <= 2) or (FValue.Buffer[FValue.Length-2] <> ']') or
  2604. (FValue.Buffer[FValue.Length-3] <> ']') then Continue;
  2605. if InCData then // got a ']]>' separator
  2606. begin
  2607. Dec(FValue.Length, 3);
  2608. InCDATA := False;
  2609. if FCDSectionsAsText then
  2610. Continue;
  2611. SetNodeInfoWithValue(ntCDATA);
  2612. FToken := xtCDSect;
  2613. FNext := xtText;
  2614. Result := True;
  2615. Exit;
  2616. end
  2617. else
  2618. FatalError('Literal '']]>'' is not allowed in text', 3);
  2619. end
  2620. else if wc = '&' then
  2621. begin
  2622. if FState <> rsRoot then
  2623. FatalError('Illegal at document level');
  2624. if FCurrContentType = ctEmpty then
  2625. ValidationError('References are illegal in EMPTY elements', []);
  2626. if ParseRef(FValue) or ResolvePredefined then
  2627. begin
  2628. nonWs := True; // CharRef to whitespace is not considered whitespace
  2629. Continue;
  2630. end
  2631. else
  2632. begin
  2633. FCurrEntity := EntityCheck;
  2634. if Assigned(FCurrEntity) and FExpandEntities then
  2635. begin
  2636. ContextPush(FCurrEntity);
  2637. Continue;
  2638. end;
  2639. tok := xtEntity;
  2640. end;
  2641. end;
  2642. if FValue.Length <> 0 then
  2643. begin
  2644. SetNodeInfoWithValue(textNodeTypes[nonWs]);
  2645. if nonWs then FToken := xtText else FToken := xtWhitespace;
  2646. FNext := tok;
  2647. Result := True;
  2648. Exit;
  2649. end;
  2650. Break;
  2651. until False
  2652. else // not (FNext in [xtText, xtCDSect])
  2653. tok := FNext;
  2654. FToken := tok;
  2655. FNext := xtText;
  2656. case tok of
  2657. xtEntity: AppendReference(FCursorStack[FNesting], FCurrEntity);
  2658. xtElement: ParseStartTag;
  2659. xtEndElement: ParseEndTag;
  2660. xtPI: ParsePI;
  2661. xtDoctype: ParseDoctypeDecl;
  2662. xtComment: ParseComment(False);
  2663. end;
  2664. Result := tok <> xtEOF;
  2665. end;
  2666. procedure TXMLCharSource.NextChar;
  2667. begin
  2668. Inc(FBuf);
  2669. if FBuf >= FBufEnd then
  2670. Reload;
  2671. end;
  2672. procedure TXMLReader.ExpectChar(wc: WideChar);
  2673. begin
  2674. if FSource.FBuf^ = wc then
  2675. FSource.NextChar
  2676. else
  2677. FatalError(wc);
  2678. end;
  2679. // Element name already in FNameBuffer
  2680. procedure TXMLReader.ParseStartTag; // [39] [40] [44]
  2681. var
  2682. NewElem: TDOMElement;
  2683. Attr: TDOMAttr;
  2684. ElDef: TElementDecl;
  2685. IsEmpty: Boolean;
  2686. ElName: PHashItem;
  2687. b: TBinding;
  2688. i: Integer;
  2689. begin
  2690. if FState > rsRoot then
  2691. FatalError('Only one top-level element allowed', FName.Length)
  2692. else if FState < rsRoot then
  2693. begin
  2694. if FValidate then
  2695. ValidateRoot;
  2696. FState := rsRoot;
  2697. end;
  2698. // we're about to process a new set of attributes
  2699. Inc(FAttrTag);
  2700. NewElem := doc.CreateElementBuf(FName.Buffer, FName.Length);
  2701. FCursorStack[FNesting].InternalAppend(NewElem);
  2702. // Remember the hash entry, we'll need it often
  2703. ElName := NewElem.NSI.QName;
  2704. // Find declaration for this element
  2705. ElDef := TElementDecl(ElName^.Data);
  2706. if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then
  2707. ValidationError('Using undeclared element ''%s''',[ElName^.Key], FName.Length);
  2708. // Check if new element is allowed in current context
  2709. if FValidate and not FValidators[FNesting].IsElementAllowed(ElDef) then
  2710. ValidationError('Element ''%s'' is not allowed in this context',[ElName^.Key], FName.Length);
  2711. IsEmpty := False;
  2712. FAttrCount := 0;
  2713. FPrefixedAttrs := 0;
  2714. PushVC(ElDef); // this increases FNesting
  2715. FCursorStack[FNesting] := NewElem;
  2716. FCurrNode^.FQName := ElName;
  2717. FCurrNode^.FNodeType := ntElement;
  2718. FCurrNode^.FColonPos := FColonPos;
  2719. if FNamespaces then
  2720. begin
  2721. FNSHelper.StartElement;
  2722. if FColonPos > 0 then
  2723. FCurrNode^.FPrefix := FNSHelper.GetPrefix(FName.Buffer, FColonPos);
  2724. end;
  2725. while (FSource.FBuf^ <> '>') and (FSource.FBuf^ <> '/') do
  2726. begin
  2727. SkipS(True);
  2728. if (FSource.FBuf^ = '>') or (FSource.FBuf^ = '/') then
  2729. Break;
  2730. ParseAttribute(ElDef);
  2731. end;
  2732. if FSource.FBuf^ = '/' then
  2733. begin
  2734. IsEmpty := True;
  2735. FSource.NextChar;
  2736. end;
  2737. ExpectChar('>');
  2738. if Assigned(ElDef) and ElDef.NeedsDefaultPass then
  2739. ProcessDefaultAttributes(ElDef);
  2740. // Adding attributes might have reallocated FNodeStack, so restore FCurrNode once again
  2741. FCurrNode := @FNodeStack[FNesting];
  2742. if FNamespaces then
  2743. begin
  2744. { Assign namespace URIs to prefixed attrs }
  2745. ProcessNamespaceAtts;
  2746. { Expand the element name }
  2747. if Assigned(FCurrNode^.FPrefix) then
  2748. begin
  2749. b := TBinding(FCurrNode^.FPrefix^.Data);
  2750. if not (Assigned(b) and (b.uri <> '')) then
  2751. FatalError('Unbound prefix "%s"', [FCurrNode^.FPrefix^.Key]);
  2752. FCurrNode^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
  2753. NewElem.SetNSI(b.uri, FCurrNode^.FColonPos+1);
  2754. end
  2755. else
  2756. begin
  2757. b := FNSHelper.DefaultNSBinding;
  2758. if Assigned(b) then
  2759. begin
  2760. FCurrNode^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
  2761. NewElem.SetNSI(b.uri, FCurrNode^.FColonPos+1);
  2762. end;
  2763. end;
  2764. end;
  2765. for i := 1 to FAttrCount do
  2766. begin
  2767. Attr := LoadAttribute(doc, @FNodeStack[FNesting+i]);
  2768. NewElem.SetAttributeNode(Attr);
  2769. ValidateAttrValue(Attr, FNodeStack[FNesting+i].FValueStr);
  2770. end;
  2771. if not IsEmpty then
  2772. begin
  2773. if not FPreserveWhitespace then // critical for testsuite compliance
  2774. SkipS;
  2775. FNext := xtPushElement;
  2776. end
  2777. else
  2778. FNext := xtPopEmptyElement;
  2779. end;
  2780. procedure TXMLReader.DoEndElement;
  2781. begin
  2782. if (FNesting = 1) and (FCursorStack[0] = doc) then
  2783. FState := rsEpilog;
  2784. if FValidate and FValidators[FNesting].Incomplete then
  2785. ValidationError('Element ''%s'' is missing required sub-elements', [FNodeStack[FNesting].FQName^.Key], -1);
  2786. end;
  2787. procedure TXMLReader.ParseEndTag; // [42]
  2788. var
  2789. ElName: PHashItem;
  2790. begin
  2791. if FNesting <= FSource.FStartNesting then
  2792. FatalError('End-tag is not allowed here');
  2793. Inc(FSource.FBuf);
  2794. FCurrNode := @FNodeStack[FNesting]; // move off the possible child
  2795. FCurrNode^.FNodeType := ntEndElement;
  2796. ElName := FCurrNode^.FQName;
  2797. CheckName;
  2798. if not BufEquals(FName, ElName^.Key) then
  2799. FatalError('Unmatching element end tag (expected "</%s>")', [ElName^.Key], FName.Length);
  2800. if FSource.FBuf^ = '>' then // this handles majority of cases
  2801. FSource.NextChar
  2802. else
  2803. begin
  2804. SkipS;
  2805. ExpectChar('>');
  2806. end;
  2807. Inc(FTokenStart.LinePos, 2); // move over '</' chars
  2808. FNext := xtPopElement;
  2809. end;
  2810. procedure TXMLReader.ParseAttribute(ElDef: TElementDecl);
  2811. var
  2812. attrName: PHashItem;
  2813. attrData: PNodeData;
  2814. AttDef: TAttributeDef;
  2815. i: Integer;
  2816. normalized: Boolean;
  2817. procedure CheckValue;
  2818. begin
  2819. // TODO: what about normalization of AttDef.Value? (Currently it IS normalized)
  2820. if (AttDef.Default = adFixed) and (AttDef.Data^.FValueStr <> attrData^.FValueStr) then
  2821. ValidationError('Value of attribute ''%s'' does not match its #FIXED default',[attrData^.FQName^.Key], -1);
  2822. if not ValidateAttrSyntax(AttDef, attrData^.FValueStr) then
  2823. ValidationError('Attribute ''%s'' type mismatch', [attrData^.FQName^.Key], -1);
  2824. // ValidateAttrValue(Attr, attrData^.FValueStr);
  2825. end;
  2826. begin
  2827. CheckName;
  2828. attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
  2829. attrData := AllocAttributeData(attrName);
  2830. attrData^.FColonPos := FColonPos;
  2831. if Assigned(ElDef) then
  2832. begin
  2833. AttDef := ElDef.GetAttrDef(attrName);
  2834. if AttDef = nil then
  2835. ValidationError('Using undeclared attribute ''%s'' on element ''%s''',
  2836. [attrName^.Key, FNodeStack[FNesting].FQName^.Key], FName.Length)
  2837. else
  2838. AttDef.Tag := FAttrTag; // indicates that this one is specified
  2839. end
  2840. else
  2841. AttDef := nil;
  2842. attrData^.FTypeInfo := AttDef;
  2843. // check for duplicates
  2844. for i := 1 to FAttrCount-1 do
  2845. if FNodeStack[FNesting+i].FQName = attrName then
  2846. FatalError('Duplicate attribute', FName.Length);
  2847. if FNamespaces then
  2848. begin
  2849. if ((FName.Length = 5) or (FColonPos = 5)) and
  2850. (FName.Buffer[0] = 'x') and (FName.Buffer[1] = 'm') and
  2851. (FName.Buffer[2] = 'l') and (FName.Buffer[3] = 'n') and
  2852. (FName.Buffer[4] = 's') then
  2853. begin
  2854. if FColonPos > 0 then
  2855. attrData^.FPrefix := FStdPrefix_xmlns;
  2856. attrData^.FNsUri := FStdUri_xmlns;
  2857. end
  2858. else if FColonPos > 0 then
  2859. begin
  2860. attrData^.FPrefix := FNSHelper.GetPrefix(FName.Buffer, FColonPos);
  2861. Inc(FPrefixedAttrs);
  2862. end;
  2863. end;
  2864. ExpectEq;
  2865. normalized := ExpectAttValue(attrData, Assigned(AttDef) and (AttDef.DataType <> dtCDATA));
  2866. if Assigned(AttDef) and ((AttDef.DataType <> dtCdata) or (AttDef.Default = adFixed)) then
  2867. begin
  2868. if normalized and FStandalone and AttDef.ExternallyDeclared then
  2869. StandaloneError(-1);
  2870. CheckValue;
  2871. end;
  2872. if Assigned(attrData^.FNsUri) then
  2873. AddBinding(attrData);
  2874. end;
  2875. procedure TXMLReader.AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer);
  2876. var
  2877. w: PForwardRef;
  2878. begin
  2879. New(w);
  2880. SetString(w^.Value, Buf, Length);
  2881. w^.Loc := FTokenStart;
  2882. aList.Add(w);
  2883. end;
  2884. procedure TXMLReader.ClearRefs(aList: TFPList);
  2885. var
  2886. I: Integer;
  2887. begin
  2888. for I := 0 to aList.Count-1 do
  2889. Dispose(PForwardRef(aList.List^[I]));
  2890. aList.Clear;
  2891. end;
  2892. procedure TXMLReader.ValidateIdRefs;
  2893. var
  2894. I: Integer;
  2895. begin
  2896. for I := 0 to FIDRefs.Count-1 do
  2897. with PForwardRef(FIDRefs.List^[I])^ do
  2898. if Doc.GetElementById(Value) = nil then
  2899. DoErrorPos(esError, Format('The ID ''%s'' does not match any element', [Value]), Loc);
  2900. ClearRefs(FIDRefs);
  2901. end;
  2902. procedure TXMLReader.ProcessDefaultAttributes(ElDef: TElementDecl);
  2903. var
  2904. I: Integer;
  2905. AttDef: TAttributeDef;
  2906. attrData: PNodeData;
  2907. begin
  2908. for I := 0 to ElDef.AttrDefCount-1 do
  2909. begin
  2910. AttDef := ElDef.AttrDefs[I];
  2911. if AttDef.Tag <> FAttrTag then // this one wasn't specified
  2912. begin
  2913. case AttDef.Default of
  2914. adDefault, adFixed: begin
  2915. if FStandalone and AttDef.ExternallyDeclared then
  2916. StandaloneError;
  2917. attrData := AllocAttributeData(nil);
  2918. attrData^ := AttDef.Data^;
  2919. if FNamespaces then
  2920. begin
  2921. if AttDef.IsNamespaceDecl then
  2922. begin
  2923. if attrData^.FColonPos > 0 then
  2924. attrData^.FPrefix := FStdPrefix_xmlns;
  2925. attrData^.FNsUri := FStdUri_xmlns;
  2926. AddBinding(attrData);
  2927. end
  2928. else if attrData^.FColonPos > 0 then
  2929. begin
  2930. attrData^.FPrefix := FNSHelper.GetPrefix(PWideChar(attrData^.FQName^.Key), attrData^.FColonPos);
  2931. Inc(FPrefixedAttrs);
  2932. end;
  2933. end;
  2934. end;
  2935. adRequired:
  2936. ValidationError('Required attribute ''%s'' of element ''%s'' is missing',
  2937. [AttDef.Data^.FQName^.Key, FNodeStack[FNesting].FQName^.Key], 0)
  2938. end;
  2939. end;
  2940. end;
  2941. end;
  2942. procedure TXMLReader.AddBinding(attrData: PNodeData);
  2943. var
  2944. nsUri, Pfx: PHashItem;
  2945. begin
  2946. nsUri := doc.Names.FindOrAdd(PWideChar(attrData^.FValueStr), Length(attrData^.FValueStr));
  2947. if attrData^.FColonPos > 0 then
  2948. Pfx := FNSHelper.GetPrefix(@attrData^.FQName^.key[7], Length(attrData^.FQName^.key)-6)
  2949. else
  2950. Pfx := FNSHelper.GetPrefix(nil, 0); { will return the default prefix }
  2951. { 'xml' is allowed to be bound to the correct namespace }
  2952. if ((nsUri = FStduri_xml) <> (Pfx = FStdPrefix_xml)) or
  2953. (Pfx = FStdPrefix_xmlns) or
  2954. (nsUri = FStduri_xmlns) then
  2955. begin
  2956. if (Pfx = FStdPrefix_xml) or (Pfx = FStdPrefix_xmlns) then
  2957. FatalError('Illegal usage of reserved prefix ''%s''', [Pfx^.Key])
  2958. else
  2959. FatalError('Illegal usage of reserved namespace URI ''%s''', [attrData^.FValueStr]);
  2960. end;
  2961. if (attrData^.FValueStr = '') and not (FXML11 or (Pfx^.Key = '')) then
  2962. FatalError('Illegal undefining of namespace'); { position - ? }
  2963. FNSHelper.BindPrefix(attrData^.FValueStr, Pfx);
  2964. end;
  2965. procedure TXMLReader.ProcessNamespaceAtts;
  2966. var
  2967. I, J: Integer;
  2968. Pfx, AttrName: PHashItem;
  2969. attrData: PNodeData;
  2970. b: TBinding;
  2971. begin
  2972. if FPrefixedAttrs = 0 then
  2973. Exit;
  2974. FNsAttHash.Init(FPrefixedAttrs);
  2975. for I := 1 to FAttrCount do
  2976. begin
  2977. attrData := @FNodeStack[FNesting+i];
  2978. if (attrData^.FColonPos < 1) or Assigned(attrData^.FNsUri) then
  2979. Continue;
  2980. Pfx := attrData^.FPrefix;
  2981. b := TBinding(Pfx^.Data);
  2982. if not (Assigned(b) and (b.uri <> '')) then
  2983. FatalError('Unbound prefix "%s"', [Pfx^.Key]);
  2984. { detect duplicates }
  2985. J := attrData^.FColonPos+1;
  2986. AttrName := attrData^.FQName;
  2987. if FNsAttHash.Locate(@b.uri, @AttrName^.Key[J], Length(AttrName^.Key) - J+1) then
  2988. FatalError('Duplicate prefixed attribute');
  2989. attrData^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
  2990. end;
  2991. end;
  2992. function TXMLReader.ParseExternalID(out SysID, PubID: WideString; // [75]
  2993. SysIdOptional: Boolean): Boolean;
  2994. var
  2995. I: Integer;
  2996. wc: WideChar;
  2997. begin
  2998. Result := False;
  2999. if FSource.Matches('SYSTEM') then
  3000. SysIdOptional := False
  3001. else if FSource.Matches('PUBLIC') then
  3002. begin
  3003. ExpectWhitespace;
  3004. ParseLiteral(FValue, ltPubid, True);
  3005. SetString(PubID, FValue.Buffer, FValue.Length);
  3006. for I := 1 to Length(PubID) do
  3007. begin
  3008. wc := PubID[I];
  3009. if (wc > #255) or not (Char(ord(wc)) in PubidChars) then
  3010. FatalError('Illegal Public ID literal', -1);
  3011. end;
  3012. end
  3013. else
  3014. Exit;
  3015. if SysIdOptional then
  3016. SkipWhitespace
  3017. else
  3018. ExpectWhitespace;
  3019. if ParseLiteral(FValue, ltPlain, not SysIdOptional) then
  3020. SetString(SysID, FValue.Buffer, FValue.Length);
  3021. Result := True;
  3022. end;
  3023. function TXMLReader.ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: WideString): Boolean;
  3024. begin
  3025. case AttrDef.DataType of
  3026. dtId, dtIdRef, dtEntity: Result := IsXmlName(aValue, FXML11) and
  3027. ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
  3028. dtIdRefs, dtEntities: Result := IsXmlNames(aValue, FXML11) and
  3029. ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
  3030. dtNmToken: Result := IsXmlNmToken(aValue, FXML11) and AttrDef.HasEnumToken(aValue);
  3031. dtNmTokens: Result := IsXmlNmTokens(aValue, FXML11);
  3032. // IsXmlName() not necessary - enum is never empty and contains valid names
  3033. dtNotation: Result := AttrDef.HasEnumToken(aValue);
  3034. else
  3035. Result := True;
  3036. end;
  3037. end;
  3038. procedure TXMLReader.ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
  3039. var
  3040. L, StartPos, EndPos: Integer;
  3041. Entity: TDOMEntity;
  3042. begin
  3043. L := Length(aValue);
  3044. case Attr.DataType of
  3045. dtId: if not Doc.AddID(Attr) then
  3046. ValidationError('The ID ''%s'' is not unique', [aValue], -1);
  3047. dtIdRef, dtIdRefs: begin
  3048. StartPos := 1;
  3049. while StartPos <= L do
  3050. begin
  3051. EndPos := StartPos;
  3052. while (EndPos <= L) and (aValue[EndPos] <> #32) do
  3053. Inc(EndPos);
  3054. AddForwardRef(FIDRefs, @aValue[StartPos], EndPos-StartPos);
  3055. StartPos := EndPos + 1;
  3056. end;
  3057. end;
  3058. dtEntity, dtEntities: begin
  3059. StartPos := 1;
  3060. while StartPos <= L do
  3061. begin
  3062. EndPos := StartPos;
  3063. while (EndPos <= L) and (aValue[EndPos] <> #32) do
  3064. Inc(EndPos);
  3065. if Assigned(FGEMap) then
  3066. Entity := TDOMEntity(FGEMap.Get(@aValue[StartPos], EndPos-StartPos))
  3067. else
  3068. Entity := nil;
  3069. if (Entity = nil) or (Entity.NotationName = '') then
  3070. ValidationError('Attribute ''%s'' type mismatch', [Attr.Name], -1);
  3071. StartPos := EndPos + 1;
  3072. end;
  3073. end;
  3074. end;
  3075. end;
  3076. procedure TXMLReader.ValidateRoot;
  3077. begin
  3078. if Assigned(FDocType) then
  3079. begin
  3080. if not BufEquals(FName, FDocType.Name) then
  3081. ValidationError('Root element name does not match DTD', [], FName.Length);
  3082. end
  3083. else
  3084. ValidationError('Missing DTD', [], FName.Length);
  3085. end;
  3086. procedure TXMLReader.ValidateDTD;
  3087. var
  3088. I: Integer;
  3089. begin
  3090. if FValidate then
  3091. for I := 0 to FNotationRefs.Count-1 do
  3092. with PForwardRef(FNotationRefs[I])^ do
  3093. if FDocType.Notations.GetNamedItem(Value) = nil then
  3094. DoErrorPos(esError, Format('Notation ''%s'' is not declared', [Value]), Loc);
  3095. ClearRefs(FNotationRefs);
  3096. end;
  3097. procedure TXMLReader.DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean);
  3098. var
  3099. TextNode: TDOMText;
  3100. begin
  3101. if FState <> rsRoot then
  3102. if not Whitespace then
  3103. FatalError('Illegal at document level', -1)
  3104. else
  3105. Exit;
  3106. if (Whitespace and (not FPreserveWhitespace)) or (Count = 0) then
  3107. Exit;
  3108. // Validating filter part
  3109. case FCurrContentType of
  3110. ctChildren:
  3111. if not Whitespace then
  3112. ValidationError('Character data is not allowed in element-only content',[])
  3113. else
  3114. if FSaViolation then
  3115. StandaloneError(-1);
  3116. ctEmpty:
  3117. ValidationError('Character data is not allowed in EMPTY elements', []);
  3118. end;
  3119. // Document builder part
  3120. TextNode := Doc.CreateTextNodeBuf(ch, Count, Whitespace and (FCurrContentType = ctChildren));
  3121. FCursorStack[FNesting].InternalAppend(TextNode);
  3122. end;
  3123. procedure TXMLReader.DoComment(ch: PWideChar; Count: Integer);
  3124. var
  3125. Node: TDOMComment;
  3126. begin
  3127. // validation filter part
  3128. if FCurrContentType = ctEmpty then
  3129. ValidationError('Comments are not allowed within EMPTY elements', []);
  3130. // DOM builder part
  3131. if (not FIgnoreComments) and (FState <> rsDTD) then
  3132. begin
  3133. Node := Doc.CreateCommentBuf(ch, Count);
  3134. FCursorStack[FNesting].InternalAppend(Node);
  3135. end;
  3136. end;
  3137. procedure TXMLReader.DoCDSect(ch: PWideChar; Count: Integer);
  3138. var
  3139. s: WideString;
  3140. begin
  3141. Assert(not FCDSectionsAsText, 'Should not be called when CDSectionsAsText=True');
  3142. if FCurrContentType = ctChildren then
  3143. ValidationError('CDATA sections are not allowed in element-only content',[]);
  3144. SetString(s, ch, Count);
  3145. FCursorStack[FNesting].InternalAppend(doc.CreateCDATASection(s));
  3146. end;
  3147. procedure TXMLReader.DoNotationDecl(const aName, aPubID, aSysID: WideString);
  3148. var
  3149. Notation: TDOMNotationEx;
  3150. begin
  3151. if FDocType.Notations.GetNamedItem(aName) = nil then
  3152. begin
  3153. Notation := TDOMNotationEx(TDOMNotation.Create(doc));
  3154. Notation.FName := aName;
  3155. Notation.FPublicID := aPubID;
  3156. Notation.FSystemID := aSysID;
  3157. FDocType.Notations.SetNamedItem(Notation);
  3158. end
  3159. else
  3160. ValidationError('Duplicate notation declaration: ''%s''', [aName]);
  3161. end;
  3162. function TXMLReader.AllocAttributeData(AName: PHashItem): PNodeData;
  3163. begin
  3164. Result := AllocNodeData(FNesting + FAttrCount + 1);
  3165. Result^.FNodeType := ntAttribute;
  3166. Result^.FQName := AName;
  3167. Result^.FPrefix := nil;
  3168. Result^.FNsUri := nil;
  3169. Result^.FIsDefault := False;
  3170. Inc(FAttrCount);
  3171. end;
  3172. function TXMLReader.AllocNodeData(AIndex: Integer): PNodeData;
  3173. begin
  3174. {make sure we have an extra slot to place child text/comment/etc}
  3175. if AIndex >= Length(FNodeStack)-1 then
  3176. SetLength(FNodeStack, AIndex * 2 + 2);
  3177. Result := @FNodeStack[AIndex];
  3178. end;
  3179. function TXMLReader.AllocAttributeValueChunk(APrev: PNodeData): PNodeData;
  3180. begin
  3181. { when parsing DTD, don't take ownership of allocated data }
  3182. if FState = rsDTD then
  3183. begin
  3184. New(result);
  3185. FillChar(result^, sizeof(TNodeData), 0);
  3186. end
  3187. else
  3188. begin
  3189. result := FFreeAttrChunk;
  3190. if Assigned(result) then
  3191. begin
  3192. FFreeAttrChunk := result^.FNext;
  3193. result^.FNext := nil;
  3194. end
  3195. else { no free chunks, create a new one }
  3196. begin
  3197. New(result);
  3198. FillChar(result^, sizeof(TNodeData), 0);
  3199. FAttrChunks.Add(result);
  3200. end;
  3201. end;
  3202. APrev^.FNext := result;
  3203. end;
  3204. procedure TXMLReader.CleanupAttributeData;
  3205. var
  3206. i: Integer;
  3207. chunk, tmp: PNodeData;
  3208. begin
  3209. for i := 1 to FAttrCount do
  3210. begin
  3211. chunk := FNodeStack[FNesting+i].FNext;
  3212. {don't unlink chunks of default attributes, they are owned by DTD}
  3213. if not FNodeStack[FNesting+i].FIsDefault then
  3214. while Assigned(chunk) do
  3215. begin
  3216. tmp := chunk^.FNext;
  3217. chunk^.FNext := FFreeAttrChunk;
  3218. FFreeAttrChunk := chunk;
  3219. chunk := tmp;
  3220. end;
  3221. FNodeStack[FNesting+i].FNext := nil;
  3222. end;
  3223. FAttrCleanupFlag := False;
  3224. end;
  3225. procedure TXMLReader.SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
  3226. begin
  3227. {FNesting+1 is available due to overallocation in AllocNodeData() }
  3228. FCurrNode := @FNodeStack[FNesting+1];
  3229. FCurrNode^.FNodeType := typ;
  3230. FCurrNode^.FQName := AName;
  3231. FCurrNode^.FValueStart := FValue.Buffer;
  3232. FCurrNode^.FValueLength := FValue.Length;
  3233. end;
  3234. procedure TXMLReader.PushVC(aElDef: TElementDecl);
  3235. begin
  3236. Inc(FNesting);
  3237. FCurrNode := AllocNodeData(FNesting);
  3238. FCurrNode^.FPrefix := nil;
  3239. FCurrNode^.FNsUri := nil;
  3240. if FNesting >= Length(FCursorStack) then
  3241. begin
  3242. SetLength(FCursorStack, FNesting * 2);
  3243. SetLength(FValidators, FNesting * 2);
  3244. end;
  3245. FValidators[FNesting].FElementDef := aElDef;
  3246. FValidators[FNesting].FCurCP := nil;
  3247. FValidators[FNesting].FFailed := False;
  3248. UpdateConstraints;
  3249. end;
  3250. procedure TXMLReader.PopVC;
  3251. begin
  3252. if FNesting > 0 then Dec(FNesting);
  3253. FCurrNode := @FNodeStack[FNesting];
  3254. UpdateConstraints;
  3255. end;
  3256. procedure TXMLReader.UpdateConstraints;
  3257. begin
  3258. if FValidate and Assigned(FValidators[FNesting].FElementDef) then
  3259. begin
  3260. FCurrContentType := FValidators[FNesting].FElementDef.ContentType;
  3261. FSaViolation := FStandalone and (FValidators[FNesting].FElementDef.ExternallyDeclared);
  3262. end
  3263. else
  3264. begin
  3265. FCurrContentType := ctAny;
  3266. FSaViolation := False;
  3267. end;
  3268. end;
  3269. { TElementValidator }
  3270. function TElementValidator.IsElementAllowed(Def: TElementDecl): Boolean;
  3271. var
  3272. Next: TContentParticle;
  3273. begin
  3274. Result := True;
  3275. // if element is not declared, non-validity has been already reported, no need to report again...
  3276. if Assigned(Def) and Assigned(FElementDef) then
  3277. begin
  3278. case FElementDef.ContentType of
  3279. ctEmpty: Result := False;
  3280. ctChildren, ctMixed: begin
  3281. if FFailed then // if already detected a mismatch, don't waste time
  3282. Exit;
  3283. if FCurCP = nil then
  3284. Next := FElementDef.RootCP.FindFirst(Def)
  3285. else
  3286. Next := FCurCP.FindNext(Def, 0); { second arg ignored here }
  3287. Result := Assigned(Next);
  3288. if Result then
  3289. FCurCP := Next
  3290. else
  3291. FFailed := True; // used to prevent extra error at the end of element
  3292. end;
  3293. // ctAny, ctUndeclared: returns True by default
  3294. end;
  3295. end;
  3296. end;
  3297. function TElementValidator.Incomplete: Boolean;
  3298. begin
  3299. if Assigned(FElementDef) and (FElementDef.ContentType = ctChildren) and (not FFailed) then
  3300. begin
  3301. if FCurCP <> nil then
  3302. Result := FCurCP.MoreRequired(0) { arg ignored here }
  3303. else
  3304. Result := FElementDef.RootCP.IsRequired;
  3305. end
  3306. else
  3307. Result := False;
  3308. end;
  3309. { plain calls }
  3310. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text);
  3311. var
  3312. Reader: TXMLReader;
  3313. Src: TXMLCharSource;
  3314. begin
  3315. ADoc := nil;
  3316. Src := TXMLFileInputSource.Create(f);
  3317. Reader := TXMLReader.Create;
  3318. try
  3319. Reader.ProcessXML(Src);
  3320. finally
  3321. ADoc := TXMLDocument(Reader.Doc);
  3322. Reader.Free;
  3323. end;
  3324. end;
  3325. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String);
  3326. var
  3327. Reader: TXMLReader;
  3328. Src: TXMLCharSource;
  3329. begin
  3330. ADoc := nil;
  3331. Reader := TXMLReader.Create;
  3332. try
  3333. Src := TXMLStreamInputSource.Create(f, False);
  3334. Src.SystemID := ABaseURI;
  3335. Reader.ProcessXML(Src);
  3336. finally
  3337. ADoc := TXMLDocument(Reader.doc);
  3338. Reader.Free;
  3339. end;
  3340. end;
  3341. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream);
  3342. begin
  3343. ReadXMLFile(ADoc, f, 'stream:');
  3344. end;
  3345. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String);
  3346. var
  3347. FileStream: TStream;
  3348. begin
  3349. ADoc := nil;
  3350. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3351. try
  3352. ReadXMLFile(ADoc, FileStream, FilenameToURI(AFilename));
  3353. finally
  3354. FileStream.Free;
  3355. end;
  3356. end;
  3357. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text);
  3358. var
  3359. Reader: TXMLReader;
  3360. Src: TXMLCharSource;
  3361. begin
  3362. Reader := TXMLReader.Create;
  3363. try
  3364. Src := TXMLFileInputSource.Create(f);
  3365. Reader.ProcessFragment(Src, AParentNode);
  3366. finally
  3367. Reader.Free;
  3368. end;
  3369. end;
  3370. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream; const ABaseURI: String);
  3371. var
  3372. Reader: TXMLReader;
  3373. Src: TXMLCharSource;
  3374. begin
  3375. Reader := TXMLReader.Create;
  3376. try
  3377. Src := TXMLStreamInputSource.Create(f, False);
  3378. Src.SystemID := ABaseURI;
  3379. Reader.ProcessFragment(Src, AParentNode);
  3380. finally
  3381. Reader.Free;
  3382. end;
  3383. end;
  3384. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream);
  3385. begin
  3386. ReadXMLFragment(AParentNode, f, 'stream:');
  3387. end;
  3388. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  3389. var
  3390. Stream: TStream;
  3391. begin
  3392. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3393. try
  3394. ReadXMLFragment(AParentNode, Stream, FilenameToURI(AFilename));
  3395. finally
  3396. Stream.Free;
  3397. end;
  3398. end;
  3399. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text);
  3400. var
  3401. Reader: TXMLReader;
  3402. Src: TXMLCharSource;
  3403. begin
  3404. ADoc := nil;
  3405. Reader := TXMLReader.Create;
  3406. try
  3407. Src := TXMLFileInputSource.Create(f);
  3408. Reader.ProcessDTD(Src);
  3409. finally
  3410. ADoc := TXMLDocument(Reader.doc);
  3411. Reader.Free;
  3412. end;
  3413. end;
  3414. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String);
  3415. var
  3416. Reader: TXMLReader;
  3417. Src: TXMLCharSource;
  3418. begin
  3419. ADoc := nil;
  3420. Reader := TXMLReader.Create;
  3421. try
  3422. Src := TXMLStreamInputSource.Create(f, False);
  3423. Src.SystemID := ABaseURI;
  3424. Reader.ProcessDTD(Src);
  3425. finally
  3426. ADoc := TXMLDocument(Reader.doc);
  3427. Reader.Free;
  3428. end;
  3429. end;
  3430. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream);
  3431. begin
  3432. ReadDTDFile(ADoc, f, 'stream:');
  3433. end;
  3434. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String);
  3435. var
  3436. Stream: TStream;
  3437. begin
  3438. ADoc := nil;
  3439. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3440. try
  3441. ReadDTDFile(ADoc, Stream, FilenameToURI(AFilename));
  3442. finally
  3443. Stream.Free;
  3444. end;
  3445. end;
  3446. end.